diff --git a/.travis.yml b/.travis.yml index b3e57791fc8..529f3ea0ae3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -328,7 +328,7 @@ script: php upgrade2.php 6.0.0 7.0.0 MAIN_MODULE_WEBSITE,MAIN_MODULE_SUPPLIERPROPOSAL > $TRAVIS_BUILD_DIR/upgrade600700-2.log php step5.php 6.0.0 7.0.0 > $TRAVIS_BUILD_DIR/upgrade600700-3.log php upgrade.php 7.0.0 8.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade700800.log - php upgrade2.php 7.0.0 8.0.0 > $TRAVIS_BUILD_DIR/upgrade700800-2.log + php upgrade2.php 7.0.0 8.0.0 MAIN_MODULE_TICKETSUP > $TRAVIS_BUILD_DIR/upgrade700800-2.log php step5.php 7.0.0 8.0.0 > $TRAVIS_BUILD_DIR/upgrade700800-3.log cd - set +e diff --git a/.tx/config b/.tx/config index 89d123b0362..a5cbac0997f 100644 --- a/.tx/config +++ b/.tx/config @@ -152,12 +152,6 @@ source_file = htdocs/langs/en_US/hrm.lang source_lang = en_US type = MOZILLAPROPERTIES -[dolibarr.incoterm] -file_filter = htdocs/langs//incoterm.lang -source_file = htdocs/langs/en_US/incoterm.lang -source_lang = en_US -type = MOZILLAPROPERTIES - [dolibarr.install] file_filter = htdocs/langs//install.lang source_file = htdocs/langs/en_US/install.lang @@ -356,6 +350,12 @@ source_file = htdocs/langs/en_US/supplier_proposal.lang source_lang = en_US type = MOZILLAPROPERTIES +[dolibarr.ticketsup] +file_filter = htdocs/langs//ticketsup.lang +source_file = htdocs/langs/en_US/ticketsup.lang +source_lang = en_US +type = MOZILLAPROPERTIES + [dolibarr.trips] file_filter = htdocs/langs//trips.lang source_file = htdocs/langs/en_US/trips.lang diff --git a/ChangeLog b/ChangeLog index 5aff4d6d3ba..610f34d669f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,7 +14,80 @@ Following changes may create regressions for some external modules, but were nec CONTRACT_SERVICE_CLOSE into LINECONTRACT_CLOSE * Remove triggers *_CLONE. The trigger CREATE with context 'createfromclone' is already called so this is a duplicated feature. Cloning is not a business event, the business event is CREATE, so no trigger required. - +* PHP 5.3 is no more supported. Minimum PHP is now 5.4+ +* Remove the old deprecated code of doActions and getInstanceDao in canvas. The doActions of standard hooks are + already available and are better. +* Removed method fetch_prods() and get_each_prod() not used, keep only get_arbo_each_prod() that is better. +* The hook contaxt commcard has been renamed thirdpartycomm +* The hook contaxt thirdpartycard has been renamed thirdpartycontact + + +***** ChangeLog for 7.0.1 compared to 7.0.0 ***** +FIX: #8139 User search does not work if MAIN_USE_OLD_SEARCH_FORM, missing list.php +FIX: #8200 +FIX: #8219 +FIX: #8232 +FIX: #8269 +FIX: #8277 +FIX: #8285 Extrafields now reported by /api/index.php/agendaevents/{id} +FIX: #8289 add a configuration for stock calculation +FIX: Activate all also if there are inactive services +FIX: add planned delivery to order exports +FIX: approval date was not visible if leave was canceled after +FIX: avoid "Array" on screen +FIX: Avoid empty value to fk_multicurrency attribute +FIX: Bad var for substitution of free text +FIX: Can't activate tasks on projects configuration +FIX: Can use odx templates that does not include lines tags +FIX: check shipping on delete order +FIX: check verif exped on delete order +FIX: comment on tasks +FIX: country must not be mandatory for accounting report groups +FIX: css +FIX: Delete tasks on project delete will now trigger TASK_DELETE +FIX: Do not lose filter when editing comment of a time spent in task view +FIX: duplicate confirm message. Missing reposition class +FIX: Duplicate product_type asignement on order addline +FIX: email use the validate user instead of approver in holiday approval +FIX: Error management in leave request +FIX: for nondisplay of fk_element 's id in REST API response +FIX: Generic substitution of constant disabled for sensitive constant +FIX: if we make a mistake with situation_percent, now we can correct it. before situation_final was always set to 1 and no way to go back +FIX: Import process must stop after ending line nb to import +FIX: Infinite loop on deletion of temp file when there is symbolic links +FIX: Input of holiday for subordinates was ko +FIX: invoice creation fails when next date not defined +FIX: Label of event show twice +FIX: letter for month March +FIX: Look and feel v7 +FIX: Make a redirect after the remove_file action to avoid deletion done +FIX: migration script for product photo +FIX: missing email of customer in stripe info payments +FIX: missing object entity in fetch +FIX: Missing restore_lastsearch_values +FIX: multicompany compatibility and fix reports +FIX: natural search double quote +FIX: navigation and filters on holiday list +FIX: Parameter must be an array or an object that implements Countable +FIX: Payment mode not correctly set in donation and document +FIX: Permission in list of holiday +FIX: Properties updated if update successfull. +FIX: reverse field to have object loaded in doaction +FIX: Saving wrong localtax on order addline +FIX: Search criteria on vat +FIX: security report by DIGITEMIS CYBERSECURITY & PRIVACY +FIX: show status on societe banner +FIX: solve column mismatch in user card with multicompany transverse mode + code cleanup +FIX: Subscription events not recorded into agenda +FIX: Subscription not correctly log in blockedlog +FIX: Temporary dir for mail files must be cleaned at beginning of form +FIX: Trad and creation date in subscription create +FIX: translation of holiday types +FIX: Unknown column 'pl.amount_requested' in compta/prelevement/factures.php +FIX: Useless clean of tree +FIX: Use of undefined constant _ROWS_2 +FIX: warning when adding ECM files using old photo path + ***** ChangeLog for 7.0.0 compared to 6.0.5 ***** For users: @@ -303,6 +376,41 @@ Following changes may create regressions for some external modules, but were nec multicompany module to a version that support Dolibarr v7, everything should work as expected. + +***** ChangeLog for 6.0.6 compared to 6.0.6 ***** +FIX: #7974 Contract - Invalid reference on the document +FIX: #8139 +FIX: #8139 User search does not work if MAIN_USE_OLD_SEARCH_FORM, missing list.php +FIX: #8151 +FIX: #8200 +FIX: add planned delivery to order exports +FIX: a discount is a percent, not an amount, so we use vatrate not price +FIX: Avoid empty value to fk_multicurrency attribute +FIX: Bad localtaxes assignment in cashdesk +FIX: check shipping on delete order +FIX: check verif exped on delete order +FIX: creer into lire +FIX: Delete tasks on project delete will now trigger TASK_DELETE +FIX: Global on $user parameter reset the variable +FIX: if we make a mistake with situation_percent, now we can correct… +FIX: if we make a mistake with situation_percent, now we can correct it. before situation_final was always set to 1 and no way to go back +FIX: Import process must stop after ending line nb to import +FIX: migration script for product photo +FIX: natural search double quote +FIX: reverse field to have object loaded in doaction +FIX: Saving wrong localtax on order addline +FIX: show status on societe banner +FIX: solve column mismatch in user card's usergroup list + code cleanup +FIX: solve column mismatch in user card with multicompany transverse mode + code cleanup +FIX: unset categorie +FIX: update_extras on fourn card +FIX: warning when adding ECM files using old photo path +FIX: Withdrawals lines not filter by company name and not respect dropdown limit lines by page +NEW: Add sale representative einstein_pdf_modules +NEW_einstein_pdf_modules +NEW: field commerciaux and categ export CustomersInvoicesAndPayments + + ***** ChangeLog for 6.0.5 compared to 6.0.4 ***** FIX: security vulnerability reported by ADLab of Venustech CVE-2017-17897, CVE-2017-17898, CVE-2017-17899, CVE-2017-17900 diff --git a/build/exe/doliwamp/doliwamp.iss b/build/exe/doliwamp/doliwamp.iss index 7dcb8c738bd..2d4668b447c 100644 --- a/build/exe/doliwamp/doliwamp.iss +++ b/build/exe/doliwamp/doliwamp.iss @@ -28,9 +28,9 @@ OutputBaseFilename=__FILENAMEEXEDOLIWAMP__ SourceDir=..\..\.. AppId=doliwamp AppPublisher=NLTechno -AppPublisherURL=http://www.nltechno.com -AppSupportURL=http://www.dolibarr.org -AppUpdatesURL=http://www.dolibarr.org +AppPublisherURL=https://www.nltechno.com +AppSupportURL=https://www.dolibarr.org +AppUpdatesURL=https://www.dolibarr.org AppComments=DoliWamp includes Dolibarr, Apache, PHP and Mysql softwares. AppCopyright=Copyright (C) 2008-2017 Laurent Destailleur (NLTechno), Fabian Rodriguez (Le Goût du Libre) DefaultDirName=c:\dolibarr @@ -100,9 +100,9 @@ Source: "build\exe\doliwamp\builddemosslfiles.bat.install"; DestDir: "{app}\"; F Source: "build\exe\doliwamp\UsedPort.exe"; DestDir: "{app}\"; Flags: ignoreversion; ; PhpMyAdmin, Apache, Php, Mysql ; Put here path of Wampserver applications -; Value OK: apache 2.2.6, php 5.2.5 (5.2.11, 5.3.0 and 5.3.1 fails if php_exif, php_pgsql, php_zip is on), mysql 5.0.45 +; Value OK: apache 2.2.6, php 5.2.5 (5.2.11, 5.3.0 and 5.3.1 fails if php_exif, php_pgsql, php_zip is on), mysql 5.0.45 ; Value OK: apache 2.2.11, php 5.3.0 (if no php_exif, php_pgsql, php_zip), mysql 5.0.45 -; Value ???: apache 2.4.19, php 5.5.12, mysql 5.0.45 instead of 5.6.17 (wampserver2.5-Apache-2.4.9-Mysql-5.6.17-php5.5.12-32b.exe) +; Value OK: apache 2.4.19, php 5.5.12, mysql 5.0.45 instead of 5.6.17 (wampserver2.5-Apache-2.4.9-Mysql-5.6.17-php5.5.12-32b.exe) Source: "C:\Program Files\Wamp\apps\phpmyadmin4.1.14\*.*"; DestDir: "{app}\apps\phpmyadmin4.1.14"; Flags: ignoreversion recursesubdirs; Excludes: "config.inc.php,wampserver.conf,*.log,*_log,darkblue_orange" Source: "C:\Program Files\Wamp\bin\apache\apache2.4.9\*.*"; DestDir: "{app}\bin\apache\apache2.4.9"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,httpd.conf,wampserver.conf,*.log,*_log" Source: "C:\Program Files\Wamp\bin\php\php5.5.12\*.*"; DestDir: "{app}\bin\php\php5.5.12"; Flags: ignoreversion recursesubdirs; Excludes: "php.ini,phpForApache.ini,wampserver.conf,*.log,*_log" diff --git a/build/generate_filelist_xml.php b/build/generate_filelist_xml.php index e6336e30e97..5f54c95d7e0 100755 --- a/build/generate_filelist_xml.php +++ b/build/generate_filelist_xml.php @@ -17,7 +17,7 @@ */ /** - * \file build/generate_filecheck_xml.php + * \file build/generate_filelist_xml.php * \ingroup dev * \brief This script create a xml checksum file */ @@ -45,7 +45,7 @@ $includeconstants=array(); if (empty($argv[1])) { - print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1] [includeconstant=CC:MY_CONF_NAME:value]\n"; + print "Usage: ".$script_file." release=auto|x.y.z[-mybuild] [includecustom=1] [includeconstant=CC:MY_CONF_NAME:value]\n"; print "Example: ".$script_file." release=6.0.0 includecustom=1 includeconstant=FR:INVOICE_CAN_ALWAYS_BE_REMOVED:0 includeconstant=all:MAILING_NO_USING_PHPMAIL:1\n"; exit -1; } @@ -68,11 +68,20 @@ while ($i < $argc) $i++; } +// If release is auto, we take current version +$tmpver=explode('-', $release, 2); +if ($tmpver[0] == 'auto') +{ + $release=DOL_VERSION; + if ($tmpver[1]) $release.='-'.$tmpver[1]; +} + if (empty($includecustom)) { - if (DOL_VERSION != $release) + $tmpver=explode('-', $release, 2); + if (DOL_VERSION != $tmpver[0]) { - print 'Error: When parameter "includecustom" is not set, version declared into filefunc.in.php ('.DOL_VERSION.') must be exact same value than "release" parameter ('.$release.')'."\n"; + print 'Error: When parameter "includecustom" is not set and there is no suffix in release parameter, version declared into filefunc.in.php ('.DOL_VERSION.') must be exact same value than "release" parameter ('.$tmpver[0].')'."\n"; print "Usage: ".$script_file." release=x.y.z[-...] [includecustom=1]\n"; exit -1; } diff --git a/build/makepack-dolibarr.pl b/build/makepack-dolibarr.pl index 839a8092a7c..77d78da1dc5 100755 --- a/build/makepack-dolibarr.pl +++ b/build/makepack-dolibarr.pl @@ -388,7 +388,7 @@ if ($nboftargetok) { #----------------------- if ($CHOOSEDTARGET{'-CHKSUM'}) { - print 'Create xml check file with md5 checksum with command php '.$SOURCE.'/build/generate_filecheck_xml.php release='.$MAJOR.'.'.$MINOR.'.'.$BUILD."\n"; + print 'Create xml check file with md5 checksum with command php '.$SOURCE.'/build/generate_filelist_xml.php release='.$MAJOR.'.'.$MINOR.'.'.$BUILD."\n"; $ret=`php $SOURCE/build/generate_filelist_xml.php release=$MAJOR.$MINOR.$BUILD`; print $ret."\n"; # Copy to final dir diff --git a/build/perl/virtualmin/dolibarr.pl b/build/perl/virtualmin/dolibarr.pl index 08d02e8b2b8..589e2a3f55c 100644 --- a/build/perl/virtualmin/dolibarr.pl +++ b/build/perl/virtualmin/dolibarr.pl @@ -1,7 +1,7 @@ #---------------------------------------------------------------------------- # \file dolibarr.pl # \brief Dolibarr script install for Virtualmin Pro -# \author (c)2009-2017 Regis Houssin +# \author (c)2009-2018 Regis Houssin #---------------------------------------------------------------------------- @@ -30,7 +30,7 @@ return "Regis Houssin"; # script_dolibarr_versions() sub script_dolibarr_versions { -return ( "5.0.4", "4.0.6", "3.9.4" ); +return ( "7.0.0", "6.0.5", "5.0.7" ); } sub script_dolibarr_release @@ -386,14 +386,16 @@ sub script_dolibarr_check_latest { local ($ver) = @_; local @vers = &osdn_package_versions("dolibarr", - $ver >= 5.0 ? "dolibarr\\-(5\\.0\\.[0-9\\.]+)\\.tgz" : - $ver >= 4.0 ? "dolibarr\\-(4\\.0\\.[0-9\\.]+)\\.tgz" : - $ver >= 3.9 ? "dolibarr\\-(3\\.9\\.[0-9\\.]+)\\.tgz" : - $ver >= 3.8 ? "dolibarr\\-(3\\.8\\.[0-9\\.]+)\\.tgz" : - $ver >= 3.7 ? "dolibarr\\-(3\\.7\\.[0-9\\.]+)\\.tgz" : - $ver >= 3.6 ? "dolibarr\\-(3\\.6\\.[0-9\\.]+)\\.tgz" : - $ver >= 3.5 ? "dolibarr\\-(3\\.5\\.[0-9\\.]+)\\.tgz" : - $ver >= 2.9 ? "dolibarr\\-(2\\.9\\.[0-9\\.]+)\\.tgz" : + $ver >= 7.0 ? "dolibarr\\-(7\\.0\\.[0-9\\.]+)\\.tgz" : + $ver >= 6.0 ? "dolibarr\\-(6\\.0\\.[0-9\\.]+)\\.tgz" : + $ver >= 5.0 ? "dolibarr\\-(5\\.0\\.[0-9\\.]+)\\.tgz" : + $ver >= 4.0 ? "dolibarr\\-(4\\.0\\.[0-9\\.]+)\\.tgz" : + $ver >= 3.9 ? "dolibarr\\-(3\\.9\\.[0-9\\.]+)\\.tgz" : + $ver >= 3.8 ? "dolibarr\\-(3\\.8\\.[0-9\\.]+)\\.tgz" : + $ver >= 3.7 ? "dolibarr\\-(3\\.7\\.[0-9\\.]+)\\.tgz" : + $ver >= 3.6 ? "dolibarr\\-(3\\.6\\.[0-9\\.]+)\\.tgz" : + $ver >= 3.5 ? "dolibarr\\-(3\\.5\\.[0-9\\.]+)\\.tgz" : + $ver >= 2.9 ? "dolibarr\\-(2\\.9\\.[0-9\\.]+)\\.tgz" : "dolibarr\\-(2\\.8\\.[0-9\\.]+)\\.tgz"); return "Failed to find versions" if (!@vers); return $ver eq $vers[0] ? undef : $vers[0]; diff --git a/build/rpm/dolibarr_fedora.spec b/build/rpm/dolibarr_fedora.spec index 6df6ccf052e..df00a982f29 100755 --- a/build/rpm/dolibarr_fedora.spec +++ b/build/rpm/dolibarr_fedora.spec @@ -176,6 +176,7 @@ done >>%{name}.lang %_datadir/dolibarr/htdocs/core %_datadir/dolibarr/htdocs/cron %_datadir/dolibarr/htdocs/custom +%_datadir/dolibarr/htdocs/dav %_datadir/dolibarr/htdocs/don %_datadir/dolibarr/htdocs/ecm %_datadir/dolibarr/htdocs/expedition diff --git a/build/rpm/dolibarr_generic.spec b/build/rpm/dolibarr_generic.spec index f99836b7f74..908a7335415 100755 --- a/build/rpm/dolibarr_generic.spec +++ b/build/rpm/dolibarr_generic.spec @@ -256,6 +256,7 @@ done >>%{name}.lang %_datadir/dolibarr/htdocs/core %_datadir/dolibarr/htdocs/cron %_datadir/dolibarr/htdocs/custom +%_datadir/dolibarr/htdocs/dav %_datadir/dolibarr/htdocs/don %_datadir/dolibarr/htdocs/ecm %_datadir/dolibarr/htdocs/expedition diff --git a/build/rpm/dolibarr_mandriva.spec b/build/rpm/dolibarr_mandriva.spec index 1034615c80a..4a07c838b78 100755 --- a/build/rpm/dolibarr_mandriva.spec +++ b/build/rpm/dolibarr_mandriva.spec @@ -173,6 +173,7 @@ done >>%{name}.lang %_datadir/dolibarr/htdocs/core %_datadir/dolibarr/htdocs/cron %_datadir/dolibarr/htdocs/custom +%_datadir/dolibarr/htdocs/dav %_datadir/dolibarr/htdocs/don %_datadir/dolibarr/htdocs/ecm %_datadir/dolibarr/htdocs/expedition diff --git a/build/rpm/dolibarr_opensuse.spec b/build/rpm/dolibarr_opensuse.spec index eb1887f229f..6f1632a57e3 100755 --- a/build/rpm/dolibarr_opensuse.spec +++ b/build/rpm/dolibarr_opensuse.spec @@ -184,6 +184,7 @@ done >>%{name}.lang %_datadir/dolibarr/htdocs/core %_datadir/dolibarr/htdocs/cron %_datadir/dolibarr/htdocs/custom +%_datadir/dolibarr/htdocs/dav %_datadir/dolibarr/htdocs/don %_datadir/dolibarr/htdocs/ecm %_datadir/dolibarr/htdocs/expedition diff --git a/dev/initdemo/initdemo.sh b/dev/initdemo/initdemo.sh index ddba2ea6fd9..2e49327ae1d 100755 --- a/dev/initdemo/initdemo.sh +++ b/dev/initdemo/initdemo.sh @@ -171,6 +171,7 @@ then cp -pr $mydir/../../htdocs/install/doctemplates/* "$documentdir/doctemplates/" mkdir -p "$documentdir/ecm/Administrative documents" mkdir -p "$documentdir/ecm/Images" + rm -f "$documentdir/doctemplates/"*/index.html echo cp -pr $mydir/../../doc/images/* "$documentdir/ecm/Images" cp -pr $mydir/../../doc/images/* "$documentdir/ecm/Images" else diff --git a/dev/initdemo/mysqldump_dolibarr_7.0.0.sql b/dev/initdemo/mysqldump_dolibarr_7.0.0.sql index 57e13bb369b..bd5789f0a6b 100644 --- a/dev/initdemo/mysqldump_dolibarr_7.0.0.sql +++ b/dev/initdemo/mysqldump_dolibarr_7.0.0.sql @@ -1,13 +1,13 @@ --- MySQL dump 10.15 Distrib 10.0.33-MariaDB, for debian-linux-gnu (x86_64) +-- MySQL dump 10.13 Distrib 5.5.59, for debian-linux-gnu (x86_64) -- --- Host: localhost Database: dolibarrdemo +-- Host: localhost Database: dolibarr_7 -- ------------------------------------------------------ --- Server version 10.0.33-MariaDB-0ubuntu0.16.04.1 +-- Server version 5.5.59-0ubuntu0.14.04.1 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; +/*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; @@ -27,23 +27,23 @@ CREATE TABLE `llx_accounting_account` ( `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `fk_pcg_version` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `pcg_type` varchar(20) NOT NULL, - `pcg_subtype` varchar(20) NOT NULL, - `account_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `account_parent` varchar(32) DEFAULT '0', - `label` varchar(255) DEFAULT NULL, + `fk_pcg_version` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `pcg_type` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `pcg_subtype` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `account_number` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `account_parent` varchar(32) COLLATE utf8_unicode_ci DEFAULT '0', + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_accounting_category` int(11) DEFAULT '0', `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_accountingaccount_fk_pcg_version` (`fk_pcg_version`), KEY `idx_accounting_account_account_number` (`account_number`), CONSTRAINT `fk_accounting_account_fk_pcg_version` FOREIGN KEY (`fk_pcg_version`) REFERENCES `llx_accounting_system` (`pcg_version`) -) ENGINE=InnoDB AUTO_INCREMENT=4785 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4785 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -52,7 +52,7 @@ CREATE TABLE `llx_accounting_account` ( LOCK TABLES `llx_accounting_account` WRITE; /*!40000 ALTER TABLE `llx_accounting_account` DISABLE KEYS */; -INSERT INTO `llx_accounting_account` VALUES (1,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','CAPITAL','101','1401','Capital',0,NULL,NULL,1,NULL,NULL),(2,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','105','1401','Ecarts de réévaluation',0,NULL,NULL,1,NULL,NULL),(3,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1061','1401','Réserve légale',0,NULL,NULL,1,NULL,NULL),(4,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1063','1401','Réserves statutaires ou contractuelles',0,NULL,NULL,1,NULL,NULL),(5,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1064','1401','Réserves réglementées',0,NULL,NULL,1,NULL,NULL),(6,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1068','1401','Autres réserves',0,NULL,NULL,1,NULL,NULL),(7,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','108','1401','Compte de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(8,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','12','1401','Résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(9,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','145','1401','Amortissements dérogatoires',0,NULL,NULL,1,NULL,NULL),(10,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','146','1401','Provision spéciale de réévaluation',0,NULL,NULL,1,NULL,NULL),(11,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','147','1401','Plus-values réinvesties',0,NULL,NULL,1,NULL,NULL),(12,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','148','1401','Autres provisions réglementées',0,NULL,NULL,1,NULL,NULL),(13,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','15','1401','Provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(14,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','16','1401','Emprunts et dettes assimilees',0,NULL,NULL,1,NULL,NULL),(15,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','20','1402','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(16,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','201','15','Frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(17,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','206','15','Droit au bail',0,NULL,NULL,1,NULL,NULL),(18,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','207','15','Fonds commercial',0,NULL,NULL,1,NULL,NULL),(19,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','208','15','Autres immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(20,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','21','1402','Immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(21,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','23','1402','Immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(22,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','27','1402','Autres immobilisations financieres',0,NULL,NULL,1,NULL,NULL),(23,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','280','1402','Amortissements des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(24,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','281','1402','Amortissements des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(25,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','290','1402','Provisions pour dépréciation des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(26,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','291','1402','Provisions pour dépréciation des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(27,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','297','1402','Provisions pour dépréciation des autres immobilisations financières',0,NULL,NULL,1,NULL,NULL),(28,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','31','1403','Matieres premières',0,NULL,NULL,1,NULL,NULL),(29,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','32','1403','Autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(30,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','33','1403','En-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(31,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','34','1403','En-cours de production de services',0,NULL,NULL,1,NULL,NULL),(32,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','35','1403','Stocks de produits',0,NULL,NULL,1,NULL,NULL),(33,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','37','1403','Stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(34,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','391','1403','Provisions pour dépréciation des matières premières',0,NULL,NULL,1,NULL,NULL),(35,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','392','1403','Provisions pour dépréciation des autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(36,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','393','1403','Provisions pour dépréciation des en-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(37,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','394','1403','Provisions pour dépréciation des en-cours de production de services',0,NULL,NULL,1,NULL,NULL),(38,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','395','1403','Provisions pour dépréciation des stocks de produits',0,NULL,NULL,1,NULL,NULL),(39,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','397','1403','Provisions pour dépréciation des stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(40,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','SUPPLIER','400','1404','Fournisseurs et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(41,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','409','1404','Fournisseurs débiteurs',0,NULL,NULL,1,NULL,NULL),(42,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','CUSTOMER','410','1404','Clients et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(43,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','419','1404','Clients créditeurs',0,NULL,NULL,1,NULL,NULL),(44,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','421','1404','Personnel',0,NULL,NULL,1,NULL,NULL),(45,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','428','1404','Personnel',0,NULL,NULL,1,NULL,NULL),(46,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','43','1404','Sécurité sociale et autres organismes sociaux',0,NULL,NULL,1,NULL,NULL),(47,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','444','1404','Etat - impôts sur bénéfice',0,NULL,NULL,1,NULL,NULL),(48,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','445','1404','Etat - Taxes sur chiffre affaires',0,NULL,NULL,1,NULL,NULL),(49,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','447','1404','Autres impôts, taxes et versements assimilés',0,NULL,NULL,1,NULL,NULL),(50,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','45','1404','Groupe et associes',0,NULL,NULL,1,NULL,NULL),(51,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','455','50','Associés',0,NULL,NULL,1,NULL,NULL),(52,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','46','1404','Débiteurs divers et créditeurs divers',0,NULL,NULL,1,NULL,NULL),(53,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','47','1404','Comptes transitoires ou d\'attente',0,NULL,NULL,1,NULL,NULL),(54,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','481','1404','Charges à répartir sur plusieurs exercices',0,NULL,NULL,1,NULL,NULL),(55,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','486','1404','Charges constatées d\'avance',0,NULL,NULL,1,NULL,NULL),(56,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','487','1404','Produits constatés d\'avance',0,NULL,NULL,1,NULL,NULL),(57,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','491','1404','Provisions pour dépréciation des comptes de clients',0,NULL,NULL,1,NULL,NULL),(58,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','496','1404','Provisions pour dépréciation des comptes de débiteurs divers',0,NULL,NULL,1,NULL,NULL),(59,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','50','1405','Valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(60,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','BANK','51','1405','Banques, établissements financiers et assimilés',0,NULL,NULL,1,NULL,NULL),(61,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','CASH','53','1405','Caisse',0,NULL,NULL,1,NULL,NULL),(62,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','54','1405','Régies d\'avance et accréditifs',0,NULL,NULL,1,NULL,NULL),(63,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','58','1405','Virements internes',0,NULL,NULL,1,NULL,NULL),(64,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','590','1405','Provisions pour dépréciation des valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(65,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','PRODUCT','60','1406','Achats',0,NULL,NULL,1,NULL,NULL),(66,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','603','65','Variations des stocks',0,NULL,NULL,1,NULL,NULL),(67,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','SERVICE','61','1406','Services extérieurs',0,NULL,NULL,1,NULL,NULL),(68,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','62','1406','Autres services extérieurs',0,NULL,NULL,1,NULL,NULL),(69,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','63','1406','Impôts, taxes et versements assimiles',0,NULL,NULL,1,NULL,NULL),(70,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','641','1406','Rémunérations du personnel',0,NULL,NULL,1,NULL,NULL),(71,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','644','1406','Rémunération du travail de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(72,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','SOCIAL','645','1406','Charges de sécurité sociale et de prévoyance',0,NULL,NULL,1,NULL,NULL),(73,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','646','1406','Cotisations sociales personnelles de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(74,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','65','1406','Autres charges de gestion courante',0,NULL,NULL,1,NULL,NULL),(75,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','66','1406','Charges financières',0,NULL,NULL,1,NULL,NULL),(76,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','67','1406','Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(77,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','681','1406','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(78,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','686','1406','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(79,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','687','1406','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(80,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','691','1406','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(81,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','695','1406','Impôts sur les bénéfices',0,NULL,NULL,1,NULL,NULL),(82,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','697','1406','Imposition forfaitaire annuelle des sociétés',0,NULL,NULL,1,NULL,NULL),(83,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','699','1406','Produits',0,NULL,NULL,1,NULL,NULL),(84,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','PRODUCT','701','1407','Ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(85,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','SERVICE','706','1407','Prestations de services',0,NULL,NULL,1,NULL,NULL),(86,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','PRODUCT','707','1407','Ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(87,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','PRODUCT','708','1407','Produits des activités annexes',0,NULL,NULL,1,NULL,NULL),(88,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','709','1407','Rabais, remises et ristournes accordés par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(89,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','713','1407','Variation des stocks',0,NULL,NULL,1,NULL,NULL),(90,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','72','1407','Production immobilisée',0,NULL,NULL,1,NULL,NULL),(91,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','73','1407','Produits nets partiels sur opérations à long terme',0,NULL,NULL,1,NULL,NULL),(92,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','74','1407','Subventions d\'exploitation',0,NULL,NULL,1,NULL,NULL),(93,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','75','1407','Autres produits de gestion courante',0,NULL,NULL,1,NULL,NULL),(94,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','753','93','Jetons de présence et rémunérations d\'administrateurs, gérants,...',0,NULL,NULL,1,NULL,NULL),(95,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','754','93','Ristournes perçues des coopératives',0,NULL,NULL,1,NULL,NULL),(96,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','755','93','Quotes-parts de résultat sur opérations faites en commun',0,NULL,NULL,1,NULL,NULL),(97,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','76','1407','Produits financiers',0,NULL,NULL,1,NULL,NULL),(98,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','77','1407','Produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(99,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','781','1407','Reprises sur amortissements et provisions',0,NULL,NULL,1,NULL,NULL),(100,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','786','1407','Reprises sur provisions pour risques',0,NULL,NULL,1,NULL,NULL),(101,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','787','1407','Reprises sur provisions',0,NULL,NULL,1,NULL,NULL),(102,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','79','1407','Transferts de charges',0,NULL,NULL,1,NULL,NULL),(103,1,NULL,'2017-02-20 10:49:11','PCG99-BASE','CAPIT','XXXXXX','10','1501','Capital et réserves',0,NULL,NULL,1,NULL,NULL),(104,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','CAPITAL','101','103','Capital',0,NULL,NULL,1,NULL,NULL),(105,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','104','103','Primes liées au capital social',0,NULL,NULL,1,NULL,NULL),(106,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','105','103','Ecarts de réévaluation',0,NULL,NULL,1,NULL,NULL),(107,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','106','103','Réserves',0,NULL,NULL,1,NULL,NULL),(108,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','107','103','Ecart d\'equivalence',0,NULL,NULL,1,NULL,NULL),(109,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','108','103','Compte de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(110,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','109','103','Actionnaires : capital souscrit - non appelé',0,NULL,NULL,1,NULL,NULL),(111,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','11','1501','Report à nouveau (solde créditeur ou débiteur)',0,NULL,NULL,1,NULL,NULL),(112,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','110','111','Report à nouveau (solde créditeur)',0,NULL,NULL,1,NULL,NULL),(113,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','119','111','Report à nouveau (solde débiteur)',0,NULL,NULL,1,NULL,NULL),(114,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','12','1501','Résultat de l\'exercice (bénéfice ou perte)',0,NULL,NULL,1,NULL,NULL),(115,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','120','114','Résultat de l\'exercice (bénéfice)',0,NULL,NULL,1,NULL,NULL),(116,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','129','114','Résultat de l\'exercice (perte)',0,NULL,NULL,1,NULL,NULL),(117,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','13','1501','Subventions d\'investissement',0,NULL,NULL,1,NULL,NULL),(118,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','131','117','Subventions d\'équipement',0,NULL,NULL,1,NULL,NULL),(119,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','138','117','Autres subventions d\'investissement',0,NULL,NULL,1,NULL,NULL),(120,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','139','117','Subventions d\'investissement inscrites au compte de résultat',0,NULL,NULL,1,NULL,NULL),(121,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','14','1501','Provisions réglementées',0,NULL,NULL,1,NULL,NULL),(122,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','142','121','Provisions réglementées relatives aux immobilisations',0,NULL,NULL,1,NULL,NULL),(123,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','143','121','Provisions réglementées relatives aux stocks',0,NULL,NULL,1,NULL,NULL),(124,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','144','121','Provisions réglementées relatives aux autres éléments de l\'actif',0,NULL,NULL,1,NULL,NULL),(125,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','145','121','Amortissements dérogatoires',0,NULL,NULL,1,NULL,NULL),(126,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','146','121','Provision spéciale de réévaluation',0,NULL,NULL,1,NULL,NULL),(127,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','147','121','Plus-values réinvesties',0,NULL,NULL,1,NULL,NULL),(128,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','148','121','Autres provisions réglementées',0,NULL,NULL,1,NULL,NULL),(129,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','15','1501','Provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(130,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','151','129','Provisions pour risques',0,NULL,NULL,1,NULL,NULL),(131,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','153','129','Provisions pour pensions et obligations similaires',0,NULL,NULL,1,NULL,NULL),(132,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','154','129','Provisions pour restructurations',0,NULL,NULL,1,NULL,NULL),(133,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','155','129','Provisions pour impôts',0,NULL,NULL,1,NULL,NULL),(134,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','156','129','Provisions pour renouvellement des immobilisations (entreprises concessionnaires)',0,NULL,NULL,1,NULL,NULL),(135,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','157','129','Provisions pour charges à répartir sur plusieurs exercices',0,NULL,NULL,1,NULL,NULL),(136,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','158','129','Autres provisions pour charges',0,NULL,NULL,1,NULL,NULL),(137,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','16','1501','Emprunts et dettes assimilees',0,NULL,NULL,1,NULL,NULL),(138,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','161','137','Emprunts obligataires convertibles',0,NULL,NULL,1,NULL,NULL),(139,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','163','137','Autres emprunts obligataires',0,NULL,NULL,1,NULL,NULL),(140,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','164','137','Emprunts auprès des établissements de crédit',0,NULL,NULL,1,NULL,NULL),(141,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','165','137','Dépôts et cautionnements reçus',0,NULL,NULL,1,NULL,NULL),(142,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','166','137','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(143,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','167','137','Emprunts et dettes assortis de conditions particulières',0,NULL,NULL,1,NULL,NULL),(144,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','168','137','Autres emprunts et dettes assimilées',0,NULL,NULL,1,NULL,NULL),(145,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','169','137','Primes de remboursement des obligations',0,NULL,NULL,1,NULL,NULL),(146,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','17','1501','Dettes rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(147,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','171','146','Dettes rattachées à des participations (groupe)',0,NULL,NULL,1,NULL,NULL),(148,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','174','146','Dettes rattachées à des participations (hors groupe)',0,NULL,NULL,1,NULL,NULL),(149,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','178','146','Dettes rattachées à des sociétés en participation',0,NULL,NULL,1,NULL,NULL),(150,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','18','1501','Comptes de liaison des établissements et sociétés en participation',0,NULL,NULL,1,NULL,NULL),(151,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','181','150','Comptes de liaison des établissements',0,NULL,NULL,1,NULL,NULL),(152,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','186','150','Biens et prestations de services échangés entre établissements (charges)',0,NULL,NULL,1,NULL,NULL),(153,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','187','150','Biens et prestations de services échangés entre établissements (produits)',0,NULL,NULL,1,NULL,NULL),(154,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','188','150','Comptes de liaison des sociétés en participation',0,NULL,NULL,1,NULL,NULL),(155,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','20','1502','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(156,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','201','155','Frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(157,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','203','155','Frais de recherche et de développement',0,NULL,NULL,1,NULL,NULL),(158,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','205','155','Concessions et droits similaires, brevets, licences, marques, procédés, logiciels, droits et valeurs similaires',0,NULL,NULL,1,NULL,NULL),(159,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','206','155','Droit au bail',0,NULL,NULL,1,NULL,NULL),(160,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','207','155','Fonds commercial',0,NULL,NULL,1,NULL,NULL),(161,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','208','155','Autres immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(162,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','21','1502','Immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(163,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','211','162','Terrains',0,NULL,NULL,1,NULL,NULL),(164,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','212','162','Agencements et aménagements de terrains',0,NULL,NULL,1,NULL,NULL),(165,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','213','162','Constructions',0,NULL,NULL,1,NULL,NULL),(166,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','214','162','Constructions sur sol d\'autrui',0,NULL,NULL,1,NULL,NULL),(167,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','215','162','Installations techniques, matériels et outillage industriels',0,NULL,NULL,1,NULL,NULL),(168,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','218','162','Autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(169,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','22','1502','Immobilisations mises en concession',0,NULL,NULL,1,NULL,NULL),(170,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','23','1502','Immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(171,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','231','170','Immobilisations corporelles en cours',0,NULL,NULL,1,NULL,NULL),(172,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','232','170','Immobilisations incorporelles en cours',0,NULL,NULL,1,NULL,NULL),(173,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','237','170','Avances et acomptes versés sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(174,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','238','170','Avances et acomptes versés sur commandes d\'immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(175,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','25','1502','Parts dans des entreprises liées et créances sur des entreprises liées',0,NULL,NULL,1,NULL,NULL),(176,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','26','1502','Participations et créances rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(177,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','261','176','Titres de participation',0,NULL,NULL,1,NULL,NULL),(178,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','266','176','Autres formes de participation',0,NULL,NULL,1,NULL,NULL),(179,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','267','176','Créances rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(180,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','268','176','Créances rattachées à des sociétés en participation',0,NULL,NULL,1,NULL,NULL),(181,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','269','176','Versements restant à effectuer sur titres de participation non libérés',0,NULL,NULL,1,NULL,NULL),(182,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','27','1502','Autres immobilisations financieres',0,NULL,NULL,1,NULL,NULL),(183,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','271','183','Titres immobilisés autres que les titres immobilisés de l\'activité de portefeuille (droit de propriété)',0,NULL,NULL,1,NULL,NULL),(184,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','272','183','Titres immobilisés (droit de créance)',0,NULL,NULL,1,NULL,NULL),(185,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','273','183','Titres immobilisés de l\'activité de portefeuille',0,NULL,NULL,1,NULL,NULL),(186,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','274','183','Prêts',0,NULL,NULL,1,NULL,NULL),(187,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','275','183','Dépôts et cautionnements versés',0,NULL,NULL,1,NULL,NULL),(188,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','276','183','Autres créances immobilisées',0,NULL,NULL,1,NULL,NULL),(189,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','277','183','(Actions propres ou parts propres)',0,NULL,NULL,1,NULL,NULL),(190,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','279','183','Versements restant à effectuer sur titres immobilisés non libérés',0,NULL,NULL,1,NULL,NULL),(191,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','28','1502','Amortissements des immobilisations',0,NULL,NULL,1,NULL,NULL),(192,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','280','191','Amortissements des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(193,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','281','191','Amortissements des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(194,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','282','191','Amortissements des immobilisations mises en concession',0,NULL,NULL,1,NULL,NULL),(195,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','29','1502','Dépréciations des immobilisations',0,NULL,NULL,1,NULL,NULL),(196,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','290','195','Dépréciations des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(197,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','291','195','Dépréciations des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(198,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','292','195','Dépréciations des immobilisations mises en concession',0,NULL,NULL,1,NULL,NULL),(199,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','293','195','Dépréciations des immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(200,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','296','195','Provisions pour dépréciation des participations et créances rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(201,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','297','195','Provisions pour dépréciation des autres immobilisations financières',0,NULL,NULL,1,NULL,NULL),(202,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','31','1503','Matières premières (et fournitures)',0,NULL,NULL,1,NULL,NULL),(203,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','311','202','Matières (ou groupe) A',0,NULL,NULL,1,NULL,NULL),(204,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','312','202','Matières (ou groupe) B',0,NULL,NULL,1,NULL,NULL),(205,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','317','202','Fournitures A, B, C,',0,NULL,NULL,1,NULL,NULL),(206,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','32','1503','Autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(207,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','321','206','Matières consommables',0,NULL,NULL,1,NULL,NULL),(208,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','322','206','Fournitures consommables',0,NULL,NULL,1,NULL,NULL),(209,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','326','206','Emballages',0,NULL,NULL,1,NULL,NULL),(210,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','33','1503','En-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(211,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','331','210','Produits en cours',0,NULL,NULL,1,NULL,NULL),(212,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','335','210','Travaux en cours',0,NULL,NULL,1,NULL,NULL),(213,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','34','1503','En-cours de production de services',0,NULL,NULL,1,NULL,NULL),(214,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','341','213','Etudes en cours',0,NULL,NULL,1,NULL,NULL),(215,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','345','213','Prestations de services en cours',0,NULL,NULL,1,NULL,NULL),(216,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','35','1503','Stocks de produits',0,NULL,NULL,1,NULL,NULL),(217,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','351','216','Produits intermédiaires',0,NULL,NULL,1,NULL,NULL),(218,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','355','216','Produits finis',0,NULL,NULL,1,NULL,NULL),(219,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','358','216','Produits résiduels (ou matières de récupération)',0,NULL,NULL,1,NULL,NULL),(220,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','37','1503','Stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(221,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','371','220','Marchandises (ou groupe) A',0,NULL,NULL,1,NULL,NULL),(222,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','372','220','Marchandises (ou groupe) B',0,NULL,NULL,1,NULL,NULL),(223,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','39','1503','Provisions pour dépréciation des stocks et en-cours',0,NULL,NULL,1,NULL,NULL),(224,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','391','223','Provisions pour dépréciation des matières premières',0,NULL,NULL,1,NULL,NULL),(225,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','392','223','Provisions pour dépréciation des autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(226,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','393','223','Provisions pour dépréciation des en-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(227,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','394','223','Provisions pour dépréciation des en-cours de production de services',0,NULL,NULL,1,NULL,NULL),(228,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','395','223','Provisions pour dépréciation des stocks de produits',0,NULL,NULL,1,NULL,NULL),(229,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','397','223','Provisions pour dépréciation des stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(230,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','40','1504','Fournisseurs et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(231,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','400','230','Fournisseurs et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(232,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','SUPPLIER','401','230','Fournisseurs',0,NULL,NULL,1,NULL,NULL),(233,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','403','230','Fournisseurs - Effets à payer',0,NULL,NULL,1,NULL,NULL),(234,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','404','230','Fournisseurs d\'immobilisations',0,NULL,NULL,1,NULL,NULL),(235,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','405','230','Fournisseurs d\'immobilisations - Effets à payer',0,NULL,NULL,1,NULL,NULL),(236,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','408','230','Fournisseurs - Factures non parvenues',0,NULL,NULL,1,NULL,NULL),(237,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','409','230','Fournisseurs débiteurs',0,NULL,NULL,1,NULL,NULL),(238,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','41','1504','Clients et comptes rattachés',0,NULL,NULL,1,NULL,NULL),(239,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','410','238','Clients et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(240,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','CUSTOMER','411','238','Clients',0,NULL,NULL,1,NULL,NULL),(241,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','413','238','Clients - Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(242,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','416','238','Clients douteux ou litigieux',0,NULL,NULL,1,NULL,NULL),(243,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','418','238','Clients - Produits non encore facturés',0,NULL,NULL,1,NULL,NULL),(244,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','419','238','Clients créditeurs',0,NULL,NULL,1,NULL,NULL),(245,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','42','1504','Personnel et comptes rattachés',0,NULL,NULL,1,NULL,NULL),(246,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','421','245','Personnel - Rémunérations dues',0,NULL,NULL,1,NULL,NULL),(247,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','422','245','Comités d\'entreprises, d\'établissement, ...',0,NULL,NULL,1,NULL,NULL),(248,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','424','245','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(249,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','425','245','Personnel - Avances et acomptes',0,NULL,NULL,1,NULL,NULL),(250,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','426','245','Personnel - Dépôts',0,NULL,NULL,1,NULL,NULL),(251,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','427','245','Personnel - Oppositions',0,NULL,NULL,1,NULL,NULL),(252,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','428','245','Personnel - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(253,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','43','1504','Sécurité sociale et autres organismes sociaux',0,NULL,NULL,1,NULL,NULL),(254,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','431','253','Sécurité sociale',0,NULL,NULL,1,NULL,NULL),(255,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','437','253','Autres organismes sociaux',0,NULL,NULL,1,NULL,NULL),(256,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','438','253','Organismes sociaux - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(257,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','44','1504','État et autres collectivités publiques',0,NULL,NULL,1,NULL,NULL),(258,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','441','257','État - Subventions à recevoir',0,NULL,NULL,1,NULL,NULL),(259,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','442','257','Etat - Impôts et taxes recouvrables sur des tiers',0,NULL,NULL,1,NULL,NULL),(260,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','443','257','Opérations particulières avec l\'Etat, les collectivités publiques, les organismes internationaux',0,NULL,NULL,1,NULL,NULL),(261,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','444','257','Etat - Impôts sur les bénéfices',0,NULL,NULL,1,NULL,NULL),(262,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','445','257','Etat - Taxes sur le chiffre d\'affaires',0,NULL,NULL,1,NULL,NULL),(263,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','446','257','Obligations cautionnées',0,NULL,NULL,1,NULL,NULL),(264,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','447','257','Autres impôts, taxes et versements assimilés',0,NULL,NULL,1,NULL,NULL),(265,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','448','257','Etat - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(266,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','449','257','Quotas d\'émission à restituer à l\'Etat',0,NULL,NULL,1,NULL,NULL),(267,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','45','1504','Groupe et associes',0,NULL,NULL,1,NULL,NULL),(268,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','451','267','Groupe',0,NULL,NULL,1,NULL,NULL),(269,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','455','267','Associés - Comptes courants',0,NULL,NULL,1,NULL,NULL),(270,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','456','267','Associés - Opérations sur le capital',0,NULL,NULL,1,NULL,NULL),(271,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','457','267','Associés - Dividendes à payer',0,NULL,NULL,1,NULL,NULL),(272,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','458','267','Associés - Opérations faites en commun et en G.I.E.',0,NULL,NULL,1,NULL,NULL),(273,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','46','1504','Débiteurs divers et créditeurs divers',0,NULL,NULL,1,NULL,NULL),(274,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','462','273','Créances sur cessions d\'immobilisations',0,NULL,NULL,1,NULL,NULL),(275,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','464','273','Dettes sur acquisitions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(276,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','465','273','Créances sur cessions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(277,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','467','273','Autres comptes débiteurs ou créditeurs',0,NULL,NULL,1,NULL,NULL),(278,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','468','273','Divers - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(279,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','47','1504','Comptes transitoires ou d\'attente',0,NULL,NULL,1,NULL,NULL),(280,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','471','279','Comptes d\'attente',0,NULL,NULL,1,NULL,NULL),(281,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','476','279','Différence de conversion - Actif',0,NULL,NULL,1,NULL,NULL),(282,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','477','279','Différences de conversion - Passif',0,NULL,NULL,1,NULL,NULL),(283,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','478','279','Autres comptes transitoires',0,NULL,NULL,1,NULL,NULL),(284,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','48','1504','Comptes de régularisation',0,NULL,NULL,1,NULL,NULL),(285,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','481','284','Charges à répartir sur plusieurs exercices',0,NULL,NULL,1,NULL,NULL),(286,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','486','284','Charges constatées d\'avance',0,NULL,NULL,1,NULL,NULL),(287,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','487','284','Produits constatés d\'avance',0,NULL,NULL,1,NULL,NULL),(288,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','488','284','Comptes de répartition périodique des charges et des produits',0,NULL,NULL,1,NULL,NULL),(289,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','489','284','Quotas d\'émission alloués par l\'Etat',0,NULL,NULL,1,NULL,NULL),(290,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','49','1504','Provisions pour dépréciation des comptes de tiers',0,NULL,NULL,1,NULL,NULL),(291,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','491','290','Provisions pour dépréciation des comptes de clients',0,NULL,NULL,1,NULL,NULL),(292,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','495','290','Provisions pour dépréciation des comptes du groupe et des associés',0,NULL,NULL,1,NULL,NULL),(293,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','496','290','Provisions pour dépréciation des comptes de débiteurs divers',0,NULL,NULL,1,NULL,NULL),(294,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','50','1505','Valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(295,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','501','294','Parts dans des entreprises liées',0,NULL,NULL,1,NULL,NULL),(296,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','502','294','Actions propres',0,NULL,NULL,1,NULL,NULL),(297,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','503','294','Actions',0,NULL,NULL,1,NULL,NULL),(298,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','504','294','Autres titres conférant un droit de propriété',0,NULL,NULL,1,NULL,NULL),(299,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','505','294','Obligations et bons émis par la société et rachetés par elle',0,NULL,NULL,1,NULL,NULL),(300,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','506','294','Obligations',0,NULL,NULL,1,NULL,NULL),(301,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','507','294','Bons du Trésor et bons de caisse à court terme',0,NULL,NULL,1,NULL,NULL),(302,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','508','294','Autres valeurs mobilières de placement et autres créances assimilées',0,NULL,NULL,1,NULL,NULL),(303,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','509','294','Versements restant à effectuer sur valeurs mobilières de placement non libérées',0,NULL,NULL,1,NULL,NULL),(304,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','51','1505','Banques, établissements financiers et assimilés',0,NULL,NULL,1,NULL,NULL),(305,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','511','304','Valeurs à l\'encaissement',0,NULL,NULL,1,NULL,NULL),(306,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','BANK','512','304','Banques',0,NULL,NULL,1,NULL,NULL),(307,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','514','304','Chèques postaux',0,NULL,NULL,1,NULL,NULL),(308,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','515','304','\"Caisses\" du Trésor et des établissements publics',0,NULL,NULL,1,NULL,NULL),(309,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','516','304','Sociétés de bourse',0,NULL,NULL,1,NULL,NULL),(310,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','517','304','Autres organismes financiers',0,NULL,NULL,1,NULL,NULL),(311,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','518','304','Intérêts courus',0,NULL,NULL,1,NULL,NULL),(312,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','519','304','Concours bancaires courants',0,NULL,NULL,1,NULL,NULL),(313,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','52','1505','Instruments de trésorerie',0,NULL,NULL,1,NULL,NULL),(314,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','CASH','53','1505','Caisse',0,NULL,NULL,1,NULL,NULL),(315,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','531','314','Caisse siège social',0,NULL,NULL,1,NULL,NULL),(316,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','532','314','Caisse succursale (ou usine) A',0,NULL,NULL,1,NULL,NULL),(317,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','533','314','Caisse succursale (ou usine) B',0,NULL,NULL,1,NULL,NULL),(318,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','54','1505','Régies d\'avance et accréditifs',0,NULL,NULL,1,NULL,NULL),(319,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','58','1505','Virements internes',0,NULL,NULL,1,NULL,NULL),(320,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','59','1505','Provisions pour dépréciation des comptes financiers',0,NULL,NULL,1,NULL,NULL),(321,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','590','320','Provisions pour dépréciation des valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(322,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','PRODUCT','60','1506','Achats',0,NULL,NULL,1,NULL,NULL),(323,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','601','322','Achats stockés - Matières premières (et fournitures)',0,NULL,NULL,1,NULL,NULL),(324,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','602','322','Achats stockés - Autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(325,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','603','322','Variations des stocks (approvisionnements et marchandises)',0,NULL,NULL,1,NULL,NULL),(326,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','604','322','Achats stockés - Matières premières (et fournitures)',0,NULL,NULL,1,NULL,NULL),(327,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','605','322','Achats de matériel, équipements et travaux',0,NULL,NULL,1,NULL,NULL),(328,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','606','322','Achats non stockés de matière et fournitures',0,NULL,NULL,1,NULL,NULL),(329,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','607','322','Achats de marchandises',0,NULL,NULL,1,NULL,NULL),(330,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','608','322','(Compte réservé, le cas échéant, à la récapitulation des frais accessoires incorporés aux achats)',0,NULL,NULL,1,NULL,NULL),(331,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','609','322','Rabais, remises et ristournes obtenus sur achats',0,NULL,NULL,1,NULL,NULL),(332,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','SERVICE','61','1506','Services extérieurs',0,NULL,NULL,1,NULL,NULL),(333,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','611','332','Sous-traitance générale',0,NULL,NULL,1,NULL,NULL),(334,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','612','332','Redevances de crédit-bail',0,NULL,NULL,1,NULL,NULL),(335,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','613','332','Locations',0,NULL,NULL,1,NULL,NULL),(336,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','614','332','Charges locatives et de copropriété',0,NULL,NULL,1,NULL,NULL),(337,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','615','332','Entretien et réparations',0,NULL,NULL,1,NULL,NULL),(338,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','616','332','Primes d\'assurances',0,NULL,NULL,1,NULL,NULL),(339,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','617','332','Etudes et recherches',0,NULL,NULL,1,NULL,NULL),(340,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','618','332','Divers',0,NULL,NULL,1,NULL,NULL),(341,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','619','332','Rabais, remises et ristournes obtenus sur services extérieurs',0,NULL,NULL,1,NULL,NULL),(342,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','62','1506','Autres services extérieurs',0,NULL,NULL,1,NULL,NULL),(343,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','621','342','Personnel extérieur à l\'entreprise',0,NULL,NULL,1,NULL,NULL),(344,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','622','342','Rémunérations d\'intermédiaires et honoraires',0,NULL,NULL,1,NULL,NULL),(345,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','623','342','Publicité, publications, relations publiques',0,NULL,NULL,1,NULL,NULL),(346,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','624','342','Transports de biens et transports collectifs du personnel',0,NULL,NULL,1,NULL,NULL),(347,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','625','342','Déplacements, missions et réceptions',0,NULL,NULL,1,NULL,NULL),(348,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','626','342','Frais postaux et de télécommunications',0,NULL,NULL,1,NULL,NULL),(349,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','627','342','Services bancaires et assimilés',0,NULL,NULL,1,NULL,NULL),(350,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','628','342','Divers',0,NULL,NULL,1,NULL,NULL),(351,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','629','342','Rabais, remises et ristournes obtenus sur autres services extérieurs',0,NULL,NULL,1,NULL,NULL),(352,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','63','1506','Impôts, taxes et versements assimilés',0,NULL,NULL,1,NULL,NULL),(353,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','631','352','Impôts, taxes et versements assimilés sur rémunérations (administrations des impôts)',0,NULL,NULL,1,NULL,NULL),(354,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','633','352','Impôts, taxes et versements assimilés sur rémunérations (autres organismes)',0,NULL,NULL,1,NULL,NULL),(355,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','635','352','Autres impôts, taxes et versements assimilés (administrations des impôts)',0,NULL,NULL,1,NULL,NULL),(356,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','637','352','Autres impôts, taxes et versements assimilés (autres organismes)',0,NULL,NULL,1,NULL,NULL),(357,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','64','1506','Charges de personnel',0,NULL,NULL,1,NULL,NULL),(358,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','641','357','Rémunérations du personnel',0,NULL,NULL,1,NULL,NULL),(359,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','644','357','Rémunération du travail de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(360,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','SOCIAL','645','357','Charges de sécurité sociale et de prévoyance',0,NULL,NULL,1,NULL,NULL),(361,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','646','357','Cotisations sociales personnelles de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(362,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','647','357','Autres charges sociales',0,NULL,NULL,1,NULL,NULL),(363,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','648','357','Autres charges de personnel',0,NULL,NULL,1,NULL,NULL),(364,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','65','1506','Autres charges de gestion courante',0,NULL,NULL,1,NULL,NULL),(365,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','651','364','Redevances pour concessions, brevets, licences, marques, procédés, logiciels, droits et valeurs similaires',0,NULL,NULL,1,NULL,NULL),(366,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','653','364','Jetons de présence',0,NULL,NULL,1,NULL,NULL),(367,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','654','364','Pertes sur créances irrécouvrables',0,NULL,NULL,1,NULL,NULL),(368,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','655','364','Quote-part de résultat sur opérations faites en commun',0,NULL,NULL,1,NULL,NULL),(369,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','658','364','Charges diverses de gestion courante',0,NULL,NULL,1,NULL,NULL),(370,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','66','1506','Charges financières',0,NULL,NULL,1,NULL,NULL),(371,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','661','370','Charges d\'intérêts',0,NULL,NULL,1,NULL,NULL),(372,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','664','370','Pertes sur créances liées à des participations',0,NULL,NULL,1,NULL,NULL),(373,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','665','370','Escomptes accordés',0,NULL,NULL,1,NULL,NULL),(374,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','666','370','Pertes de change',0,NULL,NULL,1,NULL,NULL),(375,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','667','370','Charges nettes sur cessions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(376,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','668','370','Autres charges financières',0,NULL,NULL,1,NULL,NULL),(377,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','67','1506','Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(378,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','671','377','Charges exceptionnelles sur opérations de gestion',0,NULL,NULL,1,NULL,NULL),(379,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','672','377','(Compte à la disposition des entités pour enregistrer, en cours d\'exercice, les charges sur exercices antérieurs)',0,NULL,NULL,1,NULL,NULL),(380,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','675','377','Valeurs comptables des éléments d\'actif cédés',0,NULL,NULL,1,NULL,NULL),(381,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','678','377','Autres charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(382,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','68','1506','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(383,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','681','382','Dotations aux amortissements et aux provisions - Charges d\'exploitation',0,NULL,NULL,1,NULL,NULL),(384,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','686','382','Dotations aux amortissements et aux provisions - Charges financières',0,NULL,NULL,1,NULL,NULL),(385,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','687','382','Dotations aux amortissements et aux provisions - Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(386,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','69','1506','Participation des salariés - impôts sur les bénéfices et assimiles',0,NULL,NULL,1,NULL,NULL),(387,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','691','386','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(388,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','695','386','Impôts sur les bénéfices',0,NULL,NULL,1,NULL,NULL),(389,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','696','386','Suppléments d\'impôt sur les sociétés liés aux distributions',0,NULL,NULL,1,NULL,NULL),(390,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','697','386','Imposition forfaitaire annuelle des sociétés',0,NULL,NULL,1,NULL,NULL),(391,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','698','386','Intégration fiscale',0,NULL,NULL,1,NULL,NULL),(392,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','699','386','Produits - Reports en arrière des déficits',0,NULL,NULL,1,NULL,NULL),(393,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','70','1507','Ventes de produits fabriqués, prestations de services, marchandises',0,NULL,NULL,1,NULL,NULL),(394,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','PRODUCT','701','393','Ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(395,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','702','393','Ventes de produits intermédiaires',0,NULL,NULL,1,NULL,NULL),(396,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','703','393','Ventes de produits résiduels',0,NULL,NULL,1,NULL,NULL),(397,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','704','393','Travaux',0,NULL,NULL,1,NULL,NULL),(398,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','705','393','Etudes',0,NULL,NULL,1,NULL,NULL),(399,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','SERVICE','706','393','Prestations de services',0,NULL,NULL,1,NULL,NULL),(400,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','PRODUCT','707','393','Ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(401,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','PRODUCT','708','393','Produits des activités annexes',0,NULL,NULL,1,NULL,NULL),(402,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','709','393','Rabais, remises et ristournes accordés par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(403,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','71','1507','Production stockée (ou déstockage)',0,NULL,NULL,1,NULL,NULL),(404,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','713','403','Variation des stocks (en-cours de production, produits)',0,NULL,NULL,1,NULL,NULL),(405,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','72','1507','Production immobilisée',0,NULL,NULL,1,NULL,NULL),(406,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','721','405','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(407,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','722','405','Immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(408,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','74','1507','Subventions d\'exploitation',0,NULL,NULL,1,NULL,NULL),(409,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','75','1507','Autres produits de gestion courante',0,NULL,NULL,1,NULL,NULL),(410,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','751','409','Redevances pour concessions, brevets, licences, marques, procédés, logiciels, droits et valeurs similaires',0,NULL,NULL,1,NULL,NULL),(411,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','752','409','Revenus des immeubles non affectés à des activités professionnelles',0,NULL,NULL,1,NULL,NULL),(412,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','753','409','Jetons de présence et rémunérations d\'administrateurs, gérants,...',0,NULL,NULL,1,NULL,NULL),(413,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','754','409','Ristournes perçues des coopératives (provenant des excédents)',0,NULL,NULL,1,NULL,NULL),(414,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','755','409','Quotes-parts de résultat sur opérations faites en commun',0,NULL,NULL,1,NULL,NULL),(415,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','758','409','Produits divers de gestion courante',0,NULL,NULL,1,NULL,NULL),(416,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','76','1507','Produits financiers',0,NULL,NULL,1,NULL,NULL),(417,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','761','416','Produits de participations',0,NULL,NULL,1,NULL,NULL),(418,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','762','416','Produits des autres immobilisations financières',0,NULL,NULL,1,NULL,NULL),(419,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','763','416','Revenus des autres créances',0,NULL,NULL,1,NULL,NULL),(420,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','764','416','Revenus des valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(421,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','765','416','Escomptes obtenus',0,NULL,NULL,1,NULL,NULL),(422,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','766','416','Gains de change',0,NULL,NULL,1,NULL,NULL),(423,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','767','416','Produits nets sur cessions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(424,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','768','416','Autres produits financiers',0,NULL,NULL,1,NULL,NULL),(425,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','77','1507','Produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(426,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','771','425','Produits exceptionnels sur opérations de gestion',0,NULL,NULL,1,NULL,NULL),(427,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','772','425','(Compte à la disposition des entités pour enregistrer, en cours d\'exercice, les produits sur exercices antérieurs)',0,NULL,NULL,1,NULL,NULL),(428,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','775','425','Produits des cessions d\'éléments d\'actif',0,NULL,NULL,1,NULL,NULL),(429,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','777','425','Quote-part des subventions d\'investissement virée au résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(430,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','778','425','Autres produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(431,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','78','1507','Reprises sur amortissements et provisions',0,NULL,NULL,1,NULL,NULL),(432,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','781','431','Reprises sur amortissements et provisions (à inscrire dans les produits d\'exploitation)',0,NULL,NULL,1,NULL,NULL),(433,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','786','431','Reprises sur provisions pour risques (à inscrire dans les produits financiers)',0,NULL,NULL,1,NULL,NULL),(434,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','787','431','Reprises sur provisions (à inscrire dans les produits exceptionnels)',0,NULL,NULL,1,NULL,NULL),(435,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','79','1507','Transferts de charges',0,NULL,NULL,1,NULL,NULL),(436,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','791','435','Transferts de charges d\'exploitation ',0,NULL,NULL,1,NULL,NULL),(437,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','796','435','Transferts de charges financières',0,NULL,NULL,1,NULL,NULL),(438,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','797','435','Transferts de charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(439,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','10','1351','Capital',0,NULL,NULL,1,NULL,NULL),(440,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','100','439','Capital souscrit ou capital personnel',0,NULL,NULL,1,NULL,NULL),(441,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1000','440','Capital non amorti',0,NULL,NULL,1,NULL,NULL),(442,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1001','440','Capital amorti',0,NULL,NULL,1,NULL,NULL),(443,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','101','439','Capital non appelé',0,NULL,NULL,1,NULL,NULL),(444,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','109','439','Compte de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(445,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1090','444','Opérations courantes',0,NULL,NULL,1,NULL,NULL),(446,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1091','444','Impôts personnels',0,NULL,NULL,1,NULL,NULL),(447,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1092','444','Rémunérations et autres avantages',0,NULL,NULL,1,NULL,NULL),(448,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','11','1351','Primes d\'émission',0,NULL,NULL,1,NULL,NULL),(449,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','12','1351','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(450,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','120','449','Plus-values de réévaluation sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(451,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1200','450','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(452,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1201','450','Reprises de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(453,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','121','449','Plus-values de réévaluation sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(454,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1210','453','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(455,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1211','453','Reprises de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(456,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','122','449','Plus-values de réévaluation sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(457,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1220','456','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(458,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1221','456','Reprises de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(459,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','123','449','Plus-values de réévaluation sur stocks',0,NULL,NULL,1,NULL,NULL),(460,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','124','449','Reprises de réductions de valeur sur placements de trésorerie',0,NULL,NULL,1,NULL,NULL),(461,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','13','1351','Réserve',0,NULL,NULL,1,NULL,NULL),(462,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','130','461','Réserve légale',0,NULL,NULL,1,NULL,NULL),(463,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','131','461','Réserves indisponibles',0,NULL,NULL,1,NULL,NULL),(464,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1310','463','Réserve pour actions propres',0,NULL,NULL,1,NULL,NULL),(465,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1311','463','Autres réserves indisponibles',0,NULL,NULL,1,NULL,NULL),(466,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','132','461','Réserves immunisées',0,NULL,NULL,1,NULL,NULL),(467,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','133','461','Réserves disponibles',0,NULL,NULL,1,NULL,NULL),(468,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1330','467','Réserve pour régularisation de dividendes',0,NULL,NULL,1,NULL,NULL),(469,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1331','467','Réserve pour renouvellement des immobilisations',0,NULL,NULL,1,NULL,NULL),(470,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1332','467','Réserve pour installations en faveur du personnel 1333 Réserves libres',0,NULL,NULL,1,NULL,NULL),(471,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','14','1351','Bénéfice reporté (ou perte reportée)',0,NULL,NULL,1,NULL,NULL),(472,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','15','1351','Subsides en capital',0,NULL,NULL,1,NULL,NULL),(473,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','150','472','Montants obtenus',0,NULL,NULL,1,NULL,NULL),(474,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','151','472','Montants transférés aux résultats',0,NULL,NULL,1,NULL,NULL),(475,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','16','1351','Provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(476,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','160','475','Provisions pour pensions et obligations similaires',0,NULL,NULL,1,NULL,NULL),(477,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','161','475','Provisions pour charges fiscales',0,NULL,NULL,1,NULL,NULL),(478,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','162','475','Provisions pour grosses réparations et gros entretiens',0,NULL,NULL,1,NULL,NULL),(479,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','163','475','à 169 Provisions pour autres risques et charges',0,NULL,NULL,1,NULL,NULL),(480,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','164','475','Provisions pour sûretés personnelles ou réelles constituées à l\'appui de dettes et d\'engagements de tiers',0,NULL,NULL,1,NULL,NULL),(481,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','165','475','Provisions pour engagements relatifs à l\'acquisition ou à la cession d\'immobilisations',0,NULL,NULL,1,NULL,NULL),(482,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','166','475','Provisions pour exécution de commandes passées ou reçues',0,NULL,NULL,1,NULL,NULL),(483,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','167','475','Provisions pour positions et marchés à terme en devises ou positions et marchés à terme en marchandises',0,NULL,NULL,1,NULL,NULL),(484,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','168','475','Provisions pour garanties techniques attachées aux ventes et prestations déjà effectuées par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(485,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','169','475','Provisions pour autres risques et charges',0,NULL,NULL,1,NULL,NULL),(486,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1690','485','Pour litiges en cours',0,NULL,NULL,1,NULL,NULL),(487,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1691','485','Pour amendes, doubles droits et pénalités',0,NULL,NULL,1,NULL,NULL),(488,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1692','485','Pour propre assureur',0,NULL,NULL,1,NULL,NULL),(489,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1693','485','Pour risques inhérents aux opérations de crédits à moyen ou long terme',0,NULL,NULL,1,NULL,NULL),(490,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1695','485','Provision pour charge de liquidation',0,NULL,NULL,1,NULL,NULL),(491,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1696','485','Provision pour départ de personnel',0,NULL,NULL,1,NULL,NULL),(492,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1699','485','Pour risques divers',0,NULL,NULL,1,NULL,NULL),(493,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17','1351','Dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(494,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','170','493','Emprunts subordonnés',0,NULL,NULL,1,NULL,NULL),(495,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1700','494','Convertibles',0,NULL,NULL,1,NULL,NULL),(496,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1701','494','Non convertibles',0,NULL,NULL,1,NULL,NULL),(497,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','171','493','Emprunts obligataires non subordonnés',0,NULL,NULL,1,NULL,NULL),(498,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1710','498','Convertibles',0,NULL,NULL,1,NULL,NULL),(499,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1711','498','Non convertibles',0,NULL,NULL,1,NULL,NULL),(500,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','172','493','Dettes de location-financement et assimilés',0,NULL,NULL,1,NULL,NULL),(501,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1720','500','Dettes de location-financement de biens immobiliers',0,NULL,NULL,1,NULL,NULL),(502,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1721','500','Dettes de location-financement de biens mobiliers',0,NULL,NULL,1,NULL,NULL),(503,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1722','500','Dettes sur droits réels sur immeubles',0,NULL,NULL,1,NULL,NULL),(504,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','173','493','Etablissements de crédit',0,NULL,NULL,1,NULL,NULL),(505,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1730','504','Dettes en compte',0,NULL,NULL,1,NULL,NULL),(506,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17300','505','Banque A',0,NULL,NULL,1,NULL,NULL),(507,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17301','505','Banque B',0,NULL,NULL,1,NULL,NULL),(508,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17302','505','Banque C',0,NULL,NULL,1,NULL,NULL),(509,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17303','505','Banque D',0,NULL,NULL,1,NULL,NULL),(510,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1731','504','Promesses',0,NULL,NULL,1,NULL,NULL),(511,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17310','510','Banque A',0,NULL,NULL,1,NULL,NULL),(512,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17311','510','Banque B',0,NULL,NULL,1,NULL,NULL),(513,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17312','510','Banque C',0,NULL,NULL,1,NULL,NULL),(514,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17313','510','Banque D',0,NULL,NULL,1,NULL,NULL),(515,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1732','504','Crédits d\'acceptation',0,NULL,NULL,1,NULL,NULL),(516,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17320','515','Banque A',0,NULL,NULL,1,NULL,NULL),(517,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17321','515','Banque B',0,NULL,NULL,1,NULL,NULL),(518,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17322','515','Banque C',0,NULL,NULL,1,NULL,NULL),(519,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17323','515','Banque D',0,NULL,NULL,1,NULL,NULL),(520,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','174','493','Autres emprunts',0,NULL,NULL,1,NULL,NULL),(521,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175','493','Dettes commerciales',0,NULL,NULL,1,NULL,NULL),(522,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1750','521','Fournisseurs : dettes en compte',0,NULL,NULL,1,NULL,NULL),(523,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17500','522','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(524,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175000','523','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(525,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175001','523','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(526,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17501','522','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(527,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175010','526','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(528,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175011','526','Fournisseurs C.E.E.',0,NULL,NULL,1,NULL,NULL),(529,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175012','526','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(530,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1751','521','Effets à payer',0,NULL,NULL,1,NULL,NULL),(531,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17510','530','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(532,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175100','531','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(533,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175101','531','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(534,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17511','530','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(535,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175110','534','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(536,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175111','534','Fournisseurs C.E.E.',0,NULL,NULL,1,NULL,NULL),(537,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175112','534','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(538,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','176','493','Acomptes reçus sur commandes',0,NULL,NULL,1,NULL,NULL),(539,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','178','493','Cautionnements reçus en numéraires',0,NULL,NULL,1,NULL,NULL),(540,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','179','493','Dettes diverses',0,NULL,NULL,1,NULL,NULL),(541,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1790','540','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(542,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1791','540','Autres entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(543,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1792','540','Administrateurs, gérants et associés',0,NULL,NULL,1,NULL,NULL),(544,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1794','540','Rentes viagères capitalisées',0,NULL,NULL,1,NULL,NULL),(545,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1798','540','Dettes envers les coparticipants des associations momentanées et en participation',0,NULL,NULL,1,NULL,NULL),(546,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1799','540','Autres dettes diverses',0,NULL,NULL,1,NULL,NULL),(547,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','18','1351','Comptes de liaison des établissements et succursales',0,NULL,NULL,1,NULL,NULL),(548,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','20','1352','Frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(549,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','200','548','Frais de constitution et d\'augmentation de capital',0,NULL,NULL,1,NULL,NULL),(550,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2000','549','Frais de constitution et d\'augmentation de capital',0,NULL,NULL,1,NULL,NULL),(551,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2009','549','Amortissements sur frais de constitution et d\'augmentation de capital',0,NULL,NULL,1,NULL,NULL),(552,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','201','548','Frais d\'émission d\'emprunts et primes de remboursement',0,NULL,NULL,1,NULL,NULL),(553,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2010','552','Agios sur emprunts et frais d\'émission d\'emprunts',0,NULL,NULL,1,NULL,NULL),(554,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2019','552','Amortissements sur agios sur emprunts et frais d\'émission d\'emprunts',0,NULL,NULL,1,NULL,NULL),(555,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','202','548','Autres frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(556,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2020','555','Autres frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(557,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2029','555','Amortissements sur autres frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(558,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','203','548','Intérêts intercalaires',0,NULL,NULL,1,NULL,NULL),(559,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2030','558','Intérêts intercalaires',0,NULL,NULL,1,NULL,NULL),(560,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2039','558','Amortissements sur intérêts intercalaires',0,NULL,NULL,1,NULL,NULL),(561,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','204','548','Frais de restructuration',0,NULL,NULL,1,NULL,NULL),(562,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2040','561','Coût des frais de restructuration',0,NULL,NULL,1,NULL,NULL),(563,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2049','561','Amortissements sur frais de restructuration',0,NULL,NULL,1,NULL,NULL),(564,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','21','1352','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(565,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','210','564','Frais de recherche et de développement',0,NULL,NULL,1,NULL,NULL),(566,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2100','565','Frais de recherche et de mise au point',0,NULL,NULL,1,NULL,NULL),(567,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2108','565','Plus-values actées sur frais de recherche et de mise au point',0,NULL,NULL,1,NULL,NULL),(568,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2109','565','Amortissements sur frais de recherche et de mise au point',0,NULL,NULL,1,NULL,NULL),(569,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','211','564','Concessions, brevets, licences, savoir-faire, marque et droits similaires',0,NULL,NULL,1,NULL,NULL),(570,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2110','569','Concessions, brevets, licences, marques, etc',0,NULL,NULL,1,NULL,NULL),(571,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2118','569','Plus-values actées sur concessions, etc',0,NULL,NULL,1,NULL,NULL),(572,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2119','569','Amortissements sur concessions, etc',0,NULL,NULL,1,NULL,NULL),(573,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','212','564','Goodwill',0,NULL,NULL,1,NULL,NULL),(574,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2120','573','Coût d\'acquisition',0,NULL,NULL,1,NULL,NULL),(575,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2128','573','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(576,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2129','573','Amortissements sur goodwill',0,NULL,NULL,1,NULL,NULL),(577,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','213','564','Acomptes versés',0,NULL,NULL,1,NULL,NULL),(578,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22','1352','Terrains et constructions',0,NULL,NULL,1,NULL,NULL),(579,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','220','578','Terrains',0,NULL,NULL,1,NULL,NULL),(580,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2200','579','Terrains',0,NULL,NULL,1,NULL,NULL),(581,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2201','579','Frais d\'acquisition sur terrains',0,NULL,NULL,1,NULL,NULL),(582,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2208','579','Plus-values actées sur terrains',0,NULL,NULL,1,NULL,NULL),(583,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2209','579','Amortissements et réductions de valeur',0,NULL,NULL,1,NULL,NULL),(584,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22090','583','Amortissements sur frais d\'acquisition',0,NULL,NULL,1,NULL,NULL),(585,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22091','583','Réductions de valeur sur terrains',0,NULL,NULL,1,NULL,NULL),(586,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','221','578','Constructions',0,NULL,NULL,1,NULL,NULL),(587,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2210','586','Bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(588,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2211','586','Bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(589,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2212','586','Autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(590,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2213','586','Voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(591,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2215','586','Constructions sur sol d\'autrui',0,NULL,NULL,1,NULL,NULL),(592,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2216','586','Frais d\'acquisition sur constructions',0,NULL,NULL,1,NULL,NULL),(593,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2218','586','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(594,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22180','593','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(595,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22181','593','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(596,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22182','593','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(597,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22184','593','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(598,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2219','586','Amortissements sur constructions',0,NULL,NULL,1,NULL,NULL),(599,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22190','598','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(600,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22191','598','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(601,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22192','598','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(602,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22194','598','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(603,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22195','598','Sur constructions sur sol d\'autrui',0,NULL,NULL,1,NULL,NULL),(604,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22196','598','Sur frais d\'acquisition sur constructions',0,NULL,NULL,1,NULL,NULL),(605,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','222','578','Terrains bâtis',0,NULL,NULL,1,NULL,NULL),(606,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2220','605','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(607,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22200','606','Bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(608,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22201','606','Bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(609,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22202','606','Autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(610,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22203','606','Voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(611,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22204','606','Frais d\'acquisition des terrains à bâtir',0,NULL,NULL,1,NULL,NULL),(612,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2228','605','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(613,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22280','612','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(614,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22281','612','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(615,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22282','612','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(616,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22283','612','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(617,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2229','605','Amortissements sur terrains bâtis',0,NULL,NULL,1,NULL,NULL),(618,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22290','617','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(619,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22291','617','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(620,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22292','617','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(621,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22293','617','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(622,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22294','617','Sur frais d\'acquisition des terrains bâtis',0,NULL,NULL,1,NULL,NULL),(623,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','223','578','Autres droits réels sur des immeubles',0,NULL,NULL,1,NULL,NULL),(624,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2230','623','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(625,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2238','623','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(626,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2239','623','Amortissements',0,NULL,NULL,1,NULL,NULL),(627,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','23','1352','Installations, machines et outillages',0,NULL,NULL,1,NULL,NULL),(628,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','230','627','Installations',0,NULL,NULL,1,NULL,NULL),(629,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2300','628','Installations bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(630,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2301','628','Installations bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(631,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2302','628','Installations bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(632,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2303','628','Installations voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(633,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2300','628','Installation d\'eau',0,NULL,NULL,1,NULL,NULL),(634,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2301','628','Installation d\'électricité',0,NULL,NULL,1,NULL,NULL),(635,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2302','628','Installation de vapeur',0,NULL,NULL,1,NULL,NULL),(636,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2303','628','Installation de gaz',0,NULL,NULL,1,NULL,NULL),(637,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2304','628','Installation de chauffage',0,NULL,NULL,1,NULL,NULL),(638,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2305','628','Installation de conditionnement d\'air',0,NULL,NULL,1,NULL,NULL),(639,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2306','628','Installation de chargement',0,NULL,NULL,1,NULL,NULL),(640,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','231','627','Machines',0,NULL,NULL,1,NULL,NULL),(641,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2310','640','Division A',0,NULL,NULL,1,NULL,NULL),(642,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2311','640','Division B',0,NULL,NULL,1,NULL,NULL),(643,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2312','640','Division C',0,NULL,NULL,1,NULL,NULL),(644,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','237','627','Outillage',0,NULL,NULL,1,NULL,NULL),(645,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2370','644','Division A',0,NULL,NULL,1,NULL,NULL),(646,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2371','644','Division B',0,NULL,NULL,1,NULL,NULL),(647,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2372','644','Division C',0,NULL,NULL,1,NULL,NULL),(648,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','238','627','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(649,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2380','648','Sur installations',0,NULL,NULL,1,NULL,NULL),(650,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2381','648','Sur machines',0,NULL,NULL,1,NULL,NULL),(651,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2382','648','Sur outillage',0,NULL,NULL,1,NULL,NULL),(652,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','239','627','Amortissements',0,NULL,NULL,1,NULL,NULL),(653,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2390','652','Sur installations',0,NULL,NULL,1,NULL,NULL),(654,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2391','652','Sur machines',0,NULL,NULL,1,NULL,NULL),(655,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2392','652','Sur outillage',0,NULL,NULL,1,NULL,NULL),(656,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24','1352','Mobilier et matériel roulant',0,NULL,NULL,1,NULL,NULL),(657,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','240','656','Mobilier',0,NULL,NULL,1,NULL,NULL),(658,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2400','656','Mobilier',0,NULL,NULL,1,NULL,NULL),(659,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24000','658','Mobilier des bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(660,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24001','658','Mobilier des bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(661,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24002','658','Mobilier des autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(662,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24003','658','Mobilier oeuvres sociales',0,NULL,NULL,1,NULL,NULL),(663,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2401','657','Matériel de bureau et de service social',0,NULL,NULL,1,NULL,NULL),(664,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24010','663','Des bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(665,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24011','663','Des bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(666,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24012','663','Des autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(667,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24013','663','Des oeuvres sociales',0,NULL,NULL,1,NULL,NULL),(668,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2408','657','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(669,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24080','668','Plus-values actées sur mobilier',0,NULL,NULL,1,NULL,NULL),(670,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24081','668','Plus-values actées sur matériel de bureau et service social',0,NULL,NULL,1,NULL,NULL),(671,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2409','657','Amortissements',0,NULL,NULL,1,NULL,NULL),(672,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24090','671','Amortissements sur mobilier',0,NULL,NULL,1,NULL,NULL),(673,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24091','671','Amortissements sur matériel de bureau et service social',0,NULL,NULL,1,NULL,NULL),(674,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','241','656','Matériel roulant',0,NULL,NULL,1,NULL,NULL),(675,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2410','674','Matériel automobile',0,NULL,NULL,1,NULL,NULL),(676,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24100','675','Voitures',0,NULL,NULL,1,NULL,NULL),(677,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24105','675','Camions',0,NULL,NULL,1,NULL,NULL),(678,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2411','674','Matériel ferroviaire',0,NULL,NULL,1,NULL,NULL),(679,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2412','674','Matériel fluvial',0,NULL,NULL,1,NULL,NULL),(680,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2413','674','Matériel naval',0,NULL,NULL,1,NULL,NULL),(681,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2414','674','Matériel aérien',0,NULL,NULL,1,NULL,NULL),(682,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2418','674','Plus-values sur matériel roulant',0,NULL,NULL,1,NULL,NULL),(683,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24180','682','Plus-values sur matériel automobile',0,NULL,NULL,1,NULL,NULL),(684,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24181','682','Idem sur matériel ferroviaire',0,NULL,NULL,1,NULL,NULL),(685,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24182','682','Idem sur matériel fluvial',0,NULL,NULL,1,NULL,NULL),(686,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24183','682','Idem sur matériel naval',0,NULL,NULL,1,NULL,NULL),(687,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24184','682','Idem sur matériel aérien',0,NULL,NULL,1,NULL,NULL),(688,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2419','674','Amortissements sur matériel roulant',0,NULL,NULL,1,NULL,NULL),(689,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24190','688','Amortissements sur matériel automobile',0,NULL,NULL,1,NULL,NULL),(690,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24191','688','Idem sur matériel ferroviaire',0,NULL,NULL,1,NULL,NULL),(691,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24192','688','Idem sur matériel fluvial',0,NULL,NULL,1,NULL,NULL),(692,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24193','688','Idem sur matériel naval',0,NULL,NULL,1,NULL,NULL),(693,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24194','688','Idem sur matériel aérien',0,NULL,NULL,1,NULL,NULL),(694,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','25','1352','Immobilisation détenues en location-financement et droits similaires',0,NULL,NULL,1,NULL,NULL),(695,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','250','694','Terrains et constructions',0,NULL,NULL,1,NULL,NULL),(696,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2500','695','Terrains',0,NULL,NULL,1,NULL,NULL),(697,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2501','695','Constructions',0,NULL,NULL,1,NULL,NULL),(698,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2508','695','Plus-values sur emphytéose, leasing et droits similaires : terrains et constructions',0,NULL,NULL,1,NULL,NULL),(699,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2509','695','Amortissements et réductions de valeur sur terrains et constructions en leasing',0,NULL,NULL,1,NULL,NULL),(700,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','251','694','Installations, machines et outillage',0,NULL,NULL,1,NULL,NULL),(701,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2510','700','Installations',0,NULL,NULL,1,NULL,NULL),(702,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2511','700','Machines',0,NULL,NULL,1,NULL,NULL),(703,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2512','700','Outillage',0,NULL,NULL,1,NULL,NULL),(704,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2518','700','Plus-values actées sur installations machines et outillage pris en leasing',0,NULL,NULL,1,NULL,NULL),(705,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2519','700','Amortissements sur installations machines et outillage pris en leasing',0,NULL,NULL,1,NULL,NULL),(706,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','252','694','Mobilier et matériel roulant',0,NULL,NULL,1,NULL,NULL),(707,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2520','706','Mobilier',0,NULL,NULL,1,NULL,NULL),(708,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2521','706','Matériel roulant',0,NULL,NULL,1,NULL,NULL),(709,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2528','706','Plus-values actées sur mobilier et matériel roulant en leasing',0,NULL,NULL,1,NULL,NULL),(710,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2529','706','Amortissements sur mobilier et matériel roulant en leasing',0,NULL,NULL,1,NULL,NULL),(711,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','26','1352','Autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(712,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','260','711','Frais d\'aménagements de locaux pris en location',0,NULL,NULL,1,NULL,NULL),(713,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','261','711','Maison d\'habitation',0,NULL,NULL,1,NULL,NULL),(714,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','262','711','Réserve immobilière',0,NULL,NULL,1,NULL,NULL),(715,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','263','711','Matériel d\'emballage',0,NULL,NULL,1,NULL,NULL),(716,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','264','711','Emballages récupérables',0,NULL,NULL,1,NULL,NULL),(717,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','268','711','Plus-values actées sur autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(718,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','269','711','Amortissements sur autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(719,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2690','718','Amortissements sur frais d\'aménagement des locaux pris en location',0,NULL,NULL,1,NULL,NULL),(720,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2691','718','Amortissements sur maison d\'habitation',0,NULL,NULL,1,NULL,NULL),(721,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2692','718','Amortissements sur réserve immobilière',0,NULL,NULL,1,NULL,NULL),(722,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2693','718','Amortissements sur matériel d\'emballage',0,NULL,NULL,1,NULL,NULL),(723,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2694','718','Amortissements sur emballages récupérables',0,NULL,NULL,1,NULL,NULL),(724,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','27','1352','Immobilisations corporelles en cours et acomptes versés',0,NULL,NULL,1,NULL,NULL),(725,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','270','724','Immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(726,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2700','725','Constructions',0,NULL,NULL,1,NULL,NULL),(727,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2701','725','Installations machines et outillage',0,NULL,NULL,1,NULL,NULL),(728,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2702','725','Mobilier et matériel roulant',0,NULL,NULL,1,NULL,NULL),(729,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2703','725','Autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(730,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','271','724','Avances et acomptes versés sur immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(731,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','28','1352','Immobilisations financières',0,NULL,NULL,1,NULL,NULL),(732,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','280','731','Participations dans des entreprises liées',0,NULL,NULL,1,NULL,NULL),(733,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2800','732','Valeur d\'acquisition (peut être subdivisé par participation)',0,NULL,NULL,1,NULL,NULL),(734,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2801','732','Montants non appelés (idem)',0,NULL,NULL,1,NULL,NULL),(735,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2808','732','Plus-values actées (idem)',0,NULL,NULL,1,NULL,NULL),(736,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2809','732','Réductions de valeurs actées (idem)',0,NULL,NULL,1,NULL,NULL),(737,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','281','731','Créances sur des entreprises liées',0,NULL,NULL,1,NULL,NULL),(738,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2810','737','Créances en compte',0,NULL,NULL,1,NULL,NULL),(739,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2811','737','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(740,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2812','737','Titres à revenu fixes',0,NULL,NULL,1,NULL,NULL),(741,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2817','737','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(742,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2819','737','Réductions de valeurs actées',0,NULL,NULL,1,NULL,NULL),(743,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','282','731','Participations dans des entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(744,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2820','743','Valeur d\'acquisition (peut être subdivisé par participation)',0,NULL,NULL,1,NULL,NULL),(745,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2821','743','Montants non appelés (idem)',0,NULL,NULL,1,NULL,NULL),(746,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2828','743','Plus-values actées (idem)',0,NULL,NULL,1,NULL,NULL),(747,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2829','743','Réductions de valeurs actées (idem)',0,NULL,NULL,1,NULL,NULL),(748,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','283','731','Créances sur des entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(749,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2830','748','Créances en compte',0,NULL,NULL,1,NULL,NULL),(750,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2831','748','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(751,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2832','748','Titres à revenu fixe',0,NULL,NULL,1,NULL,NULL),(752,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2837','748','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(753,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2839','748','Réductions de valeurs actées',0,NULL,NULL,1,NULL,NULL),(754,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','284','731','Autres actions et parts',0,NULL,NULL,1,NULL,NULL),(755,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2840','754','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(756,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2841','754','Montants non appelés',0,NULL,NULL,1,NULL,NULL),(757,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2848','754','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(758,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2849','754','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(759,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','285','731','Autres créances',0,NULL,NULL,1,NULL,NULL),(760,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2850','759','Créances en compte',0,NULL,NULL,1,NULL,NULL),(761,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2851','759','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(762,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2852','759','Titres à revenu fixe',0,NULL,NULL,1,NULL,NULL),(763,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2857','759','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(764,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2859','759','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(765,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','288','731','Cautionnements versés en numéraires',0,NULL,NULL,1,NULL,NULL),(766,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2880','765','Téléphone, téléfax, télex',0,NULL,NULL,1,NULL,NULL),(767,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2881','765','Gaz',0,NULL,NULL,1,NULL,NULL),(768,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2882','765','Eau',0,NULL,NULL,1,NULL,NULL),(769,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2883','765','Electricité',0,NULL,NULL,1,NULL,NULL),(770,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2887','765','Autres cautionnements versés en numéraires',0,NULL,NULL,1,NULL,NULL),(771,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29','1352','Créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(772,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','290','771','Créances commerciales',0,NULL,NULL,1,NULL,NULL),(773,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2900','772','Clients',0,NULL,NULL,1,NULL,NULL),(774,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29000','773','Créances en compte sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(775,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29001','773','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(776,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29002','773','Sur clients Belgique',0,NULL,NULL,1,NULL,NULL),(777,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29003','773','Sur clients C.E.E.',0,NULL,NULL,1,NULL,NULL),(778,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29004','773','Sur clients exportation hors C.E.E.',0,NULL,NULL,1,NULL,NULL),(779,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29005','773','Créances sur les coparticipants (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(780,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2901','772','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(781,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29010','780','Sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(782,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29011','780','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(783,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29012','780','Sur clients Belgique',0,NULL,NULL,1,NULL,NULL),(784,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29013','780','Sur clients C.E.E.',0,NULL,NULL,1,NULL,NULL),(785,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29014','780','Sur clients exportation hors C.E.E.',0,NULL,NULL,1,NULL,NULL),(786,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2905','772','Retenues sur garanties',0,NULL,NULL,1,NULL,NULL),(787,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2906','772','Acomptes versés',0,NULL,NULL,1,NULL,NULL),(788,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2907','772','Créances douteuses (à ventiler comme clients 2900)',0,NULL,NULL,1,NULL,NULL),(789,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2909','772','Réductions de valeur actées (à ventiler comme clients 2900)',0,NULL,NULL,1,NULL,NULL),(790,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','291','771','Autres créances',0,NULL,NULL,1,NULL,NULL),(791,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2910','790','Créances en compte',0,NULL,NULL,1,NULL,NULL),(792,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29100','791','Sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(793,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29101','791','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(794,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29102','791','Sur autres débiteurs',0,NULL,NULL,1,NULL,NULL),(795,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2911','790','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(796,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29110','795','Sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(797,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29111','795','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(798,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29112','795','Sur autres débiteurs',0,NULL,NULL,1,NULL,NULL),(799,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2912','790','Créances résultant de la cession d\'immobilisations données en leasing',0,NULL,NULL,1,NULL,NULL),(800,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2917','790','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(801,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2919','790','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(802,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','30','1353','Approvisionnements - matières premières',0,NULL,NULL,1,NULL,NULL),(803,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','300','802','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(804,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','309','802','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(805,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','31','1353','Approvsionnements et fournitures',0,NULL,NULL,1,NULL,NULL),(806,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','310','805','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(807,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3100','806','Matières d\'approvisionnement',0,NULL,NULL,1,NULL,NULL),(808,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3101','806','Energie, charbon, coke, mazout, essence, propane',0,NULL,NULL,1,NULL,NULL),(809,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3102','806','Produits d\'entretien',0,NULL,NULL,1,NULL,NULL),(810,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3103','806','Fournitures diverses et petit outillage',0,NULL,NULL,1,NULL,NULL),(811,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3104','806','Imprimés et fournitures de bureau',0,NULL,NULL,1,NULL,NULL),(812,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3105','806','Fournitures de services sociaux',0,NULL,NULL,1,NULL,NULL),(813,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3106','806','Emballages commerciaux',0,NULL,NULL,1,NULL,NULL),(814,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','31060','813','Emballages perdus',0,NULL,NULL,1,NULL,NULL),(815,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','31061','813','Emballages récupérables',0,NULL,NULL,1,NULL,NULL),(816,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','319','805','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(817,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','32','1353','En cours de fabrication',0,NULL,NULL,1,NULL,NULL),(818,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','320','817','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(819,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3200','818','Produits semi-ouvrés',0,NULL,NULL,1,NULL,NULL),(820,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3201','818','Produits en cours de fabrication',0,NULL,NULL,1,NULL,NULL),(821,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3202','818','Travaux en cours',0,NULL,NULL,1,NULL,NULL),(822,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3205','818','Déchets',0,NULL,NULL,1,NULL,NULL),(823,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3206','818','Rebuts',0,NULL,NULL,1,NULL,NULL),(824,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3209','818','Travaux en association momentanée',0,NULL,NULL,1,NULL,NULL),(825,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','329','817','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(826,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','33','1353','Produits finis',0,NULL,NULL,1,NULL,NULL),(827,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','330','826','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(828,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3300','827','Produits finis',0,NULL,NULL,1,NULL,NULL),(829,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','339','826','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(830,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','34','1353','Marchandises',0,NULL,NULL,1,NULL,NULL),(831,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','340','830','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(832,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3400','831','Groupe A',0,NULL,NULL,1,NULL,NULL),(833,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3401','831','Groupe B',0,NULL,NULL,1,NULL,NULL),(834,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3402','831','Groupe C',0,NULL,NULL,1,NULL,NULL),(835,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','349','830','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(836,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','35','1353','Immeubles destinés à la vente',0,NULL,NULL,1,NULL,NULL),(837,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','350','836','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(838,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3500','837','Immeuble A',0,NULL,NULL,1,NULL,NULL),(839,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3501','837','Immeuble B',0,NULL,NULL,1,NULL,NULL),(840,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3502','837','Immeuble C',0,NULL,NULL,1,NULL,NULL),(841,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','351','836','Immeubles construits en vue de leur revente',0,NULL,NULL,1,NULL,NULL),(842,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3510','841','Immeuble A',0,NULL,NULL,1,NULL,NULL),(843,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3511','841','Immeuble B',0,NULL,NULL,1,NULL,NULL),(844,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3512','841','Immeuble C',0,NULL,NULL,1,NULL,NULL),(845,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','359','836','Réductions de valeurs actées',0,NULL,NULL,1,NULL,NULL),(846,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','36','1353','Acomptes versés sur achats pour stocks',0,NULL,NULL,1,NULL,NULL),(847,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','360','846','Acomptes versés (à ventiler éventuellement par catégorie)',0,NULL,NULL,1,NULL,NULL),(848,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','369','846','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(849,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','37','1353','Commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(850,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','370','849','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(851,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','371','849','Bénéfice pris en compte',0,NULL,NULL,1,NULL,NULL),(852,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','379','849','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(853,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','40','1354','Créances commerciales',0,NULL,NULL,1,NULL,NULL),(854,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','400','853','Clients',0,NULL,NULL,1,NULL,NULL),(855,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4007','854','Rabais, remises et ristournes à accorder et autres notes de crédit à établir',0,NULL,NULL,1,NULL,NULL),(856,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4008','854','Créances résultant de livraisons de biens (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(857,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','401','853','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(858,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4010','857','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(859,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4013','857','Effets à l\'encaissement',0,NULL,NULL,1,NULL,NULL),(860,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4015','857','Effets à l\'escompte',0,NULL,NULL,1,NULL,NULL),(861,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','402','853','Clients, créances courantes, entreprises apparentées, administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(862,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4020','861','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(863,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4021','861','Autres entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(864,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4022','861','Administrateurs et gérants d\'entreprise',0,NULL,NULL,1,NULL,NULL),(865,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','403','853','Effets à recevoir sur entreprises apparentées et administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(866,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4030','865','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(867,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4031','865','Autres entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(868,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4032','865','Administrateurs et gérants de l\'entreprise',0,NULL,NULL,1,NULL,NULL),(869,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','404','853','Produits à recevoir (factures à établir)',0,NULL,NULL,1,NULL,NULL),(870,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','405','853','Clients : retenues sur garanties',0,NULL,NULL,1,NULL,NULL),(871,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','406','853','Acomptes versés',0,NULL,NULL,1,NULL,NULL),(872,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','407','853','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(873,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','408','853','Compensation clients',0,NULL,NULL,1,NULL,NULL),(874,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','409','853','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(875,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','41','1354','Autres créances',0,NULL,NULL,1,NULL,NULL),(876,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','410','875','Capital appelé, non versé',0,NULL,NULL,1,NULL,NULL),(877,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4100','876','Appels de fonds',0,NULL,NULL,1,NULL,NULL),(878,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4101','876','Actionnaires défaillants',0,NULL,NULL,1,NULL,NULL),(879,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','411','875','T.V.A. à récupérer',0,NULL,NULL,1,NULL,NULL),(880,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4110','879','T.V.A. due',0,NULL,NULL,1,NULL,NULL),(881,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4111','879','T.V.A. déductible',0,NULL,NULL,1,NULL,NULL),(882,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4112','879','Compte courant administration T.V.A.',0,NULL,NULL,1,NULL,NULL),(883,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4118','879','Taxe d\'égalisation due',0,NULL,NULL,1,NULL,NULL),(884,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','412','875','Impôts et versements fiscaux à récupérer',0,NULL,NULL,1,NULL,NULL),(885,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4120','884','Impôts belges sur le résultat',0,NULL,NULL,1,NULL,NULL),(886,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4125','884','Autres impôts belges',0,NULL,NULL,1,NULL,NULL),(887,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4128','884','Impôts étrangers',0,NULL,NULL,1,NULL,NULL),(888,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','414','875','Produits à recevoir',0,NULL,NULL,1,NULL,NULL),(889,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','416','875','Créances diverses',0,NULL,NULL,1,NULL,NULL),(890,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4160','889','Associés (compte d\'apport en société)',0,NULL,NULL,1,NULL,NULL),(891,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4161','889','Avances et prêts au personnel',0,NULL,NULL,1,NULL,NULL),(892,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4162','889','Compte courant des associés en S.P.R.L.',0,NULL,NULL,1,NULL,NULL),(893,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4163','889','Compte courant des administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(894,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4164','889','Créances sur sociétés apparentées',0,NULL,NULL,1,NULL,NULL),(895,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4166','889','Emballages et matériel à rendre',0,NULL,NULL,1,NULL,NULL),(896,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4167','889','Etat et établissements publics',0,NULL,NULL,1,NULL,NULL),(897,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','41670','896','Subsides à recevoir',0,NULL,NULL,1,NULL,NULL),(898,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','41671','896','Autres créances',0,NULL,NULL,1,NULL,NULL),(899,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4168','889','Rabais, ristournes et remises à obtenir et autres avoirs non encore reçus',0,NULL,NULL,1,NULL,NULL),(900,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','417','875','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(901,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','418','875','Cautionnements versés en numéraires',0,NULL,NULL,1,NULL,NULL),(902,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','419','875','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(903,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','42','1354','Dettes à plus d\'un an échéant dans l\'année',0,NULL,NULL,1,NULL,NULL),(904,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','420','903','Emprunts subordonnés',0,NULL,NULL,1,NULL,NULL),(905,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4200','904','Convertibles',0,NULL,NULL,1,NULL,NULL),(906,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4201','904','Non convertibles',0,NULL,NULL,1,NULL,NULL),(907,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','421','903','Emprunts obligataires non subordonnés',0,NULL,NULL,1,NULL,NULL),(908,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4210','907','Convertibles',0,NULL,NULL,1,NULL,NULL),(909,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4211','907','Non convertibles',0,NULL,NULL,1,NULL,NULL),(910,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','422','903','Dettes de location-financement et assimilées',0,NULL,NULL,1,NULL,NULL),(911,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4220','910','Financement de biens immobiliers',0,NULL,NULL,1,NULL,NULL),(912,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4221','910','Financement de biens mobiliers',0,NULL,NULL,1,NULL,NULL),(913,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','423','903','Etablissements de crédit',0,NULL,NULL,1,NULL,NULL),(914,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4230','913','Dettes en compte',0,NULL,NULL,1,NULL,NULL),(915,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4231','913','Promesses',0,NULL,NULL,1,NULL,NULL),(916,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4232','913','Crédits d\'acceptation',0,NULL,NULL,1,NULL,NULL),(917,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','424','903','Autres emprunts',0,NULL,NULL,1,NULL,NULL),(918,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','425','903','Dettes commerciales',0,NULL,NULL,1,NULL,NULL),(919,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4250','918','Fournisseurs',0,NULL,NULL,1,NULL,NULL),(920,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4251','918','Effets à payer',0,NULL,NULL,1,NULL,NULL),(921,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','426','903','Cautionnements reçus en numéraires',0,NULL,NULL,1,NULL,NULL),(922,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','429','903','Dettes diverses',0,NULL,NULL,1,NULL,NULL),(923,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4290','922','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(924,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4291','922','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(925,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4292','922','Administrateurs, gérants, associés',0,NULL,NULL,1,NULL,NULL),(926,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4299','922','Autres dettes',0,NULL,NULL,1,NULL,NULL),(927,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','43','1354','Dettes financières',0,NULL,NULL,1,NULL,NULL),(928,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','430','927','Etablissements de crédit. Emprunts en compte à terme fixe',0,NULL,NULL,1,NULL,NULL),(929,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','431','927','Etablissements de crédit. Promesses',0,NULL,NULL,1,NULL,NULL),(930,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','432','927','Etablissements de crédit. Crédits d\'acceptation',0,NULL,NULL,1,NULL,NULL),(931,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','433','927','Etablissements de crédit. Dettes en compte courant',0,NULL,NULL,1,NULL,NULL),(932,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','439','927','Autres emprunts',0,NULL,NULL,1,NULL,NULL),(933,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44','1354','Dettes commerciales',0,NULL,NULL,1,NULL,NULL),(934,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','440','933','Fournisseurs',0,NULL,NULL,1,NULL,NULL),(935,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4400','934','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(936,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44000','935','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(937,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44001','935','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(938,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4401','934','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(939,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44010','938','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(940,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44011','938','Fournisseurs CEE',0,NULL,NULL,1,NULL,NULL),(941,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44012','938','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(942,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4402','934','Dettes envers les coparticipants (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(943,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4403','934','Fournisseurs - retenues de garanties',0,NULL,NULL,1,NULL,NULL),(944,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','441','933','Effets à payer',0,NULL,NULL,1,NULL,NULL),(945,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4410','944','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(946,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44100','945','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(947,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44101','945','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(948,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4411','944','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(949,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44110','948','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(950,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44111','948','Fournisseurs CEE',0,NULL,NULL,1,NULL,NULL),(951,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44112','948','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(952,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','444','933','Factures à recevoir',0,NULL,NULL,1,NULL,NULL),(953,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','446','933','Acomptes reçus',0,NULL,NULL,1,NULL,NULL),(954,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','448','933','Compensations fournisseurs',0,NULL,NULL,1,NULL,NULL),(955,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45','1354','Dettes fiscales, salariales et sociales',0,NULL,NULL,1,NULL,NULL),(956,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','450','955','Dettes fiscales estimées',0,NULL,NULL,1,NULL,NULL),(957,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4501','956','Impôts sur le résultat',0,NULL,NULL,1,NULL,NULL),(958,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4505','956','Autres impôts en Belgique',0,NULL,NULL,1,NULL,NULL),(959,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4508','956','Impôts à l\'étranger',0,NULL,NULL,1,NULL,NULL),(960,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','451','955','T.V.A. à payer',0,NULL,NULL,1,NULL,NULL),(961,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4510','960','T.V.A. due',0,NULL,NULL,1,NULL,NULL),(962,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4511','960','T.V.A. déductible',0,NULL,NULL,1,NULL,NULL),(963,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4512','960','Compte courant administration T.V.A.',0,NULL,NULL,1,NULL,NULL),(964,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4518','960','Taxe d\'égalisation due',0,NULL,NULL,1,NULL,NULL),(965,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','452','955','Impôts et taxes à payer',0,NULL,NULL,1,NULL,NULL),(966,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4520','965','Autres impôts sur le résultat',0,NULL,NULL,1,NULL,NULL),(967,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4525','965','Autres impôts et taxes en Belgique',0,NULL,NULL,1,NULL,NULL),(968,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45250','967','Précompte immobilier',0,NULL,NULL,1,NULL,NULL),(969,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45251','967','Impôts communaux à payer',0,NULL,NULL,1,NULL,NULL),(970,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45252','967','Impôts provinciaux à payer',0,NULL,NULL,1,NULL,NULL),(971,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45253','967','Autres impôts et taxes à payer',0,NULL,NULL,1,NULL,NULL),(972,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4528','965','Impôts et taxes à l\'étranger',0,NULL,NULL,1,NULL,NULL),(973,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','453','955','Précomptes retenus',0,NULL,NULL,1,NULL,NULL),(974,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4530','973','Précompte professionnel retenu sur rémunérations',0,NULL,NULL,1,NULL,NULL),(975,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4531','973','Précompte professionnel retenu sur tantièmes',0,NULL,NULL,1,NULL,NULL),(976,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4532','973','Précompte mobilier retenu sur dividendes attribués',0,NULL,NULL,1,NULL,NULL),(977,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4533','973','Précompte mobilier retenu sur intérêts payés',0,NULL,NULL,1,NULL,NULL),(978,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4538','973','Autres précomptes retenus',0,NULL,NULL,1,NULL,NULL),(979,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','454','955','Office National de la Sécurité Sociale',0,NULL,NULL,1,NULL,NULL),(980,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4540','979','Arriérés',0,NULL,NULL,1,NULL,NULL),(981,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4541','979','1er trimestre',0,NULL,NULL,1,NULL,NULL),(982,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4542','979','2ème trimestre',0,NULL,NULL,1,NULL,NULL),(983,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4543','979','3ème trimestre',0,NULL,NULL,1,NULL,NULL),(984,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4544','979','4ème trimestre',0,NULL,NULL,1,NULL,NULL),(985,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','455','955','Rémunérations',0,NULL,NULL,1,NULL,NULL),(986,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4550','985','Administrateurs, gérants et commissaires (non réviseurs)',0,NULL,NULL,1,NULL,NULL),(987,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4551','985','Direction',0,NULL,NULL,1,NULL,NULL),(988,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4552','985','Employés',0,NULL,NULL,1,NULL,NULL),(989,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4553','985','Ouvriers',0,NULL,NULL,1,NULL,NULL),(990,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','456','955','Pécules de vacances',0,NULL,NULL,1,NULL,NULL),(991,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4560','990','Direction',0,NULL,NULL,1,NULL,NULL),(992,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4561','990','Employés',0,NULL,NULL,1,NULL,NULL),(993,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4562','990','Ouvriers',0,NULL,NULL,1,NULL,NULL),(994,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','459','955','Autres dettes sociales',0,NULL,NULL,1,NULL,NULL),(995,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4590','994','Provision pour gratifications de fin d\'année',0,NULL,NULL,1,NULL,NULL),(996,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4591','994','Départs de personnel',0,NULL,NULL,1,NULL,NULL),(997,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4592','994','Oppositions sur rémunérations',0,NULL,NULL,1,NULL,NULL),(998,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4593','994','Assurances relatives au personnel',0,NULL,NULL,1,NULL,NULL),(999,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45930','998','Assurance loi',0,NULL,NULL,1,NULL,NULL),(1000,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45931','998','Assurance salaire garanti',0,NULL,NULL,1,NULL,NULL),(1001,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45932','998','Assurance groupe',0,NULL,NULL,1,NULL,NULL),(1002,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45933','998','Assurances individuelles',0,NULL,NULL,1,NULL,NULL),(1003,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4594','994','Caisse d\'assurances sociales pour travailleurs indépendants',0,NULL,NULL,1,NULL,NULL),(1004,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4597','994','Dettes et provisions sociales diverses',0,NULL,NULL,1,NULL,NULL),(1005,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','46','1354','Acomptes reçus sur commande',0,NULL,NULL,1,NULL,NULL),(1006,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','47','1354','Dettes découlant de l\'affectation des résultats',0,NULL,NULL,1,NULL,NULL),(1007,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','470','1006','Dividendes et tantièmes d\'exercices antérieurs',0,NULL,NULL,1,NULL,NULL),(1008,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','471','1006','Dividendes de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1009,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','472','1006','Tantièmes de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1010,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','473','1006','Autres allocataires',0,NULL,NULL,1,NULL,NULL),(1011,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','48','4','Dettes diverses',0,NULL,NULL,1,NULL,NULL),(1012,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','480','1011','Obligations et coupons échus',0,NULL,NULL,1,NULL,NULL),(1013,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','481','1011','Actionnaires - capital à rembourser',0,NULL,NULL,1,NULL,NULL),(1014,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','482','1011','Participation du personnel à payer',0,NULL,NULL,1,NULL,NULL),(1015,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','483','1011','Acomptes reçus d\'autres tiers à moins d\'un an',0,NULL,NULL,1,NULL,NULL),(1016,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','486','1011','Emballages et matériel consignés',0,NULL,NULL,1,NULL,NULL),(1017,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','488','1011','Cautionnements reçus en numéraires',0,NULL,NULL,1,NULL,NULL),(1018,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','489','1011','Autres dettes diverses',0,NULL,NULL,1,NULL,NULL),(1019,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49','1354','Comptes de régularisation et compte d\'attente',0,NULL,NULL,1,NULL,NULL),(1020,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','490','1019','Charges à reporter (à subdiviser par catégorie de charges)',0,NULL,NULL,1,NULL,NULL),(1021,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','491','1019','Produits acquis',0,NULL,NULL,1,NULL,NULL),(1022,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4910','1021','Produits d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1023,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49100','1022','Ristournes et rabais à obtenir',0,NULL,NULL,1,NULL,NULL),(1024,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49101','1022','Commissions à obtenir',0,NULL,NULL,1,NULL,NULL),(1025,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49102','1022','Autres produits d\'exploitation (redevances par exemple)',0,NULL,NULL,1,NULL,NULL),(1026,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4911','1021','Produits financiers',0,NULL,NULL,1,NULL,NULL),(1027,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49110','1026','Intérêts courus et non échus sur prêts et débits',0,NULL,NULL,1,NULL,NULL),(1028,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49111','1026','Autres produits financiers',0,NULL,NULL,1,NULL,NULL),(1029,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','492','1019','Charges à imputer (à subdiviser par catégorie de charges)',0,NULL,NULL,1,NULL,NULL),(1030,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','493','1019','Produits à reporter',0,NULL,NULL,1,NULL,NULL),(1031,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4930','1030','Produits d\'exploitation à reporter',0,NULL,NULL,1,NULL,NULL),(1032,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4931','1030','Produits financiers à reporter',0,NULL,NULL,1,NULL,NULL),(1033,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','499','1019','Comptes d\'attente',0,NULL,NULL,1,NULL,NULL),(1034,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4990','1033','Compte d\'attente',0,NULL,NULL,1,NULL,NULL),(1035,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4991','1033','Compte de répartition périodique des charges',0,NULL,NULL,1,NULL,NULL),(1036,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4999','1033','Transferts d\'exercice',0,NULL,NULL,1,NULL,NULL),(1037,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','50','1355','Actions propres',0,NULL,NULL,1,NULL,NULL),(1038,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','51','1355','Actions et parts',0,NULL,NULL,1,NULL,NULL),(1039,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','510','1038','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(1040,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','511','1038','Montants non appelés',0,NULL,NULL,1,NULL,NULL),(1041,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','519','1038','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1042,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','52','1355','Titres à revenus fixes',0,NULL,NULL,1,NULL,NULL),(1043,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','520','1042','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(1044,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','529','1042','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1045,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','53','1355','Dépots à terme',0,NULL,NULL,1,NULL,NULL),(1046,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','530','1045','De plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1047,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','531','1045','De plus d\'un mois et à un an au plus',0,NULL,NULL,1,NULL,NULL),(1048,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','532','1045','d\'un mois au plus',0,NULL,NULL,1,NULL,NULL),(1049,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','539','1045','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1050,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','54','1355','Valeurs échues à l\'encaissement',0,NULL,NULL,1,NULL,NULL),(1051,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','540','1050','Chèques à encaisser',0,NULL,NULL,1,NULL,NULL),(1052,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','541','1050','Coupons à encaisser',0,NULL,NULL,1,NULL,NULL),(1053,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','55','1355','Etablissements de crédit - Comptes ouverts auprès des divers établissements.',0,NULL,NULL,1,NULL,NULL),(1054,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','550','1053','Comptes courants',0,NULL,NULL,1,NULL,NULL),(1055,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','551','1053','Chèques émis',0,NULL,NULL,1,NULL,NULL),(1056,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','559','1053','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1057,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','56','1355','Office des chèques postaux',0,NULL,NULL,1,NULL,NULL),(1058,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','560','1057','Compte courant',0,NULL,NULL,1,NULL,NULL),(1059,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','561','1057','Chèques émis',0,NULL,NULL,1,NULL,NULL),(1060,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','57','1355','Caisses',0,NULL,NULL,1,NULL,NULL),(1061,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','570','1060','à 577 Caisses - espèces ( 0 - centrale ; 7 - succursales et agences)',0,NULL,NULL,1,NULL,NULL),(1062,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','578','1060','Caisses - timbres ( 0 - fiscaux ; 1 - postaux)',0,NULL,NULL,1,NULL,NULL),(1063,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','58','1355','Virements internes',0,NULL,NULL,1,NULL,NULL),(1064,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','60','1356','Approvisionnements et marchandises',0,NULL,NULL,1,NULL,NULL),(1065,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','600','1064','Achats de matières premières',0,NULL,NULL,1,NULL,NULL),(1066,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','601','1064','Achats de fournitures',0,NULL,NULL,1,NULL,NULL),(1067,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','602','1064','Achats de services, travaux et études',0,NULL,NULL,1,NULL,NULL),(1068,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','603','1064','Sous-traitances générales',0,NULL,NULL,1,NULL,NULL),(1069,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','604','1064','Achats de marchandises',0,NULL,NULL,1,NULL,NULL),(1070,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','605','1064','Achats d\'immeubles destinés à la revente',0,NULL,NULL,1,NULL,NULL),(1071,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','608','1064','Remises , ristournes et rabais obtenus sur achats',0,NULL,NULL,1,NULL,NULL),(1072,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','609','1064','Variations de stocks',0,NULL,NULL,1,NULL,NULL),(1073,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6090','1072','De matières premières',0,NULL,NULL,1,NULL,NULL),(1074,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6091','1072','De fournitures',0,NULL,NULL,1,NULL,NULL),(1075,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6094','1072','De marchandises',0,NULL,NULL,1,NULL,NULL),(1076,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6095','1072','d\'immeubles destinés à la vente',0,NULL,NULL,1,NULL,NULL),(1077,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61','1356','Services et biens divers',0,NULL,NULL,1,NULL,NULL),(1078,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','610','1077','Loyers et charges locatives',0,NULL,NULL,1,NULL,NULL),(1079,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6100','1078','Loyers divers',0,NULL,NULL,1,NULL,NULL),(1080,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6101','1078','Charges locatives (assurances, frais de confort,etc)',0,NULL,NULL,1,NULL,NULL),(1081,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','611','1077','Entretien et réparation (fournitures et prestations)',0,NULL,NULL,1,NULL,NULL),(1082,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','612','1077','Fournitures faites à l\'entreprise',0,NULL,NULL,1,NULL,NULL),(1083,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6120','1082','Eau, gaz, électricité, vapeur',0,NULL,NULL,1,NULL,NULL),(1084,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61200','1083','Eau',0,NULL,NULL,1,NULL,NULL),(1085,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61201','1083','Gaz',0,NULL,NULL,1,NULL,NULL),(1086,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61202','1083','Electricité',0,NULL,NULL,1,NULL,NULL),(1087,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61203','1083','Vapeur',0,NULL,NULL,1,NULL,NULL),(1088,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6121','1082','Téléphone, télégrammes, télex, téléfax, frais postaux',0,NULL,NULL,1,NULL,NULL),(1089,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61210','1088','Téléphone',0,NULL,NULL,1,NULL,NULL),(1090,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61211','1088','Télégrammes',0,NULL,NULL,1,NULL,NULL),(1091,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61212','1088','Télex et téléfax',0,NULL,NULL,1,NULL,NULL),(1092,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61213','1088','Frais postaux',0,NULL,NULL,1,NULL,NULL),(1093,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6122','1082','Livres, bibliothèque',0,NULL,NULL,1,NULL,NULL),(1094,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6123','1082','Imprimés et fournitures de bureau (si non comptabilisé au 601)',0,NULL,NULL,1,NULL,NULL),(1095,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','613','1077','Rétributions de tiers',0,NULL,NULL,1,NULL,NULL),(1096,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6130','1095','Redevances et royalties',0,NULL,NULL,1,NULL,NULL),(1097,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61300','1096','Redevances pour brevets, licences, marques et accessoires',0,NULL,NULL,1,NULL,NULL),(1098,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61301','1096','Autres redevances (procédés de fabrication)',0,NULL,NULL,1,NULL,NULL),(1099,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6131','1095','Assurances non relatives au personnel',0,NULL,NULL,1,NULL,NULL),(1100,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61310','1099','Assurance incendie',0,NULL,NULL,1,NULL,NULL),(1101,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61311','1099','Assurance vol',0,NULL,NULL,1,NULL,NULL),(1102,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61312','1099','Assurance autos',0,NULL,NULL,1,NULL,NULL),(1103,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61313','1099','Assurance crédit',0,NULL,NULL,1,NULL,NULL),(1104,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61314','1099','Assurances frais généraux',0,NULL,NULL,1,NULL,NULL),(1105,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6132','1095','Divers',0,NULL,NULL,1,NULL,NULL),(1106,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61320','1105','Commissions aux tiers',0,NULL,NULL,1,NULL,NULL),(1107,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61321','1105','Honoraires d\'avocats, d\'experts, etc',0,NULL,NULL,1,NULL,NULL),(1108,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61322','1105','Cotisations aux groupements professionnels',0,NULL,NULL,1,NULL,NULL),(1109,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61323','1105','Dons, libéralités, etc',0,NULL,NULL,1,NULL,NULL),(1110,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61324','1105','Frais de contentieux',0,NULL,NULL,1,NULL,NULL),(1111,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61325','1105','Publications légales',0,NULL,NULL,1,NULL,NULL),(1112,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6133','1095','Transports et déplacements',0,NULL,NULL,1,NULL,NULL),(1113,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61330','1112','Transports de personnel',0,NULL,NULL,1,NULL,NULL),(1114,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61331','1112','Voyages, déplacements et représentations',0,NULL,NULL,1,NULL,NULL),(1115,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6134','1095','Personnel intérimaire',0,NULL,NULL,1,NULL,NULL),(1116,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','614','1077','Annonces, publicité, propagande et documentation',0,NULL,NULL,1,NULL,NULL),(1117,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6140','1116','Annonces et insertions',0,NULL,NULL,1,NULL,NULL),(1118,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6141','1116','Catalogues et imprimés',0,NULL,NULL,1,NULL,NULL),(1119,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6142','1116','Echantillons',0,NULL,NULL,1,NULL,NULL),(1120,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6143','1116','Foires et expositions',0,NULL,NULL,1,NULL,NULL),(1121,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6144','1116','Primes',0,NULL,NULL,1,NULL,NULL),(1122,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6145','1116','Cadeaux à la clientèle',0,NULL,NULL,1,NULL,NULL),(1123,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6146','1116','Missions et réceptions',0,NULL,NULL,1,NULL,NULL),(1124,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6147','1116','Documentation',0,NULL,NULL,1,NULL,NULL),(1125,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','615','1077','Sous-traitants',0,NULL,NULL,1,NULL,NULL),(1126,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6150','1125','Sous-traitants pour activités propres',0,NULL,NULL,1,NULL,NULL),(1127,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6151','1125','Sous-traitants d\'associations momentanées (coparticipants)',0,NULL,NULL,1,NULL,NULL),(1128,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6152','1125','Quote-part bénéficiaire des coparticipants',0,NULL,NULL,1,NULL,NULL),(1129,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','617','1077','Personnel intérimaire et personnes mises à la disposition de l\'entreprise',0,NULL,NULL,1,NULL,NULL),(1130,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','618','1077','Rémunérations, primes pour assurances extralégales, pensions de retraite et de survie des administrateurs, gérants et associés actifs qui ne sont pas attribuées en vertu d\'un contrat de travail',0,NULL,NULL,1,NULL,NULL),(1131,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62','1356','Rémunérations, charges sociales et pensions',0,NULL,NULL,1,NULL,NULL),(1132,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','620','1131','Rémunérations et avantages sociaux directs',0,NULL,NULL,1,NULL,NULL),(1133,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6200','1132','Administrateurs ou gérants',0,NULL,NULL,1,NULL,NULL),(1134,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6201','1132','Personnel de direction',0,NULL,NULL,1,NULL,NULL),(1135,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6202','1132','Employés',0,NULL,NULL,1,NULL,NULL),(1136,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6203','1132','Ouvriers',0,NULL,NULL,1,NULL,NULL),(1137,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6204','1132','Autres membres du personnel',0,NULL,NULL,1,NULL,NULL),(1138,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','621','1131','Cotisations patronales d\'assurances sociales',0,NULL,NULL,1,NULL,NULL),(1139,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6210','1138','Sur salaires',0,NULL,NULL,1,NULL,NULL),(1140,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6211','1138','Sur appointements et commissions',0,NULL,NULL,1,NULL,NULL),(1141,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','622','1131','Primes patronales pour assurances extralégales',0,NULL,NULL,1,NULL,NULL),(1142,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','623','1131','Autres frais de personnel',0,NULL,NULL,1,NULL,NULL),(1143,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6230','1142','Assurances du personnel',0,NULL,NULL,1,NULL,NULL),(1144,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62300','1143','Assurances loi, responsabilité civile, chemin du travail',0,NULL,NULL,1,NULL,NULL),(1145,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62301','1143','Assurance salaire garanti',0,NULL,NULL,1,NULL,NULL),(1146,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62302','1143','Assurances individuelles',0,NULL,NULL,1,NULL,NULL),(1147,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6231','1142','Charges sociales diverses',0,NULL,NULL,1,NULL,NULL),(1148,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62310','1147','Jours fériés payés',0,NULL,NULL,1,NULL,NULL),(1149,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62311','1147','Salaire hebdomadaire garanti',0,NULL,NULL,1,NULL,NULL),(1150,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62312','1147','Allocations familiales complémentaires',0,NULL,NULL,1,NULL,NULL),(1151,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6232','1142','Charges sociales des administrateurs, gérants et commissaires',0,NULL,NULL,1,NULL,NULL),(1152,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62320','1151','Allocations familiales complémentaires pour non salariés',0,NULL,NULL,1,NULL,NULL),(1153,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62321','1151','Lois sociales pour indépendants',0,NULL,NULL,1,NULL,NULL),(1154,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62322','1151','Divers',0,NULL,NULL,1,NULL,NULL),(1155,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','624','1131','Pensions de retraite et de survie',0,NULL,NULL,1,NULL,NULL),(1156,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6240','1155','Administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(1157,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6241','1155','Personnel',0,NULL,NULL,1,NULL,NULL),(1158,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','625','1131','Provision pour pécule de vacances',0,NULL,NULL,1,NULL,NULL),(1159,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6250','1158','Dotations',0,NULL,NULL,1,NULL,NULL),(1160,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6251','1158','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1161,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','63','1356','Amortissements, réductions de valeur et provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(1162,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','630','1161','Dotations aux amortissements et aux réductions de valeur sur immobilisations',0,NULL,NULL,1,NULL,NULL),(1163,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6300','1162','Dotations aux amortissements sur frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(1164,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6301','1162','Dotations aux amortissements sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1165,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6302','1162','Dotations aux amortissements sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1166,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6308','1162','Dotations aux réductions de valeur sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1167,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6309','1162','Dotations aux réductions de valeur sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1168,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','631','1161','Réductions de valeur sur stocks',0,NULL,NULL,1,NULL,NULL),(1169,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6310','1168','Dotations',0,NULL,NULL,1,NULL,NULL),(1170,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6311','1168','Reprises',0,NULL,NULL,1,NULL,NULL),(1171,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','632','1161','Réductions de valeur sur commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1172,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6320','1171','Dotations',0,NULL,NULL,1,NULL,NULL),(1173,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6321','1171','Reprises',0,NULL,NULL,1,NULL,NULL),(1174,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','633','1161','Réductions de valeur sur créances commerciales à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1175,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6330','1174','Dotations',0,NULL,NULL,1,NULL,NULL),(1176,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6331','1174','Reprises',0,NULL,NULL,1,NULL,NULL),(1177,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','634','1161','Réductions de valeur sur créances commerciales à un an au plus',0,NULL,NULL,1,NULL,NULL),(1178,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6340','1177','Dotations',0,NULL,NULL,1,NULL,NULL),(1179,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6341','1177','Reprises',0,NULL,NULL,1,NULL,NULL),(1180,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','635','1161','Provisions pour pensions et obligations similaires',0,NULL,NULL,1,NULL,NULL),(1181,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6350','1180','Dotations',0,NULL,NULL,1,NULL,NULL),(1182,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6351','1180','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1183,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','636','11613','Provisions pour grosses réparations et gros entretiens',0,NULL,NULL,1,NULL,NULL),(1184,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6360','1183','Dotations',0,NULL,NULL,1,NULL,NULL),(1185,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6361','1183','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1186,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','637','1161','Provisions pour autres risques et charges',0,NULL,NULL,1,NULL,NULL),(1187,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6370','1186','Dotations',0,NULL,NULL,1,NULL,NULL),(1188,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6371','1186','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1189,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64','1356','Autres charges d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1190,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','640','1189','Charges fiscales d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1191,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6400','1190','Taxes et impôts directs',0,NULL,NULL,1,NULL,NULL),(1192,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64000','1191','Taxes sur autos et camions',0,NULL,NULL,1,NULL,NULL),(1193,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6401','1190','Taxes et impôts indirects',0,NULL,NULL,1,NULL,NULL),(1194,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64010','1193','Timbres fiscaux pris en charge par la firme',0,NULL,NULL,1,NULL,NULL),(1195,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64011','1193','Droits d\'enregistrement',0,NULL,NULL,1,NULL,NULL),(1196,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64012','1193','T.V.A. non déductible',0,NULL,NULL,1,NULL,NULL),(1197,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6402','1190','Impôts provinciaux et communaux',0,NULL,NULL,1,NULL,NULL),(1198,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64020','1197','Taxe sur la force motrice',0,NULL,NULL,1,NULL,NULL),(1199,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64021','1197','Taxe sur le personnel occupé',0,NULL,NULL,1,NULL,NULL),(1200,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6403','1190','Taxes diverses',0,NULL,NULL,1,NULL,NULL),(1201,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','641','1189','Moins-values sur réalisations courantes d\'immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1202,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','642','1189','Moins-values sur réalisations de créances commerciales',0,NULL,NULL,1,NULL,NULL),(1203,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','643','1189','à 648 Charges d\'exploitations diverses',0,NULL,NULL,1,NULL,NULL),(1204,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','649','1189','Charges d\'exploitation portées à l\'actif au titre de restructuration',0,NULL,NULL,1,NULL,NULL),(1205,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','65','1356','Charges financières',0,NULL,NULL,1,NULL,NULL),(1206,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','650','1205','Charges des dettes',0,NULL,NULL,1,NULL,NULL),(1207,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6500','1206','Intérêts, commissions et frais afférents aux dettes',0,NULL,NULL,1,NULL,NULL),(1208,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6501','1206','Amortissements des agios et frais d\'émission d\'emprunts',0,NULL,NULL,1,NULL,NULL),(1209,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6502','1206','Autres charges de dettes',0,NULL,NULL,1,NULL,NULL),(1210,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6503','1206','Intérêts intercalaires portés à l\'actif',0,NULL,NULL,1,NULL,NULL),(1211,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','651','1205','Réductions de valeur sur actifs circulants',0,NULL,NULL,1,NULL,NULL),(1212,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6510','1211','Dotations',0,NULL,NULL,1,NULL,NULL),(1213,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6511','1211','Reprises',0,NULL,NULL,1,NULL,NULL),(1214,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','652','1205','Moins-values sur réalisation d\'actifs circulants',0,NULL,NULL,1,NULL,NULL),(1215,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','653','1205','Charges d\'escompte de créances',0,NULL,NULL,1,NULL,NULL),(1216,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','654','1205','Différences de change',0,NULL,NULL,1,NULL,NULL),(1217,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','655','1205','Ecarts de conversion des devises',0,NULL,NULL,1,NULL,NULL),(1218,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','656','1205','Frais de banques, de chèques postaux',0,NULL,NULL,1,NULL,NULL),(1219,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','657','1205','Commissions sur ouvertures de crédit, cautions et avals',0,NULL,NULL,1,NULL,NULL),(1220,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','658','1205','Frais de vente des titres',0,NULL,NULL,1,NULL,NULL),(1221,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','66','1356','Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(1222,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','660','1221','Amortissements et réductions de valeur exceptionnels',0,NULL,NULL,1,NULL,NULL),(1223,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6600','1222','Sur frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(1224,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6601','1222','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1225,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6602','1222','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1226,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','661','1221','Réductions de valeur sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1227,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','662','1221','Provisions pour risques et charges exceptionnels',0,NULL,NULL,1,NULL,NULL),(1228,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','663','1221','Moins-values sur réalisation d\'actifs immobilisés',0,NULL,NULL,1,NULL,NULL),(1229,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6630','1228','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1230,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6631','1228','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1231,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6632','1228','Sur immobilisations détenues en location-financement et droits similaires',0,NULL,NULL,1,NULL,NULL),(1232,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6633','1228','Sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1233,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6634','1228','Sur immeubles acquis ou construits en vue de la revente',0,NULL,NULL,1,NULL,NULL),(1234,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','664','1221','à 668 Autres charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(1235,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','664','1221','Pénalités et amendes diverses',0,NULL,NULL,1,NULL,NULL),(1236,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','665','1221','Différence de charge',0,NULL,NULL,1,NULL,NULL),(1237,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','669','1221','Charges exceptionnelles transférées à l\'actif en frais de restructuration',0,NULL,NULL,1,NULL,NULL),(1238,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','67','1356','Impôts sur le résultat',0,NULL,NULL,1,NULL,NULL),(1239,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','670','1238','Impôts belges sur le résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1240,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6700','1239','Impôts et précomptes dus ou versés',0,NULL,NULL,1,NULL,NULL),(1241,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6701','1239','Excédent de versements d\'impôts et précomptes porté à l\'actif',0,NULL,NULL,1,NULL,NULL),(1242,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6702','1239','Charges fiscales estimées',0,NULL,NULL,1,NULL,NULL),(1243,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','671','1238','Impôts belges sur le résultat d\'exercices antérieurs',0,NULL,NULL,1,NULL,NULL),(1244,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6710','1243','Suppléments d\'impôts dus ou versés',0,NULL,NULL,1,NULL,NULL),(1245,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6711','1243','Suppléments d\'impôts estimés',0,NULL,NULL,1,NULL,NULL),(1246,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6712','1243','Provisions fiscales constituées',0,NULL,NULL,1,NULL,NULL),(1247,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','672','1238','Impôts étrangers sur le résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1248,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','673','1238','Impôts étrangers sur le résultat d\'exercices antérieurs',0,NULL,NULL,1,NULL,NULL),(1249,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','68','1356','Transferts aux réserves immunisées',0,NULL,NULL,1,NULL,NULL),(1250,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','69','1356','Affectation des résultats',0,NULL,NULL,1,NULL,NULL),(1251,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','690','1250','Perte reportée de l\'exercice précédent',0,NULL,NULL,1,NULL,NULL),(1252,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','691','1250','Dotation à la réserve légale',0,NULL,NULL,1,NULL,NULL),(1253,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','692','1250','Dotation aux autres réserves',0,NULL,NULL,1,NULL,NULL),(1254,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','693','1250','Bénéfice à reporter',0,NULL,NULL,1,NULL,NULL),(1255,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','694','1250','Rémunération du capital',0,NULL,NULL,1,NULL,NULL),(1256,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','695','1250','Administrateurs ou gérants',0,NULL,NULL,1,NULL,NULL),(1257,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','696','1250','Autres allocataires',0,NULL,NULL,1,NULL,NULL),(1258,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','70','1357','Chiffre d\'affaires',0,NULL,NULL,1,NULL,NULL),(1260,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','700','1258','Ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(1261,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7000','1260','Ventes en Belgique',0,NULL,NULL,1,NULL,NULL),(1262,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7001','1260','Ventes dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1263,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7002','1260','Ventes à l\'exportation',0,NULL,NULL,1,NULL,NULL),(1264,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','701','1258','Ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(1265,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7010','1264','Ventes en Belgique',0,NULL,NULL,1,NULL,NULL),(1266,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7011','1264','Ventes dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1267,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7012','1264','Ventes à l\'exportation',0,NULL,NULL,1,NULL,NULL),(1268,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','702','1258','Ventes de déchets et rebuts',0,NULL,NULL,1,NULL,NULL),(1269,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7020','1268','Ventes en Belgique',0,NULL,NULL,1,NULL,NULL),(1270,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7021','1268','Ventes dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1271,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7022','1268','Ventes à l\'exportation',0,NULL,NULL,1,NULL,NULL),(1272,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','703','1258','Ventes d\'emballages récupérables',0,NULL,NULL,1,NULL,NULL),(1273,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','704','1258','Facturations des travaux en cours (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(1274,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','705','1258','Prestations de services',0,NULL,NULL,1,NULL,NULL),(1275,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7050','1274','Prestations de services en Belgique',0,NULL,NULL,1,NULL,NULL),(1276,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7051','1274','Prestations de services dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1277,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7052','1274','Prestations de services en vue de l\'exportation',0,NULL,NULL,1,NULL,NULL),(1278,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','706','1258','Pénalités et dédits obtenus par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(1279,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','708','1258','Remises, ristournes et rabais accordés',0,NULL,NULL,1,NULL,NULL),(1280,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7080','1279','Sur ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(1281,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7081','1279','Sur ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(1282,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7082','1279','Sur ventes de déchets et rebuts',0,NULL,NULL,1,NULL,NULL),(1283,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7083','1279','Sur prestations de services',0,NULL,NULL,1,NULL,NULL),(1284,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7084','1279','Mali sur travaux facturés aux associations momentanées',0,NULL,NULL,1,NULL,NULL),(1285,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71','1357','Variation des stocks et des commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1286,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','712','1285','Des en cours de fabrication',0,NULL,NULL,1,NULL,NULL),(1287,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','713','1285','Des produits finis',0,NULL,NULL,1,NULL,NULL),(1288,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','715','1285','Des immeubles construits destinés à la vente',0,NULL,NULL,1,NULL,NULL),(1289,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','717','1285','Des commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1290,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7170','1289','Commandes en cours - Coût de revient',0,NULL,NULL,1,NULL,NULL),(1291,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71700','1290','Coût des commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1292,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71701','1290','Coût des travaux en cours des associations momentanées',0,NULL,NULL,1,NULL,NULL),(1293,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7171','1289','Bénéfices portés en compte sur commandes en cours',0,NULL,NULL,1,NULL,NULL),(1294,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71710','1293','Sur commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1295,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71711','1293','Sur travaux en cours des associations momentanées',0,NULL,NULL,1,NULL,NULL),(1296,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','72','1357','Production immobilisée',0,NULL,NULL,1,NULL,NULL),(1297,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','720','1296','En frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(1298,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','721','1296','En immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1299,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','722','1296','En immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1300,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','723','1296','En immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(1301,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','74','1357','Autres produits d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1302,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','740','1301','Subsides d\'exploitation et montants compensatoires',0,NULL,NULL,1,NULL,NULL),(1303,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','741','1301','Plus-values sur réalisations courantes d\'immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1304,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','742','1301','Plus-values sur réalisations de créances commerciales',0,NULL,NULL,1,NULL,NULL),(1305,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','743','1301','à 749 Produits d\'exploitation divers',0,NULL,NULL,1,NULL,NULL),(1306,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','743','1301','Produits de services exploités dans l\'intérêt du personnel',0,NULL,NULL,1,NULL,NULL),(1307,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','744','1301','Commissions et courtages',0,NULL,NULL,1,NULL,NULL),(1308,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','745','1301','Redevances pour brevets et licences',0,NULL,NULL,1,NULL,NULL),(1309,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','746','1301','Prestations de services (transports, études, etc)',0,NULL,NULL,1,NULL,NULL),(1310,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','747','1301','Revenus des immeubles affectés aux activités non professionnelles',0,NULL,NULL,1,NULL,NULL),(1311,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','748','1301','Locations diverses à caractère professionnel',0,NULL,NULL,1,NULL,NULL),(1312,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','749','1301','Produits divers',0,NULL,NULL,1,NULL,NULL),(1313,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7490','1312','Bonis sur reprises d\'emballages consignés',0,NULL,NULL,1,NULL,NULL),(1314,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7491','1312','Bonis sur travaux en associations momentanées',0,NULL,NULL,1,NULL,NULL),(1315,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','75','1357','Produits financiers',0,NULL,NULL,1,NULL,NULL),(1316,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','750','1315','Produits des immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1317,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7500','1316','Revenus des actions',0,NULL,NULL,1,NULL,NULL),(1318,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7501','1316','Revenus des obligations',0,NULL,NULL,1,NULL,NULL),(1319,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7502','1316','Revenus des créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1320,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','751','1315','Produits des actifs circulants',0,NULL,NULL,1,NULL,NULL),(1321,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','752','1315','Plus-values sur réalisations d\'actifs circulants',0,NULL,NULL,1,NULL,NULL),(1322,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','753','1315','Subsides en capital et en intérêts',0,NULL,NULL,1,NULL,NULL),(1323,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','754','1315','Différences de change',0,NULL,NULL,1,NULL,NULL),(1324,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','755','1315','Ecarts de conversion des devises',0,NULL,NULL,1,NULL,NULL),(1325,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','756','1315','à 759 Produits financiers divers',0,NULL,NULL,1,NULL,NULL),(1326,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','756','1315','Produits des autres créances',0,NULL,NULL,1,NULL,NULL),(1327,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','757','1315','Escomptes obtenus',0,NULL,NULL,1,NULL,NULL),(1328,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','76','1357','Produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(1329,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','760','1328','Reprises d\'amortissements et de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(1330,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7600','1329','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1331,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7601','1329','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1332,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','761','1328','Reprises de réductions de valeur sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1333,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','762','1328','Reprises de provisions pour risques et charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(1334,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','763','1328','Plus-values sur réalisation d\'actifs immobilisés',0,NULL,NULL,1,NULL,NULL),(1335,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7630','1334','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1336,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7631','1334','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1337,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7632','1334','Sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1338,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','764','1328','Autres produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(1339,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','77','1357','Régularisations d\'impôts et reprises de provisions fiscales',0,NULL,NULL,1,NULL,NULL),(1340,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','771','1339','Impôts belges sur le résultat',0,NULL,NULL,1,NULL,NULL),(1341,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7710','1340','Régularisations d\'impôts dus ou versés',0,NULL,NULL,1,NULL,NULL),(1342,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7711','1340','Régularisations d\'impôts estimés',0,NULL,NULL,1,NULL,NULL),(1343,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7712','1340','Reprises de provisions fiscales',0,NULL,NULL,1,NULL,NULL),(1344,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','773','1339','Impôts étrangers sur le résultat',0,NULL,NULL,1,NULL,NULL),(1345,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','79','1357','Affectation aux résultats',0,NULL,NULL,1,NULL,NULL),(1346,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','790','1345','Bénéfice reporté de l\'exercice précédent',0,NULL,NULL,1,NULL,NULL),(1347,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','791','1345','Prélèvement sur le capital et les primes d\'émission',0,NULL,NULL,1,NULL,NULL),(1348,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','792','1345','Prélèvement sur les réserves',0,NULL,NULL,1,NULL,NULL),(1349,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','793','1345','Perte à reporter',0,NULL,NULL,1,NULL,NULL),(1350,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','794','1345','Intervention d\'associés (ou du propriétaire) dans la perte',0,NULL,NULL,1,NULL,NULL),(1351,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','CAPIT','XXXXXX','1','0','Fonds propres, provisions pour risques et charges et dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1352,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','IMMO','XXXXXX','2','0','Frais d\'établissement. Actifs immobilisés et créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1353,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','STOCK','XXXXXX','3','0','Stock et commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1354,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','TIERS','XXXXXX','4','0','Créances et dettes à un an au plus',0,NULL,NULL,1,NULL,NULL),(1355,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','FINAN','XXXXXX','5','0','Placement de trésorerie et de valeurs disponibles',0,NULL,NULL,1,NULL,NULL),(1356,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6','0','Charges',0,NULL,NULL,1,NULL,NULL),(1357,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7','0','Produits',0,NULL,NULL,1,NULL,NULL),(1401,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','CAPIT','XXXXXX','1','0','Fonds propres, provisions pour risques et charges et dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1402,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','IMMO','XXXXXX','2','0','Frais d\'établissement. Actifs immobilisés et créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1403,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','STOCK','XXXXXX','3','0','Stock et commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1404,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','TIERS','XXXXXX','4','0','Créances et dettes à un an au plus',0,NULL,NULL,1,NULL,NULL),(1405,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','FINAN','XXXXXX','5','0','Placement de trésorerie et de valeurs disponibles',0,NULL,NULL,1,NULL,NULL),(1406,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','6','0','Charges',0,NULL,NULL,1,NULL,NULL),(1407,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','7','0','Produits',0,NULL,NULL,1,NULL,NULL),(1501,1,NULL,'2017-02-20 10:46:43','PCG99-BASE','CAPIT','XXXXXX','1','0','Fonds propres, provisions pour risques et charges et dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1502,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','IMMO','XXXXXX','2','0','Frais d\'établissement. Actifs immobilisés et créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1503,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','STOCK','XXXXXX','3','0','Stock et commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1504,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','TIERS','XXXXXX','4','0','Créances et dettes à un an au plus',0,NULL,NULL,1,NULL,NULL),(1505,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','FINAN','XXXXXX','5','0','Placement de trésorerie et de valeurs disponibles',0,NULL,NULL,1,NULL,NULL),(1506,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','6','0','Charges',0,NULL,NULL,1,NULL,NULL),(1507,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','7','0','Produits',0,NULL,NULL,1,NULL,NULL),(4001,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','FINANCIACION','XXXXXX','1','0','Financiación básica',0,NULL,NULL,1,NULL,NULL),(4002,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','ACTIVO','XXXXXX','2','0','Activo no corriente',0,NULL,NULL,1,NULL,NULL),(4003,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','EXISTENCIAS','XXXXXX','3','0','Existencias',0,NULL,NULL,1,NULL,NULL),(4004,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4','0','Acreedores y deudores por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4005,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5','0','Cuentas financieras',0,NULL,NULL,1,NULL,NULL),(4006,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6','0','Compras y gastos',0,NULL,NULL,1,NULL,NULL),(4007,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7','0','Ventas e ingresos',0,NULL,NULL,1,NULL,NULL),(4008,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','10','4001','CAPITAL',0,NULL,NULL,1,NULL,NULL),(4009,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','100','4008','Capital social',0,NULL,NULL,1,NULL,NULL),(4010,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','101','4008','Fondo social',0,NULL,NULL,1,NULL,NULL),(4011,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','CAPITAL','102','4008','Capital',0,NULL,NULL,1,NULL,NULL),(4012,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','103','4008','Socios por desembolsos no exigidos',0,NULL,NULL,1,NULL,NULL),(4013,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1030','4012','Socios por desembolsos no exigidos capital social',0,NULL,NULL,1,NULL,NULL),(4014,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1034','4012','Socios por desembolsos no exigidos capital pendiente de inscripción',0,NULL,NULL,1,NULL,NULL),(4015,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','104','4008','Socios por aportaciones no dineradas pendientes',0,NULL,NULL,1,NULL,NULL),(4016,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1040','4015','Socios por aportaciones no dineradas pendientes capital social',0,NULL,NULL,1,NULL,NULL),(4017,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1044','4015','Socios por aportaciones no dineradas pendientes capital pendiente de inscripción',0,NULL,NULL,1,NULL,NULL),(4018,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','108','4008','Acciones o participaciones propias en situaciones especiales',0,NULL,NULL,1,NULL,NULL),(4019,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','109','4008','Acciones o participaciones propias para reducción de capital',0,NULL,NULL,1,NULL,NULL),(4020,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','11','4001','Reservas y otros instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4021,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','110','4020','Prima de emisión o asunción',0,NULL,NULL,1,NULL,NULL),(4022,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','111','4020','Otros instrumentos de patrimonio neto',0,NULL,NULL,1,NULL,NULL),(4023,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1110','4022','Patrimonio neto por emisión de instrumentos financieros compuestos',0,NULL,NULL,1,NULL,NULL),(4024,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1111','4022','Resto de instrumentos de patrimoio neto',0,NULL,NULL,1,NULL,NULL),(4025,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','112','4020','Reserva legal',0,NULL,NULL,1,NULL,NULL),(4026,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','113','4020','Reservas voluntarias',0,NULL,NULL,1,NULL,NULL),(4027,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','114','4020','Reservas especiales',0,NULL,NULL,1,NULL,NULL),(4028,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1140','4027','Reservas para acciones o participaciones de la sociedad dominante',0,NULL,NULL,1,NULL,NULL),(4029,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1141','4027','Reservas estatutarias',0,NULL,NULL,1,NULL,NULL),(4030,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1142','4027','Reservas por capital amortizado',0,NULL,NULL,1,NULL,NULL),(4031,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1143','4027','Reservas por fondo de comercio',0,NULL,NULL,1,NULL,NULL),(4032,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1144','4028','Reservas por acciones propias aceptadas en garantía',0,NULL,NULL,1,NULL,NULL),(4033,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','115','4020','Reservas por pérdidas y ganancias actuariales y otros ajustes',0,NULL,NULL,1,NULL,NULL),(4034,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','118','4020','Aportaciones de socios o propietarios',0,NULL,NULL,1,NULL,NULL),(4035,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','119','4020','Diferencias por ajuste del capital a euros',0,NULL,NULL,1,NULL,NULL),(4036,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','12','4001','Resultados pendientes de aplicación',0,NULL,NULL,1,NULL,NULL),(4037,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','120','4036','Remanente',0,NULL,NULL,1,NULL,NULL),(4038,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','121','4036','Resultados negativos de ejercicios anteriores',0,NULL,NULL,1,NULL,NULL),(4039,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','129','4036','Resultado del ejercicio',0,NULL,NULL,1,NULL,NULL),(4040,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','13','4001','Subvenciones, donaciones y ajustes por cambio de valor',0,NULL,NULL,1,NULL,NULL),(4041,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','130','4040','Subvenciones oficiales de capital',0,NULL,NULL,1,NULL,NULL),(4042,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','131','4040','Donaciones y legados de capital',0,NULL,NULL,1,NULL,NULL),(4043,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','132','4040','Otras subvenciones, donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4044,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','133','4040','Ajustes por valoración en activos financieros disponibles para la venta',0,NULL,NULL,1,NULL,NULL),(4045,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','134','4040','Operaciones de cobertura',0,NULL,NULL,1,NULL,NULL),(4046,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1340','4045','Cobertura de flujos de efectivo',0,NULL,NULL,1,NULL,NULL),(4047,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1341','4045','Cobertura de una inversión neta en un negocio extranjero',0,NULL,NULL,1,NULL,NULL),(4048,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','135','4040','Diferencias de conversión',0,NULL,NULL,1,NULL,NULL),(4049,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','136','4040','Ajustes por valoración en activos no corrientes y grupos enajenables de elementos mantenidos para la venta',0,NULL,NULL,1,NULL,NULL),(4050,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','137','4040','Ingresos fiscales a distribuir en varios ejercicios',0,NULL,NULL,1,NULL,NULL),(4051,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1370','4050','Ingresos fiscales por diferencias permanentes a distribuir en varios ejercicios',0,NULL,NULL,1,NULL,NULL),(4052,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1371','4050','Ingresos fiscales por deducciones y bonificaciones a distribuir en varios ejercicios',0,NULL,NULL,1,NULL,NULL),(4053,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','14','4001','Provisiones',0,NULL,NULL,1,NULL,NULL),(4054,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','141','4053','Provisión para impuestos',0,NULL,NULL,1,NULL,NULL),(4055,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','142','4053','Provisión para otras responsabilidades',0,NULL,NULL,1,NULL,NULL),(4056,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','143','4053','Provisión por desmantelamiento, retiro o rehabilitación del inmovilizado',0,NULL,NULL,1,NULL,NULL),(4057,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','145','4053','Provisión para actuaciones medioambientales',0,NULL,NULL,1,NULL,NULL),(4058,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','15','4001','Deudas a largo plazo con características especiales',0,NULL,NULL,1,NULL,NULL),(4059,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','150','4058','Acciones o participaciones a largo plazo consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4060,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','153','4058','Desembolsos no exigidos por acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4061,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1533','4060','Desembolsos no exigidos empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4062,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1534','4060','Desembolsos no exigidos empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4063,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1535','4060','Desembolsos no exigidos otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4064,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1536','4060','Otros desembolsos no exigidos',0,NULL,NULL,1,NULL,NULL),(4065,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','154','4058','Aportaciones no dinerarias pendientes por acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4066,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1543','4065','Aportaciones no dinerarias pendientes empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4067,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1544','4065','Aportaciones no dinerarias pendientes empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4068,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1545','4065','Aportaciones no dinerarias pendientes otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4069,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1546','4065','Otras aportaciones no dinerarias pendientes',0,NULL,NULL,1,NULL,NULL),(4070,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','16','4001','Deudas a largo plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4071,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','160','4070','Deudas a largo plazo con entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4072,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1603','4071','Deudas a largo plazo con entidades de crédito empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4073,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1604','4071','Deudas a largo plazo con entidades de crédito empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4074,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1605','4071','Deudas a largo plazo con otras entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4075,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','161','4070','Proveedores de inmovilizado a largo plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4076,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1613','4075','Proveedores de inmovilizado a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4077,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1614','4075','Proveedores de inmovilizado a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4078,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1615','4075','Proveedores de inmovilizado a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4079,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','162','4070','Acreedores por arrendamiento financiero a largo plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4080,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1623','4079','Acreedores por arrendamiento financiero a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4081,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1624','4080','Acreedores por arrendamiento financiero a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4082,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1625','4080','Acreedores por arrendamiento financiero a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4083,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','163','4070','Otras deudas a largo plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4084,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1633','4083','Otras deudas a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4085,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1634','4083','Otras deudas a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4086,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1635','4083','Otras deudas a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4087,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','17','4001','Deudas a largo plazo por préstamos recibidos empresitos y otros conceptos',0,NULL,NULL,1,NULL,NULL),(4088,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','170','4087','Deudas a largo plazo con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4089,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','171','4087','Deudas a largo plazo',0,NULL,NULL,1,NULL,NULL),(4090,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','172','4087','Deudas a largo plazo transformables en suvbenciones donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4091,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','173','4087','Proveedores de inmovilizado a largo plazo',0,NULL,NULL,1,NULL,NULL),(4092,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','174','4087','Acreedores por arrendamiento financiero a largo plazo',0,NULL,NULL,1,NULL,NULL),(4093,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','175','4087','Efectos a pagar a largo plazo',0,NULL,NULL,1,NULL,NULL),(4094,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','176','4087','Pasivos por derivados financieros a largo plazo',0,NULL,NULL,1,NULL,NULL),(4095,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','177','4087','Obligaciones y bonos',0,NULL,NULL,1,NULL,NULL),(4096,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','179','4087','Deudas representadas en otros valores negociables',0,NULL,NULL,1,NULL,NULL),(4097,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','18','4001','Pasivos por fianzas garantias y otros conceptos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4098,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','180','4097','Fianzas recibidas a largo plazo',0,NULL,NULL,1,NULL,NULL),(4099,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','181','4097','Anticipos recibidos por ventas o prestaciones de servicios a largo plazo',0,NULL,NULL,1,NULL,NULL),(4100,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','185','4097','Depositos recibidos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4101,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','19','4001','Situaciones transitorias de financiación',0,NULL,NULL,1,NULL,NULL),(4102,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','190','4101','Acciones o participaciones emitidas',0,NULL,NULL,1,NULL,NULL),(4103,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','192','4101','Suscriptores de acciones',0,NULL,NULL,1,NULL,NULL),(4104,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','194','4101','Capital emitido pendiente de inscripción',0,NULL,NULL,1,NULL,NULL),(4105,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','195','4101','Acciones o participaciones emitidas consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4106,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','197','4101','Suscriptores de acciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4107,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','199','4101','Acciones o participaciones emitidas consideradas como pasivos financieros pendientes de inscripción',0,NULL,NULL,1,NULL,NULL),(4108,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','20','4002','Inmovilizaciones intangibles',0,NULL,NULL,1,NULL,NULL),(4109,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','200','4108','Investigación',0,NULL,NULL,1,NULL,NULL),(4110,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','201','4108','Desarrollo',0,NULL,NULL,1,NULL,NULL),(4111,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','202','4108','Concesiones administrativas',0,NULL,NULL,1,NULL,NULL),(4112,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','203','4108','Propiedad industrial',0,NULL,NULL,1,NULL,NULL),(4113,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','205','4108','Derechos de transpaso',0,NULL,NULL,1,NULL,NULL),(4114,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','206','4108','Aplicaciones informáticas',0,NULL,NULL,1,NULL,NULL),(4115,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','209','4108','Anticipos para inmovilizaciones intangibles',0,NULL,NULL,1,NULL,NULL),(4116,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','21','4002','Inmovilizaciones materiales',0,NULL,NULL,1,NULL,NULL),(4117,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','210','4116','Terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4118,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','211','4116','Construcciones',0,NULL,NULL,1,NULL,NULL),(4119,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','212','4116','Instalaciones técnicas',0,NULL,NULL,1,NULL,NULL),(4120,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','213','4116','Maquinaria',0,NULL,NULL,1,NULL,NULL),(4121,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','214','4116','Utillaje',0,NULL,NULL,1,NULL,NULL),(4122,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','215','4116','Otras instalaciones',0,NULL,NULL,1,NULL,NULL),(4123,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','216','4116','Mobiliario',0,NULL,NULL,1,NULL,NULL),(4124,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','217','4116','Equipos para procesos de información',0,NULL,NULL,1,NULL,NULL),(4125,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','218','4116','Elementos de transporte',0,NULL,NULL,1,NULL,NULL),(4126,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','219','4116','Otro inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4127,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','22','4002','Inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4128,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','220','4127','Inversiones en terreons y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4129,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','221','4127','Inversiones en construcciones',0,NULL,NULL,1,NULL,NULL),(4130,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','23','4002','Inmovilizaciones materiales en curso',0,NULL,NULL,1,NULL,NULL),(4131,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','230','4130','Adaptación de terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4132,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','231','4130','Construcciones en curso',0,NULL,NULL,1,NULL,NULL),(4133,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','232','4130','Instalaciones técnicas en montaje',0,NULL,NULL,1,NULL,NULL),(4134,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','233','4130','Maquinaria en montaje',0,NULL,NULL,1,NULL,NULL),(4135,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','237','4130','Equipos para procesos de información en montaje',0,NULL,NULL,1,NULL,NULL),(4136,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','239','4130','Anticipos para inmovilizaciones materiales',0,NULL,NULL,1,NULL,NULL),(4137,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','24','4002','Inversiones financieras a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4138,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','240','4137','Participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4139,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2403','4138','Participaciones a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4140,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2404','4138','Participaciones a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4141,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2405','4138','Participaciones a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4142,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','241','4137','Valores representativos de deuda a largo plazo de partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4143,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2413','4142','Valores representativos de deuda a largo plazo de empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4144,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2414','4142','Valores representativos de deuda a largo plazo de empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4145,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2415','4142','Valores representativos de deuda a largo plazo de otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4146,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','242','4137','Créditos a largo plazo a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4147,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2423','4146','Créditos a largo plazo a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4148,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2424','4146','Créditos a largo plazo a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4149,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2425','4146','Créditos a largo plazo a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4150,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','249','4137','Desembolsos pendientes sobre participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4151,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2493','4150','Desembolsos pendientes sobre participaciones a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4152,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2494','4150','Desembolsos pendientes sobre participaciones a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4153,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2495','4150','Desembolsos pendientes sobre participaciones a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4154,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','25','4002','Otras inversiones financieras a largo plazo',0,NULL,NULL,1,NULL,NULL),(4155,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','250','4154','Inversiones financieras a largo plazo en instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4156,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','251','4154','Valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4157,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','252','4154','Créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4158,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','253','4154','Créditos a largo plazo por enajenación de inmovilizado',0,NULL,NULL,1,NULL,NULL),(4159,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','254','4154','Créditos a largo plazo al personal',0,NULL,NULL,1,NULL,NULL),(4160,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','255','4154','Activos por derivados financieros a largo plazo',0,NULL,NULL,1,NULL,NULL),(4161,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','258','4154','Imposiciones a largo plazo',0,NULL,NULL,1,NULL,NULL),(4162,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','259','4154','Desembolsos pendientes sobre participaciones en el patrimonio neto a largo plazo',0,NULL,NULL,1,NULL,NULL),(4163,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','26','4002','Fianzas y depósitos constituidos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4164,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','260','4163','Fianzas constituidas a largo plazo',0,NULL,NULL,1,NULL,NULL),(4165,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','261','4163','Depósitos constituidos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4166,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','28','4002','Amortización acumulada del inmovilizado',0,NULL,NULL,1,NULL,NULL),(4167,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','280','4166','Amortización acumulado del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4168,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2800','4167','Amortización acumulada de investigación',0,NULL,NULL,1,NULL,NULL),(4169,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2801','4167','Amortización acumulada de desarrollo',0,NULL,NULL,1,NULL,NULL),(4170,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2802','4167','Amortización acumulada de concesiones administrativas',0,NULL,NULL,1,NULL,NULL),(4171,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2803','4167','Amortización acumulada de propiedad industrial',0,NULL,NULL,1,NULL,NULL),(4172,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2805','4167','Amortización acumulada de derechos de transpaso',0,NULL,NULL,1,NULL,NULL),(4173,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2806','4167','Amortización acumulada de aplicaciones informáticas',0,NULL,NULL,1,NULL,NULL),(4174,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','281','4166','Amortización acumulado del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4175,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2811','4174','Amortización acumulada de construcciones',0,NULL,NULL,1,NULL,NULL),(4176,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2812','4174','Amortización acumulada de instalaciones técnicas',0,NULL,NULL,1,NULL,NULL),(4177,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2813','4174','Amortización acumulada de maquinaria',0,NULL,NULL,1,NULL,NULL),(4178,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2814','4174','Amortización acumulada de utillaje',0,NULL,NULL,1,NULL,NULL),(4179,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2815','4174','Amortización acumulada de otras instalaciones',0,NULL,NULL,1,NULL,NULL),(4180,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2816','4174','Amortización acumulada de mobiliario',0,NULL,NULL,1,NULL,NULL),(4181,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2817','4174','Amortización acumulada de equipos para proceso de información',0,NULL,NULL,1,NULL,NULL),(4182,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2818','4174','Amortización acumulada de elementos de transporte',0,NULL,NULL,1,NULL,NULL),(4183,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2819','4175','Amortización acumulada de otro inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4184,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','282','4166','Amortización acumulada de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4185,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','29','4002','Deterioro de valor de activos no corrientes',0,NULL,NULL,1,NULL,NULL),(4186,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','290','4185','Deterioro de valor del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4187,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2900','4186','Deterioro de valor de investigación',0,NULL,NULL,1,NULL,NULL),(4188,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2901','4186','Deterioro de valor de desarrollo',0,NULL,NULL,1,NULL,NULL),(4189,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2902','4186','Deterioro de valor de concesiones administrativas',0,NULL,NULL,1,NULL,NULL),(4190,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2903','4186','Deterioro de valor de propiedad industrial',0,NULL,NULL,1,NULL,NULL),(4191,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2905','4186','Deterioro de valor de derechos de transpaso',0,NULL,NULL,1,NULL,NULL),(4192,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2906','4186','Deterioro de valor de aplicaciones informáticas',0,NULL,NULL,1,NULL,NULL),(4193,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','291','4185','Deterioro de valor del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4194,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2910','4193','Deterioro de valor de terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4195,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2911','4193','Deterioro de valor de construcciones',0,NULL,NULL,1,NULL,NULL),(4196,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2912','4193','Deterioro de valor de instalaciones técnicas',0,NULL,NULL,1,NULL,NULL),(4197,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2913','4193','Deterioro de valor de maquinaria',0,NULL,NULL,1,NULL,NULL),(4198,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2914','4193','Deterioro de valor de utillajes',0,NULL,NULL,1,NULL,NULL),(4199,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2915','4194','Deterioro de valor de otras instalaciones',0,NULL,NULL,1,NULL,NULL),(4200,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2916','4194','Deterioro de valor de mobiliario',0,NULL,NULL,1,NULL,NULL),(4201,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2917','4194','Deterioro de valor de equipos para proceso de información',0,NULL,NULL,1,NULL,NULL),(4202,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2918','4194','Deterioro de valor de elementos de transporte',0,NULL,NULL,1,NULL,NULL),(4203,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2919','4194','Deterioro de valor de otro inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4204,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','292','4185','Deterioro de valor de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4205,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2920','4204','Deterioro de valor de terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4206,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2921','4204','Deterioro de valor de construcciones',0,NULL,NULL,1,NULL,NULL),(4207,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','293','4185','Deterioro de valor de participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4208,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2933','4207','Deterioro de valor de participaciones a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4209,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2934','4207','Deterioro de valor de sobre participaciones a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4210,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2935','4207','Deterioro de valor de sobre participaciones a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4211,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','294','4185','Deterioro de valor de valores representativos de deuda a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4212,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2943','4211','Deterioro de valor de valores representativos de deuda a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4213,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2944','4211','Deterioro de valor de valores representativos de deuda a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4214,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2945','4211','Deterioro de valor de valores representativos de deuda a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4215,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','295','4185','Deterioro de valor de créditos a largo plazo a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4216,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2953','4215','Deterioro de valor de créditos a largo plazo a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4217,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2954','4215','Deterioro de valor de créditos a largo plazo a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4218,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2955','4215','Deterioro de valor de créditos a largo plazo a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4219,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','296','4185','Deterioro de valor de participaciones en el patrimonio netoa largo plazo',0,NULL,NULL,1,NULL,NULL),(4220,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','297','4185','Deterioro de valor de valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4221,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','298','4185','Deterioro de valor de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4222,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','30','4003','Comerciales',0,NULL,NULL,1,NULL,NULL),(4223,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','300','4222','Mercaderías A',0,NULL,NULL,1,NULL,NULL),(4224,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','301','4222','Mercaderías B',0,NULL,NULL,1,NULL,NULL),(4225,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','31','4003','Materias primas',0,NULL,NULL,1,NULL,NULL),(4226,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','310','4225','Materias primas A',0,NULL,NULL,1,NULL,NULL),(4227,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','311','4225','Materias primas B',0,NULL,NULL,1,NULL,NULL),(4228,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','32','4003','Otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4229,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','320','4228','Elementos y conjuntos incorporables',0,NULL,NULL,1,NULL,NULL),(4230,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','321','4228','Combustibles',0,NULL,NULL,1,NULL,NULL),(4231,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','322','4228','Repuestos',0,NULL,NULL,1,NULL,NULL),(4232,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','325','4228','Materiales diversos',0,NULL,NULL,1,NULL,NULL),(4233,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','326','4228','Embalajes',0,NULL,NULL,1,NULL,NULL),(4234,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','327','4228','Envases',0,NULL,NULL,1,NULL,NULL),(4235,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','328','4229','Material de oficina',0,NULL,NULL,1,NULL,NULL),(4236,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','33','4003','Productos en curso',0,NULL,NULL,1,NULL,NULL),(4237,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','330','4236','Productos en curos A',0,NULL,NULL,1,NULL,NULL),(4238,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','331','4236','Productos en curso B',0,NULL,NULL,1,NULL,NULL),(4239,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','34','4003','Productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4240,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','340','4239','Productos semiterminados A',0,NULL,NULL,1,NULL,NULL),(4241,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','341','4239','Productos semiterminados B',0,NULL,NULL,1,NULL,NULL),(4242,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','35','4003','Productos terminados',0,NULL,NULL,1,NULL,NULL),(4243,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','350','4242','Productos terminados A',0,NULL,NULL,1,NULL,NULL),(4244,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','351','4242','Productos terminados B',0,NULL,NULL,1,NULL,NULL),(4245,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','36','4003','Subproductos, residuos y materiales recuperados',0,NULL,NULL,1,NULL,NULL),(4246,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','360','4245','Subproductos A',0,NULL,NULL,1,NULL,NULL),(4247,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','361','4245','Subproductos B',0,NULL,NULL,1,NULL,NULL),(4248,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','365','4245','Residuos A',0,NULL,NULL,1,NULL,NULL),(4249,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','366','4245','Residuos B',0,NULL,NULL,1,NULL,NULL),(4250,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','368','4245','Materiales recuperados A',0,NULL,NULL,1,NULL,NULL),(4251,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','369','4245','Materiales recuperados B',0,NULL,NULL,1,NULL,NULL),(4252,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','39','4003','Deterioro de valor de las existencias',0,NULL,NULL,1,NULL,NULL),(4253,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','390','4252','Deterioro de valor de las mercaderías',0,NULL,NULL,1,NULL,NULL),(4254,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','391','4252','Deterioro de valor de las materias primas',0,NULL,NULL,1,NULL,NULL),(4255,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','392','4252','Deterioro de valor de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4256,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','393','4252','Deterioro de valor de los productos en curso',0,NULL,NULL,1,NULL,NULL),(4257,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','394','4252','Deterioro de valor de los productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4258,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','395','4252','Deterioro de valor de los productos terminados',0,NULL,NULL,1,NULL,NULL),(4259,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','396','4252','Deterioro de valor de los subproductos, residuos y materiales recuperados',0,NULL,NULL,1,NULL,NULL),(4260,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','PROVEEDORES','40','4004','Proveedores',0,NULL,NULL,1,NULL,NULL),(4261,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','PROVEEDORES','400','4260','Proveedores',0,NULL,NULL,1,NULL,NULL),(4262,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4000','4261','Proveedores euros',0,NULL,NULL,1,NULL,NULL),(4263,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4004','4261','Proveedores moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4264,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4009','4261','Proveedores facturas pendientes de recibir o formalizar',0,NULL,NULL,1,NULL,NULL),(4265,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','401','4260','Proveedores efectos comerciales a pagar',0,NULL,NULL,1,NULL,NULL),(4266,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','403','4260','Proveedores empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4267,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4030','4266','Proveedores empresas del grupo euros',0,NULL,NULL,1,NULL,NULL),(4268,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4031','4266','Efectos comerciales a pagar empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4269,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4034','4266','Proveedores empresas del grupo moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4270,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4036','4266','Envases y embalajes a devolver a proveedores empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4271,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4039','4266','Proveedores empresas del grupo facturas pendientes de recibir o de formalizar',0,NULL,NULL,1,NULL,NULL),(4272,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','404','4260','Proveedores empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4273,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','405','4260','Proveedores otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4274,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','406','4260','Envases y embalajes a devolver a proveedores',0,NULL,NULL,1,NULL,NULL),(4275,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','407','4260','Anticipos a proveedores',0,NULL,NULL,1,NULL,NULL),(4276,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','41','4004','Acreedores varios',0,NULL,NULL,1,NULL,NULL),(4277,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','410','4276','Acreedores por prestaciones de servicios',0,NULL,NULL,1,NULL,NULL),(4278,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4100','4277','Acreedores por prestaciones de servicios euros',0,NULL,NULL,1,NULL,NULL),(4279,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4104','4277','Acreedores por prestaciones de servicios moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4280,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4109','4277','Acreedores por prestaciones de servicios facturas pendientes de recibir o formalizar',0,NULL,NULL,1,NULL,NULL),(4281,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','411','4276','Acreedores efectos comerciales a pagar',0,NULL,NULL,1,NULL,NULL),(4282,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','419','4276','Acreedores por operaciones en común',0,NULL,NULL,1,NULL,NULL),(4283,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','CLIENTES','43','4004','Clientes',0,NULL,NULL,1,NULL,NULL),(4284,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','CLIENTES','430','4283','Clientes',0,NULL,NULL,1,NULL,NULL),(4285,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4300','4284','Clientes euros',0,NULL,NULL,1,NULL,NULL),(4286,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4304','4284','Clientes moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4287,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4309','4284','Clientes facturas pendientes de formalizar',0,NULL,NULL,1,NULL,NULL),(4288,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','431','4283','Clientes efectos comerciales a cobrar',0,NULL,NULL,1,NULL,NULL),(4289,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4310','4288','Efectos comerciales en cartera',0,NULL,NULL,1,NULL,NULL),(4290,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4311','4288','Efectos comerciales descontados',0,NULL,NULL,1,NULL,NULL),(4291,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4312','4288','Efectos comerciales en gestión de cobro',0,NULL,NULL,1,NULL,NULL),(4292,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4315','4288','Efectos comerciales impagados',0,NULL,NULL,1,NULL,NULL),(4293,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','432','4283','Clientes operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4294,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','433','4283','Clientes empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4295,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4330','4294','Clientes empresas del grupo euros',0,NULL,NULL,1,NULL,NULL),(4296,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4331','4294','Efectos comerciales a cobrar empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4297,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4332','4294','Clientes empresas del grupo operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4298,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4334','4294','Clientes empresas del grupo moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4299,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4336','4294','Clientes empresas del grupo dudoso cobro',0,NULL,NULL,1,NULL,NULL),(4300,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4337','4294','Envases y embalajes a devolver a clientes empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4301,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4339','4294','Clientes empresas del grupo facturas pendientes de formalizar',0,NULL,NULL,1,NULL,NULL),(4302,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','434','4283','Clientes empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4303,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','435','4283','Clientes otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4304,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','436','4283','Clientes de dudoso cobro',0,NULL,NULL,1,NULL,NULL),(4305,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','437','4283','Envases y embalajes a devolver por clientes',0,NULL,NULL,1,NULL,NULL),(4306,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','438','4283','Anticipos de clientes',0,NULL,NULL,1,NULL,NULL),(4307,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','44','4004','Deudores varios',0,NULL,NULL,1,NULL,NULL),(4308,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','440','4307','Deudores',0,NULL,NULL,1,NULL,NULL),(4309,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4400','4308','Deudores euros',0,NULL,NULL,1,NULL,NULL),(4310,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4404','4308','Deudores moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4311,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4409','4308','Deudores facturas pendientes de formalizar',0,NULL,NULL,1,NULL,NULL),(4312,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','441','4307','Deudores efectos comerciales a cobrar',0,NULL,NULL,1,NULL,NULL),(4313,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4410','4312','Deudores efectos comerciales en cartera',0,NULL,NULL,1,NULL,NULL),(4314,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4411','4312','Deudores efectos comerciales descontados',0,NULL,NULL,1,NULL,NULL),(4315,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4412','4312','Deudores efectos comerciales en gestión de cobro',0,NULL,NULL,1,NULL,NULL),(4316,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4415','4312','Deudores efectos comerciales impagados',0,NULL,NULL,1,NULL,NULL),(4317,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','446','4307','Deudores de dusoso cobro',0,NULL,NULL,1,NULL,NULL),(4318,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','449','4307','Deudores por operaciones en común',0,NULL,NULL,1,NULL,NULL),(4319,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','46','4004','Personal',0,NULL,NULL,1,NULL,NULL),(4320,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','460','4319','Anticipos de renumeraciones',0,NULL,NULL,1,NULL,NULL),(4321,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','465','4319','Renumeraciones pendientes de pago',0,NULL,NULL,1,NULL,NULL),(4322,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','47','4004','Administraciones Públicas',0,NULL,NULL,1,NULL,NULL),(4323,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','470','4322','Hacienda Pública deudora por diversos conceptos',0,NULL,NULL,1,NULL,NULL),(4324,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4700','4323','Hacienda Pública deudora por IVA',0,NULL,NULL,1,NULL,NULL),(4325,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4708','4323','Hacienda Pública deudora por subvenciones concedidas',0,NULL,NULL,1,NULL,NULL),(4326,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4709','4323','Hacienda Pública deudora por devolución de impuestos',0,NULL,NULL,1,NULL,NULL),(4327,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','471','4322','Organismos de la Seguridad Social deudores',0,NULL,NULL,1,NULL,NULL),(4328,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','472','4322','Hacienda Pública IVA soportado',0,NULL,NULL,1,NULL,NULL),(4329,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','473','4322','Hacienda Pública retenciones y pagos a cuenta',0,NULL,NULL,1,NULL,NULL),(4330,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','474','4322','Activos por impuesto diferido',0,NULL,NULL,1,NULL,NULL),(4331,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4740','4330','Activos por diferencias temporarias deducibles',0,NULL,NULL,1,NULL,NULL),(4332,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4742','4330','Derechos por deducciones y bonificaciones pendientes de aplicar',0,NULL,NULL,1,NULL,NULL),(4333,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4745','4330','Crédito por pérdidasa compensar del ejercicio',0,NULL,NULL,1,NULL,NULL),(4334,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','475','4322','Hacienda Pública acreedora por conceptos fiscales',0,NULL,NULL,1,NULL,NULL),(4335,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4750','4334','Hacienda Pública acreedora por IVA',0,NULL,NULL,1,NULL,NULL),(4336,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4751','4334','Hacienda Pública acreedora por retenciones practicadas',0,NULL,NULL,1,NULL,NULL),(4337,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4752','4334','Hacienda Pública acreedora por impuesto sobre sociedades',0,NULL,NULL,1,NULL,NULL),(4338,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4758','4334','Hacienda Pública acreedora por subvenciones a integrar',0,NULL,NULL,1,NULL,NULL),(4339,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','476','4322','Organismos de la Seguridad Social acreedores',0,NULL,NULL,1,NULL,NULL),(4340,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','477','4322','Hacienda Pública IVA repercutido',0,NULL,NULL,1,NULL,NULL),(4341,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','479','4322','Pasivos por diferencias temporarias imponibles',0,NULL,NULL,1,NULL,NULL),(4342,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','48','4004','Ajustes por periodificación',0,NULL,NULL,1,NULL,NULL),(4343,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','480','4342','Gastos anticipados',0,NULL,NULL,1,NULL,NULL),(4344,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','485','4342','Ingresos anticipados',0,NULL,NULL,1,NULL,NULL),(4345,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','49','4004','Deterioro de valor de créditos comerciales y provisiones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4346,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','490','4345','Deterioro de valor de créditos por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4347,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','493','4345','Deterioro de valor de créditos por operaciones comerciales con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4348,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4933','4347','Deterioro de valor de créditos por operaciones comerciales con empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4349,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4934','4347','Deterioro de valor de créditos por operaciones comerciales con empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4350,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4935','4347','Deterioro de valor de créditos por operaciones comerciales con otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4351,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','499','4345','Provisiones por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4352,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4994','4351','Provisión para contratos anerosos',0,NULL,NULL,1,NULL,NULL),(4353,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4999','4351','Provisión para otras operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4354,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','50','4005','Emprésitos deudas con características especiales y otras emisiones análogas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4355,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','500','4354','Obligaciones y bonos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4356,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','502','4354','Acciones o participaciones a corto plazo consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4357,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','505','4354','Deudas representadas en otros valores negociables a corto plazo',0,NULL,NULL,1,NULL,NULL),(4358,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','506','4354','Intereses a corto plazo de emprésitos y otras emisiones analógicas',0,NULL,NULL,1,NULL,NULL),(4359,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','507','4354','Dividendos de acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4360,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','509','4354','Valores negociables amortizados',0,NULL,NULL,1,NULL,NULL),(4361,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5090','4360','Obligaciones y bonos amortizados',0,NULL,NULL,1,NULL,NULL),(4362,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5095','4360','Otros valores negociables amortizados',0,NULL,NULL,1,NULL,NULL),(4363,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','51','4005','Deudas a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4364,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','510','4363','Deudas a corto plazo con entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4365,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5103','4364','Deudas a corto plazo con entidades de crédito empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4366,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5104','4364','Deudas a corto plazo con entidades de crédito empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4367,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5105','4364','Deudas a corto plazo con otras entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4368,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','511','4363','Proveedores de inmovilizado a corto plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4369,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5113','4368','Proveedores de inmovilizado a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4370,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5114','4368','Proveedores de inmovilizado a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4371,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5115','4368','Proveedores de inmovilizado a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4372,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','512','4363','Acreedores por arrendamiento financiero a corto plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4373,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5123','4372','Acreedores por arrendamiento financiero a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4374,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5124','4372','Acreedores por arrendamiento financiero a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4375,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5125','4372','Acreedores por arrendamiento financiero a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4376,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','513','4363','Otras deudas a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4377,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5133','4376','Otras deudas a corto plazo con empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4378,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5134','4376','Otras deudas a corto plazo con empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4379,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5135','4376','Otras deudas a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4380,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','514','4363','Intereses a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4381,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5143','4380','Intereses a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4382,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5144','4380','Intereses a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4383,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5145','4380','Intereses deudas a corto plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4384,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','52','4005','Deudas a corto plazo por préstamos recibidos y otros conceptos',0,NULL,NULL,1,NULL,NULL),(4385,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','520','4384','Deudas a corto plazo con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4386,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5200','4385','Préstamos a corto plazo de entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4387,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5201','4385','Deudas a corto plazo por crédito dispuesto',0,NULL,NULL,1,NULL,NULL),(4388,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5208','4385','Deudas por efectos descontados',0,NULL,NULL,1,NULL,NULL),(4389,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5209','4385','Deudas por operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4390,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','521','4384','Deudas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4391,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','522','4384','Deudas a corto plazo transformables en subvenciones donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4392,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','523','4384','Proveedores de inmovilizado a corto plazo',0,NULL,NULL,1,NULL,NULL),(4393,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','526','4384','Dividendo activo a pagar',0,NULL,NULL,1,NULL,NULL),(4394,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','527','4384','Intereses a corto plazo de deudas con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4395,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','528','4384','Intereses a corto plazo de deudas',0,NULL,NULL,1,NULL,NULL),(4396,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','529','4384','Provisiones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4397,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5291','4396','Provisión a corto plazo para impuestos',0,NULL,NULL,1,NULL,NULL),(4398,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5292','4396','Provisión a corto plazo para otras responsabilidades',0,NULL,NULL,1,NULL,NULL),(4399,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5293','4396','Provisión a corto plazo por desmantelamiento retiro o rehabilitación del inmovilizado',0,NULL,NULL,1,NULL,NULL),(4400,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5295','4396','Provisión a corto plazo para actuaciones medioambientales',0,NULL,NULL,1,NULL,NULL),(4401,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','53','4005','Inversiones financieras a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4402,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','530','4401','Participaciones a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4403,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5303','4402','Participaciones a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4404,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5304','4402','Participaciones a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4405,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5305','4402','Participaciones a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4406,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','531','4401','Valores representativos de deuda a corto plazo de partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4407,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5313','4406','Valores representativos de deuda a corto plazo de empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4408,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5314','4406','Valores representativos de deuda a corto plazo de empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4409,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5315','4406','Valores representativos de deuda a corto plazo de otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4410,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','532','4401','Créditos a corto plazo a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4411,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5323','4410','Créditos a corto plazo a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4412,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5324','4410','Créditos a corto plazo a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4413,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5325','4410','Créditos a corto plazo a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4414,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','533','4401','Intereses a corto plazo de valores representativos de deuda de partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4415,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5333','4414','Intereses a corto plazo de valores representativos de deuda en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4416,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5334','4414','Intereses a corto plazo de valores representativos de deuda en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4417,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5335','4414','Intereses a corto plazo de valores representativos de deuda en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4418,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','534','4401','Intereses a corto plazo de créditos a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4419,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5343','4418','Intereses a corto plazo de créditos a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4420,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5344','4418','Intereses a corto plazo de créditos a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4421,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5345','4418','Intereses a corto plazo de créditos a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4422,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','535','4401','Dividendo a cobrar de inversiones financieras en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4423,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5353','4422','Dividendo a cobrar de empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4424,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5354','4422','Dividendo a cobrar de empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4425,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5355','4422','Dividendo a cobrar de otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4426,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','539','4401','Desembolsos pendientes sobre participaciones a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4427,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5393','4426','Desembolsos pendientes sobre participaciones a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4428,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5394','4426','Desembolsos pendientes sobre participaciones a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4429,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5395','4426','Desembolsos pendientes sobre participaciones a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4430,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','54','4005','Otras inversiones financieras a corto plazo',0,NULL,NULL,1,NULL,NULL),(4431,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','540','4430','Inversiones financieras a corto plazo en instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4432,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','541','4430','Valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4433,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','542','4430','Créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4434,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','543','4430','Créditos a corto plazo por enejenación de inmovilizado',0,NULL,NULL,1,NULL,NULL),(4435,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','544','4430','Créditos a corto plazo al personal',0,NULL,NULL,1,NULL,NULL),(4436,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','545','4430','Dividendo a cobrar',0,NULL,NULL,1,NULL,NULL),(4437,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','546','4430','Intereses a corto plazo de valores reprsentativos de deuda',0,NULL,NULL,1,NULL,NULL),(4438,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','547','4430','Intereses a corto plazo de créditos',0,NULL,NULL,1,NULL,NULL),(4439,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','548','4430','Imposiciones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4440,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','549','4430','Desembolsos pendientes sobre participaciones en el patrimonio neto a corto plazo',0,NULL,NULL,1,NULL,NULL),(4441,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','55','4005','Otras cuentas no bancarias',0,NULL,NULL,1,NULL,NULL),(4442,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','550','4441','Titular de la explotación',0,NULL,NULL,1,NULL,NULL),(4443,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','551','4441','Cuenta corriente con socios y administradores',0,NULL,NULL,1,NULL,NULL),(4444,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','552','4441','Cuenta corriente otras personas y entidades vinculadas',0,NULL,NULL,1,NULL,NULL),(4445,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5523','4444','Cuenta corriente con empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4446,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5524','4444','Cuenta corriente con empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4447,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5525','4444','Cuenta corriente con otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4448,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','554','4441','Cuenta corriente con uniones temporales de empresas y comunidades de bienes',0,NULL,NULL,1,NULL,NULL),(4449,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','555','4441','Partidas pendientes de aplicación',0,NULL,NULL,1,NULL,NULL),(4450,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','556','4441','Desembolsos exigidos sobre participaciones en el patrimonio neto',0,NULL,NULL,1,NULL,NULL),(4451,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5563','4450','Desembolsos exigidos sobre participaciones empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4452,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5564','4450','Desembolsos exigidos sobre participaciones empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4453,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5565','4450','Desembolsos exigidos sobre participaciones otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4454,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5566','4450','Desembolsos exigidos sobre participaciones otras empresas',0,NULL,NULL,1,NULL,NULL),(4455,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','557','4441','Dividendo activo a cuenta',0,NULL,NULL,1,NULL,NULL),(4456,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','558','4441','Socios por desembolsos exigidos',0,NULL,NULL,1,NULL,NULL),(4457,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5580','4456','Socios por desembolsos exigidos sobre acciones o participaciones ordinarias',0,NULL,NULL,1,NULL,NULL),(4458,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5585','4456','Socios por desembolsos exigidos sobre acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4459,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','559','4441','Derivados financieros a corto plazo',0,NULL,NULL,1,NULL,NULL),(4460,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5590','4459','Activos por derivados financieros a corto plazo',0,NULL,NULL,1,NULL,NULL),(4461,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5595','4459','Pasivos por derivados financieros a corto plazo',0,NULL,NULL,1,NULL,NULL),(4462,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','56','4005','Finanzas y depósitos recibidos y constituidos a corto plazo y ajustes por periodificación',0,NULL,NULL,1,NULL,NULL),(4463,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','560','4462','Finanzas recibidas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4464,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','561','4462','Depósitos recibidos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4465,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','565','4462','Finanzas constituidas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4466,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','566','4462','Depósitos constituidos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4467,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','567','4462','Intereses pagados por anticipado',0,NULL,NULL,1,NULL,NULL),(4468,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','568','4462','Intereses cobrados a corto plazo',0,NULL,NULL,1,NULL,NULL),(4469,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','57','4005','Tesorería',0,NULL,NULL,1,NULL,NULL),(4470,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','CAJA','570','4469','Caja euros',0,NULL,NULL,1,NULL,NULL),(4471,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','571','4469','Caja moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4472,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','BANCOS','572','4469','Bancos e instituciones de crédito cc vista euros',0,NULL,NULL,1,NULL,NULL),(4473,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','573','4469','Bancos e instituciones de crédito cc vista moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4474,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','574','4469','Bancos e instituciones de crédito cuentas de ahorro euros',0,NULL,NULL,1,NULL,NULL),(4475,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','575','4469','Bancos e instituciones de crédito cuentas de ahorro moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4476,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','576','4469','Inversiones a corto plazo de gran liquidez',0,NULL,NULL,1,NULL,NULL),(4477,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','59','4005','Deterioro del valor de las inversiones financieras a corto plazo',0,NULL,NULL,1,NULL,NULL),(4478,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','593','4477','Deterioro del valor de participaciones a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4479,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5933','4478','Deterioro del valor de participaciones a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4480,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5934','4478','Deterioro del valor de participaciones a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4481,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5935','4478','Deterioro del valor de participaciones a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4482,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','594','4477','Deterioro del valor de valores representativos de deuda a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4483,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5943','4482','Deterioro del valor de valores representativos de deuda a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4484,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5944','4482','Deterioro del valor de valores representativos de deuda a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4485,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5945','4482','Deterioro del valor de valores representativos de deuda a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4486,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','595','4477','Deterioro del valor de créditos a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4487,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5953','4486','Deterioro del valor de créditos a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4488,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5954','4486','Deterioro del valor de créditos a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4489,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5955','4486','Deterioro del valor de créditos a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4490,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','596','4477','Deterioro del valor de participaciones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4491,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','597','4477','Deterioro del valor de valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4492,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','598','4477','Deterioro de valor de créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4493,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','60','4006','Compras',0,NULL,NULL,1,NULL,NULL),(4494,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','COMPRAS','600','4493','Compras de mercaderías',0,NULL,NULL,1,NULL,NULL),(4495,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','COMPRAS','601','4493','Compras de materias primas',0,NULL,NULL,1,NULL,NULL),(4496,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','602','4493','Compras de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4497,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','606','4493','Descuentos sobre compras por pronto pago',0,NULL,NULL,1,NULL,NULL),(4498,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6060','4497','Descuentos sobre compras por pronto pago de mercaderías',0,NULL,NULL,1,NULL,NULL),(4499,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6061','4497','Descuentos sobre compras por pronto pago de materias primas',0,NULL,NULL,1,NULL,NULL),(4500,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6062','4497','Descuentos sobre compras por pronto pago de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4501,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','COMPRAS','607','4493','Trabajos realizados por otras empresas',0,NULL,NULL,1,NULL,NULL),(4502,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','608','4493','Devoluciones de compras y operaciones similares',0,NULL,NULL,1,NULL,NULL),(4503,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6080','4502','Devoluciones de compras de mercaderías',0,NULL,NULL,1,NULL,NULL),(4504,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6081','4502','Devoluciones de compras de materias primas',0,NULL,NULL,1,NULL,NULL),(4505,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6082','4502','Devoluciones de compras de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4506,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','609','4493','Rappels por compras',0,NULL,NULL,1,NULL,NULL),(4507,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6090','4506','Rappels por compras de mercaderías',0,NULL,NULL,1,NULL,NULL),(4508,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6091','4506','Rappels por compras de materias primas',0,NULL,NULL,1,NULL,NULL),(4509,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6092','4506','Rappels por compras de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4510,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','61','4006','Variación de existencias',0,NULL,NULL,1,NULL,NULL),(4511,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','610','4510','Variación de existencias de mercaderías',0,NULL,NULL,1,NULL,NULL),(4512,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','611','4510','Variación de existencias de materias primas',0,NULL,NULL,1,NULL,NULL),(4513,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','612','4510','Variación de existencias de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4514,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','62','4006','Servicios exteriores',0,NULL,NULL,1,NULL,NULL),(4515,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','620','4514','Gastos en investigación y desarrollo del ejercicio',0,NULL,NULL,1,NULL,NULL),(4516,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','621','4514','Arrendamientos y cánones',0,NULL,NULL,1,NULL,NULL),(4517,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','622','4514','Reparaciones y conservación',0,NULL,NULL,1,NULL,NULL),(4518,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','623','4514','Servicios profesionales independientes',0,NULL,NULL,1,NULL,NULL),(4519,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','624','4514','Transportes',0,NULL,NULL,1,NULL,NULL),(4520,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','625','4514','Primas de seguros',0,NULL,NULL,1,NULL,NULL),(4521,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','626','4514','Servicios bancarios y similares',0,NULL,NULL,1,NULL,NULL),(4522,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','627','4514','Publicidad, propaganda y relaciones públicas',0,NULL,NULL,1,NULL,NULL),(4523,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','628','4514','Suministros',0,NULL,NULL,1,NULL,NULL),(4524,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','629','4514','Otros servicios',0,NULL,NULL,1,NULL,NULL),(4525,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','63','4006','Tributos',0,NULL,NULL,1,NULL,NULL),(4526,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','630','4525','Impuesto sobre benecifios',0,NULL,NULL,1,NULL,NULL),(4527,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6300','4526','Impuesto corriente',0,NULL,NULL,1,NULL,NULL),(4528,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6301','4526','Impuesto diferido',0,NULL,NULL,1,NULL,NULL),(4529,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','631','4525','Otros tributos',0,NULL,NULL,1,NULL,NULL),(4530,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','633','4525','Ajustes negativos en la imposición sobre beneficios',0,NULL,NULL,1,NULL,NULL),(4531,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','634','4525','Ajustes negativos en la imposición indirecta',0,NULL,NULL,1,NULL,NULL),(4532,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6341','4531','Ajustes negativos en IVA de activo corriente',0,NULL,NULL,1,NULL,NULL),(4533,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6342','4531','Ajustes negativos en IVA de inversiones',0,NULL,NULL,1,NULL,NULL),(4534,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','636','4525','Devolución de impuestos',0,NULL,NULL,1,NULL,NULL),(4535,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','638','4525','Ajustes positivos en la imposición sobre beneficios',0,NULL,NULL,1,NULL,NULL),(4536,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','639','4525','Ajustes positivos en la imposición directa',0,NULL,NULL,1,NULL,NULL),(4537,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6391','4536','Ajustes positivos en IVA de activo corriente',0,NULL,NULL,1,NULL,NULL),(4538,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6392','4536','Ajustes positivos en IVA de inversiones',0,NULL,NULL,1,NULL,NULL),(4539,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','64','4006','Gastos de personal',0,NULL,NULL,1,NULL,NULL),(4540,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','640','4539','Sueldos y salarios',0,NULL,NULL,1,NULL,NULL),(4541,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','641','4539','Indemnizaciones',0,NULL,NULL,1,NULL,NULL),(4542,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','642','4539','Seguridad social a cargo de la empresa',0,NULL,NULL,1,NULL,NULL),(4543,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','649','4539','Otros gastos sociales',0,NULL,NULL,1,NULL,NULL),(4544,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','65','4006','Otros gastos de gestión',0,NULL,NULL,1,NULL,NULL),(4545,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','650','4544','Pérdidas de créditos comerciales incobrables',0,NULL,NULL,1,NULL,NULL),(4546,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','651','4544','Resultados de operaciones en común',0,NULL,NULL,1,NULL,NULL),(4547,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6510','4546','Beneficio transferido gestor',0,NULL,NULL,1,NULL,NULL),(4548,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6511','4546','Pérdida soportada participe o asociado no gestor',0,NULL,NULL,1,NULL,NULL),(4549,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','659','4544','Otras pérdidas en gestión corriente',0,NULL,NULL,1,NULL,NULL),(4550,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','66','4006','Gastos financieros',0,NULL,NULL,1,NULL,NULL),(4551,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','660','4550','Gastos financieros por actualización de provisiones',0,NULL,NULL,1,NULL,NULL),(4552,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','661','4550','Intereses de obligaciones y bonos',0,NULL,NULL,1,NULL,NULL),(4553,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6610','4452','Intereses de obligaciones y bonos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4554,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6611','4452','Intereses de obligaciones y bonos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4555,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6612','4452','Intereses de obligaciones y bonos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4556,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6613','4452','Intereses de obligaciones y bonos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4557,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6615','4452','Intereses de obligaciones y bonos a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4558,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6616','4452','Intereses de obligaciones y bonos a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4559,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6617','4452','Intereses de obligaciones y bonos a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4560,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6618','4452','Intereses de obligaciones y bonos a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4561,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','662','4550','Intereses de deudas',0,NULL,NULL,1,NULL,NULL),(4562,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6620','4561','Intereses de deudas empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4563,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6621','4561','Intereses de deudas empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4564,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6622','4561','Intereses de deudas otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4565,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6623','4561','Intereses de deudas con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4566,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6624','4561','Intereses de deudas otras empresas',0,NULL,NULL,1,NULL,NULL),(4567,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','663','4550','Pérdidas por valorización de activos y pasivos financieros por su valor razonable',0,NULL,NULL,1,NULL,NULL),(4568,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','664','4550','Gastos por dividendos de acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4569,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6640','4568','Dividendos de pasivos empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4570,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6641','4568','Dividendos de pasivos empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4571,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6642','4568','Dividendos de pasivos otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4572,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6643','4568','Dividendos de pasivos otras empresas',0,NULL,NULL,1,NULL,NULL),(4573,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','665','4550','Intereses por descuento de efectos y operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4574,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6650','4573','Intereses por descuento de efectos en entidades de crédito del grupo',0,NULL,NULL,1,NULL,NULL),(4575,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6651','4573','Intereses por descuento de efectos en entidades de crédito asociadas',0,NULL,NULL,1,NULL,NULL),(4576,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6652','4573','Intereses por descuento de efectos en entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4577,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6653','4573','Intereses por descuento de efectos en otras entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4578,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6654','4573','Intereses por operaciones de factoring con entidades de crédito del grupo',0,NULL,NULL,1,NULL,NULL),(4579,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6655','4573','Intereses por operaciones de factoring con entidades de crédito asociadas',0,NULL,NULL,1,NULL,NULL),(4580,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6656','4573','Intereses por operaciones de factoring con otras entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4581,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6657','4573','Intereses por operaciones de factoring con otras entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4582,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','666','4550','Pérdidas en participaciones y valores representativos de deuda',0,NULL,NULL,1,NULL,NULL),(4583,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6660','4582','Pérdidas en valores representativos de deuda a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4584,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6661','4582','Pérdidas en valores representativos de deuda a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4585,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6662','4582','Pérdidas en valores representativos de deuda a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4586,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6663','4582','Pérdidas en participaciones y valores representativos de deuda a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4587,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6665','4582','Pérdidas en participaciones y valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4588,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6666','4582','Pérdidas en participaciones y valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4589,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6667','4582','Pérdidas en valores representativos de deuda a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4590,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6668','4582','Pérdidas en valores representativos de deuda a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4591,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','667','4550','Pérdidas de créditos no comerciales',0,NULL,NULL,1,NULL,NULL),(4592,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6670','4591','Pérdidas de créditos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4593,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6671','4591','Pérdidas de créditos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4594,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6672','4591','Pérdidas de créditos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4595,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6673','4591','Pérdidas de créditos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4596,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6675','4591','Pérdidas de créditos a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4597,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6676','4591','Pérdidas de créditos a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4598,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6677','4591','Pérdidas de créditos a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4599,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6678','4591','Pérdidas de créditos a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4600,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','668','4550','Diferencias negativas de cambio',0,NULL,NULL,1,NULL,NULL),(4601,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','669','4550','Otros gastos financieros',0,NULL,NULL,1,NULL,NULL),(4602,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','67','4006','Pérdidas procedentes de activos no corrientes y gastos excepcionales',0,NULL,NULL,1,NULL,NULL),(4603,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','670','4602','Pérdidas procedentes del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4604,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','671','4602','Pérdidas procedentes del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4605,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','672','4602','Pérdidas procedentes de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4607,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','673','4602','Pérdidas procedentes de participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4608,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6733','4607','Pérdidas procedentes de participaciones a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4609,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6734','4607','Pérdidas procedentes de participaciones a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4610,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6735','4607','Pérdidas procedentes de participaciones a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4611,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','675','4602','Pérdidas por operaciones con obligaciones propias',0,NULL,NULL,1,NULL,NULL),(4612,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','678','4602','Gastos excepcionales',0,NULL,NULL,1,NULL,NULL),(4613,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','68','4006','Dotaciones para amortizaciones',0,NULL,NULL,1,NULL,NULL),(4614,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','680','4613','Amortización del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4615,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','681','4613','Amortización del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4616,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','682','4613','Amortización de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4617,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','69','4006','Pérdidas por deterioro y otras dotaciones',0,NULL,NULL,1,NULL,NULL),(4618,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','690','4617','Pérdidas por deterioro del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4619,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','691','4617','Pérdidas por deterioro del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4620,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','692','4617','Pérdidas por deterioro de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4621,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','693','4617','Pérdidas por deterioro de existencias',0,NULL,NULL,1,NULL,NULL),(4622,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6930','4621','Pérdidas por deterioro de productos terminados y en curso de fabricación',0,NULL,NULL,1,NULL,NULL),(4623,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6931','4621','Pérdidas por deterioro de mercaderías',0,NULL,NULL,1,NULL,NULL),(4624,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6932','4621','Pérdidas por deterioro de materias primas',0,NULL,NULL,1,NULL,NULL),(4625,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6933','4621','Pérdidas por deterioro de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4626,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','694','4617','Pérdidas por deterioro de créditos por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4627,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','695','4617','Dotación a la provisión por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4628,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6954','4627','Dotación a la provisión por contratos onerosos',0,NULL,NULL,1,NULL,NULL),(4629,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6959','4628','Dotación a la provisión para otras operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4630,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','696','4617','Pérdidas por deterioro de participaciones y valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4631,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6960','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4632,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6961','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4633,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6962','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4634,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6963','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4635,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6965','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4636,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6966','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4637,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6967','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4638,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6968','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4639,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','697','4617','Pérdidas por deterioro de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4640,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6970','4639','Pérdidas por deterioro de créditos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4641,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6971','4639','Pérdidas por deterioro de créditos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4642,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6972','4639','Pérdidas por deterioro de créditos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4643,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6973','4639','Pérdidas por deterioro de créditos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4644,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','698','4617','Pérdidas por deterioro de participaciones y valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4645,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6980','4644','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4646,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6981','4644','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4647,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6985','4644','Pérdidas por deterioro en valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4648,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6986','4644','Pérdidas por deterioro en valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4649,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6988','4644','Pérdidas por deterioro en valores representativos de deuda a corto plazo de otras empresas',0,NULL,NULL,1,NULL,NULL),(4650,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','699','4617','Pérdidas por deterioro de crédito a corto plazo',0,NULL,NULL,1,NULL,NULL),(4651,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6990','4650','Pérdidas por deterioro de crédito a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4652,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6991','4650','Pérdidas por deterioro de crédito a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4653,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6992','4650','Pérdidas por deterioro de crédito a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4654,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6993','4650','Pérdidas por deterioro de crédito a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4655,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','70','4007','Ventas',0,NULL,NULL,1,NULL,NULL),(4656,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','VENTAS','700','4655','Ventas de mercaderías',0,NULL,NULL,1,NULL,NULL),(4657,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','VENTAS','701','4655','Ventas de productos terminados',0,NULL,NULL,1,NULL,NULL),(4658,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','702','4655','Ventas de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4659,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','703','4655','Ventas de subproductos y residuos',0,NULL,NULL,1,NULL,NULL),(4660,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','704','4655','Ventas de envases y embalajes',0,NULL,NULL,1,NULL,NULL),(4661,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','VENTAS','705','4655','Prestaciones de servicios',0,NULL,NULL,1,NULL,NULL),(4662,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','706','4655','Descuentos sobre ventas por pronto pago',0,NULL,NULL,1,NULL,NULL),(4663,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7060','4662','Descuentos sobre ventas por pronto pago de mercaderías',0,NULL,NULL,1,NULL,NULL),(4664,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7061','4662','Descuentos sobre ventas por pronto pago de productos terminados',0,NULL,NULL,1,NULL,NULL),(4665,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7062','4662','Descuentos sobre ventas por pronto pago de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4666,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7063','4662','Descuentos sobre ventas por pronto pago de subproductos y residuos',0,NULL,NULL,1,NULL,NULL),(4667,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','708','4655','Devoluciones de ventas y operacioes similares',0,NULL,NULL,1,NULL,NULL),(4668,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7080','4667','Devoluciones de ventas de mercaderías',0,NULL,NULL,1,NULL,NULL),(4669,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7081','4667','Devoluciones de ventas de productos terminados',0,NULL,NULL,1,NULL,NULL),(4670,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7082','4667','Devoluciones de ventas de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4671,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7083','4667','Devoluciones de ventas de subproductos y residuos',0,NULL,NULL,1,NULL,NULL),(4672,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7084','4667','Devoluciones de ventas de envases y embalajes',0,NULL,NULL,1,NULL,NULL),(4673,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','71','4007','Variación de existencias',0,NULL,NULL,1,NULL,NULL),(4674,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','710','4673','Variación de existencias de productos en curso',0,NULL,NULL,1,NULL,NULL),(4675,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','711','4673','Variación de existencias de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4676,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','712','4673','Variación de existencias de productos terminados',0,NULL,NULL,1,NULL,NULL),(4677,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','713','4673','Variación de existencias de subproductos, residuos y materiales recuperados',0,NULL,NULL,1,NULL,NULL),(4678,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','73','4007','Trabajos realizados para la empresa',0,NULL,NULL,1,NULL,NULL),(4679,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','730','4678','Trabajos realizados para el inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4680,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','731','4678','Trabajos realizados para el inmovilizado tangible',0,NULL,NULL,1,NULL,NULL),(4681,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','732','4678','Trabajos realizados en inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4682,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','733','4678','Trabajos realizados para el inmovilizado material en curso',0,NULL,NULL,1,NULL,NULL),(4683,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','74','4007','Subvenciones, donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4684,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','740','4683','Subvenciones, donaciones y legados a la explotación',0,NULL,NULL,1,NULL,NULL),(4685,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','746','4683','Subvenciones, donaciones y legados de capital transferidos al resultado del ejercicio',0,NULL,NULL,1,NULL,NULL),(4686,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','747','4683','Otras subvenciones, donaciones y legados transferidos al resultado del ejercicio',0,NULL,NULL,1,NULL,NULL),(4687,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','75','4007','Otros ingresos de gestión',0,NULL,NULL,1,NULL,NULL),(4688,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','751','4687','Resultados de operaciones en común',0,NULL,NULL,1,NULL,NULL),(4689,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7510','4688','Pérdida transferida gestor',0,NULL,NULL,1,NULL,NULL),(4690,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7511','4688','Beneficio atribuido participe o asociado no gestor',0,NULL,NULL,1,NULL,NULL),(4691,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','752','4687','Ingreso por arrendamiento',0,NULL,NULL,1,NULL,NULL),(4692,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','753','4687','Ingresos de propiedad industrial cedida en explotación',0,NULL,NULL,1,NULL,NULL),(4693,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','754','4687','Ingresos por comisiones',0,NULL,NULL,1,NULL,NULL),(4694,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','755','4687','Ingresos por servicios al personal',0,NULL,NULL,1,NULL,NULL),(4695,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','759','4687','Ingresos por servicios diversos',0,NULL,NULL,1,NULL,NULL),(4696,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76','4007','Ingresos financieros',0,NULL,NULL,1,NULL,NULL),(4697,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','760','4696','Ingresos de participaciones en instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4698,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7600','4697','Ingresos de participaciones en instrumentos de patrimonio empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4699,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7601','4697','Ingresos de participaciones en instrumentos de patrimonio empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4700,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7602','4697','Ingresos de participaciones en instrumentos de patrimonio otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4701,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7603','4697','Ingresos de participaciones en instrumentos de patrimonio otras empresas',0,NULL,NULL,1,NULL,NULL),(4702,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','761','4696','Ingresos de valores representativos de deuda',0,NULL,NULL,1,NULL,NULL),(4703,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7610','4702','Ingresos de valores representativos de deuda empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4704,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7611','4702','Ingresos de valores representativos de deuda empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4705,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7612','4702','Ingresos de valores representativos de deuda otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4706,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7613','4702','Ingresos de valores representativos de deuda otras empresas',0,NULL,NULL,1,NULL,NULL),(4707,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','762','4696','Ingresos de créditos',0,NULL,NULL,1,NULL,NULL),(4708,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7620','4707','Ingresos de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4709,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76200','4708','Ingresos de crédito a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4710,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76201','4708','Ingresos de crédito a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4711,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76202','4708','Ingresos de crédito a largo plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4712,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76203','4708','Ingresos de crédito a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4713,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7621','4707','Ingresos de créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4714,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76210','4713','Ingresos de crédito a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4715,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76211','4713','Ingresos de crédito a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4716,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76212','4713','Ingresos de crédito a corto plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4717,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76213','4713','Ingresos de crédito a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4718,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','763','4696','Beneficios por valorización de activos y pasivos financieros por su valor razonable',0,NULL,NULL,1,NULL,NULL),(4719,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','766','4696','Beneficios en participaciones y valores representativos de deuda',0,NULL,NULL,1,NULL,NULL),(4720,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7660','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4721,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7661','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4722,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7662','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4723,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7663','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4724,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7665','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4725,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7666','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4726,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7667','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4727,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7668','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4728,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','768','4696','Diferencias positivas de cambio',0,NULL,NULL,1,NULL,NULL),(4729,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','769','4696','Otros ingresos financieros',0,NULL,NULL,1,NULL,NULL),(4730,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','77','4007','Beneficios procedentes de activos no corrientes e ingresos excepcionales',0,NULL,NULL,1,NULL,NULL),(4731,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','770','4730','Beneficios procedentes del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4732,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','771','4730','Beneficios procedentes del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4733,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','772','4730','Beneficios procedentes de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4734,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','773','4730','Beneficios procedentes de participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4735,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7733','4734','Beneficios procedentes de participaciones a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4736,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7734','4734','Beneficios procedentes de participaciones a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4737,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7735','4734','Beneficios procedentes de participaciones a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4738,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','775','4730','Beneficios por operaciones con obligaciones propias',0,NULL,NULL,1,NULL,NULL),(4739,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','778','4730','Ingresos excepcionales',0,NULL,NULL,1,NULL,NULL),(4741,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','79','4007','Excesos y aplicaciones de provisiones y pérdidas por deterioro',0,NULL,NULL,1,NULL,NULL),(4742,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','790','4741','Revisión del deterioro del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4743,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','791','4741','Revisión del deterioro del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4744,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','792','4741','Revisión del deterioro de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4745,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','793','4741','Revisión del deterioro de las existencias',0,NULL,NULL,1,NULL,NULL),(4746,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7930','4745','Revisión del deterioro de productos terminados y en curso de fabricación',0,NULL,NULL,1,NULL,NULL),(4747,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7931','4745','Revisión del deterioro de mercaderías',0,NULL,NULL,1,NULL,NULL),(4748,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7932','4745','Revisión del deterioro de materias primas',0,NULL,NULL,1,NULL,NULL),(4749,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7933','4745','Revisión del deterioro de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4750,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','794','4741','Revisión del deterioro de créditos por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4751,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','795','4741','Exceso de provisiones',0,NULL,NULL,1,NULL,NULL),(4752,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7951','4751','Exceso de provisión para impuestos',0,NULL,NULL,1,NULL,NULL),(4753,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7952','4751','Exceso de provisión para otras responsabilidades',0,NULL,NULL,1,NULL,NULL),(4755,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7954','4751','Exceso de provisión para operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4756,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','79544','4755','Exceso de provisión por contratos onerosos',0,NULL,NULL,1,NULL,NULL),(4757,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','79549','4755','Exceso de provisión para otras operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4758,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7955','4751','Exceso de provisión para actuaciones medioambienteales',0,NULL,NULL,1,NULL,NULL),(4759,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','796','4741','Revisión del deterioro de participaciones y valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4760,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7960','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4761,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7961','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4762,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7962','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4763,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7963','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4764,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7965','4759','Revisión del deterioro de valores representativos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4765,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7966','4759','Revisión del deterioro de valores representativos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4766,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7967','4759','Revisión del deterioro de valores representativos a largo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4767,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7968','4759','Revisión del deterioro de valores representativos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4768,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','797','4741','Revisión del deterioro de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4769,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7970','4768','Revisión del deterioro de créditos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4770,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7971','4768','Revisión del deterioro de créditos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4771,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7972','4768','Revisión del deterioro de créditos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4772,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7973','4768','Revisión del deterioro de créditos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4773,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','798','4741','Revisión del deterioro de participaciones y valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4774,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7980','4773','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4775,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7981','4773','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4776,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7985','4773','Revisión del deterioro de valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4777,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7986','4773','Revisión del deterioro de valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4778,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7987','4773','Revisión del deterioro de valores representativos de deuda a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4779,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7988','4773','Revisión del deterioro de valores representativos de deuda a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4780,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','799','4741','Revisión del deterioro de créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4781,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7990','4780','Revisión del deterioro de créditos a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4782,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7991','4780','Revisión del deterioro de créditos a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4783,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7992','4780','Revisión del deterioro de créditos a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4784,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7993','4780','Revisión del deterioro de créditos a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL); +INSERT INTO `llx_accounting_account` VALUES (1,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','CAPITAL','101','1401','Capital',0,NULL,NULL,1,NULL,NULL),(2,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','105','1401','Ecarts de réévaluation',0,NULL,NULL,1,NULL,NULL),(3,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1061','1401','Réserve légale',0,NULL,NULL,1,NULL,NULL),(4,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1063','1401','Réserves statutaires ou contractuelles',0,NULL,NULL,1,NULL,NULL),(5,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1064','1401','Réserves réglementées',0,NULL,NULL,1,NULL,NULL),(6,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','1068','1401','Autres réserves',0,NULL,NULL,1,NULL,NULL),(7,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','108','1401','Compte de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(8,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','12','1401','Résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(9,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','145','1401','Amortissements dérogatoires',0,NULL,NULL,1,NULL,NULL),(10,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','146','1401','Provision spéciale de réévaluation',0,NULL,NULL,1,NULL,NULL),(11,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','147','1401','Plus-values réinvesties',0,NULL,NULL,1,NULL,NULL),(12,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','148','1401','Autres provisions réglementées',0,NULL,NULL,1,NULL,NULL),(13,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','15','1401','Provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(14,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','CAPIT','XXXXXX','16','1401','Emprunts et dettes assimilees',0,NULL,NULL,1,NULL,NULL),(15,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','20','1402','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(16,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','201','15','Frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(17,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','206','15','Droit au bail',0,NULL,NULL,1,NULL,NULL),(18,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','207','15','Fonds commercial',0,NULL,NULL,1,NULL,NULL),(19,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','208','15','Autres immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(20,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','21','1402','Immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(21,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','23','1402','Immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(22,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','27','1402','Autres immobilisations financieres',0,NULL,NULL,1,NULL,NULL),(23,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','280','1402','Amortissements des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(24,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','281','1402','Amortissements des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(25,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','290','1402','Provisions pour dépréciation des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(26,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','291','1402','Provisions pour dépréciation des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(27,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','IMMO','XXXXXX','297','1402','Provisions pour dépréciation des autres immobilisations financières',0,NULL,NULL,1,NULL,NULL),(28,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','31','1403','Matieres premières',0,NULL,NULL,1,NULL,NULL),(29,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','32','1403','Autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(30,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','33','1403','En-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(31,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','34','1403','En-cours de production de services',0,NULL,NULL,1,NULL,NULL),(32,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','35','1403','Stocks de produits',0,NULL,NULL,1,NULL,NULL),(33,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','37','1403','Stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(34,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','391','1403','Provisions pour dépréciation des matières premières',0,NULL,NULL,1,NULL,NULL),(35,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','392','1403','Provisions pour dépréciation des autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(36,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','393','1403','Provisions pour dépréciation des en-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(37,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','394','1403','Provisions pour dépréciation des en-cours de production de services',0,NULL,NULL,1,NULL,NULL),(38,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','395','1403','Provisions pour dépréciation des stocks de produits',0,NULL,NULL,1,NULL,NULL),(39,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','STOCK','XXXXXX','397','1403','Provisions pour dépréciation des stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(40,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','SUPPLIER','400','1404','Fournisseurs et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(41,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','409','1404','Fournisseurs débiteurs',0,NULL,NULL,1,NULL,NULL),(42,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','CUSTOMER','410','1404','Clients et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(43,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','419','1404','Clients créditeurs',0,NULL,NULL,1,NULL,NULL),(44,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','421','1404','Personnel',0,NULL,NULL,1,NULL,NULL),(45,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','428','1404','Personnel',0,NULL,NULL,1,NULL,NULL),(46,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','43','1404','Sécurité sociale et autres organismes sociaux',0,NULL,NULL,1,NULL,NULL),(47,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','444','1404','Etat - impôts sur bénéfice',0,NULL,NULL,1,NULL,NULL),(48,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','445','1404','Etat - Taxes sur chiffre affaires',0,NULL,NULL,1,NULL,NULL),(49,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','447','1404','Autres impôts, taxes et versements assimilés',0,NULL,NULL,1,NULL,NULL),(50,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','45','1404','Groupe et associes',0,NULL,NULL,1,NULL,NULL),(51,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','455','50','Associés',0,NULL,NULL,1,NULL,NULL),(52,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','46','1404','Débiteurs divers et créditeurs divers',0,NULL,NULL,1,NULL,NULL),(53,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','47','1404','Comptes transitoires ou d\'attente',0,NULL,NULL,1,NULL,NULL),(54,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','481','1404','Charges à répartir sur plusieurs exercices',0,NULL,NULL,1,NULL,NULL),(55,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','486','1404','Charges constatées d\'avance',0,NULL,NULL,1,NULL,NULL),(56,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','487','1404','Produits constatés d\'avance',0,NULL,NULL,1,NULL,NULL),(57,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','491','1404','Provisions pour dépréciation des comptes de clients',0,NULL,NULL,1,NULL,NULL),(58,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','TIERS','XXXXXX','496','1404','Provisions pour dépréciation des comptes de débiteurs divers',0,NULL,NULL,1,NULL,NULL),(59,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','50','1405','Valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(60,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','BANK','51','1405','Banques, établissements financiers et assimilés',0,NULL,NULL,1,NULL,NULL),(61,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','CASH','53','1405','Caisse',0,NULL,NULL,1,NULL,NULL),(62,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','54','1405','Régies d\'avance et accréditifs',0,NULL,NULL,1,NULL,NULL),(63,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','58','1405','Virements internes',0,NULL,NULL,1,NULL,NULL),(64,1,NULL,'2016-01-22 17:28:15','PCG99-ABREGE','FINAN','XXXXXX','590','1405','Provisions pour dépréciation des valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(65,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','PRODUCT','60','1406','Achats',0,NULL,NULL,1,NULL,NULL),(66,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','603','65','Variations des stocks',0,NULL,NULL,1,NULL,NULL),(67,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','SERVICE','61','1406','Services extérieurs',0,NULL,NULL,1,NULL,NULL),(68,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','62','1406','Autres services extérieurs',0,NULL,NULL,1,NULL,NULL),(69,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','63','1406','Impôts, taxes et versements assimiles',0,NULL,NULL,1,NULL,NULL),(70,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','641','1406','Rémunérations du personnel',0,NULL,NULL,1,NULL,NULL),(71,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','644','1406','Rémunération du travail de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(72,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','SOCIAL','645','1406','Charges de sécurité sociale et de prévoyance',0,NULL,NULL,1,NULL,NULL),(73,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','646','1406','Cotisations sociales personnelles de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(74,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','65','1406','Autres charges de gestion courante',0,NULL,NULL,1,NULL,NULL),(75,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','66','1406','Charges financières',0,NULL,NULL,1,NULL,NULL),(76,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','67','1406','Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(77,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','681','1406','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(78,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','686','1406','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(79,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','687','1406','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(80,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','691','1406','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(81,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','695','1406','Impôts sur les bénéfices',0,NULL,NULL,1,NULL,NULL),(82,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','697','1406','Imposition forfaitaire annuelle des sociétés',0,NULL,NULL,1,NULL,NULL),(83,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','699','1406','Produits',0,NULL,NULL,1,NULL,NULL),(84,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','PRODUCT','701','1407','Ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(85,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','SERVICE','706','1407','Prestations de services',0,NULL,NULL,1,NULL,NULL),(86,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','PRODUCT','707','1407','Ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(87,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','PRODUCT','708','1407','Produits des activités annexes',0,NULL,NULL,1,NULL,NULL),(88,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','709','1407','Rabais, remises et ristournes accordés par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(89,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','713','1407','Variation des stocks',0,NULL,NULL,1,NULL,NULL),(90,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','72','1407','Production immobilisée',0,NULL,NULL,1,NULL,NULL),(91,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','73','1407','Produits nets partiels sur opérations à long terme',0,NULL,NULL,1,NULL,NULL),(92,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','74','1407','Subventions d\'exploitation',0,NULL,NULL,1,NULL,NULL),(93,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','75','1407','Autres produits de gestion courante',0,NULL,NULL,1,NULL,NULL),(94,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','753','93','Jetons de présence et rémunérations d\'administrateurs, gérants,...',0,NULL,NULL,1,NULL,NULL),(95,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','754','93','Ristournes perçues des coopératives',0,NULL,NULL,1,NULL,NULL),(96,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','755','93','Quotes-parts de résultat sur opérations faites en commun',0,NULL,NULL,1,NULL,NULL),(97,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','76','1407','Produits financiers',0,NULL,NULL,1,NULL,NULL),(98,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','77','1407','Produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(99,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','781','1407','Reprises sur amortissements et provisions',0,NULL,NULL,1,NULL,NULL),(100,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','786','1407','Reprises sur provisions pour risques',0,NULL,NULL,1,NULL,NULL),(101,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','787','1407','Reprises sur provisions',0,NULL,NULL,1,NULL,NULL),(102,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','79','1407','Transferts de charges',0,NULL,NULL,1,NULL,NULL),(103,1,NULL,'2017-02-20 10:49:11','PCG99-BASE','CAPIT','XXXXXX','10','1501','Capital et réserves',0,NULL,NULL,1,NULL,NULL),(104,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','CAPITAL','101','103','Capital',0,NULL,NULL,1,NULL,NULL),(105,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','104','103','Primes liées au capital social',0,NULL,NULL,1,NULL,NULL),(106,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','105','103','Ecarts de réévaluation',0,NULL,NULL,1,NULL,NULL),(107,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','106','103','Réserves',0,NULL,NULL,1,NULL,NULL),(108,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','107','103','Ecart d\'equivalence',0,NULL,NULL,1,NULL,NULL),(109,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','108','103','Compte de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(110,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','109','103','Actionnaires : capital souscrit - non appelé',0,NULL,NULL,1,NULL,NULL),(111,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','11','1501','Report à nouveau (solde créditeur ou débiteur)',0,NULL,NULL,1,NULL,NULL),(112,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','110','111','Report à nouveau (solde créditeur)',0,NULL,NULL,1,NULL,NULL),(113,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','119','111','Report à nouveau (solde débiteur)',0,NULL,NULL,1,NULL,NULL),(114,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','12','1501','Résultat de l\'exercice (bénéfice ou perte)',0,NULL,NULL,1,NULL,NULL),(115,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','120','114','Résultat de l\'exercice (bénéfice)',0,NULL,NULL,1,NULL,NULL),(116,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','129','114','Résultat de l\'exercice (perte)',0,NULL,NULL,1,NULL,NULL),(117,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','13','1501','Subventions d\'investissement',0,NULL,NULL,1,NULL,NULL),(118,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','131','117','Subventions d\'équipement',0,NULL,NULL,1,NULL,NULL),(119,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','138','117','Autres subventions d\'investissement',0,NULL,NULL,1,NULL,NULL),(120,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','139','117','Subventions d\'investissement inscrites au compte de résultat',0,NULL,NULL,1,NULL,NULL),(121,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','14','1501','Provisions réglementées',0,NULL,NULL,1,NULL,NULL),(122,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','142','121','Provisions réglementées relatives aux immobilisations',0,NULL,NULL,1,NULL,NULL),(123,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','143','121','Provisions réglementées relatives aux stocks',0,NULL,NULL,1,NULL,NULL),(124,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','144','121','Provisions réglementées relatives aux autres éléments de l\'actif',0,NULL,NULL,1,NULL,NULL),(125,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','145','121','Amortissements dérogatoires',0,NULL,NULL,1,NULL,NULL),(126,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','146','121','Provision spéciale de réévaluation',0,NULL,NULL,1,NULL,NULL),(127,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','147','121','Plus-values réinvesties',0,NULL,NULL,1,NULL,NULL),(128,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','148','121','Autres provisions réglementées',0,NULL,NULL,1,NULL,NULL),(129,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','15','1501','Provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(130,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','151','129','Provisions pour risques',0,NULL,NULL,1,NULL,NULL),(131,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','153','129','Provisions pour pensions et obligations similaires',0,NULL,NULL,1,NULL,NULL),(132,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','154','129','Provisions pour restructurations',0,NULL,NULL,1,NULL,NULL),(133,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','155','129','Provisions pour impôts',0,NULL,NULL,1,NULL,NULL),(134,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','156','129','Provisions pour renouvellement des immobilisations (entreprises concessionnaires)',0,NULL,NULL,1,NULL,NULL),(135,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','157','129','Provisions pour charges à répartir sur plusieurs exercices',0,NULL,NULL,1,NULL,NULL),(136,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','158','129','Autres provisions pour charges',0,NULL,NULL,1,NULL,NULL),(137,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','16','1501','Emprunts et dettes assimilees',0,NULL,NULL,1,NULL,NULL),(138,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','161','137','Emprunts obligataires convertibles',0,NULL,NULL,1,NULL,NULL),(139,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','163','137','Autres emprunts obligataires',0,NULL,NULL,1,NULL,NULL),(140,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','164','137','Emprunts auprès des établissements de crédit',0,NULL,NULL,1,NULL,NULL),(141,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','165','137','Dépôts et cautionnements reçus',0,NULL,NULL,1,NULL,NULL),(142,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','166','137','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(143,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','167','137','Emprunts et dettes assortis de conditions particulières',0,NULL,NULL,1,NULL,NULL),(144,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','168','137','Autres emprunts et dettes assimilées',0,NULL,NULL,1,NULL,NULL),(145,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','169','137','Primes de remboursement des obligations',0,NULL,NULL,1,NULL,NULL),(146,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','17','1501','Dettes rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(147,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','171','146','Dettes rattachées à des participations (groupe)',0,NULL,NULL,1,NULL,NULL),(148,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','174','146','Dettes rattachées à des participations (hors groupe)',0,NULL,NULL,1,NULL,NULL),(149,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','178','146','Dettes rattachées à des sociétés en participation',0,NULL,NULL,1,NULL,NULL),(150,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','18','1501','Comptes de liaison des établissements et sociétés en participation',0,NULL,NULL,1,NULL,NULL),(151,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','181','150','Comptes de liaison des établissements',0,NULL,NULL,1,NULL,NULL),(152,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','186','150','Biens et prestations de services échangés entre établissements (charges)',0,NULL,NULL,1,NULL,NULL),(153,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','187','150','Biens et prestations de services échangés entre établissements (produits)',0,NULL,NULL,1,NULL,NULL),(154,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','CAPIT','XXXXXX','188','150','Comptes de liaison des sociétés en participation',0,NULL,NULL,1,NULL,NULL),(155,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','20','1502','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(156,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','201','155','Frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(157,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','203','155','Frais de recherche et de développement',0,NULL,NULL,1,NULL,NULL),(158,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','205','155','Concessions et droits similaires, brevets, licences, marques, procédés, logiciels, droits et valeurs similaires',0,NULL,NULL,1,NULL,NULL),(159,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','206','155','Droit au bail',0,NULL,NULL,1,NULL,NULL),(160,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','207','155','Fonds commercial',0,NULL,NULL,1,NULL,NULL),(161,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','208','155','Autres immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(162,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','21','1502','Immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(163,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','211','162','Terrains',0,NULL,NULL,1,NULL,NULL),(164,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','212','162','Agencements et aménagements de terrains',0,NULL,NULL,1,NULL,NULL),(165,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','213','162','Constructions',0,NULL,NULL,1,NULL,NULL),(166,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','214','162','Constructions sur sol d\'autrui',0,NULL,NULL,1,NULL,NULL),(167,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','215','162','Installations techniques, matériels et outillage industriels',0,NULL,NULL,1,NULL,NULL),(168,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','218','162','Autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(169,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','22','1502','Immobilisations mises en concession',0,NULL,NULL,1,NULL,NULL),(170,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','23','1502','Immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(171,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','231','170','Immobilisations corporelles en cours',0,NULL,NULL,1,NULL,NULL),(172,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','232','170','Immobilisations incorporelles en cours',0,NULL,NULL,1,NULL,NULL),(173,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','237','170','Avances et acomptes versés sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(174,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','238','170','Avances et acomptes versés sur commandes d\'immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(175,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','25','1502','Parts dans des entreprises liées et créances sur des entreprises liées',0,NULL,NULL,1,NULL,NULL),(176,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','26','1502','Participations et créances rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(177,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','261','176','Titres de participation',0,NULL,NULL,1,NULL,NULL),(178,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','266','176','Autres formes de participation',0,NULL,NULL,1,NULL,NULL),(179,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','267','176','Créances rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(180,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','268','176','Créances rattachées à des sociétés en participation',0,NULL,NULL,1,NULL,NULL),(181,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','269','176','Versements restant à effectuer sur titres de participation non libérés',0,NULL,NULL,1,NULL,NULL),(182,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','27','1502','Autres immobilisations financieres',0,NULL,NULL,1,NULL,NULL),(183,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','271','183','Titres immobilisés autres que les titres immobilisés de l\'activité de portefeuille (droit de propriété)',0,NULL,NULL,1,NULL,NULL),(184,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','272','183','Titres immobilisés (droit de créance)',0,NULL,NULL,1,NULL,NULL),(185,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','273','183','Titres immobilisés de l\'activité de portefeuille',0,NULL,NULL,1,NULL,NULL),(186,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','274','183','Prêts',0,NULL,NULL,1,NULL,NULL),(187,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','275','183','Dépôts et cautionnements versés',0,NULL,NULL,1,NULL,NULL),(188,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','276','183','Autres créances immobilisées',0,NULL,NULL,1,NULL,NULL),(189,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','277','183','(Actions propres ou parts propres)',0,NULL,NULL,1,NULL,NULL),(190,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','279','183','Versements restant à effectuer sur titres immobilisés non libérés',0,NULL,NULL,1,NULL,NULL),(191,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','28','1502','Amortissements des immobilisations',0,NULL,NULL,1,NULL,NULL),(192,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','280','191','Amortissements des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(193,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','281','191','Amortissements des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(194,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','282','191','Amortissements des immobilisations mises en concession',0,NULL,NULL,1,NULL,NULL),(195,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','29','1502','Dépréciations des immobilisations',0,NULL,NULL,1,NULL,NULL),(196,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','290','195','Dépréciations des immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(197,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','291','195','Dépréciations des immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(198,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','292','195','Dépréciations des immobilisations mises en concession',0,NULL,NULL,1,NULL,NULL),(199,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','293','195','Dépréciations des immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(200,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','296','195','Provisions pour dépréciation des participations et créances rattachées à des participations',0,NULL,NULL,1,NULL,NULL),(201,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','IMMO','XXXXXX','297','195','Provisions pour dépréciation des autres immobilisations financières',0,NULL,NULL,1,NULL,NULL),(202,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','31','1503','Matières premières (et fournitures)',0,NULL,NULL,1,NULL,NULL),(203,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','311','202','Matières (ou groupe) A',0,NULL,NULL,1,NULL,NULL),(204,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','312','202','Matières (ou groupe) B',0,NULL,NULL,1,NULL,NULL),(205,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','317','202','Fournitures A, B, C,',0,NULL,NULL,1,NULL,NULL),(206,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','32','1503','Autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(207,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','321','206','Matières consommables',0,NULL,NULL,1,NULL,NULL),(208,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','322','206','Fournitures consommables',0,NULL,NULL,1,NULL,NULL),(209,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','326','206','Emballages',0,NULL,NULL,1,NULL,NULL),(210,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','33','1503','En-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(211,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','331','210','Produits en cours',0,NULL,NULL,1,NULL,NULL),(212,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','335','210','Travaux en cours',0,NULL,NULL,1,NULL,NULL),(213,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','34','1503','En-cours de production de services',0,NULL,NULL,1,NULL,NULL),(214,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','341','213','Etudes en cours',0,NULL,NULL,1,NULL,NULL),(215,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','345','213','Prestations de services en cours',0,NULL,NULL,1,NULL,NULL),(216,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','35','1503','Stocks de produits',0,NULL,NULL,1,NULL,NULL),(217,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','351','216','Produits intermédiaires',0,NULL,NULL,1,NULL,NULL),(218,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','355','216','Produits finis',0,NULL,NULL,1,NULL,NULL),(219,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','358','216','Produits résiduels (ou matières de récupération)',0,NULL,NULL,1,NULL,NULL),(220,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','37','1503','Stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(221,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','371','220','Marchandises (ou groupe) A',0,NULL,NULL,1,NULL,NULL),(222,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','372','220','Marchandises (ou groupe) B',0,NULL,NULL,1,NULL,NULL),(223,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','39','1503','Provisions pour dépréciation des stocks et en-cours',0,NULL,NULL,1,NULL,NULL),(224,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','391','223','Provisions pour dépréciation des matières premières',0,NULL,NULL,1,NULL,NULL),(225,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','392','223','Provisions pour dépréciation des autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(226,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','393','223','Provisions pour dépréciation des en-cours de production de biens',0,NULL,NULL,1,NULL,NULL),(227,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','394','223','Provisions pour dépréciation des en-cours de production de services',0,NULL,NULL,1,NULL,NULL),(228,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','395','223','Provisions pour dépréciation des stocks de produits',0,NULL,NULL,1,NULL,NULL),(229,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','STOCK','XXXXXX','397','223','Provisions pour dépréciation des stocks de marchandises',0,NULL,NULL,1,NULL,NULL),(230,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','40','1504','Fournisseurs et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(231,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','400','230','Fournisseurs et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(232,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','SUPPLIER','401','230','Fournisseurs',0,NULL,NULL,1,NULL,NULL),(233,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','403','230','Fournisseurs - Effets à payer',0,NULL,NULL,1,NULL,NULL),(234,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','404','230','Fournisseurs d\'immobilisations',0,NULL,NULL,1,NULL,NULL),(235,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','405','230','Fournisseurs d\'immobilisations - Effets à payer',0,NULL,NULL,1,NULL,NULL),(236,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','408','230','Fournisseurs - Factures non parvenues',0,NULL,NULL,1,NULL,NULL),(237,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','409','230','Fournisseurs débiteurs',0,NULL,NULL,1,NULL,NULL),(238,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','41','1504','Clients et comptes rattachés',0,NULL,NULL,1,NULL,NULL),(239,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','410','238','Clients et Comptes rattachés',0,NULL,NULL,1,NULL,NULL),(240,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','CUSTOMER','411','238','Clients',0,NULL,NULL,1,NULL,NULL),(241,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','413','238','Clients - Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(242,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','416','238','Clients douteux ou litigieux',0,NULL,NULL,1,NULL,NULL),(243,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','418','238','Clients - Produits non encore facturés',0,NULL,NULL,1,NULL,NULL),(244,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','419','238','Clients créditeurs',0,NULL,NULL,1,NULL,NULL),(245,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','42','1504','Personnel et comptes rattachés',0,NULL,NULL,1,NULL,NULL),(246,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','421','245','Personnel - Rémunérations dues',0,NULL,NULL,1,NULL,NULL),(247,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','422','245','Comités d\'entreprises, d\'établissement, ...',0,NULL,NULL,1,NULL,NULL),(248,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','424','245','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(249,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','425','245','Personnel - Avances et acomptes',0,NULL,NULL,1,NULL,NULL),(250,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','426','245','Personnel - Dépôts',0,NULL,NULL,1,NULL,NULL),(251,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','427','245','Personnel - Oppositions',0,NULL,NULL,1,NULL,NULL),(252,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','428','245','Personnel - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(253,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','43','1504','Sécurité sociale et autres organismes sociaux',0,NULL,NULL,1,NULL,NULL),(254,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','431','253','Sécurité sociale',0,NULL,NULL,1,NULL,NULL),(255,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','437','253','Autres organismes sociaux',0,NULL,NULL,1,NULL,NULL),(256,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','438','253','Organismes sociaux - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(257,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','44','1504','État et autres collectivités publiques',0,NULL,NULL,1,NULL,NULL),(258,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','441','257','État - Subventions à recevoir',0,NULL,NULL,1,NULL,NULL),(259,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','442','257','Etat - Impôts et taxes recouvrables sur des tiers',0,NULL,NULL,1,NULL,NULL),(260,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','443','257','Opérations particulières avec l\'Etat, les collectivités publiques, les organismes internationaux',0,NULL,NULL,1,NULL,NULL),(261,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','444','257','Etat - Impôts sur les bénéfices',0,NULL,NULL,1,NULL,NULL),(262,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','445','257','Etat - Taxes sur le chiffre d\'affaires',0,NULL,NULL,1,NULL,NULL),(263,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','446','257','Obligations cautionnées',0,NULL,NULL,1,NULL,NULL),(264,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','447','257','Autres impôts, taxes et versements assimilés',0,NULL,NULL,1,NULL,NULL),(265,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','448','257','Etat - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(266,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','449','257','Quotas d\'émission à restituer à l\'Etat',0,NULL,NULL,1,NULL,NULL),(267,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','45','1504','Groupe et associes',0,NULL,NULL,1,NULL,NULL),(268,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','451','267','Groupe',0,NULL,NULL,1,NULL,NULL),(269,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','455','267','Associés - Comptes courants',0,NULL,NULL,1,NULL,NULL),(270,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','456','267','Associés - Opérations sur le capital',0,NULL,NULL,1,NULL,NULL),(271,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','457','267','Associés - Dividendes à payer',0,NULL,NULL,1,NULL,NULL),(272,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','458','267','Associés - Opérations faites en commun et en G.I.E.',0,NULL,NULL,1,NULL,NULL),(273,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','46','1504','Débiteurs divers et créditeurs divers',0,NULL,NULL,1,NULL,NULL),(274,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','462','273','Créances sur cessions d\'immobilisations',0,NULL,NULL,1,NULL,NULL),(275,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','464','273','Dettes sur acquisitions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(276,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','465','273','Créances sur cessions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(277,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','467','273','Autres comptes débiteurs ou créditeurs',0,NULL,NULL,1,NULL,NULL),(278,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','468','273','Divers - Charges à payer et produits à recevoir',0,NULL,NULL,1,NULL,NULL),(279,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','47','1504','Comptes transitoires ou d\'attente',0,NULL,NULL,1,NULL,NULL),(280,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','471','279','Comptes d\'attente',0,NULL,NULL,1,NULL,NULL),(281,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','476','279','Différence de conversion - Actif',0,NULL,NULL,1,NULL,NULL),(282,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','477','279','Différences de conversion - Passif',0,NULL,NULL,1,NULL,NULL),(283,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','478','279','Autres comptes transitoires',0,NULL,NULL,1,NULL,NULL),(284,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','48','1504','Comptes de régularisation',0,NULL,NULL,1,NULL,NULL),(285,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','481','284','Charges à répartir sur plusieurs exercices',0,NULL,NULL,1,NULL,NULL),(286,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','486','284','Charges constatées d\'avance',0,NULL,NULL,1,NULL,NULL),(287,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','487','284','Produits constatés d\'avance',0,NULL,NULL,1,NULL,NULL),(288,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','488','284','Comptes de répartition périodique des charges et des produits',0,NULL,NULL,1,NULL,NULL),(289,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','489','284','Quotas d\'émission alloués par l\'Etat',0,NULL,NULL,1,NULL,NULL),(290,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','49','1504','Provisions pour dépréciation des comptes de tiers',0,NULL,NULL,1,NULL,NULL),(291,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','491','290','Provisions pour dépréciation des comptes de clients',0,NULL,NULL,1,NULL,NULL),(292,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','495','290','Provisions pour dépréciation des comptes du groupe et des associés',0,NULL,NULL,1,NULL,NULL),(293,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','TIERS','XXXXXX','496','290','Provisions pour dépréciation des comptes de débiteurs divers',0,NULL,NULL,1,NULL,NULL),(294,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','50','1505','Valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(295,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','501','294','Parts dans des entreprises liées',0,NULL,NULL,1,NULL,NULL),(296,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','502','294','Actions propres',0,NULL,NULL,1,NULL,NULL),(297,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','503','294','Actions',0,NULL,NULL,1,NULL,NULL),(298,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','504','294','Autres titres conférant un droit de propriété',0,NULL,NULL,1,NULL,NULL),(299,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','505','294','Obligations et bons émis par la société et rachetés par elle',0,NULL,NULL,1,NULL,NULL),(300,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','506','294','Obligations',0,NULL,NULL,1,NULL,NULL),(301,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','507','294','Bons du Trésor et bons de caisse à court terme',0,NULL,NULL,1,NULL,NULL),(302,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','508','294','Autres valeurs mobilières de placement et autres créances assimilées',0,NULL,NULL,1,NULL,NULL),(303,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','509','294','Versements restant à effectuer sur valeurs mobilières de placement non libérées',0,NULL,NULL,1,NULL,NULL),(304,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','51','1505','Banques, établissements financiers et assimilés',0,NULL,NULL,1,NULL,NULL),(305,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','511','304','Valeurs à l\'encaissement',0,NULL,NULL,1,NULL,NULL),(306,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','BANK','512','304','Banques',0,NULL,NULL,1,NULL,NULL),(307,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','514','304','Chèques postaux',0,NULL,NULL,1,NULL,NULL),(308,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','515','304','\"Caisses\" du Trésor et des établissements publics',0,NULL,NULL,1,NULL,NULL),(309,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','516','304','Sociétés de bourse',0,NULL,NULL,1,NULL,NULL),(310,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','517','304','Autres organismes financiers',0,NULL,NULL,1,NULL,NULL),(311,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','518','304','Intérêts courus',0,NULL,NULL,1,NULL,NULL),(312,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','519','304','Concours bancaires courants',0,NULL,NULL,1,NULL,NULL),(313,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','52','1505','Instruments de trésorerie',0,NULL,NULL,1,NULL,NULL),(314,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','CASH','53','1505','Caisse',0,NULL,NULL,1,NULL,NULL),(315,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','531','314','Caisse siège social',0,NULL,NULL,1,NULL,NULL),(316,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','532','314','Caisse succursale (ou usine) A',0,NULL,NULL,1,NULL,NULL),(317,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','533','314','Caisse succursale (ou usine) B',0,NULL,NULL,1,NULL,NULL),(318,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','54','1505','Régies d\'avance et accréditifs',0,NULL,NULL,1,NULL,NULL),(319,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','58','1505','Virements internes',0,NULL,NULL,1,NULL,NULL),(320,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','59','1505','Provisions pour dépréciation des comptes financiers',0,NULL,NULL,1,NULL,NULL),(321,1,NULL,'2016-01-22 17:28:15','PCG99-BASE','FINAN','XXXXXX','590','320','Provisions pour dépréciation des valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(322,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','PRODUCT','60','1506','Achats',0,NULL,NULL,1,NULL,NULL),(323,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','601','322','Achats stockés - Matières premières (et fournitures)',0,NULL,NULL,1,NULL,NULL),(324,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','602','322','Achats stockés - Autres approvisionnements',0,NULL,NULL,1,NULL,NULL),(325,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','603','322','Variations des stocks (approvisionnements et marchandises)',0,NULL,NULL,1,NULL,NULL),(326,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','604','322','Achats stockés - Matières premières (et fournitures)',0,NULL,NULL,1,NULL,NULL),(327,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','605','322','Achats de matériel, équipements et travaux',0,NULL,NULL,1,NULL,NULL),(328,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','606','322','Achats non stockés de matière et fournitures',0,NULL,NULL,1,NULL,NULL),(329,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','607','322','Achats de marchandises',0,NULL,NULL,1,NULL,NULL),(330,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','608','322','(Compte réservé, le cas échéant, à la récapitulation des frais accessoires incorporés aux achats)',0,NULL,NULL,1,NULL,NULL),(331,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','609','322','Rabais, remises et ristournes obtenus sur achats',0,NULL,NULL,1,NULL,NULL),(332,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','SERVICE','61','1506','Services extérieurs',0,NULL,NULL,1,NULL,NULL),(333,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','611','332','Sous-traitance générale',0,NULL,NULL,1,NULL,NULL),(334,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','612','332','Redevances de crédit-bail',0,NULL,NULL,1,NULL,NULL),(335,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','613','332','Locations',0,NULL,NULL,1,NULL,NULL),(336,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','614','332','Charges locatives et de copropriété',0,NULL,NULL,1,NULL,NULL),(337,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','615','332','Entretien et réparations',0,NULL,NULL,1,NULL,NULL),(338,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','616','332','Primes d\'assurances',0,NULL,NULL,1,NULL,NULL),(339,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','617','332','Etudes et recherches',0,NULL,NULL,1,NULL,NULL),(340,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','618','332','Divers',0,NULL,NULL,1,NULL,NULL),(341,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','619','332','Rabais, remises et ristournes obtenus sur services extérieurs',0,NULL,NULL,1,NULL,NULL),(342,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','62','1506','Autres services extérieurs',0,NULL,NULL,1,NULL,NULL),(343,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','621','342','Personnel extérieur à l\'entreprise',0,NULL,NULL,1,NULL,NULL),(344,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','622','342','Rémunérations d\'intermédiaires et honoraires',0,NULL,NULL,1,NULL,NULL),(345,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','623','342','Publicité, publications, relations publiques',0,NULL,NULL,1,NULL,NULL),(346,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','624','342','Transports de biens et transports collectifs du personnel',0,NULL,NULL,1,NULL,NULL),(347,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','625','342','Déplacements, missions et réceptions',0,NULL,NULL,1,NULL,NULL),(348,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','626','342','Frais postaux et de télécommunications',0,NULL,NULL,1,NULL,NULL),(349,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','627','342','Services bancaires et assimilés',0,NULL,NULL,1,NULL,NULL),(350,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','628','342','Divers',0,NULL,NULL,1,NULL,NULL),(351,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','629','342','Rabais, remises et ristournes obtenus sur autres services extérieurs',0,NULL,NULL,1,NULL,NULL),(352,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','63','1506','Impôts, taxes et versements assimilés',0,NULL,NULL,1,NULL,NULL),(353,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','631','352','Impôts, taxes et versements assimilés sur rémunérations (administrations des impôts)',0,NULL,NULL,1,NULL,NULL),(354,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','633','352','Impôts, taxes et versements assimilés sur rémunérations (autres organismes)',0,NULL,NULL,1,NULL,NULL),(355,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','635','352','Autres impôts, taxes et versements assimilés (administrations des impôts)',0,NULL,NULL,1,NULL,NULL),(356,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','637','352','Autres impôts, taxes et versements assimilés (autres organismes)',0,NULL,NULL,1,NULL,NULL),(357,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','64','1506','Charges de personnel',0,NULL,NULL,1,NULL,NULL),(358,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','641','357','Rémunérations du personnel',0,NULL,NULL,1,NULL,NULL),(359,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','644','357','Rémunération du travail de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(360,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','SOCIAL','645','357','Charges de sécurité sociale et de prévoyance',0,NULL,NULL,1,NULL,NULL),(361,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','646','357','Cotisations sociales personnelles de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(362,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','647','357','Autres charges sociales',0,NULL,NULL,1,NULL,NULL),(363,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','648','357','Autres charges de personnel',0,NULL,NULL,1,NULL,NULL),(364,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','65','1506','Autres charges de gestion courante',0,NULL,NULL,1,NULL,NULL),(365,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','651','364','Redevances pour concessions, brevets, licences, marques, procédés, logiciels, droits et valeurs similaires',0,NULL,NULL,1,NULL,NULL),(366,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','653','364','Jetons de présence',0,NULL,NULL,1,NULL,NULL),(367,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','654','364','Pertes sur créances irrécouvrables',0,NULL,NULL,1,NULL,NULL),(368,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','655','364','Quote-part de résultat sur opérations faites en commun',0,NULL,NULL,1,NULL,NULL),(369,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','658','364','Charges diverses de gestion courante',0,NULL,NULL,1,NULL,NULL),(370,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','66','1506','Charges financières',0,NULL,NULL,1,NULL,NULL),(371,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','661','370','Charges d\'intérêts',0,NULL,NULL,1,NULL,NULL),(372,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','664','370','Pertes sur créances liées à des participations',0,NULL,NULL,1,NULL,NULL),(373,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','665','370','Escomptes accordés',0,NULL,NULL,1,NULL,NULL),(374,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','666','370','Pertes de change',0,NULL,NULL,1,NULL,NULL),(375,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','667','370','Charges nettes sur cessions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(376,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','668','370','Autres charges financières',0,NULL,NULL,1,NULL,NULL),(377,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','67','1506','Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(378,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','671','377','Charges exceptionnelles sur opérations de gestion',0,NULL,NULL,1,NULL,NULL),(379,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','672','377','(Compte à la disposition des entités pour enregistrer, en cours d\'exercice, les charges sur exercices antérieurs)',0,NULL,NULL,1,NULL,NULL),(380,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','675','377','Valeurs comptables des éléments d\'actif cédés',0,NULL,NULL,1,NULL,NULL),(381,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','678','377','Autres charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(382,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','68','1506','Dotations aux amortissements et aux provisions',0,NULL,NULL,1,NULL,NULL),(383,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','681','382','Dotations aux amortissements et aux provisions - Charges d\'exploitation',0,NULL,NULL,1,NULL,NULL),(384,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','686','382','Dotations aux amortissements et aux provisions - Charges financières',0,NULL,NULL,1,NULL,NULL),(385,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','687','382','Dotations aux amortissements et aux provisions - Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(386,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','69','1506','Participation des salariés - impôts sur les bénéfices et assimiles',0,NULL,NULL,1,NULL,NULL),(387,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','691','386','Participation des salariés aux résultats',0,NULL,NULL,1,NULL,NULL),(388,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','695','386','Impôts sur les bénéfices',0,NULL,NULL,1,NULL,NULL),(389,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','696','386','Suppléments d\'impôt sur les sociétés liés aux distributions',0,NULL,NULL,1,NULL,NULL),(390,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','697','386','Imposition forfaitaire annuelle des sociétés',0,NULL,NULL,1,NULL,NULL),(391,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','698','386','Intégration fiscale',0,NULL,NULL,1,NULL,NULL),(392,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','699','386','Produits - Reports en arrière des déficits',0,NULL,NULL,1,NULL,NULL),(393,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','70','1507','Ventes de produits fabriqués, prestations de services, marchandises',0,NULL,NULL,1,NULL,NULL),(394,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','PRODUCT','701','393','Ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(395,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','702','393','Ventes de produits intermédiaires',0,NULL,NULL,1,NULL,NULL),(396,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','703','393','Ventes de produits résiduels',0,NULL,NULL,1,NULL,NULL),(397,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','704','393','Travaux',0,NULL,NULL,1,NULL,NULL),(398,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','705','393','Etudes',0,NULL,NULL,1,NULL,NULL),(399,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','SERVICE','706','393','Prestations de services',0,NULL,NULL,1,NULL,NULL),(400,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','PRODUCT','707','393','Ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(401,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','PRODUCT','708','393','Produits des activités annexes',0,NULL,NULL,1,NULL,NULL),(402,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','709','393','Rabais, remises et ristournes accordés par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(403,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','71','1507','Production stockée (ou déstockage)',0,NULL,NULL,1,NULL,NULL),(404,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','713','403','Variation des stocks (en-cours de production, produits)',0,NULL,NULL,1,NULL,NULL),(405,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','72','1507','Production immobilisée',0,NULL,NULL,1,NULL,NULL),(406,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','721','405','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(407,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','722','405','Immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(408,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','74','1507','Subventions d\'exploitation',0,NULL,NULL,1,NULL,NULL),(409,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','75','1507','Autres produits de gestion courante',0,NULL,NULL,1,NULL,NULL),(410,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','751','409','Redevances pour concessions, brevets, licences, marques, procédés, logiciels, droits et valeurs similaires',0,NULL,NULL,1,NULL,NULL),(411,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','752','409','Revenus des immeubles non affectés à des activités professionnelles',0,NULL,NULL,1,NULL,NULL),(412,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','753','409','Jetons de présence et rémunérations d\'administrateurs, gérants,...',0,NULL,NULL,1,NULL,NULL),(413,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','754','409','Ristournes perçues des coopératives (provenant des excédents)',0,NULL,NULL,1,NULL,NULL),(414,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','755','409','Quotes-parts de résultat sur opérations faites en commun',0,NULL,NULL,1,NULL,NULL),(415,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','758','409','Produits divers de gestion courante',0,NULL,NULL,1,NULL,NULL),(416,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','76','1507','Produits financiers',0,NULL,NULL,1,NULL,NULL),(417,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','761','416','Produits de participations',0,NULL,NULL,1,NULL,NULL),(418,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','762','416','Produits des autres immobilisations financières',0,NULL,NULL,1,NULL,NULL),(419,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','763','416','Revenus des autres créances',0,NULL,NULL,1,NULL,NULL),(420,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','764','416','Revenus des valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(421,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','765','416','Escomptes obtenus',0,NULL,NULL,1,NULL,NULL),(422,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','766','416','Gains de change',0,NULL,NULL,1,NULL,NULL),(423,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','767','416','Produits nets sur cessions de valeurs mobilières de placement',0,NULL,NULL,1,NULL,NULL),(424,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','768','416','Autres produits financiers',0,NULL,NULL,1,NULL,NULL),(425,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','77','1507','Produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(426,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','771','425','Produits exceptionnels sur opérations de gestion',0,NULL,NULL,1,NULL,NULL),(427,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','772','425','(Compte à la disposition des entités pour enregistrer, en cours d\'exercice, les produits sur exercices antérieurs)',0,NULL,NULL,1,NULL,NULL),(428,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','775','425','Produits des cessions d\'éléments d\'actif',0,NULL,NULL,1,NULL,NULL),(429,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','777','425','Quote-part des subventions d\'investissement virée au résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(430,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','778','425','Autres produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(431,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','78','1507','Reprises sur amortissements et provisions',0,NULL,NULL,1,NULL,NULL),(432,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','781','431','Reprises sur amortissements et provisions (à inscrire dans les produits d\'exploitation)',0,NULL,NULL,1,NULL,NULL),(433,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','786','431','Reprises sur provisions pour risques (à inscrire dans les produits financiers)',0,NULL,NULL,1,NULL,NULL),(434,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','787','431','Reprises sur provisions (à inscrire dans les produits exceptionnels)',0,NULL,NULL,1,NULL,NULL),(435,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','79','1507','Transferts de charges',0,NULL,NULL,1,NULL,NULL),(436,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','791','435','Transferts de charges d\'exploitation ',0,NULL,NULL,1,NULL,NULL),(437,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','796','435','Transferts de charges financières',0,NULL,NULL,1,NULL,NULL),(438,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','797','435','Transferts de charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(439,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','10','1351','Capital',0,NULL,NULL,1,NULL,NULL),(440,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','100','439','Capital souscrit ou capital personnel',0,NULL,NULL,1,NULL,NULL),(441,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1000','440','Capital non amorti',0,NULL,NULL,1,NULL,NULL),(442,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1001','440','Capital amorti',0,NULL,NULL,1,NULL,NULL),(443,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','101','439','Capital non appelé',0,NULL,NULL,1,NULL,NULL),(444,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','109','439','Compte de l\'exploitant',0,NULL,NULL,1,NULL,NULL),(445,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1090','444','Opérations courantes',0,NULL,NULL,1,NULL,NULL),(446,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1091','444','Impôts personnels',0,NULL,NULL,1,NULL,NULL),(447,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1092','444','Rémunérations et autres avantages',0,NULL,NULL,1,NULL,NULL),(448,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','11','1351','Primes d\'émission',0,NULL,NULL,1,NULL,NULL),(449,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','12','1351','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(450,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','120','449','Plus-values de réévaluation sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(451,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1200','450','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(452,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1201','450','Reprises de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(453,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','121','449','Plus-values de réévaluation sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(454,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1210','453','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(455,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1211','453','Reprises de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(456,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','122','449','Plus-values de réévaluation sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(457,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1220','456','Plus-values de réévaluation',0,NULL,NULL,1,NULL,NULL),(458,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1221','456','Reprises de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(459,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','123','449','Plus-values de réévaluation sur stocks',0,NULL,NULL,1,NULL,NULL),(460,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','124','449','Reprises de réductions de valeur sur placements de trésorerie',0,NULL,NULL,1,NULL,NULL),(461,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','13','1351','Réserve',0,NULL,NULL,1,NULL,NULL),(462,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','130','461','Réserve légale',0,NULL,NULL,1,NULL,NULL),(463,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','131','461','Réserves indisponibles',0,NULL,NULL,1,NULL,NULL),(464,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1310','463','Réserve pour actions propres',0,NULL,NULL,1,NULL,NULL),(465,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1311','463','Autres réserves indisponibles',0,NULL,NULL,1,NULL,NULL),(466,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','132','461','Réserves immunisées',0,NULL,NULL,1,NULL,NULL),(467,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','133','461','Réserves disponibles',0,NULL,NULL,1,NULL,NULL),(468,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1330','467','Réserve pour régularisation de dividendes',0,NULL,NULL,1,NULL,NULL),(469,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1331','467','Réserve pour renouvellement des immobilisations',0,NULL,NULL,1,NULL,NULL),(470,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1332','467','Réserve pour installations en faveur du personnel 1333 Réserves libres',0,NULL,NULL,1,NULL,NULL),(471,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','14','1351','Bénéfice reporté (ou perte reportée)',0,NULL,NULL,1,NULL,NULL),(472,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','15','1351','Subsides en capital',0,NULL,NULL,1,NULL,NULL),(473,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','150','472','Montants obtenus',0,NULL,NULL,1,NULL,NULL),(474,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','151','472','Montants transférés aux résultats',0,NULL,NULL,1,NULL,NULL),(475,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','16','1351','Provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(476,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','160','475','Provisions pour pensions et obligations similaires',0,NULL,NULL,1,NULL,NULL),(477,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','161','475','Provisions pour charges fiscales',0,NULL,NULL,1,NULL,NULL),(478,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','162','475','Provisions pour grosses réparations et gros entretiens',0,NULL,NULL,1,NULL,NULL),(479,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','163','475','à 169 Provisions pour autres risques et charges',0,NULL,NULL,1,NULL,NULL),(480,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','164','475','Provisions pour sûretés personnelles ou réelles constituées à l\'appui de dettes et d\'engagements de tiers',0,NULL,NULL,1,NULL,NULL),(481,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','165','475','Provisions pour engagements relatifs à l\'acquisition ou à la cession d\'immobilisations',0,NULL,NULL,1,NULL,NULL),(482,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','166','475','Provisions pour exécution de commandes passées ou reçues',0,NULL,NULL,1,NULL,NULL),(483,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','167','475','Provisions pour positions et marchés à terme en devises ou positions et marchés à terme en marchandises',0,NULL,NULL,1,NULL,NULL),(484,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','168','475','Provisions pour garanties techniques attachées aux ventes et prestations déjà effectuées par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(485,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','169','475','Provisions pour autres risques et charges',0,NULL,NULL,1,NULL,NULL),(486,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1690','485','Pour litiges en cours',0,NULL,NULL,1,NULL,NULL),(487,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1691','485','Pour amendes, doubles droits et pénalités',0,NULL,NULL,1,NULL,NULL),(488,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1692','485','Pour propre assureur',0,NULL,NULL,1,NULL,NULL),(489,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1693','485','Pour risques inhérents aux opérations de crédits à moyen ou long terme',0,NULL,NULL,1,NULL,NULL),(490,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1695','485','Provision pour charge de liquidation',0,NULL,NULL,1,NULL,NULL),(491,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1696','485','Provision pour départ de personnel',0,NULL,NULL,1,NULL,NULL),(492,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1699','485','Pour risques divers',0,NULL,NULL,1,NULL,NULL),(493,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17','1351','Dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(494,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','170','493','Emprunts subordonnés',0,NULL,NULL,1,NULL,NULL),(495,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1700','494','Convertibles',0,NULL,NULL,1,NULL,NULL),(496,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1701','494','Non convertibles',0,NULL,NULL,1,NULL,NULL),(497,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','171','493','Emprunts obligataires non subordonnés',0,NULL,NULL,1,NULL,NULL),(498,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1710','498','Convertibles',0,NULL,NULL,1,NULL,NULL),(499,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1711','498','Non convertibles',0,NULL,NULL,1,NULL,NULL),(500,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','172','493','Dettes de location-financement et assimilés',0,NULL,NULL,1,NULL,NULL),(501,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1720','500','Dettes de location-financement de biens immobiliers',0,NULL,NULL,1,NULL,NULL),(502,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1721','500','Dettes de location-financement de biens mobiliers',0,NULL,NULL,1,NULL,NULL),(503,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1722','500','Dettes sur droits réels sur immeubles',0,NULL,NULL,1,NULL,NULL),(504,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','173','493','Etablissements de crédit',0,NULL,NULL,1,NULL,NULL),(505,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1730','504','Dettes en compte',0,NULL,NULL,1,NULL,NULL),(506,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17300','505','Banque A',0,NULL,NULL,1,NULL,NULL),(507,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17301','505','Banque B',0,NULL,NULL,1,NULL,NULL),(508,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17302','505','Banque C',0,NULL,NULL,1,NULL,NULL),(509,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17303','505','Banque D',0,NULL,NULL,1,NULL,NULL),(510,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1731','504','Promesses',0,NULL,NULL,1,NULL,NULL),(511,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17310','510','Banque A',0,NULL,NULL,1,NULL,NULL),(512,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17311','510','Banque B',0,NULL,NULL,1,NULL,NULL),(513,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17312','510','Banque C',0,NULL,NULL,1,NULL,NULL),(514,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17313','510','Banque D',0,NULL,NULL,1,NULL,NULL),(515,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1732','504','Crédits d\'acceptation',0,NULL,NULL,1,NULL,NULL),(516,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17320','515','Banque A',0,NULL,NULL,1,NULL,NULL),(517,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17321','515','Banque B',0,NULL,NULL,1,NULL,NULL),(518,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17322','515','Banque C',0,NULL,NULL,1,NULL,NULL),(519,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17323','515','Banque D',0,NULL,NULL,1,NULL,NULL),(520,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','174','493','Autres emprunts',0,NULL,NULL,1,NULL,NULL),(521,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175','493','Dettes commerciales',0,NULL,NULL,1,NULL,NULL),(522,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1750','521','Fournisseurs : dettes en compte',0,NULL,NULL,1,NULL,NULL),(523,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17500','522','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(524,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175000','523','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(525,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175001','523','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(526,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17501','522','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(527,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175010','526','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(528,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175011','526','Fournisseurs C.E.E.',0,NULL,NULL,1,NULL,NULL),(529,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175012','526','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(530,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1751','521','Effets à payer',0,NULL,NULL,1,NULL,NULL),(531,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17510','530','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(532,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175100','531','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(533,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175101','531','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(534,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','17511','530','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(535,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175110','534','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(536,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175111','534','Fournisseurs C.E.E.',0,NULL,NULL,1,NULL,NULL),(537,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','175112','534','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(538,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','176','493','Acomptes reçus sur commandes',0,NULL,NULL,1,NULL,NULL),(539,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','178','493','Cautionnements reçus en numéraires',0,NULL,NULL,1,NULL,NULL),(540,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','179','493','Dettes diverses',0,NULL,NULL,1,NULL,NULL),(541,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1790','540','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(542,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1791','540','Autres entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(543,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1792','540','Administrateurs, gérants et associés',0,NULL,NULL,1,NULL,NULL),(544,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1794','540','Rentes viagères capitalisées',0,NULL,NULL,1,NULL,NULL),(545,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1798','540','Dettes envers les coparticipants des associations momentanées et en participation',0,NULL,NULL,1,NULL,NULL),(546,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','1799','540','Autres dettes diverses',0,NULL,NULL,1,NULL,NULL),(547,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','CAPIT','XXXXXX','18','1351','Comptes de liaison des établissements et succursales',0,NULL,NULL,1,NULL,NULL),(548,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','20','1352','Frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(549,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','200','548','Frais de constitution et d\'augmentation de capital',0,NULL,NULL,1,NULL,NULL),(550,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2000','549','Frais de constitution et d\'augmentation de capital',0,NULL,NULL,1,NULL,NULL),(551,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2009','549','Amortissements sur frais de constitution et d\'augmentation de capital',0,NULL,NULL,1,NULL,NULL),(552,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','201','548','Frais d\'émission d\'emprunts et primes de remboursement',0,NULL,NULL,1,NULL,NULL),(553,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2010','552','Agios sur emprunts et frais d\'émission d\'emprunts',0,NULL,NULL,1,NULL,NULL),(554,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2019','552','Amortissements sur agios sur emprunts et frais d\'émission d\'emprunts',0,NULL,NULL,1,NULL,NULL),(555,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','202','548','Autres frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(556,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2020','555','Autres frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(557,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2029','555','Amortissements sur autres frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(558,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','203','548','Intérêts intercalaires',0,NULL,NULL,1,NULL,NULL),(559,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2030','558','Intérêts intercalaires',0,NULL,NULL,1,NULL,NULL),(560,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2039','558','Amortissements sur intérêts intercalaires',0,NULL,NULL,1,NULL,NULL),(561,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','204','548','Frais de restructuration',0,NULL,NULL,1,NULL,NULL),(562,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2040','561','Coût des frais de restructuration',0,NULL,NULL,1,NULL,NULL),(563,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2049','561','Amortissements sur frais de restructuration',0,NULL,NULL,1,NULL,NULL),(564,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','21','1352','Immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(565,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','210','564','Frais de recherche et de développement',0,NULL,NULL,1,NULL,NULL),(566,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2100','565','Frais de recherche et de mise au point',0,NULL,NULL,1,NULL,NULL),(567,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2108','565','Plus-values actées sur frais de recherche et de mise au point',0,NULL,NULL,1,NULL,NULL),(568,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2109','565','Amortissements sur frais de recherche et de mise au point',0,NULL,NULL,1,NULL,NULL),(569,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','211','564','Concessions, brevets, licences, savoir-faire, marque et droits similaires',0,NULL,NULL,1,NULL,NULL),(570,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2110','569','Concessions, brevets, licences, marques, etc',0,NULL,NULL,1,NULL,NULL),(571,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2118','569','Plus-values actées sur concessions, etc',0,NULL,NULL,1,NULL,NULL),(572,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2119','569','Amortissements sur concessions, etc',0,NULL,NULL,1,NULL,NULL),(573,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','212','564','Goodwill',0,NULL,NULL,1,NULL,NULL),(574,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2120','573','Coût d\'acquisition',0,NULL,NULL,1,NULL,NULL),(575,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2128','573','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(576,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2129','573','Amortissements sur goodwill',0,NULL,NULL,1,NULL,NULL),(577,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','213','564','Acomptes versés',0,NULL,NULL,1,NULL,NULL),(578,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22','1352','Terrains et constructions',0,NULL,NULL,1,NULL,NULL),(579,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','220','578','Terrains',0,NULL,NULL,1,NULL,NULL),(580,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2200','579','Terrains',0,NULL,NULL,1,NULL,NULL),(581,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2201','579','Frais d\'acquisition sur terrains',0,NULL,NULL,1,NULL,NULL),(582,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2208','579','Plus-values actées sur terrains',0,NULL,NULL,1,NULL,NULL),(583,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2209','579','Amortissements et réductions de valeur',0,NULL,NULL,1,NULL,NULL),(584,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22090','583','Amortissements sur frais d\'acquisition',0,NULL,NULL,1,NULL,NULL),(585,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22091','583','Réductions de valeur sur terrains',0,NULL,NULL,1,NULL,NULL),(586,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','221','578','Constructions',0,NULL,NULL,1,NULL,NULL),(587,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2210','586','Bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(588,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2211','586','Bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(589,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2212','586','Autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(590,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2213','586','Voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(591,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2215','586','Constructions sur sol d\'autrui',0,NULL,NULL,1,NULL,NULL),(592,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2216','586','Frais d\'acquisition sur constructions',0,NULL,NULL,1,NULL,NULL),(593,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2218','586','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(594,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22180','593','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(595,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22181','593','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(596,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22182','593','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(597,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22184','593','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(598,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2219','586','Amortissements sur constructions',0,NULL,NULL,1,NULL,NULL),(599,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22190','598','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(600,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22191','598','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(601,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22192','598','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(602,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22194','598','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(603,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22195','598','Sur constructions sur sol d\'autrui',0,NULL,NULL,1,NULL,NULL),(604,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22196','598','Sur frais d\'acquisition sur constructions',0,NULL,NULL,1,NULL,NULL),(605,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','222','578','Terrains bâtis',0,NULL,NULL,1,NULL,NULL),(606,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2220','605','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(607,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22200','606','Bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(608,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22201','606','Bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(609,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22202','606','Autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(610,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22203','606','Voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(611,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22204','606','Frais d\'acquisition des terrains à bâtir',0,NULL,NULL,1,NULL,NULL),(612,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2228','605','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(613,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22280','612','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(614,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22281','612','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(615,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22282','612','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(616,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22283','612','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(617,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2229','605','Amortissements sur terrains bâtis',0,NULL,NULL,1,NULL,NULL),(618,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22290','617','Sur bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(619,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22291','617','Sur bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(620,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22292','617','Sur autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(621,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22293','617','Sur voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(622,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','22294','617','Sur frais d\'acquisition des terrains bâtis',0,NULL,NULL,1,NULL,NULL),(623,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','223','578','Autres droits réels sur des immeubles',0,NULL,NULL,1,NULL,NULL),(624,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2230','623','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(625,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2238','623','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(626,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2239','623','Amortissements',0,NULL,NULL,1,NULL,NULL),(627,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','23','1352','Installations, machines et outillages',0,NULL,NULL,1,NULL,NULL),(628,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','230','627','Installations',0,NULL,NULL,1,NULL,NULL),(629,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2300','628','Installations bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(630,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2301','628','Installations bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(631,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2302','628','Installations bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(632,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2303','628','Installations voies de transport et ouvrages d\'art',0,NULL,NULL,1,NULL,NULL),(637,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2304','628','Installation de chauffage',0,NULL,NULL,1,NULL,NULL),(638,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2305','628','Installation de conditionnement d\'air',0,NULL,NULL,1,NULL,NULL),(639,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2306','628','Installation de chargement',0,NULL,NULL,1,NULL,NULL),(640,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','231','627','Machines',0,NULL,NULL,1,NULL,NULL),(641,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2310','640','Division A',0,NULL,NULL,1,NULL,NULL),(642,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2311','640','Division B',0,NULL,NULL,1,NULL,NULL),(643,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2312','640','Division C',0,NULL,NULL,1,NULL,NULL),(644,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','237','627','Outillage',0,NULL,NULL,1,NULL,NULL),(645,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2370','644','Division A',0,NULL,NULL,1,NULL,NULL),(646,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2371','644','Division B',0,NULL,NULL,1,NULL,NULL),(647,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2372','644','Division C',0,NULL,NULL,1,NULL,NULL),(648,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','238','627','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(649,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2380','648','Sur installations',0,NULL,NULL,1,NULL,NULL),(650,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2381','648','Sur machines',0,NULL,NULL,1,NULL,NULL),(651,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2382','648','Sur outillage',0,NULL,NULL,1,NULL,NULL),(652,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','239','627','Amortissements',0,NULL,NULL,1,NULL,NULL),(653,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2390','652','Sur installations',0,NULL,NULL,1,NULL,NULL),(654,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2391','652','Sur machines',0,NULL,NULL,1,NULL,NULL),(655,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2392','652','Sur outillage',0,NULL,NULL,1,NULL,NULL),(656,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24','1352','Mobilier et matériel roulant',0,NULL,NULL,1,NULL,NULL),(657,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','240','656','Mobilier',0,NULL,NULL,1,NULL,NULL),(658,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2400','656','Mobilier',0,NULL,NULL,1,NULL,NULL),(659,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24000','658','Mobilier des bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(660,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24001','658','Mobilier des bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(661,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24002','658','Mobilier des autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(662,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24003','658','Mobilier oeuvres sociales',0,NULL,NULL,1,NULL,NULL),(663,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2401','657','Matériel de bureau et de service social',0,NULL,NULL,1,NULL,NULL),(664,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24010','663','Des bâtiments industriels',0,NULL,NULL,1,NULL,NULL),(665,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24011','663','Des bâtiments administratifs et commerciaux',0,NULL,NULL,1,NULL,NULL),(666,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24012','663','Des autres bâtiments d\'exploitation',0,NULL,NULL,1,NULL,NULL),(667,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24013','663','Des oeuvres sociales',0,NULL,NULL,1,NULL,NULL),(668,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2408','657','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(669,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24080','668','Plus-values actées sur mobilier',0,NULL,NULL,1,NULL,NULL),(670,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24081','668','Plus-values actées sur matériel de bureau et service social',0,NULL,NULL,1,NULL,NULL),(671,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2409','657','Amortissements',0,NULL,NULL,1,NULL,NULL),(672,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24090','671','Amortissements sur mobilier',0,NULL,NULL,1,NULL,NULL),(673,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24091','671','Amortissements sur matériel de bureau et service social',0,NULL,NULL,1,NULL,NULL),(674,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','241','656','Matériel roulant',0,NULL,NULL,1,NULL,NULL),(675,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2410','674','Matériel automobile',0,NULL,NULL,1,NULL,NULL),(676,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24100','675','Voitures',0,NULL,NULL,1,NULL,NULL),(677,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24105','675','Camions',0,NULL,NULL,1,NULL,NULL),(678,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2411','674','Matériel ferroviaire',0,NULL,NULL,1,NULL,NULL),(679,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2412','674','Matériel fluvial',0,NULL,NULL,1,NULL,NULL),(680,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2413','674','Matériel naval',0,NULL,NULL,1,NULL,NULL),(681,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2414','674','Matériel aérien',0,NULL,NULL,1,NULL,NULL),(682,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2418','674','Plus-values sur matériel roulant',0,NULL,NULL,1,NULL,NULL),(683,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24180','682','Plus-values sur matériel automobile',0,NULL,NULL,1,NULL,NULL),(684,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24181','682','Idem sur matériel ferroviaire',0,NULL,NULL,1,NULL,NULL),(685,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24182','682','Idem sur matériel fluvial',0,NULL,NULL,1,NULL,NULL),(686,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24183','682','Idem sur matériel naval',0,NULL,NULL,1,NULL,NULL),(687,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24184','682','Idem sur matériel aérien',0,NULL,NULL,1,NULL,NULL),(688,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2419','674','Amortissements sur matériel roulant',0,NULL,NULL,1,NULL,NULL),(689,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24190','688','Amortissements sur matériel automobile',0,NULL,NULL,1,NULL,NULL),(690,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24191','688','Idem sur matériel ferroviaire',0,NULL,NULL,1,NULL,NULL),(691,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24192','688','Idem sur matériel fluvial',0,NULL,NULL,1,NULL,NULL),(692,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24193','688','Idem sur matériel naval',0,NULL,NULL,1,NULL,NULL),(693,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','24194','688','Idem sur matériel aérien',0,NULL,NULL,1,NULL,NULL),(694,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','25','1352','Immobilisation détenues en location-financement et droits similaires',0,NULL,NULL,1,NULL,NULL),(695,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','250','694','Terrains et constructions',0,NULL,NULL,1,NULL,NULL),(696,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2500','695','Terrains',0,NULL,NULL,1,NULL,NULL),(697,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2501','695','Constructions',0,NULL,NULL,1,NULL,NULL),(698,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2508','695','Plus-values sur emphytéose, leasing et droits similaires : terrains et constructions',0,NULL,NULL,1,NULL,NULL),(699,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2509','695','Amortissements et réductions de valeur sur terrains et constructions en leasing',0,NULL,NULL,1,NULL,NULL),(700,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','251','694','Installations, machines et outillage',0,NULL,NULL,1,NULL,NULL),(701,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2510','700','Installations',0,NULL,NULL,1,NULL,NULL),(702,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2511','700','Machines',0,NULL,NULL,1,NULL,NULL),(703,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2512','700','Outillage',0,NULL,NULL,1,NULL,NULL),(704,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2518','700','Plus-values actées sur installations machines et outillage pris en leasing',0,NULL,NULL,1,NULL,NULL),(705,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2519','700','Amortissements sur installations machines et outillage pris en leasing',0,NULL,NULL,1,NULL,NULL),(706,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','252','694','Mobilier et matériel roulant',0,NULL,NULL,1,NULL,NULL),(707,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2520','706','Mobilier',0,NULL,NULL,1,NULL,NULL),(708,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2521','706','Matériel roulant',0,NULL,NULL,1,NULL,NULL),(709,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2528','706','Plus-values actées sur mobilier et matériel roulant en leasing',0,NULL,NULL,1,NULL,NULL),(710,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2529','706','Amortissements sur mobilier et matériel roulant en leasing',0,NULL,NULL,1,NULL,NULL),(711,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','26','1352','Autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(712,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','260','711','Frais d\'aménagements de locaux pris en location',0,NULL,NULL,1,NULL,NULL),(713,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','261','711','Maison d\'habitation',0,NULL,NULL,1,NULL,NULL),(714,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','262','711','Réserve immobilière',0,NULL,NULL,1,NULL,NULL),(715,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','263','711','Matériel d\'emballage',0,NULL,NULL,1,NULL,NULL),(716,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','264','711','Emballages récupérables',0,NULL,NULL,1,NULL,NULL),(717,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','268','711','Plus-values actées sur autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(718,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','269','711','Amortissements sur autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(719,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2690','718','Amortissements sur frais d\'aménagement des locaux pris en location',0,NULL,NULL,1,NULL,NULL),(720,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2691','718','Amortissements sur maison d\'habitation',0,NULL,NULL,1,NULL,NULL),(721,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2692','718','Amortissements sur réserve immobilière',0,NULL,NULL,1,NULL,NULL),(722,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2693','718','Amortissements sur matériel d\'emballage',0,NULL,NULL,1,NULL,NULL),(723,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2694','718','Amortissements sur emballages récupérables',0,NULL,NULL,1,NULL,NULL),(724,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','27','1352','Immobilisations corporelles en cours et acomptes versés',0,NULL,NULL,1,NULL,NULL),(725,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','270','724','Immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(726,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2700','725','Constructions',0,NULL,NULL,1,NULL,NULL),(727,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2701','725','Installations machines et outillage',0,NULL,NULL,1,NULL,NULL),(728,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2702','725','Mobilier et matériel roulant',0,NULL,NULL,1,NULL,NULL),(729,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2703','725','Autres immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(730,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','271','724','Avances et acomptes versés sur immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(731,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','28','1352','Immobilisations financières',0,NULL,NULL,1,NULL,NULL),(732,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','280','731','Participations dans des entreprises liées',0,NULL,NULL,1,NULL,NULL),(733,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2800','732','Valeur d\'acquisition (peut être subdivisé par participation)',0,NULL,NULL,1,NULL,NULL),(734,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2801','732','Montants non appelés (idem)',0,NULL,NULL,1,NULL,NULL),(735,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2808','732','Plus-values actées (idem)',0,NULL,NULL,1,NULL,NULL),(736,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2809','732','Réductions de valeurs actées (idem)',0,NULL,NULL,1,NULL,NULL),(737,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','281','731','Créances sur des entreprises liées',0,NULL,NULL,1,NULL,NULL),(738,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2810','737','Créances en compte',0,NULL,NULL,1,NULL,NULL),(739,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2811','737','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(740,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2812','737','Titres à revenu fixes',0,NULL,NULL,1,NULL,NULL),(741,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2817','737','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(742,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2819','737','Réductions de valeurs actées',0,NULL,NULL,1,NULL,NULL),(743,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','282','731','Participations dans des entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(744,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2820','743','Valeur d\'acquisition (peut être subdivisé par participation)',0,NULL,NULL,1,NULL,NULL),(745,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2821','743','Montants non appelés (idem)',0,NULL,NULL,1,NULL,NULL),(746,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2828','743','Plus-values actées (idem)',0,NULL,NULL,1,NULL,NULL),(747,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2829','743','Réductions de valeurs actées (idem)',0,NULL,NULL,1,NULL,NULL),(748,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','283','731','Créances sur des entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(749,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2830','748','Créances en compte',0,NULL,NULL,1,NULL,NULL),(750,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2831','748','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(751,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2832','748','Titres à revenu fixe',0,NULL,NULL,1,NULL,NULL),(752,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2837','748','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(753,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','2839','748','Réductions de valeurs actées',0,NULL,NULL,1,NULL,NULL),(754,1,NULL,'2016-01-22 17:28:15','PCMN-BASE','IMMO','XXXXXX','284','731','Autres actions et parts',0,NULL,NULL,1,NULL,NULL),(755,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2840','754','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(756,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2841','754','Montants non appelés',0,NULL,NULL,1,NULL,NULL),(757,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2848','754','Plus-values actées',0,NULL,NULL,1,NULL,NULL),(758,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2849','754','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(759,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','285','731','Autres créances',0,NULL,NULL,1,NULL,NULL),(760,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2850','759','Créances en compte',0,NULL,NULL,1,NULL,NULL),(761,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2851','759','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(762,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2852','759','Titres à revenu fixe',0,NULL,NULL,1,NULL,NULL),(763,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2857','759','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(764,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2859','759','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(765,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','288','731','Cautionnements versés en numéraires',0,NULL,NULL,1,NULL,NULL),(766,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2880','765','Téléphone, téléfax, télex',0,NULL,NULL,1,NULL,NULL),(767,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2881','765','Gaz',0,NULL,NULL,1,NULL,NULL),(768,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2882','765','Eau',0,NULL,NULL,1,NULL,NULL),(769,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2883','765','Electricité',0,NULL,NULL,1,NULL,NULL),(770,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2887','765','Autres cautionnements versés en numéraires',0,NULL,NULL,1,NULL,NULL),(771,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29','1352','Créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(772,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','290','771','Créances commerciales',0,NULL,NULL,1,NULL,NULL),(773,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2900','772','Clients',0,NULL,NULL,1,NULL,NULL),(774,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29000','773','Créances en compte sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(775,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29001','773','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(776,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29002','773','Sur clients Belgique',0,NULL,NULL,1,NULL,NULL),(777,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29003','773','Sur clients C.E.E.',0,NULL,NULL,1,NULL,NULL),(778,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29004','773','Sur clients exportation hors C.E.E.',0,NULL,NULL,1,NULL,NULL),(779,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29005','773','Créances sur les coparticipants (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(780,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2901','772','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(781,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29010','780','Sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(782,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29011','780','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(783,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29012','780','Sur clients Belgique',0,NULL,NULL,1,NULL,NULL),(784,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29013','780','Sur clients C.E.E.',0,NULL,NULL,1,NULL,NULL),(785,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29014','780','Sur clients exportation hors C.E.E.',0,NULL,NULL,1,NULL,NULL),(786,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2905','772','Retenues sur garanties',0,NULL,NULL,1,NULL,NULL),(787,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2906','772','Acomptes versés',0,NULL,NULL,1,NULL,NULL),(788,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2907','772','Créances douteuses (à ventiler comme clients 2900)',0,NULL,NULL,1,NULL,NULL),(789,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2909','772','Réductions de valeur actées (à ventiler comme clients 2900)',0,NULL,NULL,1,NULL,NULL),(790,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','291','771','Autres créances',0,NULL,NULL,1,NULL,NULL),(791,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2910','790','Créances en compte',0,NULL,NULL,1,NULL,NULL),(792,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29100','791','Sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(793,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29101','791','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(794,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29102','791','Sur autres débiteurs',0,NULL,NULL,1,NULL,NULL),(795,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2911','790','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(796,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29110','795','Sur entreprises liées',0,NULL,NULL,1,NULL,NULL),(797,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29111','795','Sur entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(798,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','29112','795','Sur autres débiteurs',0,NULL,NULL,1,NULL,NULL),(799,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2912','790','Créances résultant de la cession d\'immobilisations données en leasing',0,NULL,NULL,1,NULL,NULL),(800,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2917','790','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(801,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','IMMO','XXXXXX','2919','790','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(802,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','30','1353','Approvisionnements - matières premières',0,NULL,NULL,1,NULL,NULL),(803,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','300','802','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(804,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','309','802','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(805,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','31','1353','Approvsionnements et fournitures',0,NULL,NULL,1,NULL,NULL),(806,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','310','805','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(807,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3100','806','Matières d\'approvisionnement',0,NULL,NULL,1,NULL,NULL),(808,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3101','806','Energie, charbon, coke, mazout, essence, propane',0,NULL,NULL,1,NULL,NULL),(809,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3102','806','Produits d\'entretien',0,NULL,NULL,1,NULL,NULL),(810,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3103','806','Fournitures diverses et petit outillage',0,NULL,NULL,1,NULL,NULL),(811,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3104','806','Imprimés et fournitures de bureau',0,NULL,NULL,1,NULL,NULL),(812,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3105','806','Fournitures de services sociaux',0,NULL,NULL,1,NULL,NULL),(813,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3106','806','Emballages commerciaux',0,NULL,NULL,1,NULL,NULL),(814,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','31060','813','Emballages perdus',0,NULL,NULL,1,NULL,NULL),(815,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','31061','813','Emballages récupérables',0,NULL,NULL,1,NULL,NULL),(816,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','319','805','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(817,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','32','1353','En cours de fabrication',0,NULL,NULL,1,NULL,NULL),(818,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','320','817','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(819,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3200','818','Produits semi-ouvrés',0,NULL,NULL,1,NULL,NULL),(820,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3201','818','Produits en cours de fabrication',0,NULL,NULL,1,NULL,NULL),(821,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3202','818','Travaux en cours',0,NULL,NULL,1,NULL,NULL),(822,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3205','818','Déchets',0,NULL,NULL,1,NULL,NULL),(823,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3206','818','Rebuts',0,NULL,NULL,1,NULL,NULL),(824,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3209','818','Travaux en association momentanée',0,NULL,NULL,1,NULL,NULL),(825,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','329','817','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(826,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','33','1353','Produits finis',0,NULL,NULL,1,NULL,NULL),(827,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','330','826','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(828,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3300','827','Produits finis',0,NULL,NULL,1,NULL,NULL),(829,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','339','826','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(830,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','34','1353','Marchandises',0,NULL,NULL,1,NULL,NULL),(831,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','340','830','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(832,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3400','831','Groupe A',0,NULL,NULL,1,NULL,NULL),(833,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3401','831','Groupe B',0,NULL,NULL,1,NULL,NULL),(834,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3402','831','Groupe C',0,NULL,NULL,1,NULL,NULL),(835,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','349','830','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(836,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','35','1353','Immeubles destinés à la vente',0,NULL,NULL,1,NULL,NULL),(837,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','350','836','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(838,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3500','837','Immeuble A',0,NULL,NULL,1,NULL,NULL),(839,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3501','837','Immeuble B',0,NULL,NULL,1,NULL,NULL),(840,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3502','837','Immeuble C',0,NULL,NULL,1,NULL,NULL),(841,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','351','836','Immeubles construits en vue de leur revente',0,NULL,NULL,1,NULL,NULL),(842,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3510','841','Immeuble A',0,NULL,NULL,1,NULL,NULL),(843,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3511','841','Immeuble B',0,NULL,NULL,1,NULL,NULL),(844,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','3512','841','Immeuble C',0,NULL,NULL,1,NULL,NULL),(845,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','359','836','Réductions de valeurs actées',0,NULL,NULL,1,NULL,NULL),(846,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','36','1353','Acomptes versés sur achats pour stocks',0,NULL,NULL,1,NULL,NULL),(847,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','360','846','Acomptes versés (à ventiler éventuellement par catégorie)',0,NULL,NULL,1,NULL,NULL),(848,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','369','846','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(849,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','37','1353','Commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(850,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','370','849','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(851,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','371','849','Bénéfice pris en compte',0,NULL,NULL,1,NULL,NULL),(852,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','STOCK','XXXXXX','379','849','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(853,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','40','1354','Créances commerciales',0,NULL,NULL,1,NULL,NULL),(854,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','400','853','Clients',0,NULL,NULL,1,NULL,NULL),(855,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4007','854','Rabais, remises et ristournes à accorder et autres notes de crédit à établir',0,NULL,NULL,1,NULL,NULL),(856,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4008','854','Créances résultant de livraisons de biens (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(857,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','401','853','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(858,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4010','857','Effets à recevoir',0,NULL,NULL,1,NULL,NULL),(859,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4013','857','Effets à l\'encaissement',0,NULL,NULL,1,NULL,NULL),(860,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4015','857','Effets à l\'escompte',0,NULL,NULL,1,NULL,NULL),(861,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','402','853','Clients, créances courantes, entreprises apparentées, administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(862,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4020','861','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(863,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4021','861','Autres entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(864,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4022','861','Administrateurs et gérants d\'entreprise',0,NULL,NULL,1,NULL,NULL),(865,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','403','853','Effets à recevoir sur entreprises apparentées et administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(866,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4030','865','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(867,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4031','865','Autres entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(868,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4032','865','Administrateurs et gérants de l\'entreprise',0,NULL,NULL,1,NULL,NULL),(869,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','404','853','Produits à recevoir (factures à établir)',0,NULL,NULL,1,NULL,NULL),(870,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','405','853','Clients : retenues sur garanties',0,NULL,NULL,1,NULL,NULL),(871,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','406','853','Acomptes versés',0,NULL,NULL,1,NULL,NULL),(872,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','407','853','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(873,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','408','853','Compensation clients',0,NULL,NULL,1,NULL,NULL),(874,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','409','853','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(875,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','41','1354','Autres créances',0,NULL,NULL,1,NULL,NULL),(876,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','410','875','Capital appelé, non versé',0,NULL,NULL,1,NULL,NULL),(877,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4100','876','Appels de fonds',0,NULL,NULL,1,NULL,NULL),(878,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4101','876','Actionnaires défaillants',0,NULL,NULL,1,NULL,NULL),(879,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','411','875','T.V.A. à récupérer',0,NULL,NULL,1,NULL,NULL),(880,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4110','879','T.V.A. due',0,NULL,NULL,1,NULL,NULL),(881,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4111','879','T.V.A. déductible',0,NULL,NULL,1,NULL,NULL),(882,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4112','879','Compte courant administration T.V.A.',0,NULL,NULL,1,NULL,NULL),(883,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4118','879','Taxe d\'égalisation due',0,NULL,NULL,1,NULL,NULL),(884,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','412','875','Impôts et versements fiscaux à récupérer',0,NULL,NULL,1,NULL,NULL),(885,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4120','884','Impôts belges sur le résultat',0,NULL,NULL,1,NULL,NULL),(886,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4125','884','Autres impôts belges',0,NULL,NULL,1,NULL,NULL),(887,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4128','884','Impôts étrangers',0,NULL,NULL,1,NULL,NULL),(888,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','414','875','Produits à recevoir',0,NULL,NULL,1,NULL,NULL),(889,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','416','875','Créances diverses',0,NULL,NULL,1,NULL,NULL),(890,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4160','889','Associés (compte d\'apport en société)',0,NULL,NULL,1,NULL,NULL),(891,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4161','889','Avances et prêts au personnel',0,NULL,NULL,1,NULL,NULL),(892,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4162','889','Compte courant des associés en S.P.R.L.',0,NULL,NULL,1,NULL,NULL),(893,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4163','889','Compte courant des administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(894,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4164','889','Créances sur sociétés apparentées',0,NULL,NULL,1,NULL,NULL),(895,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4166','889','Emballages et matériel à rendre',0,NULL,NULL,1,NULL,NULL),(896,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4167','889','Etat et établissements publics',0,NULL,NULL,1,NULL,NULL),(897,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','41670','896','Subsides à recevoir',0,NULL,NULL,1,NULL,NULL),(898,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','41671','896','Autres créances',0,NULL,NULL,1,NULL,NULL),(899,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4168','889','Rabais, ristournes et remises à obtenir et autres avoirs non encore reçus',0,NULL,NULL,1,NULL,NULL),(900,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','417','875','Créances douteuses',0,NULL,NULL,1,NULL,NULL),(901,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','418','875','Cautionnements versés en numéraires',0,NULL,NULL,1,NULL,NULL),(902,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','419','875','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(903,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','42','1354','Dettes à plus d\'un an échéant dans l\'année',0,NULL,NULL,1,NULL,NULL),(904,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','420','903','Emprunts subordonnés',0,NULL,NULL,1,NULL,NULL),(905,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4200','904','Convertibles',0,NULL,NULL,1,NULL,NULL),(906,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4201','904','Non convertibles',0,NULL,NULL,1,NULL,NULL),(907,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','421','903','Emprunts obligataires non subordonnés',0,NULL,NULL,1,NULL,NULL),(908,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4210','907','Convertibles',0,NULL,NULL,1,NULL,NULL),(909,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4211','907','Non convertibles',0,NULL,NULL,1,NULL,NULL),(910,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','422','903','Dettes de location-financement et assimilées',0,NULL,NULL,1,NULL,NULL),(911,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4220','910','Financement de biens immobiliers',0,NULL,NULL,1,NULL,NULL),(912,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4221','910','Financement de biens mobiliers',0,NULL,NULL,1,NULL,NULL),(913,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','423','903','Etablissements de crédit',0,NULL,NULL,1,NULL,NULL),(914,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4230','913','Dettes en compte',0,NULL,NULL,1,NULL,NULL),(915,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4231','913','Promesses',0,NULL,NULL,1,NULL,NULL),(916,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4232','913','Crédits d\'acceptation',0,NULL,NULL,1,NULL,NULL),(917,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','424','903','Autres emprunts',0,NULL,NULL,1,NULL,NULL),(918,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','425','903','Dettes commerciales',0,NULL,NULL,1,NULL,NULL),(919,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4250','918','Fournisseurs',0,NULL,NULL,1,NULL,NULL),(920,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4251','918','Effets à payer',0,NULL,NULL,1,NULL,NULL),(921,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','426','903','Cautionnements reçus en numéraires',0,NULL,NULL,1,NULL,NULL),(922,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','429','903','Dettes diverses',0,NULL,NULL,1,NULL,NULL),(923,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4290','922','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(924,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4291','922','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(925,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4292','922','Administrateurs, gérants, associés',0,NULL,NULL,1,NULL,NULL),(926,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4299','922','Autres dettes',0,NULL,NULL,1,NULL,NULL),(927,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','43','1354','Dettes financières',0,NULL,NULL,1,NULL,NULL),(928,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','430','927','Etablissements de crédit. Emprunts en compte à terme fixe',0,NULL,NULL,1,NULL,NULL),(929,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','431','927','Etablissements de crédit. Promesses',0,NULL,NULL,1,NULL,NULL),(930,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','432','927','Etablissements de crédit. Crédits d\'acceptation',0,NULL,NULL,1,NULL,NULL),(931,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','433','927','Etablissements de crédit. Dettes en compte courant',0,NULL,NULL,1,NULL,NULL),(932,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','439','927','Autres emprunts',0,NULL,NULL,1,NULL,NULL),(933,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44','1354','Dettes commerciales',0,NULL,NULL,1,NULL,NULL),(934,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','440','933','Fournisseurs',0,NULL,NULL,1,NULL,NULL),(935,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4400','934','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(936,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44000','935','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(937,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44001','935','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(938,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4401','934','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(939,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44010','938','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(940,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44011','938','Fournisseurs CEE',0,NULL,NULL,1,NULL,NULL),(941,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44012','938','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(942,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4402','934','Dettes envers les coparticipants (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(943,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4403','934','Fournisseurs - retenues de garanties',0,NULL,NULL,1,NULL,NULL),(944,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','441','933','Effets à payer',0,NULL,NULL,1,NULL,NULL),(945,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4410','944','Entreprises apparentées',0,NULL,NULL,1,NULL,NULL),(946,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44100','945','Entreprises liées',0,NULL,NULL,1,NULL,NULL),(947,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44101','945','Entreprises avec lesquelles il existe un lien de participation',0,NULL,NULL,1,NULL,NULL),(948,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4411','944','Fournisseurs ordinaires',0,NULL,NULL,1,NULL,NULL),(949,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44110','948','Fournisseurs belges',0,NULL,NULL,1,NULL,NULL),(950,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44111','948','Fournisseurs CEE',0,NULL,NULL,1,NULL,NULL),(951,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','44112','948','Fournisseurs importation',0,NULL,NULL,1,NULL,NULL),(952,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','444','933','Factures à recevoir',0,NULL,NULL,1,NULL,NULL),(953,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','446','933','Acomptes reçus',0,NULL,NULL,1,NULL,NULL),(954,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','448','933','Compensations fournisseurs',0,NULL,NULL,1,NULL,NULL),(955,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45','1354','Dettes fiscales, salariales et sociales',0,NULL,NULL,1,NULL,NULL),(956,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','450','955','Dettes fiscales estimées',0,NULL,NULL,1,NULL,NULL),(957,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4501','956','Impôts sur le résultat',0,NULL,NULL,1,NULL,NULL),(958,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4505','956','Autres impôts en Belgique',0,NULL,NULL,1,NULL,NULL),(959,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4508','956','Impôts à l\'étranger',0,NULL,NULL,1,NULL,NULL),(960,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','451','955','T.V.A. à payer',0,NULL,NULL,1,NULL,NULL),(961,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4510','960','T.V.A. due',0,NULL,NULL,1,NULL,NULL),(962,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4511','960','T.V.A. déductible',0,NULL,NULL,1,NULL,NULL),(963,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4512','960','Compte courant administration T.V.A.',0,NULL,NULL,1,NULL,NULL),(964,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4518','960','Taxe d\'égalisation due',0,NULL,NULL,1,NULL,NULL),(965,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','452','955','Impôts et taxes à payer',0,NULL,NULL,1,NULL,NULL),(966,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4520','965','Autres impôts sur le résultat',0,NULL,NULL,1,NULL,NULL),(967,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4525','965','Autres impôts et taxes en Belgique',0,NULL,NULL,1,NULL,NULL),(968,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45250','967','Précompte immobilier',0,NULL,NULL,1,NULL,NULL),(969,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45251','967','Impôts communaux à payer',0,NULL,NULL,1,NULL,NULL),(970,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45252','967','Impôts provinciaux à payer',0,NULL,NULL,1,NULL,NULL),(971,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45253','967','Autres impôts et taxes à payer',0,NULL,NULL,1,NULL,NULL),(972,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4528','965','Impôts et taxes à l\'étranger',0,NULL,NULL,1,NULL,NULL),(973,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','453','955','Précomptes retenus',0,NULL,NULL,1,NULL,NULL),(974,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4530','973','Précompte professionnel retenu sur rémunérations',0,NULL,NULL,1,NULL,NULL),(975,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4531','973','Précompte professionnel retenu sur tantièmes',0,NULL,NULL,1,NULL,NULL),(976,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4532','973','Précompte mobilier retenu sur dividendes attribués',0,NULL,NULL,1,NULL,NULL),(977,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4533','973','Précompte mobilier retenu sur intérêts payés',0,NULL,NULL,1,NULL,NULL),(978,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4538','973','Autres précomptes retenus',0,NULL,NULL,1,NULL,NULL),(979,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','454','955','Office National de la Sécurité Sociale',0,NULL,NULL,1,NULL,NULL),(980,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4540','979','Arriérés',0,NULL,NULL,1,NULL,NULL),(981,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4541','979','1er trimestre',0,NULL,NULL,1,NULL,NULL),(982,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4542','979','2ème trimestre',0,NULL,NULL,1,NULL,NULL),(983,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4543','979','3ème trimestre',0,NULL,NULL,1,NULL,NULL),(984,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4544','979','4ème trimestre',0,NULL,NULL,1,NULL,NULL),(985,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','455','955','Rémunérations',0,NULL,NULL,1,NULL,NULL),(986,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4550','985','Administrateurs, gérants et commissaires (non réviseurs)',0,NULL,NULL,1,NULL,NULL),(987,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4551','985','Direction',0,NULL,NULL,1,NULL,NULL),(988,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4552','985','Employés',0,NULL,NULL,1,NULL,NULL),(989,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4553','985','Ouvriers',0,NULL,NULL,1,NULL,NULL),(990,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','456','955','Pécules de vacances',0,NULL,NULL,1,NULL,NULL),(991,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4560','990','Direction',0,NULL,NULL,1,NULL,NULL),(992,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4561','990','Employés',0,NULL,NULL,1,NULL,NULL),(993,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4562','990','Ouvriers',0,NULL,NULL,1,NULL,NULL),(994,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','459','955','Autres dettes sociales',0,NULL,NULL,1,NULL,NULL),(995,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4590','994','Provision pour gratifications de fin d\'année',0,NULL,NULL,1,NULL,NULL),(996,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4591','994','Départs de personnel',0,NULL,NULL,1,NULL,NULL),(997,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4592','994','Oppositions sur rémunérations',0,NULL,NULL,1,NULL,NULL),(998,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4593','994','Assurances relatives au personnel',0,NULL,NULL,1,NULL,NULL),(999,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45930','998','Assurance loi',0,NULL,NULL,1,NULL,NULL),(1000,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45931','998','Assurance salaire garanti',0,NULL,NULL,1,NULL,NULL),(1001,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45932','998','Assurance groupe',0,NULL,NULL,1,NULL,NULL),(1002,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','45933','998','Assurances individuelles',0,NULL,NULL,1,NULL,NULL),(1003,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4594','994','Caisse d\'assurances sociales pour travailleurs indépendants',0,NULL,NULL,1,NULL,NULL),(1004,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4597','994','Dettes et provisions sociales diverses',0,NULL,NULL,1,NULL,NULL),(1005,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','46','1354','Acomptes reçus sur commande',0,NULL,NULL,1,NULL,NULL),(1006,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','47','1354','Dettes découlant de l\'affectation des résultats',0,NULL,NULL,1,NULL,NULL),(1007,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','470','1006','Dividendes et tantièmes d\'exercices antérieurs',0,NULL,NULL,1,NULL,NULL),(1008,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','471','1006','Dividendes de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1009,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','472','1006','Tantièmes de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1010,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','473','1006','Autres allocataires',0,NULL,NULL,1,NULL,NULL),(1011,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','48','4','Dettes diverses',0,NULL,NULL,1,NULL,NULL),(1012,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','480','1011','Obligations et coupons échus',0,NULL,NULL,1,NULL,NULL),(1013,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','481','1011','Actionnaires - capital à rembourser',0,NULL,NULL,1,NULL,NULL),(1014,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','482','1011','Participation du personnel à payer',0,NULL,NULL,1,NULL,NULL),(1015,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','483','1011','Acomptes reçus d\'autres tiers à moins d\'un an',0,NULL,NULL,1,NULL,NULL),(1016,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','486','1011','Emballages et matériel consignés',0,NULL,NULL,1,NULL,NULL),(1017,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','488','1011','Cautionnements reçus en numéraires',0,NULL,NULL,1,NULL,NULL),(1018,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','489','1011','Autres dettes diverses',0,NULL,NULL,1,NULL,NULL),(1019,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49','1354','Comptes de régularisation et compte d\'attente',0,NULL,NULL,1,NULL,NULL),(1020,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','490','1019','Charges à reporter (à subdiviser par catégorie de charges)',0,NULL,NULL,1,NULL,NULL),(1021,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','491','1019','Produits acquis',0,NULL,NULL,1,NULL,NULL),(1022,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4910','1021','Produits d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1023,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49100','1022','Ristournes et rabais à obtenir',0,NULL,NULL,1,NULL,NULL),(1024,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49101','1022','Commissions à obtenir',0,NULL,NULL,1,NULL,NULL),(1025,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49102','1022','Autres produits d\'exploitation (redevances par exemple)',0,NULL,NULL,1,NULL,NULL),(1026,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4911','1021','Produits financiers',0,NULL,NULL,1,NULL,NULL),(1027,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49110','1026','Intérêts courus et non échus sur prêts et débits',0,NULL,NULL,1,NULL,NULL),(1028,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','49111','1026','Autres produits financiers',0,NULL,NULL,1,NULL,NULL),(1029,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','492','1019','Charges à imputer (à subdiviser par catégorie de charges)',0,NULL,NULL,1,NULL,NULL),(1030,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','493','1019','Produits à reporter',0,NULL,NULL,1,NULL,NULL),(1031,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4930','1030','Produits d\'exploitation à reporter',0,NULL,NULL,1,NULL,NULL),(1032,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4931','1030','Produits financiers à reporter',0,NULL,NULL,1,NULL,NULL),(1033,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','499','1019','Comptes d\'attente',0,NULL,NULL,1,NULL,NULL),(1034,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4990','1033','Compte d\'attente',0,NULL,NULL,1,NULL,NULL),(1035,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4991','1033','Compte de répartition périodique des charges',0,NULL,NULL,1,NULL,NULL),(1036,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','TIERS','XXXXXX','4999','1033','Transferts d\'exercice',0,NULL,NULL,1,NULL,NULL),(1037,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','50','1355','Actions propres',0,NULL,NULL,1,NULL,NULL),(1038,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','51','1355','Actions et parts',0,NULL,NULL,1,NULL,NULL),(1039,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','510','1038','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(1040,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','511','1038','Montants non appelés',0,NULL,NULL,1,NULL,NULL),(1041,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','519','1038','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1042,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','52','1355','Titres à revenus fixes',0,NULL,NULL,1,NULL,NULL),(1043,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','520','1042','Valeur d\'acquisition',0,NULL,NULL,1,NULL,NULL),(1044,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','529','1042','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1045,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','53','1355','Dépots à terme',0,NULL,NULL,1,NULL,NULL),(1046,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','530','1045','De plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1047,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','531','1045','De plus d\'un mois et à un an au plus',0,NULL,NULL,1,NULL,NULL),(1048,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','532','1045','d\'un mois au plus',0,NULL,NULL,1,NULL,NULL),(1049,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','539','1045','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1050,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','54','1355','Valeurs échues à l\'encaissement',0,NULL,NULL,1,NULL,NULL),(1051,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','540','1050','Chèques à encaisser',0,NULL,NULL,1,NULL,NULL),(1052,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','541','1050','Coupons à encaisser',0,NULL,NULL,1,NULL,NULL),(1053,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','55','1355','Etablissements de crédit - Comptes ouverts auprès des divers établissements.',0,NULL,NULL,1,NULL,NULL),(1054,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','550','1053','Comptes courants',0,NULL,NULL,1,NULL,NULL),(1055,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','551','1053','Chèques émis',0,NULL,NULL,1,NULL,NULL),(1056,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','559','1053','Réductions de valeur actées',0,NULL,NULL,1,NULL,NULL),(1057,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','56','1355','Office des chèques postaux',0,NULL,NULL,1,NULL,NULL),(1058,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','560','1057','Compte courant',0,NULL,NULL,1,NULL,NULL),(1059,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','561','1057','Chèques émis',0,NULL,NULL,1,NULL,NULL),(1060,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','57','1355','Caisses',0,NULL,NULL,1,NULL,NULL),(1061,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','570','1060','à 577 Caisses - espèces ( 0 - centrale ; 7 - succursales et agences)',0,NULL,NULL,1,NULL,NULL),(1062,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','578','1060','Caisses - timbres ( 0 - fiscaux ; 1 - postaux)',0,NULL,NULL,1,NULL,NULL),(1063,1,NULL,'2016-01-22 17:28:16','PCMN-BASE','FINAN','XXXXXX','58','1355','Virements internes',0,NULL,NULL,1,NULL,NULL),(1064,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','60','1356','Approvisionnements et marchandises',0,NULL,NULL,1,NULL,NULL),(1065,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','600','1064','Achats de matières premières',0,NULL,NULL,1,NULL,NULL),(1066,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','601','1064','Achats de fournitures',0,NULL,NULL,1,NULL,NULL),(1067,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','602','1064','Achats de services, travaux et études',0,NULL,NULL,1,NULL,NULL),(1068,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','603','1064','Sous-traitances générales',0,NULL,NULL,1,NULL,NULL),(1069,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','604','1064','Achats de marchandises',0,NULL,NULL,1,NULL,NULL),(1070,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','605','1064','Achats d\'immeubles destinés à la revente',0,NULL,NULL,1,NULL,NULL),(1071,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','608','1064','Remises , ristournes et rabais obtenus sur achats',0,NULL,NULL,1,NULL,NULL),(1072,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','609','1064','Variations de stocks',0,NULL,NULL,1,NULL,NULL),(1073,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6090','1072','De matières premières',0,NULL,NULL,1,NULL,NULL),(1074,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6091','1072','De fournitures',0,NULL,NULL,1,NULL,NULL),(1075,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6094','1072','De marchandises',0,NULL,NULL,1,NULL,NULL),(1076,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6095','1072','d\'immeubles destinés à la vente',0,NULL,NULL,1,NULL,NULL),(1077,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61','1356','Services et biens divers',0,NULL,NULL,1,NULL,NULL),(1078,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','610','1077','Loyers et charges locatives',0,NULL,NULL,1,NULL,NULL),(1079,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6100','1078','Loyers divers',0,NULL,NULL,1,NULL,NULL),(1080,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6101','1078','Charges locatives (assurances, frais de confort,etc)',0,NULL,NULL,1,NULL,NULL),(1081,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','611','1077','Entretien et réparation (fournitures et prestations)',0,NULL,NULL,1,NULL,NULL),(1082,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','612','1077','Fournitures faites à l\'entreprise',0,NULL,NULL,1,NULL,NULL),(1083,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6120','1082','Eau, gaz, électricité, vapeur',0,NULL,NULL,1,NULL,NULL),(1084,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61200','1083','Eau',0,NULL,NULL,1,NULL,NULL),(1085,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61201','1083','Gaz',0,NULL,NULL,1,NULL,NULL),(1086,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61202','1083','Electricité',0,NULL,NULL,1,NULL,NULL),(1087,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61203','1083','Vapeur',0,NULL,NULL,1,NULL,NULL),(1088,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6121','1082','Téléphone, télégrammes, télex, téléfax, frais postaux',0,NULL,NULL,1,NULL,NULL),(1089,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61210','1088','Téléphone',0,NULL,NULL,1,NULL,NULL),(1090,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61211','1088','Télégrammes',0,NULL,NULL,1,NULL,NULL),(1091,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61212','1088','Télex et téléfax',0,NULL,NULL,1,NULL,NULL),(1092,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61213','1088','Frais postaux',0,NULL,NULL,1,NULL,NULL),(1093,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6122','1082','Livres, bibliothèque',0,NULL,NULL,1,NULL,NULL),(1094,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6123','1082','Imprimés et fournitures de bureau (si non comptabilisé au 601)',0,NULL,NULL,1,NULL,NULL),(1095,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','613','1077','Rétributions de tiers',0,NULL,NULL,1,NULL,NULL),(1096,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6130','1095','Redevances et royalties',0,NULL,NULL,1,NULL,NULL),(1097,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61300','1096','Redevances pour brevets, licences, marques et accessoires',0,NULL,NULL,1,NULL,NULL),(1098,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61301','1096','Autres redevances (procédés de fabrication)',0,NULL,NULL,1,NULL,NULL),(1099,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6131','1095','Assurances non relatives au personnel',0,NULL,NULL,1,NULL,NULL),(1100,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61310','1099','Assurance incendie',0,NULL,NULL,1,NULL,NULL),(1101,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61311','1099','Assurance vol',0,NULL,NULL,1,NULL,NULL),(1102,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61312','1099','Assurance autos',0,NULL,NULL,1,NULL,NULL),(1103,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61313','1099','Assurance crédit',0,NULL,NULL,1,NULL,NULL),(1104,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61314','1099','Assurances frais généraux',0,NULL,NULL,1,NULL,NULL),(1105,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6132','1095','Divers',0,NULL,NULL,1,NULL,NULL),(1106,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61320','1105','Commissions aux tiers',0,NULL,NULL,1,NULL,NULL),(1107,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61321','1105','Honoraires d\'avocats, d\'experts, etc',0,NULL,NULL,1,NULL,NULL),(1108,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61322','1105','Cotisations aux groupements professionnels',0,NULL,NULL,1,NULL,NULL),(1109,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61323','1105','Dons, libéralités, etc',0,NULL,NULL,1,NULL,NULL),(1110,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61324','1105','Frais de contentieux',0,NULL,NULL,1,NULL,NULL),(1111,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61325','1105','Publications légales',0,NULL,NULL,1,NULL,NULL),(1112,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6133','1095','Transports et déplacements',0,NULL,NULL,1,NULL,NULL),(1113,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61330','1112','Transports de personnel',0,NULL,NULL,1,NULL,NULL),(1114,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','61331','1112','Voyages, déplacements et représentations',0,NULL,NULL,1,NULL,NULL),(1115,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6134','1095','Personnel intérimaire',0,NULL,NULL,1,NULL,NULL),(1116,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','614','1077','Annonces, publicité, propagande et documentation',0,NULL,NULL,1,NULL,NULL),(1117,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6140','1116','Annonces et insertions',0,NULL,NULL,1,NULL,NULL),(1118,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6141','1116','Catalogues et imprimés',0,NULL,NULL,1,NULL,NULL),(1119,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6142','1116','Echantillons',0,NULL,NULL,1,NULL,NULL),(1120,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6143','1116','Foires et expositions',0,NULL,NULL,1,NULL,NULL),(1121,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6144','1116','Primes',0,NULL,NULL,1,NULL,NULL),(1122,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6145','1116','Cadeaux à la clientèle',0,NULL,NULL,1,NULL,NULL),(1123,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6146','1116','Missions et réceptions',0,NULL,NULL,1,NULL,NULL),(1124,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6147','1116','Documentation',0,NULL,NULL,1,NULL,NULL),(1125,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','615','1077','Sous-traitants',0,NULL,NULL,1,NULL,NULL),(1126,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6150','1125','Sous-traitants pour activités propres',0,NULL,NULL,1,NULL,NULL),(1127,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6151','1125','Sous-traitants d\'associations momentanées (coparticipants)',0,NULL,NULL,1,NULL,NULL),(1128,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6152','1125','Quote-part bénéficiaire des coparticipants',0,NULL,NULL,1,NULL,NULL),(1129,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','617','1077','Personnel intérimaire et personnes mises à la disposition de l\'entreprise',0,NULL,NULL,1,NULL,NULL),(1130,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','618','1077','Rémunérations, primes pour assurances extralégales, pensions de retraite et de survie des administrateurs, gérants et associés actifs qui ne sont pas attribuées en vertu d\'un contrat de travail',0,NULL,NULL,1,NULL,NULL),(1131,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62','1356','Rémunérations, charges sociales et pensions',0,NULL,NULL,1,NULL,NULL),(1132,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','620','1131','Rémunérations et avantages sociaux directs',0,NULL,NULL,1,NULL,NULL),(1133,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6200','1132','Administrateurs ou gérants',0,NULL,NULL,1,NULL,NULL),(1134,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6201','1132','Personnel de direction',0,NULL,NULL,1,NULL,NULL),(1135,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6202','1132','Employés',0,NULL,NULL,1,NULL,NULL),(1136,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6203','1132','Ouvriers',0,NULL,NULL,1,NULL,NULL),(1137,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6204','1132','Autres membres du personnel',0,NULL,NULL,1,NULL,NULL),(1138,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','621','1131','Cotisations patronales d\'assurances sociales',0,NULL,NULL,1,NULL,NULL),(1139,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6210','1138','Sur salaires',0,NULL,NULL,1,NULL,NULL),(1140,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6211','1138','Sur appointements et commissions',0,NULL,NULL,1,NULL,NULL),(1141,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','622','1131','Primes patronales pour assurances extralégales',0,NULL,NULL,1,NULL,NULL),(1142,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','623','1131','Autres frais de personnel',0,NULL,NULL,1,NULL,NULL),(1143,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6230','1142','Assurances du personnel',0,NULL,NULL,1,NULL,NULL),(1144,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62300','1143','Assurances loi, responsabilité civile, chemin du travail',0,NULL,NULL,1,NULL,NULL),(1145,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62301','1143','Assurance salaire garanti',0,NULL,NULL,1,NULL,NULL),(1146,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62302','1143','Assurances individuelles',0,NULL,NULL,1,NULL,NULL),(1147,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6231','1142','Charges sociales diverses',0,NULL,NULL,1,NULL,NULL),(1148,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62310','1147','Jours fériés payés',0,NULL,NULL,1,NULL,NULL),(1149,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62311','1147','Salaire hebdomadaire garanti',0,NULL,NULL,1,NULL,NULL),(1150,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62312','1147','Allocations familiales complémentaires',0,NULL,NULL,1,NULL,NULL),(1151,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6232','1142','Charges sociales des administrateurs, gérants et commissaires',0,NULL,NULL,1,NULL,NULL),(1152,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62320','1151','Allocations familiales complémentaires pour non salariés',0,NULL,NULL,1,NULL,NULL),(1153,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62321','1151','Lois sociales pour indépendants',0,NULL,NULL,1,NULL,NULL),(1154,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','62322','1151','Divers',0,NULL,NULL,1,NULL,NULL),(1155,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','624','1131','Pensions de retraite et de survie',0,NULL,NULL,1,NULL,NULL),(1156,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6240','1155','Administrateurs et gérants',0,NULL,NULL,1,NULL,NULL),(1157,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6241','1155','Personnel',0,NULL,NULL,1,NULL,NULL),(1158,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','625','1131','Provision pour pécule de vacances',0,NULL,NULL,1,NULL,NULL),(1159,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6250','1158','Dotations',0,NULL,NULL,1,NULL,NULL),(1160,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6251','1158','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1161,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','63','1356','Amortissements, réductions de valeur et provisions pour risques et charges',0,NULL,NULL,1,NULL,NULL),(1162,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','630','1161','Dotations aux amortissements et aux réductions de valeur sur immobilisations',0,NULL,NULL,1,NULL,NULL),(1163,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6300','1162','Dotations aux amortissements sur frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(1164,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6301','1162','Dotations aux amortissements sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1165,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6302','1162','Dotations aux amortissements sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1166,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6308','1162','Dotations aux réductions de valeur sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1167,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6309','1162','Dotations aux réductions de valeur sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1168,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','631','1161','Réductions de valeur sur stocks',0,NULL,NULL,1,NULL,NULL),(1169,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6310','1168','Dotations',0,NULL,NULL,1,NULL,NULL),(1170,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6311','1168','Reprises',0,NULL,NULL,1,NULL,NULL),(1171,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','632','1161','Réductions de valeur sur commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1172,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6320','1171','Dotations',0,NULL,NULL,1,NULL,NULL),(1173,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6321','1171','Reprises',0,NULL,NULL,1,NULL,NULL),(1174,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','633','1161','Réductions de valeur sur créances commerciales à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1175,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6330','1174','Dotations',0,NULL,NULL,1,NULL,NULL),(1176,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6331','1174','Reprises',0,NULL,NULL,1,NULL,NULL),(1177,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','634','1161','Réductions de valeur sur créances commerciales à un an au plus',0,NULL,NULL,1,NULL,NULL),(1178,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6340','1177','Dotations',0,NULL,NULL,1,NULL,NULL),(1179,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6341','1177','Reprises',0,NULL,NULL,1,NULL,NULL),(1180,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','635','1161','Provisions pour pensions et obligations similaires',0,NULL,NULL,1,NULL,NULL),(1181,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6350','1180','Dotations',0,NULL,NULL,1,NULL,NULL),(1182,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6351','1180','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1183,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','636','11613','Provisions pour grosses réparations et gros entretiens',0,NULL,NULL,1,NULL,NULL),(1184,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6360','1183','Dotations',0,NULL,NULL,1,NULL,NULL),(1185,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6361','1183','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1186,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','637','1161','Provisions pour autres risques et charges',0,NULL,NULL,1,NULL,NULL),(1187,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6370','1186','Dotations',0,NULL,NULL,1,NULL,NULL),(1188,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6371','1186','Utilisations et reprises',0,NULL,NULL,1,NULL,NULL),(1189,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64','1356','Autres charges d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1190,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','640','1189','Charges fiscales d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1191,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6400','1190','Taxes et impôts directs',0,NULL,NULL,1,NULL,NULL),(1192,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64000','1191','Taxes sur autos et camions',0,NULL,NULL,1,NULL,NULL),(1193,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6401','1190','Taxes et impôts indirects',0,NULL,NULL,1,NULL,NULL),(1194,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64010','1193','Timbres fiscaux pris en charge par la firme',0,NULL,NULL,1,NULL,NULL),(1195,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64011','1193','Droits d\'enregistrement',0,NULL,NULL,1,NULL,NULL),(1196,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64012','1193','T.V.A. non déductible',0,NULL,NULL,1,NULL,NULL),(1197,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6402','1190','Impôts provinciaux et communaux',0,NULL,NULL,1,NULL,NULL),(1198,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64020','1197','Taxe sur la force motrice',0,NULL,NULL,1,NULL,NULL),(1199,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','64021','1197','Taxe sur le personnel occupé',0,NULL,NULL,1,NULL,NULL),(1200,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6403','1190','Taxes diverses',0,NULL,NULL,1,NULL,NULL),(1201,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','641','1189','Moins-values sur réalisations courantes d\'immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1202,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','642','1189','Moins-values sur réalisations de créances commerciales',0,NULL,NULL,1,NULL,NULL),(1203,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','643','1189','à 648 Charges d\'exploitations diverses',0,NULL,NULL,1,NULL,NULL),(1204,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','649','1189','Charges d\'exploitation portées à l\'actif au titre de restructuration',0,NULL,NULL,1,NULL,NULL),(1205,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','65','1356','Charges financières',0,NULL,NULL,1,NULL,NULL),(1206,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','650','1205','Charges des dettes',0,NULL,NULL,1,NULL,NULL),(1207,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6500','1206','Intérêts, commissions et frais afférents aux dettes',0,NULL,NULL,1,NULL,NULL),(1208,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6501','1206','Amortissements des agios et frais d\'émission d\'emprunts',0,NULL,NULL,1,NULL,NULL),(1209,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6502','1206','Autres charges de dettes',0,NULL,NULL,1,NULL,NULL),(1210,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6503','1206','Intérêts intercalaires portés à l\'actif',0,NULL,NULL,1,NULL,NULL),(1211,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','651','1205','Réductions de valeur sur actifs circulants',0,NULL,NULL,1,NULL,NULL),(1212,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6510','1211','Dotations',0,NULL,NULL,1,NULL,NULL),(1213,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6511','1211','Reprises',0,NULL,NULL,1,NULL,NULL),(1214,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','652','1205','Moins-values sur réalisation d\'actifs circulants',0,NULL,NULL,1,NULL,NULL),(1215,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','653','1205','Charges d\'escompte de créances',0,NULL,NULL,1,NULL,NULL),(1216,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','654','1205','Différences de change',0,NULL,NULL,1,NULL,NULL),(1217,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','655','1205','Ecarts de conversion des devises',0,NULL,NULL,1,NULL,NULL),(1218,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','656','1205','Frais de banques, de chèques postaux',0,NULL,NULL,1,NULL,NULL),(1219,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','657','1205','Commissions sur ouvertures de crédit, cautions et avals',0,NULL,NULL,1,NULL,NULL),(1220,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','658','1205','Frais de vente des titres',0,NULL,NULL,1,NULL,NULL),(1221,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','66','1356','Charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(1222,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','660','1221','Amortissements et réductions de valeur exceptionnels',0,NULL,NULL,1,NULL,NULL),(1223,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6600','1222','Sur frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(1224,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6601','1222','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1225,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6602','1222','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1226,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','661','1221','Réductions de valeur sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1227,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','662','1221','Provisions pour risques et charges exceptionnels',0,NULL,NULL,1,NULL,NULL),(1228,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','663','1221','Moins-values sur réalisation d\'actifs immobilisés',0,NULL,NULL,1,NULL,NULL),(1229,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6630','1228','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1230,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6631','1228','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1231,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6632','1228','Sur immobilisations détenues en location-financement et droits similaires',0,NULL,NULL,1,NULL,NULL),(1232,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6633','1228','Sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1233,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6634','1228','Sur immeubles acquis ou construits en vue de la revente',0,NULL,NULL,1,NULL,NULL),(1234,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','664','1221','à 668 Autres charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(1236,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','665','1221','Différence de charge',0,NULL,NULL,1,NULL,NULL),(1237,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','669','1221','Charges exceptionnelles transférées à l\'actif en frais de restructuration',0,NULL,NULL,1,NULL,NULL),(1238,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','67','1356','Impôts sur le résultat',0,NULL,NULL,1,NULL,NULL),(1239,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','670','1238','Impôts belges sur le résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1240,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6700','1239','Impôts et précomptes dus ou versés',0,NULL,NULL,1,NULL,NULL),(1241,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6701','1239','Excédent de versements d\'impôts et précomptes porté à l\'actif',0,NULL,NULL,1,NULL,NULL),(1242,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6702','1239','Charges fiscales estimées',0,NULL,NULL,1,NULL,NULL),(1243,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','671','1238','Impôts belges sur le résultat d\'exercices antérieurs',0,NULL,NULL,1,NULL,NULL),(1244,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6710','1243','Suppléments d\'impôts dus ou versés',0,NULL,NULL,1,NULL,NULL),(1245,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6711','1243','Suppléments d\'impôts estimés',0,NULL,NULL,1,NULL,NULL),(1246,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6712','1243','Provisions fiscales constituées',0,NULL,NULL,1,NULL,NULL),(1247,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','672','1238','Impôts étrangers sur le résultat de l\'exercice',0,NULL,NULL,1,NULL,NULL),(1248,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','673','1238','Impôts étrangers sur le résultat d\'exercices antérieurs',0,NULL,NULL,1,NULL,NULL),(1249,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','68','1356','Transferts aux réserves immunisées',0,NULL,NULL,1,NULL,NULL),(1250,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','69','1356','Affectation des résultats',0,NULL,NULL,1,NULL,NULL),(1251,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','690','1250','Perte reportée de l\'exercice précédent',0,NULL,NULL,1,NULL,NULL),(1252,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','691','1250','Dotation à la réserve légale',0,NULL,NULL,1,NULL,NULL),(1253,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','692','1250','Dotation aux autres réserves',0,NULL,NULL,1,NULL,NULL),(1254,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','693','1250','Bénéfice à reporter',0,NULL,NULL,1,NULL,NULL),(1255,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','694','1250','Rémunération du capital',0,NULL,NULL,1,NULL,NULL),(1256,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','695','1250','Administrateurs ou gérants',0,NULL,NULL,1,NULL,NULL),(1257,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','696','1250','Autres allocataires',0,NULL,NULL,1,NULL,NULL),(1258,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','70','1357','Chiffre d\'affaires',0,NULL,NULL,1,NULL,NULL),(1260,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','700','1258','Ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(1261,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7000','1260','Ventes en Belgique',0,NULL,NULL,1,NULL,NULL),(1262,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7001','1260','Ventes dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1263,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7002','1260','Ventes à l\'exportation',0,NULL,NULL,1,NULL,NULL),(1264,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','701','1258','Ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(1265,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7010','1264','Ventes en Belgique',0,NULL,NULL,1,NULL,NULL),(1266,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7011','1264','Ventes dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1267,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7012','1264','Ventes à l\'exportation',0,NULL,NULL,1,NULL,NULL),(1268,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','702','1258','Ventes de déchets et rebuts',0,NULL,NULL,1,NULL,NULL),(1269,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7020','1268','Ventes en Belgique',0,NULL,NULL,1,NULL,NULL),(1270,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7021','1268','Ventes dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1271,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7022','1268','Ventes à l\'exportation',0,NULL,NULL,1,NULL,NULL),(1272,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','703','1258','Ventes d\'emballages récupérables',0,NULL,NULL,1,NULL,NULL),(1273,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','704','1258','Facturations des travaux en cours (associations momentanées)',0,NULL,NULL,1,NULL,NULL),(1274,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','705','1258','Prestations de services',0,NULL,NULL,1,NULL,NULL),(1275,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7050','1274','Prestations de services en Belgique',0,NULL,NULL,1,NULL,NULL),(1276,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7051','1274','Prestations de services dans les pays membres de la C.E.E.',0,NULL,NULL,1,NULL,NULL),(1277,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7052','1274','Prestations de services en vue de l\'exportation',0,NULL,NULL,1,NULL,NULL),(1278,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','706','1258','Pénalités et dédits obtenus par l\'entreprise',0,NULL,NULL,1,NULL,NULL),(1279,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','708','1258','Remises, ristournes et rabais accordés',0,NULL,NULL,1,NULL,NULL),(1280,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7080','1279','Sur ventes de marchandises',0,NULL,NULL,1,NULL,NULL),(1281,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7081','1279','Sur ventes de produits finis',0,NULL,NULL,1,NULL,NULL),(1282,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7082','1279','Sur ventes de déchets et rebuts',0,NULL,NULL,1,NULL,NULL),(1283,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7083','1279','Sur prestations de services',0,NULL,NULL,1,NULL,NULL),(1284,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7084','1279','Mali sur travaux facturés aux associations momentanées',0,NULL,NULL,1,NULL,NULL),(1285,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71','1357','Variation des stocks et des commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1286,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','712','1285','Des en cours de fabrication',0,NULL,NULL,1,NULL,NULL),(1287,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','713','1285','Des produits finis',0,NULL,NULL,1,NULL,NULL),(1288,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','715','1285','Des immeubles construits destinés à la vente',0,NULL,NULL,1,NULL,NULL),(1289,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','717','1285','Des commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1290,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7170','1289','Commandes en cours - Coût de revient',0,NULL,NULL,1,NULL,NULL),(1291,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71700','1290','Coût des commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1292,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71701','1290','Coût des travaux en cours des associations momentanées',0,NULL,NULL,1,NULL,NULL),(1293,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7171','1289','Bénéfices portés en compte sur commandes en cours',0,NULL,NULL,1,NULL,NULL),(1294,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71710','1293','Sur commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1295,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','71711','1293','Sur travaux en cours des associations momentanées',0,NULL,NULL,1,NULL,NULL),(1296,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','72','1357','Production immobilisée',0,NULL,NULL,1,NULL,NULL),(1297,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','720','1296','En frais d\'établissement',0,NULL,NULL,1,NULL,NULL),(1298,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','721','1296','En immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1299,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','722','1296','En immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1300,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','723','1296','En immobilisations en cours',0,NULL,NULL,1,NULL,NULL),(1301,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','74','1357','Autres produits d\'exploitation',0,NULL,NULL,1,NULL,NULL),(1302,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','740','1301','Subsides d\'exploitation et montants compensatoires',0,NULL,NULL,1,NULL,NULL),(1303,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','741','1301','Plus-values sur réalisations courantes d\'immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1304,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','742','1301','Plus-values sur réalisations de créances commerciales',0,NULL,NULL,1,NULL,NULL),(1305,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','743','1301','à 749 Produits d\'exploitation divers',0,NULL,NULL,1,NULL,NULL),(1307,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','744','1301','Commissions et courtages',0,NULL,NULL,1,NULL,NULL),(1308,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','745','1301','Redevances pour brevets et licences',0,NULL,NULL,1,NULL,NULL),(1309,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','746','1301','Prestations de services (transports, études, etc)',0,NULL,NULL,1,NULL,NULL),(1310,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','747','1301','Revenus des immeubles affectés aux activités non professionnelles',0,NULL,NULL,1,NULL,NULL),(1311,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','748','1301','Locations diverses à caractère professionnel',0,NULL,NULL,1,NULL,NULL),(1312,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','749','1301','Produits divers',0,NULL,NULL,1,NULL,NULL),(1313,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7490','1312','Bonis sur reprises d\'emballages consignés',0,NULL,NULL,1,NULL,NULL),(1314,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7491','1312','Bonis sur travaux en associations momentanées',0,NULL,NULL,1,NULL,NULL),(1315,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','75','1357','Produits financiers',0,NULL,NULL,1,NULL,NULL),(1316,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','750','1315','Produits des immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1317,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7500','1316','Revenus des actions',0,NULL,NULL,1,NULL,NULL),(1318,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7501','1316','Revenus des obligations',0,NULL,NULL,1,NULL,NULL),(1319,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7502','1316','Revenus des créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1320,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','751','1315','Produits des actifs circulants',0,NULL,NULL,1,NULL,NULL),(1321,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','752','1315','Plus-values sur réalisations d\'actifs circulants',0,NULL,NULL,1,NULL,NULL),(1322,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','753','1315','Subsides en capital et en intérêts',0,NULL,NULL,1,NULL,NULL),(1323,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','754','1315','Différences de change',0,NULL,NULL,1,NULL,NULL),(1324,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','755','1315','Ecarts de conversion des devises',0,NULL,NULL,1,NULL,NULL),(1325,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','756','1315','à 759 Produits financiers divers',0,NULL,NULL,1,NULL,NULL),(1327,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','757','1315','Escomptes obtenus',0,NULL,NULL,1,NULL,NULL),(1328,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','76','1357','Produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(1329,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','760','1328','Reprises d\'amortissements et de réductions de valeur',0,NULL,NULL,1,NULL,NULL),(1330,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7600','1329','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1331,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7601','1329','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1332,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','761','1328','Reprises de réductions de valeur sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1333,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','762','1328','Reprises de provisions pour risques et charges exceptionnelles',0,NULL,NULL,1,NULL,NULL),(1334,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','763','1328','Plus-values sur réalisation d\'actifs immobilisés',0,NULL,NULL,1,NULL,NULL),(1335,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7630','1334','Sur immobilisations incorporelles',0,NULL,NULL,1,NULL,NULL),(1336,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7631','1334','Sur immobilisations corporelles',0,NULL,NULL,1,NULL,NULL),(1337,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7632','1334','Sur immobilisations financières',0,NULL,NULL,1,NULL,NULL),(1338,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','764','1328','Autres produits exceptionnels',0,NULL,NULL,1,NULL,NULL),(1339,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','77','1357','Régularisations d\'impôts et reprises de provisions fiscales',0,NULL,NULL,1,NULL,NULL),(1340,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','771','1339','Impôts belges sur le résultat',0,NULL,NULL,1,NULL,NULL),(1341,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7710','1340','Régularisations d\'impôts dus ou versés',0,NULL,NULL,1,NULL,NULL),(1342,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7711','1340','Régularisations d\'impôts estimés',0,NULL,NULL,1,NULL,NULL),(1343,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7712','1340','Reprises de provisions fiscales',0,NULL,NULL,1,NULL,NULL),(1344,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','773','1339','Impôts étrangers sur le résultat',0,NULL,NULL,1,NULL,NULL),(1345,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','79','1357','Affectation aux résultats',0,NULL,NULL,1,NULL,NULL),(1346,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','790','1345','Bénéfice reporté de l\'exercice précédent',0,NULL,NULL,1,NULL,NULL),(1347,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','791','1345','Prélèvement sur le capital et les primes d\'émission',0,NULL,NULL,1,NULL,NULL),(1348,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','792','1345','Prélèvement sur les réserves',0,NULL,NULL,1,NULL,NULL),(1349,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','793','1345','Perte à reporter',0,NULL,NULL,1,NULL,NULL),(1350,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','794','1345','Intervention d\'associés (ou du propriétaire) dans la perte',0,NULL,NULL,1,NULL,NULL),(1351,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','CAPIT','XXXXXX','1','0','Fonds propres, provisions pour risques et charges et dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1352,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','IMMO','XXXXXX','2','0','Frais d\'établissement. Actifs immobilisés et créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1353,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','STOCK','XXXXXX','3','0','Stock et commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1354,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','TIERS','XXXXXX','4','0','Créances et dettes à un an au plus',0,NULL,NULL,1,NULL,NULL),(1355,1,NULL,'2016-07-30 11:12:54','PCMN-BASE','FINAN','XXXXXX','5','0','Placement de trésorerie et de valeurs disponibles',0,NULL,NULL,1,NULL,NULL),(1356,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','EXPENSE','XXXXXX','6','0','Charges',0,NULL,NULL,1,NULL,NULL),(1357,1,NULL,'2018-01-19 11:17:49','PCMN-BASE','INCOME','XXXXXX','7','0','Produits',0,NULL,NULL,1,NULL,NULL),(1401,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','CAPIT','XXXXXX','1','0','Fonds propres, provisions pour risques et charges et dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1402,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','IMMO','XXXXXX','2','0','Frais d\'établissement. Actifs immobilisés et créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1403,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','STOCK','XXXXXX','3','0','Stock et commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1404,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','TIERS','XXXXXX','4','0','Créances et dettes à un an au plus',0,NULL,NULL,1,NULL,NULL),(1405,1,NULL,'2016-07-30 11:12:54','PCG99-ABREGE','FINAN','XXXXXX','5','0','Placement de trésorerie et de valeurs disponibles',0,NULL,NULL,1,NULL,NULL),(1406,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','EXPENSE','XXXXXX','6','0','Charges',0,NULL,NULL,1,NULL,NULL),(1407,1,NULL,'2018-01-19 11:17:49','PCG99-ABREGE','INCOME','XXXXXX','7','0','Produits',0,NULL,NULL,1,NULL,NULL),(1501,1,NULL,'2017-02-20 10:46:43','PCG99-BASE','CAPIT','XXXXXX','1','0','Fonds propres, provisions pour risques et charges et dettes à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1502,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','IMMO','XXXXXX','2','0','Frais d\'établissement. Actifs immobilisés et créances à plus d\'un an',0,NULL,NULL,1,NULL,NULL),(1503,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','STOCK','XXXXXX','3','0','Stock et commandes en cours d\'exécution',0,NULL,NULL,1,NULL,NULL),(1504,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','TIERS','XXXXXX','4','0','Créances et dettes à un an au plus',0,NULL,NULL,1,NULL,NULL),(1505,1,NULL,'2016-07-30 11:12:54','PCG99-BASE','FINAN','XXXXXX','5','0','Placement de trésorerie et de valeurs disponibles',0,NULL,NULL,1,NULL,NULL),(1506,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','EXPENSE','XXXXXX','6','0','Charges',0,NULL,NULL,1,NULL,NULL),(1507,1,NULL,'2018-01-19 11:17:49','PCG99-BASE','INCOME','XXXXXX','7','0','Produits',0,NULL,NULL,1,NULL,NULL),(4001,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','FINANCIACION','XXXXXX','1','0','Financiación básica',0,NULL,NULL,1,NULL,NULL),(4002,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','ACTIVO','XXXXXX','2','0','Activo no corriente',0,NULL,NULL,1,NULL,NULL),(4003,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','EXISTENCIAS','XXXXXX','3','0','Existencias',0,NULL,NULL,1,NULL,NULL),(4004,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4','0','Acreedores y deudores por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4005,1,NULL,'2016-07-30 11:12:54','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5','0','Cuentas financieras',0,NULL,NULL,1,NULL,NULL),(4006,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6','0','Compras y gastos',0,NULL,NULL,1,NULL,NULL),(4007,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7','0','Ventas e ingresos',0,NULL,NULL,1,NULL,NULL),(4008,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','10','4001','CAPITAL',0,NULL,NULL,1,NULL,NULL),(4009,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','100','4008','Capital social',0,NULL,NULL,1,NULL,NULL),(4010,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','101','4008','Fondo social',0,NULL,NULL,1,NULL,NULL),(4011,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','CAPITAL','102','4008','Capital',0,NULL,NULL,1,NULL,NULL),(4012,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','103','4008','Socios por desembolsos no exigidos',0,NULL,NULL,1,NULL,NULL),(4013,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1030','4012','Socios por desembolsos no exigidos capital social',0,NULL,NULL,1,NULL,NULL),(4014,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1034','4012','Socios por desembolsos no exigidos capital pendiente de inscripción',0,NULL,NULL,1,NULL,NULL),(4015,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','104','4008','Socios por aportaciones no dineradas pendientes',0,NULL,NULL,1,NULL,NULL),(4016,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1040','4015','Socios por aportaciones no dineradas pendientes capital social',0,NULL,NULL,1,NULL,NULL),(4017,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1044','4015','Socios por aportaciones no dineradas pendientes capital pendiente de inscripción',0,NULL,NULL,1,NULL,NULL),(4018,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','108','4008','Acciones o participaciones propias en situaciones especiales',0,NULL,NULL,1,NULL,NULL),(4019,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','109','4008','Acciones o participaciones propias para reducción de capital',0,NULL,NULL,1,NULL,NULL),(4020,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','11','4001','Reservas y otros instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4021,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','110','4020','Prima de emisión o asunción',0,NULL,NULL,1,NULL,NULL),(4022,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','111','4020','Otros instrumentos de patrimonio neto',0,NULL,NULL,1,NULL,NULL),(4023,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1110','4022','Patrimonio neto por emisión de instrumentos financieros compuestos',0,NULL,NULL,1,NULL,NULL),(4024,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1111','4022','Resto de instrumentos de patrimoio neto',0,NULL,NULL,1,NULL,NULL),(4025,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','112','4020','Reserva legal',0,NULL,NULL,1,NULL,NULL),(4026,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','113','4020','Reservas voluntarias',0,NULL,NULL,1,NULL,NULL),(4027,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','114','4020','Reservas especiales',0,NULL,NULL,1,NULL,NULL),(4028,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1140','4027','Reservas para acciones o participaciones de la sociedad dominante',0,NULL,NULL,1,NULL,NULL),(4029,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1141','4027','Reservas estatutarias',0,NULL,NULL,1,NULL,NULL),(4030,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1142','4027','Reservas por capital amortizado',0,NULL,NULL,1,NULL,NULL),(4031,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1143','4027','Reservas por fondo de comercio',0,NULL,NULL,1,NULL,NULL),(4032,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1144','4028','Reservas por acciones propias aceptadas en garantía',0,NULL,NULL,1,NULL,NULL),(4033,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','115','4020','Reservas por pérdidas y ganancias actuariales y otros ajustes',0,NULL,NULL,1,NULL,NULL),(4034,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','118','4020','Aportaciones de socios o propietarios',0,NULL,NULL,1,NULL,NULL),(4035,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','119','4020','Diferencias por ajuste del capital a euros',0,NULL,NULL,1,NULL,NULL),(4036,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','12','4001','Resultados pendientes de aplicación',0,NULL,NULL,1,NULL,NULL),(4037,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','120','4036','Remanente',0,NULL,NULL,1,NULL,NULL),(4038,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','121','4036','Resultados negativos de ejercicios anteriores',0,NULL,NULL,1,NULL,NULL),(4039,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','129','4036','Resultado del ejercicio',0,NULL,NULL,1,NULL,NULL),(4040,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','13','4001','Subvenciones, donaciones y ajustes por cambio de valor',0,NULL,NULL,1,NULL,NULL),(4041,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','130','4040','Subvenciones oficiales de capital',0,NULL,NULL,1,NULL,NULL),(4042,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','131','4040','Donaciones y legados de capital',0,NULL,NULL,1,NULL,NULL),(4043,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','132','4040','Otras subvenciones, donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4044,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','133','4040','Ajustes por valoración en activos financieros disponibles para la venta',0,NULL,NULL,1,NULL,NULL),(4045,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','134','4040','Operaciones de cobertura',0,NULL,NULL,1,NULL,NULL),(4046,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1340','4045','Cobertura de flujos de efectivo',0,NULL,NULL,1,NULL,NULL),(4047,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1341','4045','Cobertura de una inversión neta en un negocio extranjero',0,NULL,NULL,1,NULL,NULL),(4048,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','135','4040','Diferencias de conversión',0,NULL,NULL,1,NULL,NULL),(4049,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','136','4040','Ajustes por valoración en activos no corrientes y grupos enajenables de elementos mantenidos para la venta',0,NULL,NULL,1,NULL,NULL),(4050,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','137','4040','Ingresos fiscales a distribuir en varios ejercicios',0,NULL,NULL,1,NULL,NULL),(4051,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1370','4050','Ingresos fiscales por diferencias permanentes a distribuir en varios ejercicios',0,NULL,NULL,1,NULL,NULL),(4052,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1371','4050','Ingresos fiscales por deducciones y bonificaciones a distribuir en varios ejercicios',0,NULL,NULL,1,NULL,NULL),(4053,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','14','4001','Provisiones',0,NULL,NULL,1,NULL,NULL),(4054,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','141','4053','Provisión para impuestos',0,NULL,NULL,1,NULL,NULL),(4055,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','142','4053','Provisión para otras responsabilidades',0,NULL,NULL,1,NULL,NULL),(4056,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','143','4053','Provisión por desmantelamiento, retiro o rehabilitación del inmovilizado',0,NULL,NULL,1,NULL,NULL),(4057,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','145','4053','Provisión para actuaciones medioambientales',0,NULL,NULL,1,NULL,NULL),(4058,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','15','4001','Deudas a largo plazo con características especiales',0,NULL,NULL,1,NULL,NULL),(4059,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','150','4058','Acciones o participaciones a largo plazo consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4060,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','153','4058','Desembolsos no exigidos por acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4061,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1533','4060','Desembolsos no exigidos empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4062,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1534','4060','Desembolsos no exigidos empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4063,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1535','4060','Desembolsos no exigidos otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4064,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1536','4060','Otros desembolsos no exigidos',0,NULL,NULL,1,NULL,NULL),(4065,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','154','4058','Aportaciones no dinerarias pendientes por acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4066,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1543','4065','Aportaciones no dinerarias pendientes empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4067,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1544','4065','Aportaciones no dinerarias pendientes empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4068,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1545','4065','Aportaciones no dinerarias pendientes otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4069,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1546','4065','Otras aportaciones no dinerarias pendientes',0,NULL,NULL,1,NULL,NULL),(4070,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','16','4001','Deudas a largo plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4071,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','160','4070','Deudas a largo plazo con entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4072,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1603','4071','Deudas a largo plazo con entidades de crédito empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4073,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1604','4071','Deudas a largo plazo con entidades de crédito empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4074,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1605','4071','Deudas a largo plazo con otras entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4075,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','161','4070','Proveedores de inmovilizado a largo plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4076,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1613','4075','Proveedores de inmovilizado a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4077,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1614','4075','Proveedores de inmovilizado a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4078,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1615','4075','Proveedores de inmovilizado a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4079,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','162','4070','Acreedores por arrendamiento financiero a largo plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4080,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1623','4079','Acreedores por arrendamiento financiero a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4081,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1624','4080','Acreedores por arrendamiento financiero a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4082,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1625','4080','Acreedores por arrendamiento financiero a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4083,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','163','4070','Otras deudas a largo plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4084,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1633','4083','Otras deudas a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4085,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1634','4083','Otras deudas a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4086,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','1635','4083','Otras deudas a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4087,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','17','4001','Deudas a largo plazo por préstamos recibidos empresitos y otros conceptos',0,NULL,NULL,1,NULL,NULL),(4088,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','170','4087','Deudas a largo plazo con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4089,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','171','4087','Deudas a largo plazo',0,NULL,NULL,1,NULL,NULL),(4090,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','172','4087','Deudas a largo plazo transformables en suvbenciones donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4091,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','173','4087','Proveedores de inmovilizado a largo plazo',0,NULL,NULL,1,NULL,NULL),(4092,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','174','4087','Acreedores por arrendamiento financiero a largo plazo',0,NULL,NULL,1,NULL,NULL),(4093,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','175','4087','Efectos a pagar a largo plazo',0,NULL,NULL,1,NULL,NULL),(4094,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','176','4087','Pasivos por derivados financieros a largo plazo',0,NULL,NULL,1,NULL,NULL),(4095,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','177','4087','Obligaciones y bonos',0,NULL,NULL,1,NULL,NULL),(4096,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','179','4087','Deudas representadas en otros valores negociables',0,NULL,NULL,1,NULL,NULL),(4097,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','18','4001','Pasivos por fianzas garantias y otros conceptos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4098,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','180','4097','Fianzas recibidas a largo plazo',0,NULL,NULL,1,NULL,NULL),(4099,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','181','4097','Anticipos recibidos por ventas o prestaciones de servicios a largo plazo',0,NULL,NULL,1,NULL,NULL),(4100,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','185','4097','Depositos recibidos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4101,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','19','4001','Situaciones transitorias de financiación',0,NULL,NULL,1,NULL,NULL),(4102,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','190','4101','Acciones o participaciones emitidas',0,NULL,NULL,1,NULL,NULL),(4103,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','192','4101','Suscriptores de acciones',0,NULL,NULL,1,NULL,NULL),(4104,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','194','4101','Capital emitido pendiente de inscripción',0,NULL,NULL,1,NULL,NULL),(4105,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','195','4101','Acciones o participaciones emitidas consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4106,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','197','4101','Suscriptores de acciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4107,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','FINANCIACION','XXXXXX','199','4101','Acciones o participaciones emitidas consideradas como pasivos financieros pendientes de inscripción',0,NULL,NULL,1,NULL,NULL),(4108,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','20','4002','Inmovilizaciones intangibles',0,NULL,NULL,1,NULL,NULL),(4109,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','200','4108','Investigación',0,NULL,NULL,1,NULL,NULL),(4110,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','201','4108','Desarrollo',0,NULL,NULL,1,NULL,NULL),(4111,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','202','4108','Concesiones administrativas',0,NULL,NULL,1,NULL,NULL),(4112,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','203','4108','Propiedad industrial',0,NULL,NULL,1,NULL,NULL),(4113,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','205','4108','Derechos de transpaso',0,NULL,NULL,1,NULL,NULL),(4114,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','206','4108','Aplicaciones informáticas',0,NULL,NULL,1,NULL,NULL),(4115,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','209','4108','Anticipos para inmovilizaciones intangibles',0,NULL,NULL,1,NULL,NULL),(4116,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','21','4002','Inmovilizaciones materiales',0,NULL,NULL,1,NULL,NULL),(4117,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','210','4116','Terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4118,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','211','4116','Construcciones',0,NULL,NULL,1,NULL,NULL),(4119,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','212','4116','Instalaciones técnicas',0,NULL,NULL,1,NULL,NULL),(4120,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','213','4116','Maquinaria',0,NULL,NULL,1,NULL,NULL),(4121,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','214','4116','Utillaje',0,NULL,NULL,1,NULL,NULL),(4122,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','215','4116','Otras instalaciones',0,NULL,NULL,1,NULL,NULL),(4123,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','216','4116','Mobiliario',0,NULL,NULL,1,NULL,NULL),(4124,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','217','4116','Equipos para procesos de información',0,NULL,NULL,1,NULL,NULL),(4125,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','218','4116','Elementos de transporte',0,NULL,NULL,1,NULL,NULL),(4126,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','219','4116','Otro inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4127,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','22','4002','Inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4128,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','220','4127','Inversiones en terreons y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4129,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','221','4127','Inversiones en construcciones',0,NULL,NULL,1,NULL,NULL),(4130,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','23','4002','Inmovilizaciones materiales en curso',0,NULL,NULL,1,NULL,NULL),(4131,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','230','4130','Adaptación de terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4132,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','231','4130','Construcciones en curso',0,NULL,NULL,1,NULL,NULL),(4133,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','232','4130','Instalaciones técnicas en montaje',0,NULL,NULL,1,NULL,NULL),(4134,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','233','4130','Maquinaria en montaje',0,NULL,NULL,1,NULL,NULL),(4135,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','237','4130','Equipos para procesos de información en montaje',0,NULL,NULL,1,NULL,NULL),(4136,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','239','4130','Anticipos para inmovilizaciones materiales',0,NULL,NULL,1,NULL,NULL),(4137,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','24','4002','Inversiones financieras a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4138,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','240','4137','Participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4139,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2403','4138','Participaciones a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4140,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2404','4138','Participaciones a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4141,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2405','4138','Participaciones a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4142,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','241','4137','Valores representativos de deuda a largo plazo de partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4143,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2413','4142','Valores representativos de deuda a largo plazo de empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4144,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2414','4142','Valores representativos de deuda a largo plazo de empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4145,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2415','4142','Valores representativos de deuda a largo plazo de otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4146,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','242','4137','Créditos a largo plazo a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4147,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2423','4146','Créditos a largo plazo a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4148,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2424','4146','Créditos a largo plazo a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4149,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2425','4146','Créditos a largo plazo a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4150,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','249','4137','Desembolsos pendientes sobre participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4151,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2493','4150','Desembolsos pendientes sobre participaciones a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4152,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2494','4150','Desembolsos pendientes sobre participaciones a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4153,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2495','4150','Desembolsos pendientes sobre participaciones a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4154,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','25','4002','Otras inversiones financieras a largo plazo',0,NULL,NULL,1,NULL,NULL),(4155,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','250','4154','Inversiones financieras a largo plazo en instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4156,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','251','4154','Valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4157,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','252','4154','Créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4158,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','253','4154','Créditos a largo plazo por enajenación de inmovilizado',0,NULL,NULL,1,NULL,NULL),(4159,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','254','4154','Créditos a largo plazo al personal',0,NULL,NULL,1,NULL,NULL),(4160,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','255','4154','Activos por derivados financieros a largo plazo',0,NULL,NULL,1,NULL,NULL),(4161,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','258','4154','Imposiciones a largo plazo',0,NULL,NULL,1,NULL,NULL),(4162,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','259','4154','Desembolsos pendientes sobre participaciones en el patrimonio neto a largo plazo',0,NULL,NULL,1,NULL,NULL),(4163,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','26','4002','Fianzas y depósitos constituidos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4164,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','260','4163','Fianzas constituidas a largo plazo',0,NULL,NULL,1,NULL,NULL),(4165,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','261','4163','Depósitos constituidos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4166,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','28','4002','Amortización acumulada del inmovilizado',0,NULL,NULL,1,NULL,NULL),(4167,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','280','4166','Amortización acumulado del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4168,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2800','4167','Amortización acumulada de investigación',0,NULL,NULL,1,NULL,NULL),(4169,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2801','4167','Amortización acumulada de desarrollo',0,NULL,NULL,1,NULL,NULL),(4170,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2802','4167','Amortización acumulada de concesiones administrativas',0,NULL,NULL,1,NULL,NULL),(4171,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2803','4167','Amortización acumulada de propiedad industrial',0,NULL,NULL,1,NULL,NULL),(4172,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2805','4167','Amortización acumulada de derechos de transpaso',0,NULL,NULL,1,NULL,NULL),(4173,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2806','4167','Amortización acumulada de aplicaciones informáticas',0,NULL,NULL,1,NULL,NULL),(4174,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','281','4166','Amortización acumulado del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4175,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2811','4174','Amortización acumulada de construcciones',0,NULL,NULL,1,NULL,NULL),(4176,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2812','4174','Amortización acumulada de instalaciones técnicas',0,NULL,NULL,1,NULL,NULL),(4177,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2813','4174','Amortización acumulada de maquinaria',0,NULL,NULL,1,NULL,NULL),(4178,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2814','4174','Amortización acumulada de utillaje',0,NULL,NULL,1,NULL,NULL),(4179,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2815','4174','Amortización acumulada de otras instalaciones',0,NULL,NULL,1,NULL,NULL),(4180,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2816','4174','Amortización acumulada de mobiliario',0,NULL,NULL,1,NULL,NULL),(4181,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2817','4174','Amortización acumulada de equipos para proceso de información',0,NULL,NULL,1,NULL,NULL),(4182,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2818','4174','Amortización acumulada de elementos de transporte',0,NULL,NULL,1,NULL,NULL),(4183,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2819','4175','Amortización acumulada de otro inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4184,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','282','4166','Amortización acumulada de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4185,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','29','4002','Deterioro de valor de activos no corrientes',0,NULL,NULL,1,NULL,NULL),(4186,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','290','4185','Deterioro de valor del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4187,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2900','4186','Deterioro de valor de investigación',0,NULL,NULL,1,NULL,NULL),(4188,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2901','4186','Deterioro de valor de desarrollo',0,NULL,NULL,1,NULL,NULL),(4189,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2902','4186','Deterioro de valor de concesiones administrativas',0,NULL,NULL,1,NULL,NULL),(4190,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2903','4186','Deterioro de valor de propiedad industrial',0,NULL,NULL,1,NULL,NULL),(4191,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2905','4186','Deterioro de valor de derechos de transpaso',0,NULL,NULL,1,NULL,NULL),(4192,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2906','4186','Deterioro de valor de aplicaciones informáticas',0,NULL,NULL,1,NULL,NULL),(4193,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','291','4185','Deterioro de valor del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4194,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2910','4193','Deterioro de valor de terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4195,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2911','4193','Deterioro de valor de construcciones',0,NULL,NULL,1,NULL,NULL),(4196,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2912','4193','Deterioro de valor de instalaciones técnicas',0,NULL,NULL,1,NULL,NULL),(4197,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2913','4193','Deterioro de valor de maquinaria',0,NULL,NULL,1,NULL,NULL),(4198,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2914','4193','Deterioro de valor de utillajes',0,NULL,NULL,1,NULL,NULL),(4199,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2915','4194','Deterioro de valor de otras instalaciones',0,NULL,NULL,1,NULL,NULL),(4200,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2916','4194','Deterioro de valor de mobiliario',0,NULL,NULL,1,NULL,NULL),(4201,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2917','4194','Deterioro de valor de equipos para proceso de información',0,NULL,NULL,1,NULL,NULL),(4202,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2918','4194','Deterioro de valor de elementos de transporte',0,NULL,NULL,1,NULL,NULL),(4203,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2919','4194','Deterioro de valor de otro inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4204,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','292','4185','Deterioro de valor de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4205,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2920','4204','Deterioro de valor de terrenos y bienes naturales',0,NULL,NULL,1,NULL,NULL),(4206,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2921','4204','Deterioro de valor de construcciones',0,NULL,NULL,1,NULL,NULL),(4207,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','293','4185','Deterioro de valor de participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4208,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2933','4207','Deterioro de valor de participaciones a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4209,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2934','4207','Deterioro de valor de sobre participaciones a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4210,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2935','4207','Deterioro de valor de sobre participaciones a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4211,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','294','4185','Deterioro de valor de valores representativos de deuda a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4212,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2943','4211','Deterioro de valor de valores representativos de deuda a largo plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4213,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2944','4211','Deterioro de valor de valores representativos de deuda a largo plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4214,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2945','4211','Deterioro de valor de valores representativos de deuda a largo plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4215,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','295','4185','Deterioro de valor de créditos a largo plazo a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4216,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2953','4215','Deterioro de valor de créditos a largo plazo a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4217,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2954','4215','Deterioro de valor de créditos a largo plazo a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4218,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','2955','4215','Deterioro de valor de créditos a largo plazo a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4219,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','296','4185','Deterioro de valor de participaciones en el patrimonio netoa largo plazo',0,NULL,NULL,1,NULL,NULL),(4220,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','297','4185','Deterioro de valor de valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4221,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACTIVO','XXXXXX','298','4185','Deterioro de valor de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4222,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','30','4003','Comerciales',0,NULL,NULL,1,NULL,NULL),(4223,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','300','4222','Mercaderías A',0,NULL,NULL,1,NULL,NULL),(4224,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','301','4222','Mercaderías B',0,NULL,NULL,1,NULL,NULL),(4225,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','31','4003','Materias primas',0,NULL,NULL,1,NULL,NULL),(4226,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','310','4225','Materias primas A',0,NULL,NULL,1,NULL,NULL),(4227,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','311','4225','Materias primas B',0,NULL,NULL,1,NULL,NULL),(4228,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','32','4003','Otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4229,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','320','4228','Elementos y conjuntos incorporables',0,NULL,NULL,1,NULL,NULL),(4230,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','321','4228','Combustibles',0,NULL,NULL,1,NULL,NULL),(4231,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','322','4228','Repuestos',0,NULL,NULL,1,NULL,NULL),(4232,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','325','4228','Materiales diversos',0,NULL,NULL,1,NULL,NULL),(4233,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','326','4228','Embalajes',0,NULL,NULL,1,NULL,NULL),(4234,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','327','4228','Envases',0,NULL,NULL,1,NULL,NULL),(4235,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','328','4229','Material de oficina',0,NULL,NULL,1,NULL,NULL),(4236,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','33','4003','Productos en curso',0,NULL,NULL,1,NULL,NULL),(4237,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','330','4236','Productos en curos A',0,NULL,NULL,1,NULL,NULL),(4238,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','331','4236','Productos en curso B',0,NULL,NULL,1,NULL,NULL),(4239,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','34','4003','Productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4240,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','340','4239','Productos semiterminados A',0,NULL,NULL,1,NULL,NULL),(4241,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','341','4239','Productos semiterminados B',0,NULL,NULL,1,NULL,NULL),(4242,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','35','4003','Productos terminados',0,NULL,NULL,1,NULL,NULL),(4243,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','350','4242','Productos terminados A',0,NULL,NULL,1,NULL,NULL),(4244,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','351','4242','Productos terminados B',0,NULL,NULL,1,NULL,NULL),(4245,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','36','4003','Subproductos, residuos y materiales recuperados',0,NULL,NULL,1,NULL,NULL),(4246,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','360','4245','Subproductos A',0,NULL,NULL,1,NULL,NULL),(4247,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','361','4245','Subproductos B',0,NULL,NULL,1,NULL,NULL),(4248,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','365','4245','Residuos A',0,NULL,NULL,1,NULL,NULL),(4249,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','366','4245','Residuos B',0,NULL,NULL,1,NULL,NULL),(4250,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','368','4245','Materiales recuperados A',0,NULL,NULL,1,NULL,NULL),(4251,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','369','4245','Materiales recuperados B',0,NULL,NULL,1,NULL,NULL),(4252,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','39','4003','Deterioro de valor de las existencias',0,NULL,NULL,1,NULL,NULL),(4253,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','390','4252','Deterioro de valor de las mercaderías',0,NULL,NULL,1,NULL,NULL),(4254,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','391','4252','Deterioro de valor de las materias primas',0,NULL,NULL,1,NULL,NULL),(4255,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','392','4252','Deterioro de valor de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4256,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','393','4252','Deterioro de valor de los productos en curso',0,NULL,NULL,1,NULL,NULL),(4257,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','394','4252','Deterioro de valor de los productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4258,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','395','4252','Deterioro de valor de los productos terminados',0,NULL,NULL,1,NULL,NULL),(4259,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','EXISTENCIAS','XXXXXX','396','4252','Deterioro de valor de los subproductos, residuos y materiales recuperados',0,NULL,NULL,1,NULL,NULL),(4260,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','PROVEEDORES','40','4004','Proveedores',0,NULL,NULL,1,NULL,NULL),(4261,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','PROVEEDORES','400','4260','Proveedores',0,NULL,NULL,1,NULL,NULL),(4262,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4000','4261','Proveedores euros',0,NULL,NULL,1,NULL,NULL),(4263,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4004','4261','Proveedores moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4264,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4009','4261','Proveedores facturas pendientes de recibir o formalizar',0,NULL,NULL,1,NULL,NULL),(4265,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','401','4260','Proveedores efectos comerciales a pagar',0,NULL,NULL,1,NULL,NULL),(4266,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','403','4260','Proveedores empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4267,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4030','4266','Proveedores empresas del grupo euros',0,NULL,NULL,1,NULL,NULL),(4268,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4031','4266','Efectos comerciales a pagar empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4269,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4034','4266','Proveedores empresas del grupo moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4270,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4036','4266','Envases y embalajes a devolver a proveedores empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4271,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4039','4266','Proveedores empresas del grupo facturas pendientes de recibir o de formalizar',0,NULL,NULL,1,NULL,NULL),(4272,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','404','4260','Proveedores empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4273,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','405','4260','Proveedores otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4274,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','406','4260','Envases y embalajes a devolver a proveedores',0,NULL,NULL,1,NULL,NULL),(4275,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','407','4260','Anticipos a proveedores',0,NULL,NULL,1,NULL,NULL),(4276,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','41','4004','Acreedores varios',0,NULL,NULL,1,NULL,NULL),(4277,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','410','4276','Acreedores por prestaciones de servicios',0,NULL,NULL,1,NULL,NULL),(4278,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4100','4277','Acreedores por prestaciones de servicios euros',0,NULL,NULL,1,NULL,NULL),(4279,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4104','4277','Acreedores por prestaciones de servicios moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4280,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4109','4277','Acreedores por prestaciones de servicios facturas pendientes de recibir o formalizar',0,NULL,NULL,1,NULL,NULL),(4281,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','411','4276','Acreedores efectos comerciales a pagar',0,NULL,NULL,1,NULL,NULL),(4282,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','419','4276','Acreedores por operaciones en común',0,NULL,NULL,1,NULL,NULL),(4283,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','CLIENTES','43','4004','Clientes',0,NULL,NULL,1,NULL,NULL),(4284,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','CLIENTES','430','4283','Clientes',0,NULL,NULL,1,NULL,NULL),(4285,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4300','4284','Clientes euros',0,NULL,NULL,1,NULL,NULL),(4286,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4304','4284','Clientes moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4287,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4309','4284','Clientes facturas pendientes de formalizar',0,NULL,NULL,1,NULL,NULL),(4288,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','431','4283','Clientes efectos comerciales a cobrar',0,NULL,NULL,1,NULL,NULL),(4289,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4310','4288','Efectos comerciales en cartera',0,NULL,NULL,1,NULL,NULL),(4290,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4311','4288','Efectos comerciales descontados',0,NULL,NULL,1,NULL,NULL),(4291,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4312','4288','Efectos comerciales en gestión de cobro',0,NULL,NULL,1,NULL,NULL),(4292,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4315','4288','Efectos comerciales impagados',0,NULL,NULL,1,NULL,NULL),(4293,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','432','4283','Clientes operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4294,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','433','4283','Clientes empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4295,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4330','4294','Clientes empresas del grupo euros',0,NULL,NULL,1,NULL,NULL),(4296,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4331','4294','Efectos comerciales a cobrar empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4297,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4332','4294','Clientes empresas del grupo operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4298,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4334','4294','Clientes empresas del grupo moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4299,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4336','4294','Clientes empresas del grupo dudoso cobro',0,NULL,NULL,1,NULL,NULL),(4300,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4337','4294','Envases y embalajes a devolver a clientes empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4301,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4339','4294','Clientes empresas del grupo facturas pendientes de formalizar',0,NULL,NULL,1,NULL,NULL),(4302,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','434','4283','Clientes empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4303,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','435','4283','Clientes otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4304,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','436','4283','Clientes de dudoso cobro',0,NULL,NULL,1,NULL,NULL),(4305,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','437','4283','Envases y embalajes a devolver por clientes',0,NULL,NULL,1,NULL,NULL),(4306,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','438','4283','Anticipos de clientes',0,NULL,NULL,1,NULL,NULL),(4307,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','44','4004','Deudores varios',0,NULL,NULL,1,NULL,NULL),(4308,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','440','4307','Deudores',0,NULL,NULL,1,NULL,NULL),(4309,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4400','4308','Deudores euros',0,NULL,NULL,1,NULL,NULL),(4310,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4404','4308','Deudores moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4311,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4409','4308','Deudores facturas pendientes de formalizar',0,NULL,NULL,1,NULL,NULL),(4312,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','441','4307','Deudores efectos comerciales a cobrar',0,NULL,NULL,1,NULL,NULL),(4313,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4410','4312','Deudores efectos comerciales en cartera',0,NULL,NULL,1,NULL,NULL),(4314,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4411','4312','Deudores efectos comerciales descontados',0,NULL,NULL,1,NULL,NULL),(4315,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4412','4312','Deudores efectos comerciales en gestión de cobro',0,NULL,NULL,1,NULL,NULL),(4316,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4415','4312','Deudores efectos comerciales impagados',0,NULL,NULL,1,NULL,NULL),(4317,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','446','4307','Deudores de dusoso cobro',0,NULL,NULL,1,NULL,NULL),(4318,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','449','4307','Deudores por operaciones en común',0,NULL,NULL,1,NULL,NULL),(4319,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','46','4004','Personal',0,NULL,NULL,1,NULL,NULL),(4320,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','460','4319','Anticipos de renumeraciones',0,NULL,NULL,1,NULL,NULL),(4321,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','465','4319','Renumeraciones pendientes de pago',0,NULL,NULL,1,NULL,NULL),(4322,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','47','4004','Administraciones Públicas',0,NULL,NULL,1,NULL,NULL),(4323,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','470','4322','Hacienda Pública deudora por diversos conceptos',0,NULL,NULL,1,NULL,NULL),(4324,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4700','4323','Hacienda Pública deudora por IVA',0,NULL,NULL,1,NULL,NULL),(4325,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4708','4323','Hacienda Pública deudora por subvenciones concedidas',0,NULL,NULL,1,NULL,NULL),(4326,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4709','4323','Hacienda Pública deudora por devolución de impuestos',0,NULL,NULL,1,NULL,NULL),(4327,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','471','4322','Organismos de la Seguridad Social deudores',0,NULL,NULL,1,NULL,NULL),(4328,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','472','4322','Hacienda Pública IVA soportado',0,NULL,NULL,1,NULL,NULL),(4329,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','473','4322','Hacienda Pública retenciones y pagos a cuenta',0,NULL,NULL,1,NULL,NULL),(4330,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','474','4322','Activos por impuesto diferido',0,NULL,NULL,1,NULL,NULL),(4331,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4740','4330','Activos por diferencias temporarias deducibles',0,NULL,NULL,1,NULL,NULL),(4332,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4742','4330','Derechos por deducciones y bonificaciones pendientes de aplicar',0,NULL,NULL,1,NULL,NULL),(4333,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4745','4330','Crédito por pérdidasa compensar del ejercicio',0,NULL,NULL,1,NULL,NULL),(4334,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','475','4322','Hacienda Pública acreedora por conceptos fiscales',0,NULL,NULL,1,NULL,NULL),(4335,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4750','4334','Hacienda Pública acreedora por IVA',0,NULL,NULL,1,NULL,NULL),(4336,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4751','4334','Hacienda Pública acreedora por retenciones practicadas',0,NULL,NULL,1,NULL,NULL),(4337,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4752','4334','Hacienda Pública acreedora por impuesto sobre sociedades',0,NULL,NULL,1,NULL,NULL),(4338,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4758','4334','Hacienda Pública acreedora por subvenciones a integrar',0,NULL,NULL,1,NULL,NULL),(4339,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','476','4322','Organismos de la Seguridad Social acreedores',0,NULL,NULL,1,NULL,NULL),(4340,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','477','4322','Hacienda Pública IVA repercutido',0,NULL,NULL,1,NULL,NULL),(4341,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','479','4322','Pasivos por diferencias temporarias imponibles',0,NULL,NULL,1,NULL,NULL),(4342,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','48','4004','Ajustes por periodificación',0,NULL,NULL,1,NULL,NULL),(4343,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','480','4342','Gastos anticipados',0,NULL,NULL,1,NULL,NULL),(4344,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','485','4342','Ingresos anticipados',0,NULL,NULL,1,NULL,NULL),(4345,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','49','4004','Deterioro de valor de créditos comerciales y provisiones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4346,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','490','4345','Deterioro de valor de créditos por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4347,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','493','4345','Deterioro de valor de créditos por operaciones comerciales con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4348,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4933','4347','Deterioro de valor de créditos por operaciones comerciales con empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4349,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4934','4347','Deterioro de valor de créditos por operaciones comerciales con empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4350,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4935','4347','Deterioro de valor de créditos por operaciones comerciales con otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4351,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','499','4345','Provisiones por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4352,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4994','4351','Provisión para contratos anerosos',0,NULL,NULL,1,NULL,NULL),(4353,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','ACREEDORES_DEUDORES','XXXXXX','4999','4351','Provisión para otras operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4354,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','50','4005','Emprésitos deudas con características especiales y otras emisiones análogas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4355,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','500','4354','Obligaciones y bonos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4356,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','502','4354','Acciones o participaciones a corto plazo consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4357,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','505','4354','Deudas representadas en otros valores negociables a corto plazo',0,NULL,NULL,1,NULL,NULL),(4358,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','506','4354','Intereses a corto plazo de emprésitos y otras emisiones analógicas',0,NULL,NULL,1,NULL,NULL),(4359,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','507','4354','Dividendos de acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4360,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','509','4354','Valores negociables amortizados',0,NULL,NULL,1,NULL,NULL),(4361,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5090','4360','Obligaciones y bonos amortizados',0,NULL,NULL,1,NULL,NULL),(4362,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5095','4360','Otros valores negociables amortizados',0,NULL,NULL,1,NULL,NULL),(4363,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','51','4005','Deudas a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4364,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','510','4363','Deudas a corto plazo con entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4365,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5103','4364','Deudas a corto plazo con entidades de crédito empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4366,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5104','4364','Deudas a corto plazo con entidades de crédito empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4367,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5105','4364','Deudas a corto plazo con otras entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4368,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','511','4363','Proveedores de inmovilizado a corto plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4369,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5113','4368','Proveedores de inmovilizado a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4370,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5114','4368','Proveedores de inmovilizado a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4371,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5115','4368','Proveedores de inmovilizado a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4372,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','512','4363','Acreedores por arrendamiento financiero a corto plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4373,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5123','4372','Acreedores por arrendamiento financiero a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4374,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5124','4372','Acreedores por arrendamiento financiero a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4375,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5125','4372','Acreedores por arrendamiento financiero a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4376,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','513','4363','Otras deudas a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4377,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5133','4376','Otras deudas a corto plazo con empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4378,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5134','4376','Otras deudas a corto plazo con empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4379,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5135','4376','Otras deudas a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4380,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','514','4363','Intereses a corto plazo con partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4381,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5143','4380','Intereses a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4382,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5144','4380','Intereses a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4383,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5145','4380','Intereses deudas a corto plazo partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4384,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','52','4005','Deudas a corto plazo por préstamos recibidos y otros conceptos',0,NULL,NULL,1,NULL,NULL),(4385,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','520','4384','Deudas a corto plazo con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4386,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5200','4385','Préstamos a corto plazo de entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4387,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5201','4385','Deudas a corto plazo por crédito dispuesto',0,NULL,NULL,1,NULL,NULL),(4388,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5208','4385','Deudas por efectos descontados',0,NULL,NULL,1,NULL,NULL),(4389,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5209','4385','Deudas por operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4390,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','521','4384','Deudas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4391,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','522','4384','Deudas a corto plazo transformables en subvenciones donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4392,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','523','4384','Proveedores de inmovilizado a corto plazo',0,NULL,NULL,1,NULL,NULL),(4393,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','526','4384','Dividendo activo a pagar',0,NULL,NULL,1,NULL,NULL),(4394,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','527','4384','Intereses a corto plazo de deudas con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4395,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','528','4384','Intereses a corto plazo de deudas',0,NULL,NULL,1,NULL,NULL),(4396,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','529','4384','Provisiones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4397,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5291','4396','Provisión a corto plazo para impuestos',0,NULL,NULL,1,NULL,NULL),(4398,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5292','4396','Provisión a corto plazo para otras responsabilidades',0,NULL,NULL,1,NULL,NULL),(4399,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5293','4396','Provisión a corto plazo por desmantelamiento retiro o rehabilitación del inmovilizado',0,NULL,NULL,1,NULL,NULL),(4400,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5295','4396','Provisión a corto plazo para actuaciones medioambientales',0,NULL,NULL,1,NULL,NULL),(4401,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','53','4005','Inversiones financieras a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4402,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','530','4401','Participaciones a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4403,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5303','4402','Participaciones a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4404,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5304','4402','Participaciones a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4405,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5305','4402','Participaciones a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4406,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','531','4401','Valores representativos de deuda a corto plazo de partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4407,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5313','4406','Valores representativos de deuda a corto plazo de empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4408,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5314','4406','Valores representativos de deuda a corto plazo de empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4409,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5315','4406','Valores representativos de deuda a corto plazo de otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4410,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','532','4401','Créditos a corto plazo a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4411,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5323','4410','Créditos a corto plazo a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4412,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5324','4410','Créditos a corto plazo a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4413,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5325','4410','Créditos a corto plazo a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4414,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','533','4401','Intereses a corto plazo de valores representativos de deuda de partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4415,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5333','4414','Intereses a corto plazo de valores representativos de deuda en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4416,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5334','4414','Intereses a corto plazo de valores representativos de deuda en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4417,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5335','4414','Intereses a corto plazo de valores representativos de deuda en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4418,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','534','4401','Intereses a corto plazo de créditos a partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4419,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5343','4418','Intereses a corto plazo de créditos a empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4420,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5344','4418','Intereses a corto plazo de créditos a empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4421,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5345','4418','Intereses a corto plazo de créditos a otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4422,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','535','4401','Dividendo a cobrar de inversiones financieras en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4423,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5353','4422','Dividendo a cobrar de empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4424,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5354','4422','Dividendo a cobrar de empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4425,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5355','4422','Dividendo a cobrar de otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4426,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','539','4401','Desembolsos pendientes sobre participaciones a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4427,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5393','4426','Desembolsos pendientes sobre participaciones a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4428,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5394','4426','Desembolsos pendientes sobre participaciones a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4429,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5395','4426','Desembolsos pendientes sobre participaciones a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4430,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','54','4005','Otras inversiones financieras a corto plazo',0,NULL,NULL,1,NULL,NULL),(4431,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','540','4430','Inversiones financieras a corto plazo en instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4432,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','541','4430','Valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4433,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','542','4430','Créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4434,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','543','4430','Créditos a corto plazo por enejenación de inmovilizado',0,NULL,NULL,1,NULL,NULL),(4435,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','544','4430','Créditos a corto plazo al personal',0,NULL,NULL,1,NULL,NULL),(4436,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','545','4430','Dividendo a cobrar',0,NULL,NULL,1,NULL,NULL),(4437,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','546','4430','Intereses a corto plazo de valores reprsentativos de deuda',0,NULL,NULL,1,NULL,NULL),(4438,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','547','4430','Intereses a corto plazo de créditos',0,NULL,NULL,1,NULL,NULL),(4439,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','548','4430','Imposiciones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4440,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','549','4430','Desembolsos pendientes sobre participaciones en el patrimonio neto a corto plazo',0,NULL,NULL,1,NULL,NULL),(4441,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','55','4005','Otras cuentas no bancarias',0,NULL,NULL,1,NULL,NULL),(4442,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','550','4441','Titular de la explotación',0,NULL,NULL,1,NULL,NULL),(4443,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','551','4441','Cuenta corriente con socios y administradores',0,NULL,NULL,1,NULL,NULL),(4444,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','552','4441','Cuenta corriente otras personas y entidades vinculadas',0,NULL,NULL,1,NULL,NULL),(4445,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5523','4444','Cuenta corriente con empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4446,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5524','4444','Cuenta corriente con empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4447,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5525','4444','Cuenta corriente con otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4448,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','554','4441','Cuenta corriente con uniones temporales de empresas y comunidades de bienes',0,NULL,NULL,1,NULL,NULL),(4449,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','555','4441','Partidas pendientes de aplicación',0,NULL,NULL,1,NULL,NULL),(4450,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','556','4441','Desembolsos exigidos sobre participaciones en el patrimonio neto',0,NULL,NULL,1,NULL,NULL),(4451,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5563','4450','Desembolsos exigidos sobre participaciones empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4452,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5564','4450','Desembolsos exigidos sobre participaciones empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4453,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5565','4450','Desembolsos exigidos sobre participaciones otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4454,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5566','4450','Desembolsos exigidos sobre participaciones otras empresas',0,NULL,NULL,1,NULL,NULL),(4455,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','557','4441','Dividendo activo a cuenta',0,NULL,NULL,1,NULL,NULL),(4456,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','558','4441','Socios por desembolsos exigidos',0,NULL,NULL,1,NULL,NULL),(4457,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5580','4456','Socios por desembolsos exigidos sobre acciones o participaciones ordinarias',0,NULL,NULL,1,NULL,NULL),(4458,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5585','4456','Socios por desembolsos exigidos sobre acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4459,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','559','4441','Derivados financieros a corto plazo',0,NULL,NULL,1,NULL,NULL),(4460,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5590','4459','Activos por derivados financieros a corto plazo',0,NULL,NULL,1,NULL,NULL),(4461,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5595','4459','Pasivos por derivados financieros a corto plazo',0,NULL,NULL,1,NULL,NULL),(4462,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','56','4005','Finanzas y depósitos recibidos y constituidos a corto plazo y ajustes por periodificación',0,NULL,NULL,1,NULL,NULL),(4463,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','560','4462','Finanzas recibidas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4464,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','561','4462','Depósitos recibidos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4465,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','565','4462','Finanzas constituidas a corto plazo',0,NULL,NULL,1,NULL,NULL),(4466,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','566','4462','Depósitos constituidos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4467,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','567','4462','Intereses pagados por anticipado',0,NULL,NULL,1,NULL,NULL),(4468,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','568','4462','Intereses cobrados a corto plazo',0,NULL,NULL,1,NULL,NULL),(4469,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','57','4005','Tesorería',0,NULL,NULL,1,NULL,NULL),(4470,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','CAJA','570','4469','Caja euros',0,NULL,NULL,1,NULL,NULL),(4471,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','571','4469','Caja moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4472,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','BANCOS','572','4469','Bancos e instituciones de crédito cc vista euros',0,NULL,NULL,1,NULL,NULL),(4473,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','573','4469','Bancos e instituciones de crédito cc vista moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4474,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','574','4469','Bancos e instituciones de crédito cuentas de ahorro euros',0,NULL,NULL,1,NULL,NULL),(4475,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','575','4469','Bancos e instituciones de crédito cuentas de ahorro moneda extranjera',0,NULL,NULL,1,NULL,NULL),(4476,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','576','4469','Inversiones a corto plazo de gran liquidez',0,NULL,NULL,1,NULL,NULL),(4477,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','59','4005','Deterioro del valor de las inversiones financieras a corto plazo',0,NULL,NULL,1,NULL,NULL),(4478,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','593','4477','Deterioro del valor de participaciones a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4479,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5933','4478','Deterioro del valor de participaciones a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4480,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5934','4478','Deterioro del valor de participaciones a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4481,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5935','4478','Deterioro del valor de participaciones a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4482,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','594','4477','Deterioro del valor de valores representativos de deuda a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4483,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5943','4482','Deterioro del valor de valores representativos de deuda a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4484,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5944','4482','Deterioro del valor de valores representativos de deuda a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4485,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5945','4482','Deterioro del valor de valores representativos de deuda a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4486,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','595','4477','Deterioro del valor de créditos a corto plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4487,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5953','4486','Deterioro del valor de créditos a corto plazo en empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4488,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5954','4486','Deterioro del valor de créditos a corto plazo en empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4489,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','5955','4486','Deterioro del valor de créditos a corto plazo en otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4490,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','596','4477','Deterioro del valor de participaciones a corto plazo',0,NULL,NULL,1,NULL,NULL),(4491,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','597','4477','Deterioro del valor de valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4492,1,NULL,'2016-01-22 17:28:16','PCG08-PYME','CUENTAS_FINANCIERAS','XXXXXX','598','4477','Deterioro de valor de créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4493,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','60','4006','Compras',0,NULL,NULL,1,NULL,NULL),(4494,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','COMPRAS','600','4493','Compras de mercaderías',0,NULL,NULL,1,NULL,NULL),(4495,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','COMPRAS','601','4493','Compras de materias primas',0,NULL,NULL,1,NULL,NULL),(4496,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','602','4493','Compras de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4497,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','606','4493','Descuentos sobre compras por pronto pago',0,NULL,NULL,1,NULL,NULL),(4498,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6060','4497','Descuentos sobre compras por pronto pago de mercaderías',0,NULL,NULL,1,NULL,NULL),(4499,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6061','4497','Descuentos sobre compras por pronto pago de materias primas',0,NULL,NULL,1,NULL,NULL),(4500,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6062','4497','Descuentos sobre compras por pronto pago de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4501,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','COMPRAS','607','4493','Trabajos realizados por otras empresas',0,NULL,NULL,1,NULL,NULL),(4502,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','608','4493','Devoluciones de compras y operaciones similares',0,NULL,NULL,1,NULL,NULL),(4503,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6080','4502','Devoluciones de compras de mercaderías',0,NULL,NULL,1,NULL,NULL),(4504,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6081','4502','Devoluciones de compras de materias primas',0,NULL,NULL,1,NULL,NULL),(4505,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6082','4502','Devoluciones de compras de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4506,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','609','4493','Rappels por compras',0,NULL,NULL,1,NULL,NULL),(4507,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6090','4506','Rappels por compras de mercaderías',0,NULL,NULL,1,NULL,NULL),(4508,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6091','4506','Rappels por compras de materias primas',0,NULL,NULL,1,NULL,NULL),(4509,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6092','4506','Rappels por compras de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4510,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','61','4006','Variación de existencias',0,NULL,NULL,1,NULL,NULL),(4511,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','610','4510','Variación de existencias de mercaderías',0,NULL,NULL,1,NULL,NULL),(4512,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','611','4510','Variación de existencias de materias primas',0,NULL,NULL,1,NULL,NULL),(4513,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','612','4510','Variación de existencias de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4514,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','62','4006','Servicios exteriores',0,NULL,NULL,1,NULL,NULL),(4515,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','620','4514','Gastos en investigación y desarrollo del ejercicio',0,NULL,NULL,1,NULL,NULL),(4516,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','621','4514','Arrendamientos y cánones',0,NULL,NULL,1,NULL,NULL),(4517,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','622','4514','Reparaciones y conservación',0,NULL,NULL,1,NULL,NULL),(4518,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','623','4514','Servicios profesionales independientes',0,NULL,NULL,1,NULL,NULL),(4519,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','624','4514','Transportes',0,NULL,NULL,1,NULL,NULL),(4520,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','625','4514','Primas de seguros',0,NULL,NULL,1,NULL,NULL),(4521,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','626','4514','Servicios bancarios y similares',0,NULL,NULL,1,NULL,NULL),(4522,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','627','4514','Publicidad, propaganda y relaciones públicas',0,NULL,NULL,1,NULL,NULL),(4523,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','628','4514','Suministros',0,NULL,NULL,1,NULL,NULL),(4524,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','629','4514','Otros servicios',0,NULL,NULL,1,NULL,NULL),(4525,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','63','4006','Tributos',0,NULL,NULL,1,NULL,NULL),(4526,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','630','4525','Impuesto sobre benecifios',0,NULL,NULL,1,NULL,NULL),(4527,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6300','4526','Impuesto corriente',0,NULL,NULL,1,NULL,NULL),(4528,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6301','4526','Impuesto diferido',0,NULL,NULL,1,NULL,NULL),(4529,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','631','4525','Otros tributos',0,NULL,NULL,1,NULL,NULL),(4530,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','633','4525','Ajustes negativos en la imposición sobre beneficios',0,NULL,NULL,1,NULL,NULL),(4531,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','634','4525','Ajustes negativos en la imposición indirecta',0,NULL,NULL,1,NULL,NULL),(4532,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6341','4531','Ajustes negativos en IVA de activo corriente',0,NULL,NULL,1,NULL,NULL),(4533,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6342','4531','Ajustes negativos en IVA de inversiones',0,NULL,NULL,1,NULL,NULL),(4534,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','636','4525','Devolución de impuestos',0,NULL,NULL,1,NULL,NULL),(4535,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','638','4525','Ajustes positivos en la imposición sobre beneficios',0,NULL,NULL,1,NULL,NULL),(4536,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','639','4525','Ajustes positivos en la imposición directa',0,NULL,NULL,1,NULL,NULL),(4537,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6391','4536','Ajustes positivos en IVA de activo corriente',0,NULL,NULL,1,NULL,NULL),(4538,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6392','4536','Ajustes positivos en IVA de inversiones',0,NULL,NULL,1,NULL,NULL),(4539,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','64','4006','Gastos de personal',0,NULL,NULL,1,NULL,NULL),(4540,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','640','4539','Sueldos y salarios',0,NULL,NULL,1,NULL,NULL),(4541,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','641','4539','Indemnizaciones',0,NULL,NULL,1,NULL,NULL),(4542,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','642','4539','Seguridad social a cargo de la empresa',0,NULL,NULL,1,NULL,NULL),(4543,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','649','4539','Otros gastos sociales',0,NULL,NULL,1,NULL,NULL),(4544,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','65','4006','Otros gastos de gestión',0,NULL,NULL,1,NULL,NULL),(4545,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','650','4544','Pérdidas de créditos comerciales incobrables',0,NULL,NULL,1,NULL,NULL),(4546,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','651','4544','Resultados de operaciones en común',0,NULL,NULL,1,NULL,NULL),(4547,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6510','4546','Beneficio transferido gestor',0,NULL,NULL,1,NULL,NULL),(4548,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6511','4546','Pérdida soportada participe o asociado no gestor',0,NULL,NULL,1,NULL,NULL),(4549,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','659','4544','Otras pérdidas en gestión corriente',0,NULL,NULL,1,NULL,NULL),(4550,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','66','4006','Gastos financieros',0,NULL,NULL,1,NULL,NULL),(4551,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','660','4550','Gastos financieros por actualización de provisiones',0,NULL,NULL,1,NULL,NULL),(4552,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','661','4550','Intereses de obligaciones y bonos',0,NULL,NULL,1,NULL,NULL),(4553,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6610','4452','Intereses de obligaciones y bonos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4554,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6611','4452','Intereses de obligaciones y bonos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4555,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6612','4452','Intereses de obligaciones y bonos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4556,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6613','4452','Intereses de obligaciones y bonos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4557,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6615','4452','Intereses de obligaciones y bonos a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4558,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6616','4452','Intereses de obligaciones y bonos a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4559,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6617','4452','Intereses de obligaciones y bonos a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4560,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6618','4452','Intereses de obligaciones y bonos a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4561,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','662','4550','Intereses de deudas',0,NULL,NULL,1,NULL,NULL),(4562,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6620','4561','Intereses de deudas empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4563,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6621','4561','Intereses de deudas empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4564,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6622','4561','Intereses de deudas otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4565,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6623','4561','Intereses de deudas con entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4566,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6624','4561','Intereses de deudas otras empresas',0,NULL,NULL,1,NULL,NULL),(4567,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','663','4550','Pérdidas por valorización de activos y pasivos financieros por su valor razonable',0,NULL,NULL,1,NULL,NULL),(4568,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','664','4550','Gastos por dividendos de acciones o participaciones consideradas como pasivos financieros',0,NULL,NULL,1,NULL,NULL),(4569,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6640','4568','Dividendos de pasivos empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4570,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6641','4568','Dividendos de pasivos empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4571,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6642','4568','Dividendos de pasivos otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4572,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6643','4568','Dividendos de pasivos otras empresas',0,NULL,NULL,1,NULL,NULL),(4573,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','665','4550','Intereses por descuento de efectos y operaciones de factoring',0,NULL,NULL,1,NULL,NULL),(4574,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6650','4573','Intereses por descuento de efectos en entidades de crédito del grupo',0,NULL,NULL,1,NULL,NULL),(4575,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6651','4573','Intereses por descuento de efectos en entidades de crédito asociadas',0,NULL,NULL,1,NULL,NULL),(4576,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6652','4573','Intereses por descuento de efectos en entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4577,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6653','4573','Intereses por descuento de efectos en otras entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4578,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6654','4573','Intereses por operaciones de factoring con entidades de crédito del grupo',0,NULL,NULL,1,NULL,NULL),(4579,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6655','4573','Intereses por operaciones de factoring con entidades de crédito asociadas',0,NULL,NULL,1,NULL,NULL),(4580,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6656','4573','Intereses por operaciones de factoring con otras entidades de crédito vinculadas',0,NULL,NULL,1,NULL,NULL),(4581,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6657','4573','Intereses por operaciones de factoring con otras entidades de crédito',0,NULL,NULL,1,NULL,NULL),(4582,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','666','4550','Pérdidas en participaciones y valores representativos de deuda',0,NULL,NULL,1,NULL,NULL),(4583,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6660','4582','Pérdidas en valores representativos de deuda a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4584,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6661','4582','Pérdidas en valores representativos de deuda a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4585,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6662','4582','Pérdidas en valores representativos de deuda a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4586,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6663','4582','Pérdidas en participaciones y valores representativos de deuda a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4587,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6665','4582','Pérdidas en participaciones y valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4588,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6666','4582','Pérdidas en participaciones y valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4589,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6667','4582','Pérdidas en valores representativos de deuda a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4590,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6668','4582','Pérdidas en valores representativos de deuda a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4591,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','667','4550','Pérdidas de créditos no comerciales',0,NULL,NULL,1,NULL,NULL),(4592,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6670','4591','Pérdidas de créditos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4593,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6671','4591','Pérdidas de créditos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4594,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6672','4591','Pérdidas de créditos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4595,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6673','4591','Pérdidas de créditos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4596,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6675','4591','Pérdidas de créditos a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4597,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6676','4591','Pérdidas de créditos a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4598,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6677','4591','Pérdidas de créditos a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4599,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6678','4591','Pérdidas de créditos a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4600,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','668','4550','Diferencias negativas de cambio',0,NULL,NULL,1,NULL,NULL),(4601,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','669','4550','Otros gastos financieros',0,NULL,NULL,1,NULL,NULL),(4602,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','67','4006','Pérdidas procedentes de activos no corrientes y gastos excepcionales',0,NULL,NULL,1,NULL,NULL),(4603,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','670','4602','Pérdidas procedentes del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4604,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','671','4602','Pérdidas procedentes del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4605,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','672','4602','Pérdidas procedentes de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4607,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','673','4602','Pérdidas procedentes de participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4608,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6733','4607','Pérdidas procedentes de participaciones a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4609,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6734','4607','Pérdidas procedentes de participaciones a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4610,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6735','4607','Pérdidas procedentes de participaciones a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4611,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','675','4602','Pérdidas por operaciones con obligaciones propias',0,NULL,NULL,1,NULL,NULL),(4612,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','678','4602','Gastos excepcionales',0,NULL,NULL,1,NULL,NULL),(4613,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','68','4006','Dotaciones para amortizaciones',0,NULL,NULL,1,NULL,NULL),(4614,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','680','4613','Amortización del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4615,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','681','4613','Amortización del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4616,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','682','4613','Amortización de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4617,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','69','4006','Pérdidas por deterioro y otras dotaciones',0,NULL,NULL,1,NULL,NULL),(4618,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','690','4617','Pérdidas por deterioro del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4619,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','691','4617','Pérdidas por deterioro del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4620,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','692','4617','Pérdidas por deterioro de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4621,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','693','4617','Pérdidas por deterioro de existencias',0,NULL,NULL,1,NULL,NULL),(4622,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6930','4621','Pérdidas por deterioro de productos terminados y en curso de fabricación',0,NULL,NULL,1,NULL,NULL),(4623,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6931','4621','Pérdidas por deterioro de mercaderías',0,NULL,NULL,1,NULL,NULL),(4624,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6932','4621','Pérdidas por deterioro de materias primas',0,NULL,NULL,1,NULL,NULL),(4625,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6933','4621','Pérdidas por deterioro de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4626,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','694','4617','Pérdidas por deterioro de créditos por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4627,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','695','4617','Dotación a la provisión por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4628,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6954','4627','Dotación a la provisión por contratos onerosos',0,NULL,NULL,1,NULL,NULL),(4629,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6959','4628','Dotación a la provisión para otras operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4630,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','696','4617','Pérdidas por deterioro de participaciones y valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4631,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6960','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4632,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6961','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4633,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6962','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4634,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6963','4630','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4635,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6965','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4636,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6966','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4637,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6967','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4638,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6968','4630','Pérdidas por deterioro en valores representativos de deuda a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4639,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','697','4617','Pérdidas por deterioro de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4640,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6970','4639','Pérdidas por deterioro de créditos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4641,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6971','4639','Pérdidas por deterioro de créditos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4642,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6972','4639','Pérdidas por deterioro de créditos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4643,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6973','4639','Pérdidas por deterioro de créditos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4644,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','698','4617','Pérdidas por deterioro de participaciones y valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4645,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6980','4644','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4646,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6981','4644','Pérdidas por deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4647,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6985','4644','Pérdidas por deterioro en valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4648,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6986','4644','Pérdidas por deterioro en valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4649,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6988','4644','Pérdidas por deterioro en valores representativos de deuda a corto plazo de otras empresas',0,NULL,NULL,1,NULL,NULL),(4650,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','699','4617','Pérdidas por deterioro de crédito a corto plazo',0,NULL,NULL,1,NULL,NULL),(4651,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6990','4650','Pérdidas por deterioro de crédito a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4652,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6991','4650','Pérdidas por deterioro de crédito a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4653,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6992','4650','Pérdidas por deterioro de crédito a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4654,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','EXPENSE','XXXXXX','6993','4650','Pérdidas por deterioro de crédito a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4655,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','70','4007','Ventas',0,NULL,NULL,1,NULL,NULL),(4656,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','VENTAS','700','4655','Ventas de mercaderías',0,NULL,NULL,1,NULL,NULL),(4657,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','VENTAS','701','4655','Ventas de productos terminados',0,NULL,NULL,1,NULL,NULL),(4658,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','702','4655','Ventas de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4659,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','703','4655','Ventas de subproductos y residuos',0,NULL,NULL,1,NULL,NULL),(4660,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','704','4655','Ventas de envases y embalajes',0,NULL,NULL,1,NULL,NULL),(4661,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','VENTAS','705','4655','Prestaciones de servicios',0,NULL,NULL,1,NULL,NULL),(4662,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','706','4655','Descuentos sobre ventas por pronto pago',0,NULL,NULL,1,NULL,NULL),(4663,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7060','4662','Descuentos sobre ventas por pronto pago de mercaderías',0,NULL,NULL,1,NULL,NULL),(4664,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7061','4662','Descuentos sobre ventas por pronto pago de productos terminados',0,NULL,NULL,1,NULL,NULL),(4665,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7062','4662','Descuentos sobre ventas por pronto pago de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4666,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7063','4662','Descuentos sobre ventas por pronto pago de subproductos y residuos',0,NULL,NULL,1,NULL,NULL),(4667,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','708','4655','Devoluciones de ventas y operacioes similares',0,NULL,NULL,1,NULL,NULL),(4668,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7080','4667','Devoluciones de ventas de mercaderías',0,NULL,NULL,1,NULL,NULL),(4669,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7081','4667','Devoluciones de ventas de productos terminados',0,NULL,NULL,1,NULL,NULL),(4670,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7082','4667','Devoluciones de ventas de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4671,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7083','4667','Devoluciones de ventas de subproductos y residuos',0,NULL,NULL,1,NULL,NULL),(4672,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7084','4667','Devoluciones de ventas de envases y embalajes',0,NULL,NULL,1,NULL,NULL),(4673,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','71','4007','Variación de existencias',0,NULL,NULL,1,NULL,NULL),(4674,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','710','4673','Variación de existencias de productos en curso',0,NULL,NULL,1,NULL,NULL),(4675,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','711','4673','Variación de existencias de productos semiterminados',0,NULL,NULL,1,NULL,NULL),(4676,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','712','4673','Variación de existencias de productos terminados',0,NULL,NULL,1,NULL,NULL),(4677,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','713','4673','Variación de existencias de subproductos, residuos y materiales recuperados',0,NULL,NULL,1,NULL,NULL),(4678,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','73','4007','Trabajos realizados para la empresa',0,NULL,NULL,1,NULL,NULL),(4679,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','730','4678','Trabajos realizados para el inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4680,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','731','4678','Trabajos realizados para el inmovilizado tangible',0,NULL,NULL,1,NULL,NULL),(4681,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','732','4678','Trabajos realizados en inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4682,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','733','4678','Trabajos realizados para el inmovilizado material en curso',0,NULL,NULL,1,NULL,NULL),(4683,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','74','4007','Subvenciones, donaciones y legados',0,NULL,NULL,1,NULL,NULL),(4684,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','740','4683','Subvenciones, donaciones y legados a la explotación',0,NULL,NULL,1,NULL,NULL),(4685,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','746','4683','Subvenciones, donaciones y legados de capital transferidos al resultado del ejercicio',0,NULL,NULL,1,NULL,NULL),(4686,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','747','4683','Otras subvenciones, donaciones y legados transferidos al resultado del ejercicio',0,NULL,NULL,1,NULL,NULL),(4687,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','75','4007','Otros ingresos de gestión',0,NULL,NULL,1,NULL,NULL),(4688,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','751','4687','Resultados de operaciones en común',0,NULL,NULL,1,NULL,NULL),(4689,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7510','4688','Pérdida transferida gestor',0,NULL,NULL,1,NULL,NULL),(4690,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7511','4688','Beneficio atribuido participe o asociado no gestor',0,NULL,NULL,1,NULL,NULL),(4691,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','752','4687','Ingreso por arrendamiento',0,NULL,NULL,1,NULL,NULL),(4692,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','753','4687','Ingresos de propiedad industrial cedida en explotación',0,NULL,NULL,1,NULL,NULL),(4693,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','754','4687','Ingresos por comisiones',0,NULL,NULL,1,NULL,NULL),(4694,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','755','4687','Ingresos por servicios al personal',0,NULL,NULL,1,NULL,NULL),(4695,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','759','4687','Ingresos por servicios diversos',0,NULL,NULL,1,NULL,NULL),(4696,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76','4007','Ingresos financieros',0,NULL,NULL,1,NULL,NULL),(4697,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','760','4696','Ingresos de participaciones en instrumentos de patrimonio',0,NULL,NULL,1,NULL,NULL),(4698,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7600','4697','Ingresos de participaciones en instrumentos de patrimonio empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4699,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7601','4697','Ingresos de participaciones en instrumentos de patrimonio empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4700,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7602','4697','Ingresos de participaciones en instrumentos de patrimonio otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4701,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7603','4697','Ingresos de participaciones en instrumentos de patrimonio otras empresas',0,NULL,NULL,1,NULL,NULL),(4702,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','761','4696','Ingresos de valores representativos de deuda',0,NULL,NULL,1,NULL,NULL),(4703,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7610','4702','Ingresos de valores representativos de deuda empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4704,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7611','4702','Ingresos de valores representativos de deuda empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4705,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7612','4702','Ingresos de valores representativos de deuda otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4706,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7613','4702','Ingresos de valores representativos de deuda otras empresas',0,NULL,NULL,1,NULL,NULL),(4707,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','762','4696','Ingresos de créditos',0,NULL,NULL,1,NULL,NULL),(4708,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7620','4707','Ingresos de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4709,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76200','4708','Ingresos de crédito a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4710,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76201','4708','Ingresos de crédito a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4711,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76202','4708','Ingresos de crédito a largo plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4712,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76203','4708','Ingresos de crédito a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4713,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7621','4707','Ingresos de créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4714,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76210','4713','Ingresos de crédito a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4715,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76211','4713','Ingresos de crédito a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4716,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76212','4713','Ingresos de crédito a corto plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4717,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','76213','4713','Ingresos de crédito a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4718,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','763','4696','Beneficios por valorización de activos y pasivos financieros por su valor razonable',0,NULL,NULL,1,NULL,NULL),(4719,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','766','4696','Beneficios en participaciones y valores representativos de deuda',0,NULL,NULL,1,NULL,NULL),(4720,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7660','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4721,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7661','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4722,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7662','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4723,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7663','4719','Beneficios en participaciones y valores representativos de deuda a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4724,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7665','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4725,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7666','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4726,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7667','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo otras partes asociadas',0,NULL,NULL,1,NULL,NULL),(4727,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7668','4719','Beneficios en participaciones y valores representativos de deuda a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4728,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','768','4696','Diferencias positivas de cambio',0,NULL,NULL,1,NULL,NULL),(4729,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','769','4696','Otros ingresos financieros',0,NULL,NULL,1,NULL,NULL),(4730,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','77','4007','Beneficios procedentes de activos no corrientes e ingresos excepcionales',0,NULL,NULL,1,NULL,NULL),(4731,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','770','4730','Beneficios procedentes del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4732,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','771','4730','Beneficios procedentes del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4733,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','772','4730','Beneficios procedentes de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4734,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','773','4730','Beneficios procedentes de participaciones a largo plazo en partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4735,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7733','4734','Beneficios procedentes de participaciones a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4736,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7734','4734','Beneficios procedentes de participaciones a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4737,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7735','4734','Beneficios procedentes de participaciones a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4738,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','775','4730','Beneficios por operaciones con obligaciones propias',0,NULL,NULL,1,NULL,NULL),(4739,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','778','4730','Ingresos excepcionales',0,NULL,NULL,1,NULL,NULL),(4741,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','79','4007','Excesos y aplicaciones de provisiones y pérdidas por deterioro',0,NULL,NULL,1,NULL,NULL),(4742,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','790','4741','Revisión del deterioro del inmovilizado intangible',0,NULL,NULL,1,NULL,NULL),(4743,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','791','4741','Revisión del deterioro del inmovilizado material',0,NULL,NULL,1,NULL,NULL),(4744,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','792','4741','Revisión del deterioro de las inversiones inmobiliarias',0,NULL,NULL,1,NULL,NULL),(4745,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','793','4741','Revisión del deterioro de las existencias',0,NULL,NULL,1,NULL,NULL),(4746,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7930','4745','Revisión del deterioro de productos terminados y en curso de fabricación',0,NULL,NULL,1,NULL,NULL),(4747,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7931','4745','Revisión del deterioro de mercaderías',0,NULL,NULL,1,NULL,NULL),(4748,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7932','4745','Revisión del deterioro de materias primas',0,NULL,NULL,1,NULL,NULL),(4749,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7933','4745','Revisión del deterioro de otros aprovisionamientos',0,NULL,NULL,1,NULL,NULL),(4750,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','794','4741','Revisión del deterioro de créditos por operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4751,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','795','4741','Exceso de provisiones',0,NULL,NULL,1,NULL,NULL),(4752,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7951','4751','Exceso de provisión para impuestos',0,NULL,NULL,1,NULL,NULL),(4753,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7952','4751','Exceso de provisión para otras responsabilidades',0,NULL,NULL,1,NULL,NULL),(4755,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7954','4751','Exceso de provisión para operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4756,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','79544','4755','Exceso de provisión por contratos onerosos',0,NULL,NULL,1,NULL,NULL),(4757,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','79549','4755','Exceso de provisión para otras operaciones comerciales',0,NULL,NULL,1,NULL,NULL),(4758,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7955','4751','Exceso de provisión para actuaciones medioambienteales',0,NULL,NULL,1,NULL,NULL),(4759,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','796','4741','Revisión del deterioro de participaciones y valores representativos de deuda a largo plazo',0,NULL,NULL,1,NULL,NULL),(4760,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7960','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4761,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7961','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4762,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7962','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4763,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7963','4759','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4764,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7965','4759','Revisión del deterioro de valores representativos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4765,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7966','4759','Revisión del deterioro de valores representativos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4766,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7967','4759','Revisión del deterioro de valores representativos a largo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4767,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7968','4759','Revisión del deterioro de valores representativos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4768,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','797','4741','Revisión del deterioro de créditos a largo plazo',0,NULL,NULL,1,NULL,NULL),(4769,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7970','4768','Revisión del deterioro de créditos a largo plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4770,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7971','4768','Revisión del deterioro de créditos a largo plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4771,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7972','4768','Revisión del deterioro de créditos a largo plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4772,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7973','4768','Revisión del deterioro de créditos a largo plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4773,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','798','4741','Revisión del deterioro de participaciones y valores representativos de deuda a corto plazo',0,NULL,NULL,1,NULL,NULL),(4774,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7980','4773','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4775,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7981','4773','Revisión del deterioro de participaciones en instrumentos de patrimonio neto a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4776,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7985','4773','Revisión del deterioro de valores representativos de deuda a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4777,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7986','4773','Revisión del deterioro de valores representativos de deuda a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4778,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7987','4773','Revisión del deterioro de valores representativos de deuda a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4779,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7988','4773','Revisión del deterioro de valores representativos de deuda a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL),(4780,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','799','4741','Revisión del deterioro de créditos a corto plazo',0,NULL,NULL,1,NULL,NULL),(4781,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7990','4780','Revisión del deterioro de créditos a corto plazo empresas del grupo',0,NULL,NULL,1,NULL,NULL),(4782,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7991','4780','Revisión del deterioro de créditos a corto plazo empresas asociadas',0,NULL,NULL,1,NULL,NULL),(4783,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7992','4780','Revisión del deterioro de créditos a corto plazo otras partes vinculadas',0,NULL,NULL,1,NULL,NULL),(4784,1,NULL,'2018-01-19 11:17:49','PCG08-PYME','INCOME','XXXXXX','7993','4780','Revisión del deterioro de créditos a corto plazo otras empresas',0,NULL,NULL,1,NULL,NULL); /*!40000 ALTER TABLE `llx_accounting_account` ENABLE KEYS */; UNLOCK TABLES; @@ -66,26 +66,26 @@ DROP TABLE IF EXISTS `llx_accounting_bookkeeping`; CREATE TABLE `llx_accounting_bookkeeping` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `doc_date` date NOT NULL, - `doc_type` varchar(30) NOT NULL, - `doc_ref` varchar(300) NOT NULL, + `doc_type` varchar(30) COLLATE utf8_unicode_ci NOT NULL, + `doc_ref` varchar(300) COLLATE utf8_unicode_ci NOT NULL, `fk_doc` int(11) NOT NULL, `fk_docdet` int(11) NOT NULL, - `thirdparty_code` varchar(32) DEFAULT NULL, - `numero_compte` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `label_compte` varchar(255) DEFAULT NULL, - `label_operation` varchar(255) DEFAULT NULL, + `thirdparty_code` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `numero_compte` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `label_compte` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `label_operation` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `debit` double(24,8) DEFAULT NULL, `credit` double(24,8) DEFAULT NULL, `montant` double(24,8) DEFAULT NULL, - `sens` varchar(1) DEFAULT NULL, + `sens` varchar(1) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_amount` double(24,8) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, - `lettering_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `lettering_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_lettering` datetime DEFAULT NULL, `fk_user_author` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, - `code_journal` varchar(32) DEFAULT NULL, - `journal_label` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_journal` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `journal_label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `piece_num` int(11) NOT NULL, `validated` tinyint(4) NOT NULL DEFAULT '0', `date_validated` datetime DEFAULT NULL, @@ -93,14 +93,14 @@ CREATE TABLE `llx_accounting_bookkeeping` ( `fk_user_modif` int(11) DEFAULT NULL, `date_creation` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `subledger_account` varchar(32) DEFAULT NULL, - `subledger_label` varchar(255) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `subledger_account` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `subledger_label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_lim_reglement` datetime DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_accounting_bookkeeping_fk_doc` (`fk_doc`) -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -181,7 +181,7 @@ DROP TABLE IF EXISTS `llx_accounting_fiscalyear`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_accounting_fiscalyear` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `label` varchar(128) NOT NULL, + `label` varchar(128) COLLATE utf8_unicode_ci NOT NULL, `date_start` date DEFAULT NULL, `date_end` date DEFAULT NULL, `statut` tinyint(4) NOT NULL DEFAULT '0', @@ -191,7 +191,7 @@ CREATE TABLE `llx_accounting_fiscalyear` ( `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -212,14 +212,14 @@ DROP TABLE IF EXISTS `llx_accounting_journal`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_accounting_journal` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `label` varchar(128) NOT NULL, + `code` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(128) COLLATE utf8_unicode_ci NOT NULL, `nature` smallint(6) NOT NULL DEFAULT '0', `active` smallint(6) DEFAULT '0', `entity` int(11) DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_accounting_journal_code` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -241,13 +241,13 @@ DROP TABLE IF EXISTS `llx_accounting_system`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_accounting_system` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `pcg_version` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `label` varchar(128) NOT NULL, + `pcg_version` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(128) COLLATE utf8_unicode_ci NOT NULL, `active` smallint(6) DEFAULT '0', `fk_country` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_accounting_system_pcg_version` (`pcg_version`) -) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -269,13 +269,13 @@ DROP TABLE IF EXISTS `llx_actioncomm`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_actioncomm` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `ref_ext` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `datep` datetime DEFAULT NULL, `datep2` datetime DEFAULT NULL, `fk_action` int(11) DEFAULT NULL, - `code` varchar(32) DEFAULT NULL, - `label` varchar(255) NOT NULL, + `code` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_author` int(11) DEFAULT NULL, @@ -291,25 +291,25 @@ CREATE TABLE `llx_actioncomm` ( `fulldayevent` smallint(6) NOT NULL DEFAULT '0', `punctual` smallint(6) NOT NULL DEFAULT '1', `percent` smallint(6) NOT NULL DEFAULT '0', - `location` varchar(128) DEFAULT NULL, + `location` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `durationp` double DEFAULT NULL, `durationa` double DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, `fk_element` int(11) DEFAULT NULL, - `elementtype` varchar(255) DEFAULT NULL, - `email_msgid` varchar(256) DEFAULT NULL, - `email_subject` varchar(256) DEFAULT NULL, - `email_from` varchar(256) DEFAULT NULL, - `email_sender` varchar(256) DEFAULT NULL, - `email_to` varchar(256) DEFAULT NULL, - `email_tocc` varchar(256) DEFAULT NULL, - `email_tobcc` varchar(256) DEFAULT NULL, - `errors_to` varchar(256) DEFAULT NULL, - `recurid` varchar(128) DEFAULT NULL, - `recurrule` varchar(128) DEFAULT NULL, + `elementtype` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_msgid` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_subject` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_from` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_sender` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_to` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_tocc` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_tobcc` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `errors_to` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL, + `recurid` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `recurrule` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `recurdateend` datetime DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_actioncomm_fk_soc` (`fk_soc`), KEY `idx_actioncomm_fk_contact` (`fk_contact`), @@ -321,7 +321,7 @@ CREATE TABLE `llx_actioncomm` ( KEY `idx_actioncomm_datep2` (`datep2`), KEY `idx_actioncomm_recurid` (`recurid`), KEY `idx_actioncomm_ref_ext` (`ref_ext`) -) ENGINE=InnoDB AUTO_INCREMENT=329 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=329 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -345,10 +345,10 @@ CREATE TABLE `llx_actioncomm_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_actioncomm_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -402,15 +402,15 @@ DROP TABLE IF EXISTS `llx_actioncomm_resources`; CREATE TABLE `llx_actioncomm_resources` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_actioncomm` int(11) NOT NULL, - `element_type` varchar(50) NOT NULL, + `element_type` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `fk_element` int(11) NOT NULL, - `answer_status` varchar(50) DEFAULT NULL, + `answer_status` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `mandatory` smallint(6) DEFAULT NULL, `transparency` smallint(6) DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_actioncomm_resources` (`fk_actioncomm`,`element_type`,`fk_element`), KEY `idx_actioncomm_resources_fk_element` (`fk_element`) -) ENGINE=InnoDB AUTO_INCREMENT=216 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=216 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -433,50 +433,50 @@ DROP TABLE IF EXISTS `llx_adherent`; CREATE TABLE `llx_adherent` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(128) DEFAULT NULL, - `civility` varchar(6) DEFAULT NULL, - `lastname` varchar(50) DEFAULT NULL, - `firstname` varchar(50) DEFAULT NULL, - `login` varchar(50) DEFAULT NULL, - `pass` varchar(50) DEFAULT NULL, - `pass_crypted` varchar(128) DEFAULT NULL, + `ref_ext` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `civility` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `lastname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `firstname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `login` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `pass` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `pass_crypted` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_adherent_type` int(11) NOT NULL, - `morphy` varchar(3) NOT NULL, - `societe` varchar(128) DEFAULT NULL, + `morphy` varchar(3) COLLATE utf8_unicode_ci NOT NULL, + `societe` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_soc` int(11) DEFAULT NULL, - `address` text, - `zip` varchar(10) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, + `address` text COLLATE utf8_unicode_ci, + `zip` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `state_id` int(11) DEFAULT NULL, `country` int(11) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `skype` varchar(255) DEFAULT NULL, - `phone` varchar(30) DEFAULT NULL, - `phone_perso` varchar(30) DEFAULT NULL, - `phone_mobile` varchar(30) DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `skype` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone_perso` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone_mobile` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `birth` date DEFAULT NULL, - `photo` varchar(255) DEFAULT NULL, + `photo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `statut` smallint(6) NOT NULL DEFAULT '0', `public` smallint(6) NOT NULL DEFAULT '0', `datefin` datetime DEFAULT NULL, - `note_private` text, - `note_public` text, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, `datevalid` datetime DEFAULT NULL, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_author` int(11) DEFAULT NULL, `fk_user_mod` int(11) DEFAULT NULL, `fk_user_valid` int(11) DEFAULT NULL, - `canvas` varchar(32) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `model_pdf` varchar(255) DEFAULT NULL, + `canvas` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_adherent_login` (`login`,`entity`), UNIQUE KEY `uk_adherent_fk_soc` (`fk_soc`), KEY `idx_adherent_fk_adherent_type` (`fk_adherent_type`), CONSTRAINT `adherent_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_adherent_adherent_type` FOREIGN KEY (`fk_adherent_type`) REFERENCES `llx_adherent_type` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -500,14 +500,14 @@ CREATE TABLE `llx_adherent_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `zzz` varchar(125) DEFAULT NULL, - `aaa` varchar(255) DEFAULT NULL, - `sssss` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `zzz` varchar(125) COLLATE utf8_unicode_ci DEFAULT NULL, + `aaa` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `sssss` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_adherent_options` (`fk_object`), KEY `idx_adherent_extrafields` (`fk_object`) -) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -532,14 +532,14 @@ CREATE TABLE `llx_adherent_type` ( `entity` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `statut` smallint(6) NOT NULL DEFAULT '0', - `libelle` varchar(50) NOT NULL, - `subscription` varchar(3) NOT NULL DEFAULT '1', - `vote` varchar(3) NOT NULL DEFAULT 'yes', - `note` text, - `mail_valid` text, + `libelle` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `subscription` varchar(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT '1', + `vote` varchar(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'yes', + `note` text COLLATE utf8_unicode_ci, + `mail_valid` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_adherent_type_libelle` (`libelle`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -563,10 +563,10 @@ CREATE TABLE `llx_adherent_type_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_adherent_type_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -587,17 +587,17 @@ DROP TABLE IF EXISTS `llx_advtargetemailing`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_advtargetemailing` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(200) NOT NULL, + `name` varchar(200) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `fk_mailing` int(11) NOT NULL, - `filtervalue` text, + `filtervalue` text COLLATE utf8_unicode_ci, `fk_user_author` int(11) NOT NULL, `datec` datetime NOT NULL, `fk_user_mod` int(11) NOT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_advtargetemailing_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -623,27 +623,27 @@ CREATE TABLE `llx_bank` ( `datev` date DEFAULT NULL, `dateo` date DEFAULT NULL, `amount` double(24,8) NOT NULL DEFAULT '0.00000000', - `label` varchar(255) DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_account` int(11) DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_rappro` int(11) DEFAULT NULL, - `fk_type` varchar(6) DEFAULT NULL, - `num_releve` varchar(50) DEFAULT NULL, - `num_chq` varchar(50) DEFAULT NULL, + `fk_type` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `num_releve` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `num_chq` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `rappro` tinyint(4) DEFAULT '0', - `note` text, + `note` text COLLATE utf8_unicode_ci, `fk_bordereau` int(11) DEFAULT '0', - `banque` varchar(255) DEFAULT NULL, - `emetteur` varchar(255) DEFAULT NULL, - `author` varchar(40) DEFAULT NULL, - `numero_compte` varchar(32) DEFAULT NULL, + `banque` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `emetteur` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `author` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL, + `numero_compte` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_bank_datev` (`datev`), KEY `idx_bank_dateo` (`dateo`), KEY `idx_bank_fk_account` (`fk_account`), KEY `idx_bank_rappro` (`rappro`), KEY `idx_bank_num_releve` (`num_releve`) -) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -652,7 +652,7 @@ CREATE TABLE `llx_bank` ( LOCK TABLES `llx_bank` WRITE; /*!40000 ALTER TABLE `llx_bank` DISABLE KEYS */; -INSERT INTO `llx_bank` VALUES (1,'2010-07-08 23:56:14','2016-07-30 15:16:10','2016-07-08','2016-07-08',2000.00000000,'(Initial balance)',1,NULL,1,'SOLD','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(2,'2010-07-09 00:00:24','2016-07-30 15:16:10','2016-07-09','2016-07-09',500.00000000,'(Initial balance)',2,NULL,NULL,'SOLD',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(3,'2010-07-10 13:33:42','2016-07-30 15:16:10','2016-07-10','2016-07-10',0.00000000,'(Solde initial)',3,NULL,NULL,'SOLD',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(5,'2011-07-18 20:50:24','2016-07-30 15:16:10','2016-07-08','2016-07-08',20.00000000,'(CustomerInvoicePayment)',1,1,NULL,'CB','201107',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(6,'2011-07-18 20:50:47','2016-07-30 15:16:10','2016-07-08','2016-07-08',10.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(8,'2011-08-01 03:34:11','2016-07-30 15:21:31','2015-08-01','2015-08-01',5.63000000,'(CustomerInvoicePayment)',1,1,1,'CB','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(12,'2011-08-05 23:11:37','2016-07-30 15:21:31','2015-08-05','2015-08-05',-10.00000000,'(SocialContributionPayment)',1,1,1,'VIR','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(13,'2011-08-06 20:33:54','2016-07-30 15:21:31','2015-08-06','2015-08-06',5.98000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(14,'2011-08-08 02:53:40','2016-07-30 15:21:31','2015-08-08','2015-08-08',26.10000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(15,'2011-08-08 02:55:58','2016-07-30 15:21:31','2015-08-08','2015-08-08',26.96000000,'(CustomerInvoicePayment)',1,1,1,'TIP','201211',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(16,'2012-12-09 15:28:44','2016-07-30 15:21:31','2015-12-09','2015-12-09',2.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(17,'2012-12-09 15:28:53','2016-07-30 15:21:31','2015-12-09','2015-12-09',-2.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(18,'2012-12-09 17:35:55','2016-07-30 15:21:31','2015-12-09','2015-12-09',-2.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(19,'2012-12-09 17:37:02','2016-07-30 15:21:31','2015-12-09','2015-12-09',2.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(20,'2012-12-09 18:35:07','2016-07-30 15:21:31','2015-12-09','2015-12-09',-2.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(21,'2012-12-12 18:54:33','2016-07-30 15:21:31','2015-12-12','2015-12-12',1.00000000,'(CustomerInvoicePayment)',1,1,1,'TIP','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(22,'2013-03-06 16:48:16','2016-07-30 15:16:10','2016-03-06','2016-03-06',20.00000000,'(SubscriptionPayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(23,'2013-03-20 14:30:11','2016-07-30 15:16:10','2016-03-20','2016-03-20',10.00000000,'(SubscriptionPayment)',1,1,NULL,'VIR',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(24,'2014-03-02 19:57:58','2016-07-30 15:16:10','2016-07-09','2016-07-09',605.00000000,'(CustomerInvoicePayment)',1,1,NULL,'VIR',NULL,NULL,0,NULL,0,NULL,'111',NULL,NULL),(26,'2014-03-02 20:01:39','2016-07-30 15:16:10','2016-03-19','2016-03-19',500.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(27,'2014-03-02 20:02:06','2016-07-30 15:16:10','2016-03-21','2016-03-21',400.00000000,'(CustomerInvoicePayment)',1,1,NULL,'VIR',NULL,NULL,0,NULL,0,NULL,'ABC and Co',NULL,NULL),(28,'2014-03-03 19:22:32','2016-07-30 15:21:31','2015-10-03','2015-10-03',-400.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(29,'2014-03-03 19:23:16','2016-07-30 15:16:10','2016-03-10','2016-03-10',-300.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(30,'2016-01-22 18:56:34','2016-01-22 17:56:34','2016-01-22','2016-01-22',-900.00000000,'(SupplierInvoicePayment)',1,12,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(31,'2016-07-30 22:42:14','2016-07-30 14:42:14','2016-07-30','2016-07-30',0.00000000,'(Initial balance)',4,0,NULL,'SOLD',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(32,'2017-02-01 19:02:44','2017-02-01 15:02:44','2017-02-01','2017-02-01',-200.00000000,'(SupplierInvoicePayment)',3,12,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(33,'2017-02-06 08:10:24','2017-02-06 04:12:05','2016-03-22','2016-03-22',150.00000000,'(CustomerInvoicePayment)',1,12,NULL,'CHQ',NULL,NULL,0,NULL,2,NULL,'Magic Food Store',NULL,NULL),(34,'2017-02-06 08:10:50','2017-02-06 04:10:50','2016-03-25','2016-03-25',140.00000000,'(CustomerInvoicePayment)',1,12,NULL,'PRE',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(35,'2017-02-12 23:18:33','2017-02-12 19:18:33','2017-02-12','2017-02-12',50.00000000,'Patient payment',4,12,NULL,'CHQ',NULL,NULL,0,NULL,0,NULL,'aaa',NULL,NULL),(36,'2017-02-16 02:22:09','2017-02-15 22:22:09','2017-02-16','2017-02-16',-1.00000000,'(ExpenseReportPayment)',4,12,NULL,'CHQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(37,'2017-02-21 16:07:43','2017-02-21 12:07:43','2017-02-21','2017-02-21',50.00000000,'(WithdrawalPayment)',1,12,NULL,'PRE',NULL,'T170201',0,NULL,0,NULL,NULL,NULL,NULL),(38,'2017-09-06 20:08:36','2017-09-06 16:08:36','2017-09-06','2017-09-06',10.00000000,'(DonationPayment)',3,12,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,''); +INSERT INTO `llx_bank` VALUES (1,'2010-07-08 23:56:14','2016-07-30 15:16:10','2016-07-08','2016-07-08',2000.00000000,'(Initial balance)',1,NULL,1,'SOLD','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(2,'2010-07-09 00:00:24','2016-07-30 15:16:10','2016-07-09','2016-07-09',500.00000000,'(Initial balance)',2,NULL,NULL,'SOLD',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(3,'2010-07-10 13:33:42','2016-07-30 15:16:10','2016-07-10','2016-07-10',0.00000000,'(Solde initial)',3,NULL,NULL,'SOLD',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(5,'2011-07-18 20:50:24','2016-07-30 15:16:10','2016-07-08','2016-07-08',20.00000000,'(CustomerInvoicePayment)',1,1,NULL,'CB','201107',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(6,'2011-07-18 20:50:47','2016-07-30 15:16:10','2016-07-08','2016-07-08',10.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(8,'2011-08-01 03:34:11','2016-07-30 15:21:31','2015-08-01','2015-08-01',5.63000000,'(CustomerInvoicePayment)',1,1,1,'CB','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(12,'2011-08-05 23:11:37','2016-07-30 15:21:31','2015-08-05','2015-08-05',-10.00000000,'(SocialContributionPayment)',1,1,1,'VIR','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(13,'2011-08-06 20:33:54','2016-07-30 15:21:31','2015-08-06','2015-08-06',5.98000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(14,'2011-08-08 02:53:40','2016-07-30 15:21:31','2015-08-08','2015-08-08',26.10000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(15,'2011-08-08 02:55:58','2016-07-30 15:21:31','2015-08-08','2015-08-08',26.96000000,'(CustomerInvoicePayment)',1,1,1,'TIP','201211',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(16,'2012-12-09 15:28:44','2016-07-30 15:21:31','2015-12-09','2015-12-09',2.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(17,'2012-12-09 15:28:53','2016-07-30 15:21:31','2015-12-09','2015-12-09',-2.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(18,'2012-12-09 17:35:55','2016-07-30 15:21:31','2015-12-09','2015-12-09',-2.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(19,'2012-12-09 17:37:02','2016-07-30 15:21:31','2015-12-09','2015-12-09',2.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(20,'2012-12-09 18:35:07','2016-07-30 15:21:31','2015-12-09','2015-12-09',-2.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(21,'2012-12-12 18:54:33','2016-07-30 15:21:31','2015-12-12','2015-12-12',1.00000000,'(CustomerInvoicePayment)',1,1,1,'TIP','201210',NULL,1,NULL,0,NULL,NULL,NULL,NULL),(22,'2013-03-06 16:48:16','2016-07-30 15:16:10','2016-03-06','2016-03-06',20.00000000,'(SubscriptionPayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(23,'2013-03-20 14:30:11','2016-07-30 15:16:10','2016-03-20','2016-03-20',10.00000000,'(SubscriptionPayment)',1,1,NULL,'VIR',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(24,'2014-03-02 19:57:58','2016-07-30 15:16:10','2016-07-09','2016-07-09',605.00000000,'(CustomerInvoicePayment)',1,1,NULL,'VIR',NULL,NULL,0,NULL,0,NULL,'111',NULL,NULL),(26,'2014-03-02 20:01:39','2016-07-30 15:16:10','2016-03-19','2016-03-19',500.00000000,'(CustomerInvoicePayment)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(27,'2014-03-02 20:02:06','2016-07-30 15:16:10','2016-03-21','2016-03-21',400.00000000,'(CustomerInvoicePayment)',1,1,NULL,'VIR',NULL,NULL,0,NULL,0,NULL,'ABC and Co',NULL,NULL),(28,'2014-03-03 19:22:32','2016-07-30 15:21:31','2015-10-03','2015-10-03',-400.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(29,'2014-03-03 19:23:16','2016-07-30 15:16:10','2016-03-10','2016-03-10',-300.00000000,'(CustomerInvoicePaymentBack)',3,1,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(30,'2016-01-22 18:56:34','2016-01-22 17:56:34','2016-01-22','2016-01-22',-900.00000000,'(SupplierInvoicePayment)',1,12,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(31,'2016-07-30 22:42:14','2016-07-30 14:42:14','2016-07-30','2016-07-30',0.00000000,'(Initial balance)',4,0,NULL,'SOLD',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(32,'2017-02-01 19:02:44','2017-02-01 15:02:44','2017-02-01','2017-02-01',-200.00000000,'(SupplierInvoicePayment)',3,12,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(33,'2017-02-06 08:10:24','2017-02-06 04:12:05','2016-03-22','2016-03-22',150.00000000,'(CustomerInvoicePayment)',1,12,NULL,'CHQ',NULL,NULL,0,NULL,2,NULL,'Magic Food Store',NULL,NULL),(34,'2017-02-06 08:10:50','2017-02-06 04:10:50','2016-03-25','2016-03-25',140.00000000,'(CustomerInvoicePayment)',1,12,NULL,'PRE',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(35,'2017-02-12 23:18:33','2017-02-12 19:18:33','2017-02-12','2017-02-12',50.00000000,'Patient payment',4,12,NULL,'CHQ',NULL,NULL,0,NULL,0,NULL,'aaa',NULL,NULL),(36,'2017-02-16 02:22:09','2017-02-15 22:22:09','2017-02-16','2017-02-16',-1.00000000,'(ExpenseReportPayment)',4,12,NULL,'CHQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL),(37,'2017-02-21 16:07:43','2017-02-21 12:07:43','2017-02-21','2017-02-21',50.00000000,'(WithdrawalPayment)',1,12,NULL,'PRE',NULL,'T170201',0,NULL,0,NULL,NULL,NULL,NULL),(38,'2017-09-06 20:08:36','2017-09-06 16:08:36','2017-09-06','2017-09-06',10.00000000,'(DonationPayment)',3,12,NULL,'LIQ',NULL,NULL,0,NULL,0,NULL,NULL,NULL,''),(39,'2018-03-16 13:59:31','2018-03-16 09:59:31','2018-03-16','2018-03-16',10.00000000,'(CustomerInvoicePayment)',4,12,NULL,'CHQ',NULL,NULL,0,NULL,0,NULL,'Indian SAS',NULL,''); /*!40000 ALTER TABLE `llx_bank` ENABLE KEYS */; UNLOCK TABLES; @@ -667,45 +667,45 @@ CREATE TABLE `llx_bank_account` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `ref` varchar(12) NOT NULL, - `label` varchar(30) NOT NULL, + `ref` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `bank` varchar(60) DEFAULT NULL, - `code_banque` varchar(128) DEFAULT NULL, - `code_guichet` varchar(6) DEFAULT NULL, - `number` varchar(255) DEFAULT NULL, - `cle_rib` varchar(5) DEFAULT NULL, - `bic` varchar(11) DEFAULT NULL, - `iban_prefix` varchar(34) DEFAULT NULL, - `country_iban` varchar(2) DEFAULT NULL, - `cle_iban` varchar(2) DEFAULT NULL, - `domiciliation` varchar(255) DEFAULT NULL, + `bank` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_banque` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_guichet` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cle_rib` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, + `bic` varchar(11) COLLATE utf8_unicode_ci DEFAULT NULL, + `iban_prefix` varchar(34) COLLATE utf8_unicode_ci DEFAULT NULL, + `country_iban` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL, + `cle_iban` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL, + `domiciliation` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `state_id` int(11) DEFAULT NULL, `fk_pays` int(11) NOT NULL, - `proprio` varchar(60) DEFAULT NULL, - `owner_address` text, + `proprio` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, + `owner_address` text COLLATE utf8_unicode_ci, `courant` smallint(6) NOT NULL DEFAULT '0', `clos` smallint(6) NOT NULL DEFAULT '0', `rappro` smallint(6) DEFAULT '1', - `url` varchar(128) DEFAULT NULL, - `account_number` varchar(32) DEFAULT NULL, - `accountancy_journal` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `currency_code` varchar(3) NOT NULL, + `url` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `account_number` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_journal` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `currency_code` varchar(3) COLLATE utf8_unicode_ci NOT NULL, `min_allowed` int(11) DEFAULT '0', `min_desired` int(11) DEFAULT '0', - `comment` text, + `comment` text COLLATE utf8_unicode_ci, `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_accountancy_journal` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_bank_account_label` (`label`,`entity`), KEY `idx_fk_accountancy_journal` (`fk_accountancy_journal`), CONSTRAINT `fk_bank_account_accountancy_journal` FOREIGN KEY (`fk_accountancy_journal`) REFERENCES `llx_accounting_journal` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -729,10 +729,10 @@ CREATE TABLE `llx_bank_account_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_bank_account_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -753,10 +753,10 @@ DROP TABLE IF EXISTS `llx_bank_categ`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_bank_categ` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `label` varchar(255) DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -780,7 +780,7 @@ CREATE TABLE `llx_bank_class` ( `lineid` int(11) NOT NULL, `fk_categ` int(11) NOT NULL, UNIQUE KEY `uk_bank_class_lineid` (`lineid`,`fk_categ`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -803,12 +803,12 @@ CREATE TABLE `llx_bank_url` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_bank` int(11) DEFAULT NULL, `url_id` int(11) DEFAULT NULL, - `url` varchar(255) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `type` varchar(24) NOT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `type` varchar(24) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_bank_url` (`fk_bank`,`type`) -) ENGINE=InnoDB AUTO_INCREMENT=68 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -817,7 +817,7 @@ CREATE TABLE `llx_bank_url` ( LOCK TABLES `llx_bank_url` WRITE; /*!40000 ALTER TABLE `llx_bank_url` DISABLE KEYS */; -INSERT INTO `llx_bank_url` VALUES (3,5,2,'/compta/paiement/card.php?id=','(paiement)','payment'),(4,5,2,'/comm/card.php?socid=','Belin SARL','company'),(5,6,3,'/compta/paiement/card.php?id=','(paiement)','payment'),(6,6,2,'/comm/card.php?socid=','Belin SARL','company'),(9,8,5,'/compta/paiement/card.php?id=','(paiement)','payment'),(10,8,7,'/comm/card.php?socid=','Generic customer','company'),(17,12,4,'/compta/payment_sc/card.php?id=','(paiement)','payment_sc'),(18,12,4,'/compta/charges.php?id=','Assurance Chomage (fff)','sc'),(19,13,6,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(20,13,7,'/dolibarrnew/comm/card.php?socid=','Generic customer','company'),(21,14,8,'/compta/paiement/card.php?id=','(paiement)','payment'),(22,14,2,'/comm/card.php?socid=','Belin SARL','company'),(23,15,9,'/compta/paiement/card.php?id=','(paiement)','payment'),(24,15,10,'/comm/card.php?socid=','Smith Vick','company'),(25,16,17,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(26,16,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(27,17,18,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(28,17,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(29,18,19,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(30,18,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(31,19,20,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(32,19,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(33,20,21,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(34,20,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(35,21,23,'/compta/paiement/card.php?id=','(paiement)','payment'),(36,21,1,'/comm/card.php?socid=','ABC and Co','company'),(37,22,24,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(38,22,12,'/dolibarrnew/comm/card.php?socid=','Dupont Alain','company'),(39,23,25,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(40,23,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(41,24,26,'/compta/paiement/card.php?id=','(paiement)','payment'),(42,24,1,'/comm/card.php?socid=','ABC and Co','company'),(45,26,29,'/compta/paiement/card.php?id=','(paiement)','payment'),(46,26,1,'/comm/card.php?socid=','ABC and Co','company'),(47,27,30,'/compta/paiement/card.php?id=','(paiement)','payment'),(48,27,1,'/comm/card.php?socid=','ABC and Co','company'),(49,28,32,'/dolibarr_new/compta/paiement/card.php?id=','(paiement)','payment'),(50,28,1,'/dolibarr_new/comm/card.php?socid=','ABC and Co','company'),(51,29,33,'/dolibarr_new/compta/paiement/card.php?id=','(paiement)','payment'),(52,29,1,'/dolibarr_new/comm/card.php?socid=','ABC and Co','company'),(53,30,1,'/dolibarr_3.8/htdocs/fourn/paiement/card.php?id=','(paiement)','payment_supplier'),(54,30,1,'/dolibarr_3.8/htdocs/fourn/card.php?socid=','Indian SAS','company'),(55,32,2,'/dolibarr_5.0/htdocs/fourn/paiement/card.php?id=','(paiement)','payment_supplier'),(56,32,13,'/dolibarr_5.0/htdocs/fourn/card.php?socid=','Company Corp 2','company'),(57,33,34,'/dolibarr_5.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(58,33,19,'/dolibarr_5.0/htdocs/comm/card.php?socid=','Magic Food Store','company'),(59,34,35,'/dolibarr_5.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(60,34,19,'/dolibarr_5.0/htdocs/comm/card.php?socid=','Magic Food Store','company'),(61,35,2,'/dolibarr_5.0/htdocs/dolimed_5.0/cabinetmed/consultations.php?action=edit&socid=26&id=','Consultation','consultation'),(62,35,26,'','aaa','company'),(63,36,2,'/dolibarr_5.0/htdocs/expensereport/payment/card.php?rowid=','(paiement)','payment_expensereport'),(64,36,0,'/dolibarr_5.0/htdocs/user/card.php?id=','','user'),(65,37,36,'/dolibarr_5.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(66,37,1,'/dolibarr_5.0/htdocs/compta/prelevement/card.php?id=','T170201','withdraw'),(67,38,1,'/dolibarr_6.0/htdocs/don/payment/card.php?rowid=','(paiement)','payment_donation'); +INSERT INTO `llx_bank_url` VALUES (3,5,2,'/compta/paiement/card.php?id=','(paiement)','payment'),(4,5,2,'/comm/card.php?socid=','Belin SARL','company'),(5,6,3,'/compta/paiement/card.php?id=','(paiement)','payment'),(6,6,2,'/comm/card.php?socid=','Belin SARL','company'),(9,8,5,'/compta/paiement/card.php?id=','(paiement)','payment'),(10,8,7,'/comm/card.php?socid=','Generic customer','company'),(17,12,4,'/compta/payment_sc/card.php?id=','(paiement)','payment_sc'),(18,12,4,'/compta/charges.php?id=','Assurance Chomage (fff)','sc'),(19,13,6,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(20,13,7,'/dolibarrnew/comm/card.php?socid=','Generic customer','company'),(21,14,8,'/compta/paiement/card.php?id=','(paiement)','payment'),(22,14,2,'/comm/card.php?socid=','Belin SARL','company'),(23,15,9,'/compta/paiement/card.php?id=','(paiement)','payment'),(24,15,10,'/comm/card.php?socid=','Smith Vick','company'),(25,16,17,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(26,16,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(27,17,18,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(28,17,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(29,18,19,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(30,18,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(31,19,20,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(32,19,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(33,20,21,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(34,20,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(35,21,23,'/compta/paiement/card.php?id=','(paiement)','payment'),(36,21,1,'/comm/card.php?socid=','ABC and Co','company'),(37,22,24,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(38,22,12,'/dolibarrnew/comm/card.php?socid=','Dupont Alain','company'),(39,23,25,'/dolibarrnew/compta/paiement/card.php?id=','(paiement)','payment'),(40,23,10,'/dolibarrnew/comm/card.php?socid=','Smith Vick','company'),(41,24,26,'/compta/paiement/card.php?id=','(paiement)','payment'),(42,24,1,'/comm/card.php?socid=','ABC and Co','company'),(45,26,29,'/compta/paiement/card.php?id=','(paiement)','payment'),(46,26,1,'/comm/card.php?socid=','ABC and Co','company'),(47,27,30,'/compta/paiement/card.php?id=','(paiement)','payment'),(48,27,1,'/comm/card.php?socid=','ABC and Co','company'),(49,28,32,'/dolibarr_new/compta/paiement/card.php?id=','(paiement)','payment'),(50,28,1,'/dolibarr_new/comm/card.php?socid=','ABC and Co','company'),(51,29,33,'/dolibarr_new/compta/paiement/card.php?id=','(paiement)','payment'),(52,29,1,'/dolibarr_new/comm/card.php?socid=','ABC and Co','company'),(53,30,1,'/dolibarr_3.8/htdocs/fourn/paiement/card.php?id=','(paiement)','payment_supplier'),(54,30,1,'/dolibarr_3.8/htdocs/fourn/card.php?socid=','Indian SAS','company'),(55,32,2,'/dolibarr_5.0/htdocs/fourn/paiement/card.php?id=','(paiement)','payment_supplier'),(56,32,13,'/dolibarr_5.0/htdocs/fourn/card.php?socid=','Company Corp 2','company'),(57,33,34,'/dolibarr_5.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(58,33,19,'/dolibarr_5.0/htdocs/comm/card.php?socid=','Magic Food Store','company'),(59,34,35,'/dolibarr_5.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(60,34,19,'/dolibarr_5.0/htdocs/comm/card.php?socid=','Magic Food Store','company'),(61,35,2,'/dolibarr_5.0/htdocs/dolimed_5.0/cabinetmed/consultations.php?action=edit&socid=26&id=','Consultation','consultation'),(62,35,26,'','aaa','company'),(63,36,2,'/dolibarr_5.0/htdocs/expensereport/payment/card.php?rowid=','(paiement)','payment_expensereport'),(64,36,12,'/dolibarr_5.0/htdocs/user/card.php?id=','','user'),(65,37,36,'/dolibarr_5.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(66,37,1,'/dolibarr_5.0/htdocs/compta/prelevement/card.php?id=','T170201','withdraw'),(67,38,1,'/dolibarr_6.0/htdocs/don/payment/card.php?rowid=','(paiement)','payment_donation'),(68,39,38,'/dolibarr_7.0/htdocs/compta/paiement/card.php?id=','(paiement)','payment'),(69,39,1,'/dolibarr_7.0/htdocs/comm/card.php?socid=','Indian SAS','company'); /*!40000 ALTER TABLE `llx_bank_url` ENABLE KEYS */; UNLOCK TABLES; @@ -852,7 +852,7 @@ CREATE TABLE `llx_blockedlog` ( KEY `fk_user` (`fk_user`), KEY `entity_action` (`entity`,`action`), KEY `entity_action_certified` (`entity`,`action`,`certified`) -) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -861,7 +861,7 @@ CREATE TABLE `llx_blockedlog` ( LOCK TABLES `llx_blockedlog` WRITE; /*!40000 ALTER TABLE `llx_blockedlog` DISABLE KEYS */; -INSERT INTO `llx_blockedlog` VALUES (19,'2018-01-19 11:27:15','MODULE_SET',0.00000000,'e1b6bc67160763b48d4f42122938e750727d99faeaf0e7c4abd7414ee0b0498e','da3f2150d6a3dd544fdf597d6b0fd420e31ec33a15d7dcb0feefd8146781e4ae','module',1,'systemevent','2018-01-19 11:27:15','O:8:\"stdClass\":1:{s:9:\"mycompany\";O:8:\"stdClass\":29:{s:10:\"name_alias\";N;s:7:\"address\";s:24:\"21 Jump street..ll..ee \"\";s:3:\"zip\";s:5:\"75500\";s:4:\"town\";s:6:\"MyTown\";s:10:\"state_code\";N;s:5:\"phone\";s:8:\"09123123\";s:3:\"fax\";s:8:\"09123124\";s:5:\"email\";s:24:\"myemail@mybigcompany.com\";s:7:\"barcode\";N;s:7:\"idprof1\";s:6:\"123456\";s:7:\"idprof2\";s:1:\"1\";s:7:\"idprof3\";s:1:\"1\";s:7:\"idprof4\";s:1:\"1\";s:7:\"idprof5\";s:1:\"1\";s:7:\"idprof6\";s:0:\"\";s:9:\"tva_intra\";s:9:\"FR1234567\";s:15:\"localtax1_assuj\";i:1;s:15:\"localtax1_value\";N;s:15:\"localtax2_assuj\";i:1;s:15:\"localtax2_value\";N;s:8:\"managers\";s:10:\"Zack Zeceo\";s:7:\"capital\";s:5:\"10000\";s:11:\"typent_code\";N;s:20:\"forme_juridique_code\";s:0:\"\";s:11:\"code_client\";N;s:16:\"code_fournisseur\";N;s:7:\"ref_ext\";N;s:12:\"country_code\";s:2:\"IN\";s:4:\"name\";s:12:\"MyBigCompany\";}}',12,1,0,'2018-01-19 11:27:15','Alice Adminson'); +INSERT INTO `llx_blockedlog` VALUES (20,'2018-03-16 09:57:22','MODULE_RESET',0.00000000,'d6dd5fe6c2eec2de6368f3b6da30188566f0a1a7be4b1589ccd8352d2c827ad5','fbc11d0396d9b76ea48f892bd5f0fe652e5bdf7d44873acb4bf1e1b70352bd30','module',1,'systemevent','2018-03-16 13:57:22','O:8:\"stdClass\":6:{s:9:\"mycompany\";O:8:\"stdClass\":29:{s:10:\"name_alias\";N;s:7:\"address\";s:24:\"21 Jump street..ll..ee \"\";s:3:\"zip\";s:5:\"75500\";s:4:\"town\";s:6:\"MyTown\";s:10:\"state_code\";N;s:5:\"phone\";s:8:\"09123123\";s:3:\"fax\";s:8:\"09123124\";s:5:\"email\";s:24:\"myemail@mybigcompany.com\";s:7:\"barcode\";N;s:7:\"idprof1\";s:6:\"123456\";s:7:\"idprof2\";s:1:\"1\";s:7:\"idprof3\";s:1:\"1\";s:7:\"idprof4\";s:1:\"1\";s:7:\"idprof5\";s:1:\"1\";s:7:\"idprof6\";s:0:\"\";s:9:\"tva_intra\";s:9:\"FR1234567\";s:15:\"localtax1_assuj\";i:1;s:15:\"localtax1_value\";N;s:15:\"localtax2_assuj\";i:1;s:15:\"localtax2_value\";N;s:8:\"managers\";s:10:\"Zack Zeceo\";s:7:\"capital\";s:5:\"10000\";s:11:\"typent_code\";N;s:20:\"forme_juridique_code\";s:0:\"\";s:11:\"code_client\";N;s:16:\"code_fournisseur\";N;s:7:\"ref_ext\";N;s:12:\"country_code\";s:2:\"IN\";s:4:\"name\";s:12:\"MyBigCompany\";}s:2:\"id\";i:1;s:7:\"element\";s:6:\"module\";s:3:\"ref\";s:11:\"systemevent\";s:6:\"entity\";i:1;s:4:\"date\";i:1521194242;}',12,1,0,'2018-03-16 13:57:22','Alice Adminson'),(21,'2018-03-16 09:57:24','MODULE_SET',0.00000000,'d6b66df837d8d33bd8b9744e2afa46ab8c65ae8ca462246c406de19f8254e146','0a3aae975056417705f4eb7b4a4926384075cc2b6c899603715643c8f1d6ff9b','module',1,'systemevent','2018-03-16 13:57:24','O:8:\"stdClass\":6:{s:9:\"mycompany\";O:8:\"stdClass\":29:{s:10:\"name_alias\";N;s:7:\"address\";s:24:\"21 Jump street..ll..ee \"\";s:3:\"zip\";s:5:\"75500\";s:4:\"town\";s:6:\"MyTown\";s:10:\"state_code\";N;s:5:\"phone\";s:8:\"09123123\";s:3:\"fax\";s:8:\"09123124\";s:5:\"email\";s:24:\"myemail@mybigcompany.com\";s:7:\"barcode\";N;s:7:\"idprof1\";s:6:\"123456\";s:7:\"idprof2\";s:1:\"1\";s:7:\"idprof3\";s:1:\"1\";s:7:\"idprof4\";s:1:\"1\";s:7:\"idprof5\";s:1:\"1\";s:7:\"idprof6\";s:0:\"\";s:9:\"tva_intra\";s:9:\"FR1234567\";s:15:\"localtax1_assuj\";i:1;s:15:\"localtax1_value\";N;s:15:\"localtax2_assuj\";i:1;s:15:\"localtax2_value\";N;s:8:\"managers\";s:10:\"Zack Zeceo\";s:7:\"capital\";s:5:\"10000\";s:11:\"typent_code\";N;s:20:\"forme_juridique_code\";s:0:\"\";s:11:\"code_client\";N;s:16:\"code_fournisseur\";N;s:7:\"ref_ext\";N;s:12:\"country_code\";s:2:\"IN\";s:4:\"name\";s:12:\"MyBigCompany\";}s:2:\"id\";i:1;s:7:\"element\";s:6:\"module\";s:3:\"ref\";s:11:\"systemevent\";s:6:\"entity\";i:1;s:4:\"date\";i:1521194244;}',12,1,0,'2018-03-16 13:57:24','Alice Adminson'),(22,'2018-03-16 09:59:31','PAYMENT_CUSTOMER_CREATE',10.00000000,'9beb9e3ba04582d441b49f176f995900c16572c789bcf48a1c9f285a74be76c8','86813eb2563252c0e270baaf1fffade82475fe51af5f88d14613005fd0e07783','payment',38,'PAY1803-0004','2018-03-16 12:00:00','O:8:\"stdClass\":8:{s:9:\"mycompany\";O:8:\"stdClass\":29:{s:10:\"name_alias\";N;s:7:\"address\";s:24:\"21 Jump street..ll..ee \"\";s:3:\"zip\";s:5:\"75500\";s:4:\"town\";s:6:\"MyTown\";s:10:\"state_code\";N;s:5:\"phone\";s:8:\"09123123\";s:3:\"fax\";s:8:\"09123124\";s:5:\"email\";s:24:\"myemail@mybigcompany.com\";s:7:\"barcode\";N;s:7:\"idprof1\";s:6:\"123456\";s:7:\"idprof2\";s:1:\"1\";s:7:\"idprof3\";s:1:\"1\";s:7:\"idprof4\";s:1:\"1\";s:7:\"idprof5\";s:1:\"1\";s:7:\"idprof6\";s:0:\"\";s:9:\"tva_intra\";s:9:\"FR1234567\";s:15:\"localtax1_assuj\";i:1;s:15:\"localtax1_value\";N;s:15:\"localtax2_assuj\";i:1;s:15:\"localtax2_value\";N;s:8:\"managers\";s:10:\"Zack Zeceo\";s:7:\"capital\";s:5:\"10000\";s:11:\"typent_code\";N;s:20:\"forme_juridique_code\";s:0:\"\";s:11:\"code_client\";N;s:16:\"code_fournisseur\";N;s:7:\"ref_ext\";N;s:12:\"country_code\";s:2:\"IN\";s:4:\"name\";s:12:\"MyBigCompany\";}s:3:\"ref\";s:12:\"PAY1803-0004\";s:4:\"date\";i:1521187200;s:9:\"type_code\";s:3:\"CHQ\";s:11:\"payment_num\";N;s:4:\"note\";s:0:\"\";s:12:\"payment_part\";a:1:{i:1;O:8:\"stdClass\":3:{s:6:\"amount\";s:2:\"10\";s:10:\"thirdparty\";O:8:\"stdClass\":29:{s:10:\"name_alias\";s:0:\"\";s:7:\"address\";s:13:\"1 alalah road\";s:3:\"zip\";N;s:4:\"town\";s:5:\"Delhi\";s:10:\"state_code\";N;s:5:\"phone\";N;s:3:\"fax\";N;s:5:\"email\";N;s:7:\"barcode\";N;s:7:\"idprof1\";s:0:\"\";s:7:\"idprof2\";s:0:\"\";s:7:\"idprof3\";s:0:\"\";s:7:\"idprof4\";s:0:\"\";s:7:\"idprof5\";s:0:\"\";s:7:\"idprof6\";s:0:\"\";s:9:\"tva_intra\";s:0:\"\";s:15:\"localtax1_assuj\";N;s:15:\"localtax1_value\";s:5:\"0.000\";s:15:\"localtax2_assuj\";N;s:15:\"localtax2_value\";s:5:\"0.000\";s:8:\"managers\";N;s:7:\"capital\";s:13:\"5000.00000000\";s:11:\"typent_code\";s:8:\"TE_SMALL\";s:20:\"forme_juridique_code\";N;s:11:\"code_client\";s:11:\"CU1212-0007\";s:16:\"code_fournisseur\";s:11:\"SU1212-0005\";s:7:\"ref_ext\";N;s:12:\"country_code\";s:2:\"IN\";s:4:\"name\";s:10:\"Indian SAS\";}s:7:\"invoice\";O:8:\"stdClass\":9:{s:4:\"date\";i:1453147200;s:10:\"ref_client\";N;s:4:\"type\";s:1:\"0\";s:8:\"total_ht\";s:11:\"20.00000000\";s:9:\"total_tva\";s:10:\"1.80000000\";s:9:\"total_ttc\";s:11:\"23.60000000\";s:12:\"revenuestamp\";s:10:\"0.00000000\";s:3:\"ref\";s:11:\"FA1601-0024\";s:11:\"note_public\";N;}}}s:6:\"amount\";i:10;}',12,1,0,'2018-03-16 13:59:31','Alice Adminson'); /*!40000 ALTER TABLE `llx_blockedlog` ENABLE KEYS */; UNLOCK TABLES; @@ -902,16 +902,16 @@ CREATE TABLE `llx_bookmark` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_user` int(11) NOT NULL, `dateb` datetime DEFAULT NULL, - `url` varchar(255) NOT NULL, - `target` varchar(16) DEFAULT NULL, - `title` varchar(64) DEFAULT NULL, - `favicon` varchar(24) DEFAULT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `target` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + `title` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, + `favicon` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) DEFAULT '0', `entity` int(11) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_bookmark_url` (`fk_user`,`url`), UNIQUE KEY `uk_bookmark_title` (`fk_user`,`title`) -) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -935,19 +935,19 @@ CREATE TABLE `llx_bordereau_cheque` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `datec` datetime NOT NULL, `date_bordereau` date DEFAULT NULL, - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `amount` double(24,8) NOT NULL, `nbcheque` smallint(6) NOT NULL, `fk_bank_account` int(11) DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, `statut` smallint(6) NOT NULL DEFAULT '0', - `ref_ext` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_bordereau_cheque` (`ref`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -972,16 +972,16 @@ CREATE TABLE `llx_boxes` ( `entity` int(11) NOT NULL DEFAULT '1', `box_id` int(11) NOT NULL, `position` smallint(6) NOT NULL, - `box_order` varchar(3) NOT NULL, + `box_order` varchar(3) COLLATE utf8_unicode_ci NOT NULL, `fk_user` int(11) NOT NULL DEFAULT '0', `maxline` int(11) DEFAULT NULL, - `params` varchar(255) DEFAULT NULL, + `params` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_boxes` (`entity`,`box_id`,`position`,`fk_user`), KEY `idx_boxes_boxid` (`box_id`), KEY `idx_boxes_fk_user` (`fk_user`), CONSTRAINT `fk_boxes_box_id` FOREIGN KEY (`box_id`) REFERENCES `llx_boxes_def` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=1152 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=1183 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -990,7 +990,7 @@ CREATE TABLE `llx_boxes` ( LOCK TABLES `llx_boxes` WRITE; /*!40000 ALTER TABLE `llx_boxes` DISABLE KEYS */; -INSERT INTO `llx_boxes` VALUES (253,2,323,0,'0',0,NULL,NULL),(304,2,324,0,'0',0,NULL,NULL),(305,2,325,0,'0',0,NULL,NULL),(306,2,326,0,'0',0,NULL,NULL),(307,2,327,0,'0',0,NULL,NULL),(308,2,328,0,'0',0,NULL,NULL),(309,2,329,0,'0',0,NULL,NULL),(310,2,330,0,'0',0,NULL,NULL),(311,2,331,0,'0',0,NULL,NULL),(312,2,332,0,'0',0,NULL,NULL),(313,2,333,0,'0',0,NULL,NULL),(314,1,347,0,'A01',0,NULL,NULL),(315,1,348,0,'B18',0,NULL,NULL),(316,1,349,0,'A19',0,NULL,NULL),(317,1,350,0,'B20',0,NULL,NULL),(344,1,374,0,'A21',0,NULL,NULL),(347,1,377,0,'B22',0,NULL,NULL),(348,1,378,0,'A23',0,NULL,NULL),(358,1,388,0,'A25',0,NULL,NULL),(359,1,389,0,'B26',0,NULL,NULL),(360,1,390,0,'A27',0,NULL,NULL),(362,1,392,0,'A29',0,NULL,NULL),(363,1,393,0,'B30',0,NULL,NULL),(366,1,396,0,'A17',0,NULL,NULL),(387,1,403,0,'B16',0,NULL,NULL),(392,1,409,0,'A15',0,NULL,NULL),(393,1,410,0,'B02',0,NULL,NULL),(394,1,411,0,'A03',0,NULL,NULL),(395,1,412,0,'B04',0,NULL,NULL),(396,1,413,0,'A05',0,NULL,NULL),(397,1,414,0,'B06',0,NULL,NULL),(398,1,415,0,'A07',0,NULL,NULL),(399,1,416,0,'B08',0,NULL,NULL),(400,1,417,0,'A09',0,NULL,NULL),(401,1,418,0,'B10',0,NULL,NULL),(501,1,419,0,'A11',0,NULL,NULL),(1019,1,392,0,'A01',2,NULL,NULL),(1021,1,412,0,'A03',2,NULL,NULL),(1022,1,347,0,'A04',2,NULL,NULL),(1023,1,393,0,'A05',2,NULL,NULL),(1025,1,389,0,'A07',2,NULL,NULL),(1026,1,416,0,'A08',2,NULL,NULL),(1027,1,396,0,'B01',2,NULL,NULL),(1028,1,377,0,'B02',2,NULL,NULL),(1031,1,419,0,'B05',2,NULL,NULL),(1036,1,424,0,'0',0,NULL,NULL),(1037,1,425,0,'0',0,NULL,NULL),(1038,1,426,0,'0',0,NULL,NULL),(1039,1,427,0,'0',0,NULL,NULL),(1142,1,412,0,'A01',12,NULL,NULL),(1143,1,392,0,'A02',12,NULL,NULL),(1144,1,377,0,'A03',12,NULL,NULL),(1145,1,347,0,'A04',12,NULL,NULL),(1146,1,427,0,'B01',12,NULL,NULL),(1147,1,414,0,'B02',12,NULL,NULL),(1148,1,413,0,'B03',12,NULL,NULL),(1149,1,426,0,'B04',12,NULL,NULL),(1150,1,430,0,'0',0,NULL,NULL),(1151,1,431,0,'0',0,NULL,NULL); +INSERT INTO `llx_boxes` VALUES (253,2,323,0,'0',0,NULL,NULL),(304,2,324,0,'0',0,NULL,NULL),(305,2,325,0,'0',0,NULL,NULL),(306,2,326,0,'0',0,NULL,NULL),(307,2,327,0,'0',0,NULL,NULL),(308,2,328,0,'0',0,NULL,NULL),(309,2,329,0,'0',0,NULL,NULL),(310,2,330,0,'0',0,NULL,NULL),(311,2,331,0,'0',0,NULL,NULL),(312,2,332,0,'0',0,NULL,NULL),(313,2,333,0,'0',0,NULL,NULL),(314,1,347,0,'A07',0,NULL,NULL),(315,1,348,0,'A27',0,NULL,NULL),(316,1,349,0,'A15',0,NULL,NULL),(317,1,350,0,'B28',0,NULL,NULL),(344,1,374,0,'B16',0,NULL,NULL),(347,1,377,0,'A29',0,NULL,NULL),(348,1,378,0,'A17',0,NULL,NULL),(358,1,388,0,'B18',0,NULL,NULL),(359,1,389,0,'B30',0,NULL,NULL),(360,1,390,0,'A19',0,NULL,NULL),(362,1,392,0,'B20',0,NULL,NULL),(363,1,393,0,'A31',0,NULL,NULL),(366,1,396,0,'B14',0,NULL,NULL),(387,1,403,0,'B26',0,NULL,NULL),(392,1,409,0,'A13',0,NULL,NULL),(393,1,410,0,'A21',0,NULL,NULL),(394,1,411,0,'B08',0,NULL,NULL),(395,1,412,0,'B22',0,NULL,NULL),(396,1,413,0,'A09',0,NULL,NULL),(397,1,414,0,'A23',0,NULL,NULL),(398,1,415,0,'B10',0,NULL,NULL),(399,1,416,0,'B24',0,NULL,NULL),(400,1,417,0,'A11',0,NULL,NULL),(401,1,418,0,'A25',0,NULL,NULL),(501,1,419,0,'B12',0,NULL,NULL),(1019,1,392,0,'A01',2,NULL,NULL),(1021,1,412,0,'A03',2,NULL,NULL),(1022,1,347,0,'A04',2,NULL,NULL),(1023,1,393,0,'A05',2,NULL,NULL),(1025,1,389,0,'A07',2,NULL,NULL),(1026,1,416,0,'A08',2,NULL,NULL),(1027,1,396,0,'B01',2,NULL,NULL),(1028,1,377,0,'B02',2,NULL,NULL),(1031,1,419,0,'B05',2,NULL,NULL),(1036,1,424,0,'B06',0,NULL,NULL),(1037,1,425,0,'A05',0,NULL,NULL),(1038,1,426,0,'B04',0,NULL,NULL),(1039,1,427,0,'A03',0,NULL,NULL),(1150,1,430,0,'B02',0,NULL,NULL),(1151,1,431,0,'A01',0,NULL,NULL),(1152,1,429,0,'A01',1,NULL,NULL),(1153,1,429,0,'B01',2,NULL,NULL),(1154,1,429,0,'A01',11,NULL,NULL),(1156,1,429,0,'B01',0,NULL,NULL),(1174,1,412,0,'A01',12,NULL,NULL),(1175,1,392,0,'A02',12,NULL,NULL),(1176,1,377,0,'A03',12,NULL,NULL),(1177,1,347,0,'A04',12,NULL,NULL),(1178,1,429,0,'B01',12,NULL,NULL),(1179,1,427,0,'B02',12,NULL,NULL),(1180,1,414,0,'B03',12,NULL,NULL),(1181,1,413,0,'B04',12,NULL,NULL),(1182,1,426,0,'B05',12,NULL,NULL); /*!40000 ALTER TABLE `llx_boxes` ENABLE KEYS */; UNLOCK TABLES; @@ -1003,13 +1003,13 @@ DROP TABLE IF EXISTS `llx_boxes_def`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_boxes_def` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `file` varchar(200) NOT NULL, + `file` varchar(200) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `note` varchar(130) DEFAULT NULL, + `note` varchar(130) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_boxes_def` (`file`,`entity`,`note`) -) ENGINE=InnoDB AUTO_INCREMENT=432 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=432 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1032,9 +1032,9 @@ DROP TABLE IF EXISTS `llx_budget`; CREATE TABLE `llx_budget` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `label` varchar(255) NOT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `status` int(11) DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, `date_start` date DEFAULT NULL, `date_end` date DEFAULT NULL, `datec` datetime DEFAULT NULL, @@ -1043,7 +1043,7 @@ CREATE TABLE `llx_budget` ( `fk_user_modif` int(11) DEFAULT NULL, `import_key` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1065,7 +1065,7 @@ DROP TABLE IF EXISTS `llx_budget_lines`; CREATE TABLE `llx_budget_lines` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_budget` int(11) NOT NULL, - `fk_project_ids` varchar(255) NOT NULL, + `fk_project_ids` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `amount` double(24,8) NOT NULL, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -1075,7 +1075,7 @@ CREATE TABLE `llx_budget_lines` ( PRIMARY KEY (`rowid`), UNIQUE KEY `uk_budget_lines` (`fk_budget`,`fk_project_ids`), CONSTRAINT `fk_budget_lines_budget` FOREIGN KEY (`fk_budget`) REFERENCES `llx_budget` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1087,6 +1087,34 @@ LOCK TABLES `llx_budget_lines` WRITE; /*!40000 ALTER TABLE `llx_budget_lines` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_c_accountancy_category` +-- + +DROP TABLE IF EXISTS `llx_c_accountancy_category`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_c_accountancy_category` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `rangecode` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `position` int(11) DEFAULT '0', + `fk_country` int(11) DEFAULT NULL, + `active` int(11) DEFAULT '1', + PRIMARY KEY (`rowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_c_accountancy_category` +-- + +LOCK TABLES `llx_c_accountancy_category` WRITE; +/*!40000 ALTER TABLE `llx_c_accountancy_category` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_c_accountancy_category` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_c_accounting_category` -- @@ -1096,18 +1124,18 @@ DROP TABLE IF EXISTS `llx_c_accounting_category`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_accounting_category` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(16) NOT NULL, - `label` varchar(255) NOT NULL, - `range_account` varchar(255) NOT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `range_account` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `sens` tinyint(4) NOT NULL DEFAULT '0', `category_type` tinyint(4) NOT NULL DEFAULT '0', - `formula` varchar(255) NOT NULL, + `formula` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `position` int(11) DEFAULT '0', `fk_country` int(11) DEFAULT NULL, `active` int(11) DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_accounting_category` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1129,15 +1157,15 @@ DROP TABLE IF EXISTS `llx_c_action_trigger`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_action_trigger` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(32) NOT NULL, - `label` varchar(128) NOT NULL, - `description` varchar(255) DEFAULT NULL, - `elementtype` varchar(24) NOT NULL, + `code` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `elementtype` varchar(24) COLLATE utf8_unicode_ci NOT NULL, `rang` int(11) DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_action_trigger_code` (`code`), KEY `idx_action_trigger_rang` (`rang`) -) ENGINE=InnoDB AUTO_INCREMENT=216 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=225 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1146,7 +1174,7 @@ CREATE TABLE `llx_c_action_trigger` ( LOCK TABLES `llx_c_action_trigger` WRITE; /*!40000 ALTER TABLE `llx_c_action_trigger` DISABLE KEYS */; -INSERT INTO `llx_c_action_trigger` VALUES (131,'COMPANY_SENTBYMAIL','Mails sent from third party card','Executed when you send email from third party card','societe',1),(132,'COMPANY_CREATE','Third party created','Executed when a third party is created','societe',1),(133,'PROPAL_VALIDATE','Customer proposal validated','Executed when a commercial proposal is validated','propal',2),(134,'PROPAL_SENTBYMAIL','Commercial proposal sent by mail','Executed when a commercial proposal is sent by mail','propal',3),(135,'ORDER_VALIDATE','Customer order validate','Executed when a customer order is validated','commande',4),(136,'ORDER_CLOSE','Customer order classify delivered','Executed when a customer order is set delivered','commande',5),(137,'ORDER_CLASSIFY_BILLED','Customer order classify billed','Executed when a customer order is set to billed','commande',5),(138,'ORDER_CANCEL','Customer order canceled','Executed when a customer order is canceled','commande',5),(139,'ORDER_SENTBYMAIL','Customer order sent by mail','Executed when a customer order is sent by mail ','commande',5),(140,'BILL_VALIDATE','Customer invoice validated','Executed when a customer invoice is approved','facture',6),(141,'BILL_PAYED','Customer invoice payed','Executed when a customer invoice is payed','facture',7),(142,'BILL_CANCEL','Customer invoice canceled','Executed when a customer invoice is conceled','facture',8),(143,'BILL_SENTBYMAIL','Customer invoice sent by mail','Executed when a customer invoice is sent by mail','facture',9),(144,'BILL_UNVALIDATE','Customer invoice unvalidated','Executed when a customer invoice status set back to draft','facture',10),(145,'ORDER_SUPPLIER_VALIDATE','Supplier order validated','Executed when a supplier order is validated','order_supplier',11),(146,'ORDER_SUPPLIER_APPROVE','Supplier order request approved','Executed when a supplier order is approved','order_supplier',12),(147,'ORDER_SUPPLIER_REFUSE','Supplier order request refused','Executed when a supplier order is refused','order_supplier',13),(148,'ORDER_SUPPLIER_SENTBYMAIL','Supplier order sent by mail','Executed when a supplier order is sent by mail','order_supplier',14),(149,'BILL_SUPPLIER_VALIDATE','Supplier invoice validated','Executed when a supplier invoice is validated','invoice_supplier',15),(150,'BILL_SUPPLIER_PAYED','Supplier invoice payed','Executed when a supplier invoice is payed','invoice_supplier',16),(151,'BILL_SUPPLIER_SENTBYMAIL','Supplier invoice sent by mail','Executed when a supplier invoice is sent by mail','invoice_supplier',17),(152,'BILL_SUPPLIER_CANCELED','Supplier invoice cancelled','Executed when a supplier invoice is cancelled','invoice_supplier',17),(153,'CONTRACT_VALIDATE','Contract validated','Executed when a contract is validated','contrat',18),(154,'SHIPPING_VALIDATE','Shipping validated','Executed when a shipping is validated','shipping',20),(155,'SHIPPING_SENTBYMAIL','Shipping sent by mail','Executed when a shipping is sent by mail','shipping',21),(156,'MEMBER_VALIDATE','Member validated','Executed when a member is validated','member',22),(157,'MEMBER_SUBSCRIPTION','Member subscribed','Executed when a member is subscribed','member',23),(158,'MEMBER_RESILIATE','Member resiliated','Executed when a member is resiliated','member',24),(159,'MEMBER_MODIFY','Member modified','Executed when a member is modified','member',24),(160,'MEMBER_DELETE','Member deleted','Executed when a member is deleted','member',25),(161,'FICHINTER_VALIDATE','Intervention validated','Executed when a intervention is validated','ficheinter',19),(162,'FICHINTER_CLASSIFY_BILLED','Intervention set billed','Executed when a intervention is set to billed (when option FICHINTER_CLASSIFY_BILLED is set)','ficheinter',19),(163,'FICHINTER_CLASSIFY_UNBILLED','Intervention set unbilled','Executed when a intervention is set to unbilled (when option FICHINTER_CLASSIFY_BILLED is set)','ficheinter',19),(164,'FICHINTER_REOPEN','Intervention opened','Executed when a intervention is re-opened','ficheinter',19),(165,'FICHINTER_SENTBYMAIL','Intervention sent by mail','Executed when a intervention is sent by mail','ficheinter',19),(166,'PROJECT_CREATE','Project creation','Executed when a project is created','project',140),(167,'PROPAL_CLOSE_SIGNED','Customer proposal closed signed','Executed when a customer proposal is closed signed','propal',2),(168,'PROPAL_CLOSE_REFUSED','Customer proposal closed refused','Executed when a customer proposal is closed refused','propal',2),(169,'PROPAL_CLASSIFY_BILLED','Customer proposal set billed','Executed when a customer proposal is set to billed','propal',2),(170,'TASK_CREATE','Task created','Executed when a project task is created','project',35),(171,'TASK_MODIFY','Task modified','Executed when a project task is modified','project',36),(172,'TASK_DELETE','Task deleted','Executed when a project task is deleted','project',37),(173,'BILL_SUPPLIER_UNVALIDATE','Supplier invoice unvalidated','Executed when a supplier invoice status is set back to draft','invoice_supplier',15),(174,'PROJECT_MODIFY','Project modified','Executed when a project is modified','project',141),(175,'PROJECT_DELETE','Project deleted','Executed when a project is deleted','project',142),(176,'ORDER_SUPPLIER_CREATE','Supplier order validated','Executed when a supplier order is validated','order_supplier',11),(177,'ORDER_SUPPLIER_SUBMIT','Supplier order request submited','Executed when a supplier order is approved','order_supplier',12),(178,'ORDER_SUPPLIER_RECEIVE','Supplier order request received','Executed when a supplier order is received','order_supplier',12),(179,'ORDER_SUPPLIER_CLASSIFY_BILLED','Supplier order set billed','Executed when a supplier order is set as billed','order_supplier',14),(180,'PRODUCT_CREATE','Product or service created','Executed when a product or sevice is created','product',30),(181,'PRODUCT_MODIFY','Product or service modified','Executed when a product or sevice is modified','product',30),(182,'PRODUCT_DELETE','Product or service deleted','Executed when a product or sevice is deleted','product',30),(183,'EXPENSE_REPORT_CREATE','Expense report created','Executed when an expense report is created','expense_report',201),(185,'EXPENSE_REPORT_VALIDATE','Expense report validated','Executed when an expense report is validated','expense_report',202),(186,'EXPENSE_REPORT_APPROVE','Expense report approved','Executed when an expense report is approved','expense_report',203),(187,'EXPENSE_REPORT_PAYED','Expense report billed','Executed when an expense report is set as billed','expense_report',204),(192,'HOLIDAY_CREATE','Leave request created','Executed when a leave request is created','holiday',221),(193,'HOLIDAY_VALIDATE','Leave request validated','Executed when a leave request is validated','holiday',222),(194,'HOLIDAY_APPROVE','Leave request approved','Executed when a leave request is approved','holiday',223),(210,'MEMBER_SENTBYMAIL','Mails sent from member card','Executed when you send email from member card','member',23),(211,'CONTRACT_SENTBYMAIL','Contract sent by mail','Executed when a contract is sent by mail','contrat',18),(212,'PROPOSAL_SUPPLIER_VALIDATE','Price request validated','Executed when a commercial proposal is validated','proposal_supplier',10),(213,'PROPOSAL_SUPPLIER_SENTBYMAIL','Price request sent by mail','Executed when a commercial proposal is sent by mail','proposal_supplier',10),(214,'PROPOSAL_SUPPLIER_CLOSE_SIGNED','Price request closed signed','Executed when a customer proposal is closed signed','proposal_supplier',10),(215,'PROPOSAL_SUPPLIER_CLOSE_REFUSED','Price request closed refused','Executed when a customer proposal is closed refused','proposal_supplier',10); +INSERT INTO `llx_c_action_trigger` VALUES (131,'COMPANY_SENTBYMAIL','Mails sent from third party card','Executed when you send email from third party card','societe',1),(132,'COMPANY_CREATE','Third party created','Executed when a third party is created','societe',1),(133,'PROPAL_VALIDATE','Customer proposal validated','Executed when a commercial proposal is validated','propal',2),(134,'PROPAL_SENTBYMAIL','Commercial proposal sent by mail','Executed when a commercial proposal is sent by mail','propal',3),(135,'ORDER_VALIDATE','Customer order validate','Executed when a customer order is validated','commande',4),(136,'ORDER_CLOSE','Customer order classify delivered','Executed when a customer order is set delivered','commande',5),(137,'ORDER_CLASSIFY_BILLED','Customer order classify billed','Executed when a customer order is set to billed','commande',5),(138,'ORDER_CANCEL','Customer order canceled','Executed when a customer order is canceled','commande',5),(139,'ORDER_SENTBYMAIL','Customer order sent by mail','Executed when a customer order is sent by mail ','commande',5),(140,'BILL_VALIDATE','Customer invoice validated','Executed when a customer invoice is approved','facture',6),(141,'BILL_PAYED','Customer invoice payed','Executed when a customer invoice is payed','facture',7),(142,'BILL_CANCEL','Customer invoice canceled','Executed when a customer invoice is conceled','facture',8),(143,'BILL_SENTBYMAIL','Customer invoice sent by mail','Executed when a customer invoice is sent by mail','facture',9),(144,'BILL_UNVALIDATE','Customer invoice unvalidated','Executed when a customer invoice status set back to draft','facture',10),(145,'ORDER_SUPPLIER_VALIDATE','Supplier order validated','Executed when a supplier order is validated','order_supplier',11),(146,'ORDER_SUPPLIER_APPROVE','Supplier order request approved','Executed when a supplier order is approved','order_supplier',12),(147,'ORDER_SUPPLIER_REFUSE','Supplier order request refused','Executed when a supplier order is refused','order_supplier',13),(148,'ORDER_SUPPLIER_SENTBYMAIL','Supplier order sent by mail','Executed when a supplier order is sent by mail','order_supplier',14),(149,'BILL_SUPPLIER_VALIDATE','Supplier invoice validated','Executed when a supplier invoice is validated','invoice_supplier',15),(150,'BILL_SUPPLIER_PAYED','Supplier invoice payed','Executed when a supplier invoice is payed','invoice_supplier',16),(151,'BILL_SUPPLIER_SENTBYMAIL','Supplier invoice sent by mail','Executed when a supplier invoice is sent by mail','invoice_supplier',17),(152,'BILL_SUPPLIER_CANCELED','Supplier invoice cancelled','Executed when a supplier invoice is cancelled','invoice_supplier',17),(153,'CONTRACT_VALIDATE','Contract validated','Executed when a contract is validated','contrat',18),(154,'SHIPPING_VALIDATE','Shipping validated','Executed when a shipping is validated','shipping',20),(155,'SHIPPING_SENTBYMAIL','Shipping sent by mail','Executed when a shipping is sent by mail','shipping',21),(156,'MEMBER_VALIDATE','Member validated','Executed when a member is validated','member',22),(158,'MEMBER_RESILIATE','Member resiliated','Executed when a member is resiliated','member',24),(159,'MEMBER_MODIFY','Member modified','Executed when a member is modified','member',24),(160,'MEMBER_DELETE','Member deleted','Executed when a member is deleted','member',25),(161,'FICHINTER_VALIDATE','Intervention validated','Executed when a intervention is validated','ficheinter',19),(162,'FICHINTER_CLASSIFY_BILLED','Intervention set billed','Executed when a intervention is set to billed (when option FICHINTER_CLASSIFY_BILLED is set)','ficheinter',19),(163,'FICHINTER_CLASSIFY_UNBILLED','Intervention set unbilled','Executed when a intervention is set to unbilled (when option FICHINTER_CLASSIFY_BILLED is set)','ficheinter',19),(164,'FICHINTER_REOPEN','Intervention opened','Executed when a intervention is re-opened','ficheinter',19),(165,'FICHINTER_SENTBYMAIL','Intervention sent by mail','Executed when a intervention is sent by mail','ficheinter',19),(166,'PROJECT_CREATE','Project creation','Executed when a project is created','project',140),(167,'PROPAL_CLOSE_SIGNED','Customer proposal closed signed','Executed when a customer proposal is closed signed','propal',2),(168,'PROPAL_CLOSE_REFUSED','Customer proposal closed refused','Executed when a customer proposal is closed refused','propal',2),(169,'PROPAL_CLASSIFY_BILLED','Customer proposal set billed','Executed when a customer proposal is set to billed','propal',2),(170,'TASK_CREATE','Task created','Executed when a project task is created','project',35),(171,'TASK_MODIFY','Task modified','Executed when a project task is modified','project',36),(172,'TASK_DELETE','Task deleted','Executed when a project task is deleted','project',37),(173,'BILL_SUPPLIER_UNVALIDATE','Supplier invoice unvalidated','Executed when a supplier invoice status is set back to draft','invoice_supplier',15),(174,'PROJECT_MODIFY','Project modified','Executed when a project is modified','project',141),(175,'PROJECT_DELETE','Project deleted','Executed when a project is deleted','project',142),(176,'ORDER_SUPPLIER_CREATE','Supplier order validated','Executed when a supplier order is validated','order_supplier',11),(177,'ORDER_SUPPLIER_SUBMIT','Supplier order request submited','Executed when a supplier order is approved','order_supplier',12),(178,'ORDER_SUPPLIER_RECEIVE','Supplier order request received','Executed when a supplier order is received','order_supplier',12),(179,'ORDER_SUPPLIER_CLASSIFY_BILLED','Supplier order set billed','Executed when a supplier order is set as billed','order_supplier',14),(180,'PRODUCT_CREATE','Product or service created','Executed when a product or sevice is created','product',30),(181,'PRODUCT_MODIFY','Product or service modified','Executed when a product or sevice is modified','product',30),(182,'PRODUCT_DELETE','Product or service deleted','Executed when a product or sevice is deleted','product',30),(183,'EXPENSE_REPORT_CREATE','Expense report created','Executed when an expense report is created','expense_report',201),(185,'EXPENSE_REPORT_VALIDATE','Expense report validated','Executed when an expense report is validated','expense_report',202),(186,'EXPENSE_REPORT_APPROVE','Expense report approved','Executed when an expense report is approved','expense_report',203),(187,'EXPENSE_REPORT_PAYED','Expense report billed','Executed when an expense report is set as billed','expense_report',204),(192,'HOLIDAY_CREATE','Leave request created','Executed when a leave request is created','holiday',221),(193,'HOLIDAY_VALIDATE','Leave request validated','Executed when a leave request is validated','holiday',222),(194,'HOLIDAY_APPROVE','Leave request approved','Executed when a leave request is approved','holiday',223),(210,'MEMBER_SENTBYMAIL','Mails sent from member card','Executed when you send email from member card','member',23),(211,'CONTRACT_SENTBYMAIL','Contract sent by mail','Executed when a contract is sent by mail','contrat',18),(212,'PROPOSAL_SUPPLIER_VALIDATE','Price request validated','Executed when a commercial proposal is validated','proposal_supplier',10),(213,'PROPOSAL_SUPPLIER_SENTBYMAIL','Price request sent by mail','Executed when a commercial proposal is sent by mail','proposal_supplier',10),(214,'PROPOSAL_SUPPLIER_CLOSE_SIGNED','Price request closed signed','Executed when a customer proposal is closed signed','proposal_supplier',10),(215,'PROPOSAL_SUPPLIER_CLOSE_REFUSED','Price request closed refused','Executed when a customer proposal is closed refused','proposal_supplier',10),(216,'MEMBER_SUBSCRIPTION_CREATE','Member subscribtion recorded','Executed when a member subscribtion is deleted','member',24),(217,'MEMBER_SUBSCRIPTION_MODIFY','Member subscribtion modified','Executed when a member subscribtion is modified','member',24),(218,'MEMBER_SUBSCRIPTION_DELETE','Member subscribtion deleted','Executed when a member subscribtion is deleted','member',24); /*!40000 ALTER TABLE `llx_c_action_trigger` ENABLE KEYS */; UNLOCK TABLES; @@ -1159,18 +1187,18 @@ DROP TABLE IF EXISTS `llx_c_actioncomm`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_actioncomm` ( `id` int(11) NOT NULL, - `code` varchar(12) NOT NULL, - `type` varchar(50) NOT NULL DEFAULT 'system', - `libelle` varchar(48) NOT NULL, - `module` varchar(16) DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'system', + `libelle` varchar(48) COLLATE utf8_unicode_ci NOT NULL, + `module` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', `todo` tinyint(4) DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', - `color` varchar(9) DEFAULT NULL, - `picto` varchar(48) DEFAULT NULL, + `color` varchar(9) COLLATE utf8_unicode_ci DEFAULT NULL, + `picto` varchar(48) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_c_actioncomm` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1179,7 +1207,7 @@ CREATE TABLE `llx_c_actioncomm` ( LOCK TABLES `llx_c_actioncomm` WRITE; /*!40000 ALTER TABLE `llx_c_actioncomm` DISABLE KEYS */; -INSERT INTO `llx_c_actioncomm` VALUES (1,'AC_TEL','system','Phone call',NULL,1,NULL,2,NULL,NULL),(2,'AC_FAX','system','Send Fax',NULL,1,NULL,3,NULL,NULL),(4,'AC_EMAIL','system','Send Email',NULL,1,NULL,4,NULL,NULL),(5,'AC_RDV','system','Rendez-vous',NULL,1,NULL,1,NULL,NULL),(11,'AC_INT','system','Intervention on site',NULL,1,NULL,4,NULL,NULL),(40,'AC_OTH_AUTO','systemauto','Other (automatically inserted events)',NULL,1,NULL,20,NULL,NULL),(50,'AC_OTH','system','Other (manually inserted events)',NULL,1,NULL,5,NULL,NULL),(100700,'AC_CABMED','module','Send document by email','cabinetmed',0,NULL,100,NULL,NULL); +INSERT INTO `llx_c_actioncomm` VALUES (1,'AC_TEL','system','Phone call',NULL,1,NULL,2,NULL,NULL),(2,'AC_FAX','system','Send Fax',NULL,1,NULL,3,NULL,NULL),(3,'AC_PROP','systemauto','Send commercial proposal by email','propal',1,NULL,10,NULL,NULL),(4,'AC_EMAIL','system','Send Email',NULL,1,NULL,4,NULL,NULL),(5,'AC_RDV','system','Rendez-vous',NULL,1,NULL,1,NULL,NULL),(8,'AC_COM','systemauto','Send customer order by email','order',1,NULL,8,NULL,NULL),(9,'AC_FAC','systemauto','Send customer invoice by email','invoice',1,NULL,6,NULL,NULL),(10,'AC_SHIP','systemauto','Send shipping by email','shipping',1,NULL,11,NULL,NULL),(11,'AC_INT','system','Intervention on site',NULL,1,NULL,4,NULL,NULL),(30,'AC_SUP_ORD','systemauto','Send supplier order by email','order_supplier',1,NULL,9,NULL,NULL),(31,'AC_SUP_INV','systemauto','Send supplier invoice by email','invoice_supplier',1,NULL,7,NULL,NULL),(40,'AC_OTH_AUTO','systemauto','Other (automatically inserted events)',NULL,1,NULL,20,NULL,NULL),(50,'AC_OTH','system','Other (manually inserted events)',NULL,1,NULL,5,NULL,NULL),(100700,'AC_CABMED','module','Send document by email','cabinetmed',0,NULL,100,NULL,NULL); /*!40000 ALTER TABLE `llx_c_actioncomm` ENABLE KEYS */; UNLOCK TABLES; @@ -1192,12 +1220,12 @@ DROP TABLE IF EXISTS `llx_c_availability`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_availability` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(30) NOT NULL, - `label` varchar(60) NOT NULL, + `code` varchar(30) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(60) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_availability` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1219,14 +1247,14 @@ DROP TABLE IF EXISTS `llx_c_barcode_type`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_barcode_type` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(16) NOT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `libelle` varchar(50) NOT NULL, - `coder` varchar(16) NOT NULL, - `example` varchar(16) NOT NULL, + `libelle` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `coder` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `example` varchar(16) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_barcode_type` (`code`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1248,15 +1276,15 @@ DROP TABLE IF EXISTS `llx_c_chargesociales`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_chargesociales` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `libelle` varchar(80) DEFAULT NULL, + `libelle` varchar(80) COLLATE utf8_unicode_ci DEFAULT NULL, `deductible` smallint(6) NOT NULL DEFAULT '0', `active` tinyint(4) NOT NULL DEFAULT '1', - `code` varchar(12) NOT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, `fk_pays` int(11) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, - `accountancy_code` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4110 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4110 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1278,13 +1306,13 @@ DROP TABLE IF EXISTS `llx_c_civility`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_civility` ( `rowid` int(11) NOT NULL, - `code` varchar(6) NOT NULL, - `label` varchar(50) DEFAULT NULL, + `code` varchar(6) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_civility` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1306,16 +1334,16 @@ DROP TABLE IF EXISTS `llx_c_country`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_country` ( `rowid` int(11) NOT NULL, - `code` varchar(2) NOT NULL, - `code_iso` varchar(3) DEFAULT NULL, - `label` varchar(50) NOT NULL, + `code` varchar(2) COLLATE utf8_unicode_ci NOT NULL, + `code_iso` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', `favorite` tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `idx_c_country_code` (`code`), UNIQUE KEY `idx_c_country_label` (`label`), UNIQUE KEY `idx_c_country_code_iso` (`code_iso`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1336,13 +1364,13 @@ DROP TABLE IF EXISTS `llx_c_currencies`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_currencies` ( - `code_iso` varchar(3) NOT NULL, - `label` varchar(64) NOT NULL, - `unicode` varchar(32) DEFAULT NULL, + `code_iso` varchar(3) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `unicode` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`code_iso`), UNIQUE KEY `uk_c_currencies_code_iso` (`code_iso`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1364,19 +1392,19 @@ DROP TABLE IF EXISTS `llx_c_departements`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_departements` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code_departement` varchar(6) NOT NULL, + `code_departement` varchar(6) COLLATE utf8_unicode_ci NOT NULL, `fk_region` int(11) DEFAULT NULL, - `cheflieu` varchar(50) DEFAULT NULL, + `cheflieu` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `tncc` int(11) DEFAULT NULL, - `ncc` varchar(50) DEFAULT NULL, - `nom` varchar(50) DEFAULT NULL, + `ncc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `nom` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_departements` (`code_departement`,`fk_region`), KEY `idx_departements_fk_region` (`fk_region`), CONSTRAINT `fk_departements_code_region` FOREIGN KEY (`fk_region`) REFERENCES `llx_c_regions` (`code_region`), CONSTRAINT `fk_departements_fk_region` FOREIGN KEY (`fk_region`) REFERENCES `llx_c_regions` (`code_region`) -) ENGINE=InnoDB AUTO_INCREMENT=2066 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2066 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1398,15 +1426,15 @@ DROP TABLE IF EXISTS `llx_c_ecotaxe`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_ecotaxe` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(64) NOT NULL, - `libelle` varchar(255) DEFAULT NULL, + `code` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `price` double(24,8) DEFAULT NULL, - `organization` varchar(255) DEFAULT NULL, + `organization` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_pays` int(11) NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_ecotaxe` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1428,13 +1456,13 @@ DROP TABLE IF EXISTS `llx_c_effectif`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_effectif` ( `id` int(11) NOT NULL, - `code` varchar(12) NOT NULL, - `libelle` varchar(30) DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_c_effectif` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1489,25 +1517,25 @@ DROP TABLE IF EXISTS `llx_c_email_templates`; CREATE TABLE `llx_c_email_templates` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, - `type_template` varchar(32) DEFAULT NULL, - `lang` varchar(6) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `type_template` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `lang` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, `private` smallint(6) NOT NULL DEFAULT '0', `fk_user` int(11) DEFAULT NULL, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `label` varchar(255) DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `position` smallint(6) DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `topic` text, - `content` mediumtext, - `content_lines` text, - `enabled` varchar(255) DEFAULT '1', - `joinfiles` varchar(255) DEFAULT '1', + `topic` text COLLATE utf8_unicode_ci, + `content` mediumtext COLLATE utf8_unicode_ci, + `content_lines` text COLLATE utf8_unicode_ci, + `enabled` varchar(255) COLLATE utf8_unicode_ci DEFAULT '1', + `joinfiles` varchar(255) COLLATE utf8_unicode_ci DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_email_templates` (`entity`,`label`,`lang`), KEY `idx_type` (`type_template`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1583,18 +1611,18 @@ DROP TABLE IF EXISTS `llx_c_field_list`; CREATE TABLE `llx_c_field_list` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `element` varchar(64) NOT NULL, + `element` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `name` varchar(32) NOT NULL, - `alias` varchar(32) NOT NULL, - `title` varchar(32) NOT NULL, - `align` varchar(6) DEFAULT 'left', + `name` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `alias` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `title` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `align` varchar(6) COLLATE utf8_unicode_ci DEFAULT 'left', `sort` tinyint(4) NOT NULL DEFAULT '1', `search` tinyint(4) NOT NULL DEFAULT '0', - `enabled` varchar(255) DEFAULT '1', + `enabled` varchar(255) COLLATE utf8_unicode_ci DEFAULT '1', `rang` int(11) DEFAULT '0', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1616,11 +1644,11 @@ DROP TABLE IF EXISTS `llx_c_format_cards`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_format_cards` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(50) NOT NULL, - `name` varchar(50) NOT NULL, - `paper_size` varchar(20) NOT NULL, - `orientation` varchar(1) NOT NULL, - `metric` varchar(5) NOT NULL, + `code` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `name` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `paper_size` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `orientation` varchar(1) COLLATE utf8_unicode_ci NOT NULL, + `metric` varchar(5) COLLATE utf8_unicode_ci NOT NULL, `leftmargin` double(24,8) NOT NULL, `topmargin` double(24,8) NOT NULL, `nx` int(11) NOT NULL, @@ -1634,7 +1662,7 @@ CREATE TABLE `llx_c_format_cards` ( `custom_y` double(24,8) NOT NULL, `active` int(11) NOT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1658,14 +1686,14 @@ CREATE TABLE `llx_c_forme_juridique` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `code` int(11) NOT NULL, `fk_pays` int(11) NOT NULL, - `libelle` varchar(255) DEFAULT NULL, + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `isvatexempted` tinyint(4) NOT NULL DEFAULT '0', `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_forme_juridique` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=100221 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=100221 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1687,8 +1715,8 @@ DROP TABLE IF EXISTS `llx_c_holiday_types`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_holiday_types` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(16) NOT NULL, - `label` varchar(255) NOT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `affect` int(11) NOT NULL, `delay` int(11) NOT NULL, `newByMonth` double(8,5) NOT NULL DEFAULT '0.00000', @@ -1696,7 +1724,7 @@ CREATE TABLE `llx_c_holiday_types` ( `active` int(11) DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_holiday_types` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1719,11 +1747,11 @@ DROP TABLE IF EXISTS `llx_c_hrm_department`; CREATE TABLE `llx_c_hrm_department` ( `rowid` int(11) NOT NULL, `pos` tinyint(4) NOT NULL DEFAULT '0', - `code` varchar(16) NOT NULL, - `label` varchar(50) DEFAULT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1746,12 +1774,12 @@ DROP TABLE IF EXISTS `llx_c_hrm_function`; CREATE TABLE `llx_c_hrm_function` ( `rowid` int(11) NOT NULL, `pos` tinyint(4) NOT NULL DEFAULT '0', - `code` varchar(16) NOT NULL, - `label` varchar(50) DEFAULT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `c_level` tinyint(4) NOT NULL DEFAULT '0', `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1773,12 +1801,12 @@ DROP TABLE IF EXISTS `llx_c_incoterms`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_incoterms` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(3) NOT NULL, - `libelle` varchar(255) NOT NULL, + `code` varchar(3) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_incoterms` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1800,14 +1828,14 @@ DROP TABLE IF EXISTS `llx_c_input_method`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_input_method` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(30) DEFAULT NULL, - `libelle` varchar(60) DEFAULT NULL, + `code` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `libelle` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_methode_commande_fournisseur` (`code`), UNIQUE KEY `uk_c_input_method` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1829,13 +1857,13 @@ DROP TABLE IF EXISTS `llx_c_input_reason`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_input_reason` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(30) NOT NULL, - `label` varchar(60) NOT NULL, + `code` varchar(30) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(60) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_input_reason` (`code`) -) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; +) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1857,14 +1885,14 @@ DROP TABLE IF EXISTS `llx_c_lead_status`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_lead_status` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(10) DEFAULT NULL, - `label` varchar(50) DEFAULT NULL, + `code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) DEFAULT NULL, `percent` double(5,2) DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_lead_status_code` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1877,6 +1905,33 @@ INSERT INTO `llx_c_lead_status` VALUES (1,'PROSP','Prospection',10,0.00,1),(2,'Q /*!40000 ALTER TABLE `llx_c_lead_status` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_c_lead_type` +-- + +DROP TABLE IF EXISTS `llx_c_lead_type`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_c_lead_type` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(10) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `active` tinyint(4) NOT NULL DEFAULT '1', + PRIMARY KEY (`rowid`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_c_lead_type` +-- + +LOCK TABLES `llx_c_lead_type` WRITE; +/*!40000 ALTER TABLE `llx_c_lead_type` DISABLE KEYS */; +INSERT INTO `llx_c_lead_type` VALUES (1,'SUPP','Support',1),(2,'TRAIN','Formation',1),(3,'ADVI','Conseil',1); +/*!40000 ALTER TABLE `llx_c_lead_type` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_c_methode_commande_fournisseur` -- @@ -1886,12 +1941,12 @@ DROP TABLE IF EXISTS `llx_c_methode_commande_fournisseur`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_methode_commande_fournisseur` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(30) DEFAULT NULL, - `libelle` varchar(60) DEFAULT NULL, + `code` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `libelle` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_methode_commande_fournisseur` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1914,15 +1969,15 @@ DROP TABLE IF EXISTS `llx_c_paiement`; CREATE TABLE `llx_c_paiement` ( `id` int(11) NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `code` varchar(6) NOT NULL, - `libelle` varchar(62) DEFAULT NULL, + `code` varchar(6) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(62) COLLATE utf8_unicode_ci DEFAULT NULL, `type` smallint(6) DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `accountancy_code` varchar(32) DEFAULT NULL, - `module` varchar(32) DEFAULT NULL, + `accountancy_code` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', UNIQUE KEY `uk_c_paiement` (`id`,`entity`,`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1944,15 +1999,15 @@ DROP TABLE IF EXISTS `llx_c_paper_format`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_paper_format` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(16) NOT NULL, - `label` varchar(50) NOT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `width` float(6,2) DEFAULT '0.00', `height` float(6,2) DEFAULT '0.00', - `unit` varchar(5) NOT NULL, + `unit` varchar(5) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=226 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=226 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -1975,18 +2030,18 @@ DROP TABLE IF EXISTS `llx_c_payment_term`; CREATE TABLE `llx_c_payment_term` ( `rowid` int(11) NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `code` varchar(16) DEFAULT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, `sortorder` smallint(6) DEFAULT NULL, `active` tinyint(4) DEFAULT '1', - `libelle` varchar(255) DEFAULT NULL, - `libelle_facture` text, + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `libelle_facture` text COLLATE utf8_unicode_ci, `type_cdr` tinyint(4) DEFAULT NULL, `nbjour` smallint(6) DEFAULT NULL, `decalage` smallint(6) DEFAULT NULL, - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', UNIQUE KEY `uk_c_payment_term` (`rowid`,`entity`,`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2008,10 +2063,10 @@ DROP TABLE IF EXISTS `llx_c_price_expression`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_price_expression` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `title` varchar(20) NOT NULL, - `expression` varchar(80) NOT NULL, + `title` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `expression` varchar(80) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2032,11 +2087,11 @@ DROP TABLE IF EXISTS `llx_c_price_global_variable`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_price_global_variable` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(20) NOT NULL, - `description` text, + `code` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, `value` double(24,8) DEFAULT '0.00000000', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2058,14 +2113,14 @@ DROP TABLE IF EXISTS `llx_c_price_global_variable_updater`; CREATE TABLE `llx_c_price_global_variable_updater` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `type` int(11) NOT NULL, - `description` text, - `parameters` text, + `description` text COLLATE utf8_unicode_ci, + `parameters` text COLLATE utf8_unicode_ci, `fk_variable` int(11) NOT NULL, `update_interval` int(11) DEFAULT '0', `next_update` int(11) DEFAULT '0', - `last_status` text, + `last_status` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2086,12 +2141,12 @@ DROP TABLE IF EXISTS `llx_c_propalst`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_propalst` ( `id` smallint(6) NOT NULL, - `code` varchar(12) NOT NULL, - `label` varchar(30) DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`id`), UNIQUE KEY `uk_c_propalst` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2112,13 +2167,13 @@ DROP TABLE IF EXISTS `llx_c_prospectlevel`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_prospectlevel` ( - `code` varchar(12) NOT NULL, - `label` varchar(30) DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `sortorder` smallint(6) DEFAULT NULL, `active` smallint(6) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2142,16 +2197,16 @@ CREATE TABLE `llx_c_regions` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `code_region` int(11) NOT NULL, `fk_pays` int(11) NOT NULL, - `cheflieu` varchar(50) DEFAULT NULL, + `cheflieu` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `tncc` int(11) DEFAULT NULL, - `nom` varchar(50) DEFAULT NULL, + `nom` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `code_region` (`code_region`), UNIQUE KEY `uk_code_region` (`code_region`), KEY `idx_c_regions_fk_pays` (`fk_pays`), CONSTRAINT `fk_c_regions_fk_pays` FOREIGN KEY (`fk_pays`) REFERENCES `llx_c_pays` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=23346 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=23346 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2175,13 +2230,13 @@ CREATE TABLE `llx_c_revenuestamp` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_pays` int(11) NOT NULL, `taux` double NOT NULL, - `note` varchar(128) DEFAULT NULL, + `note` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `accountancy_code_sell` varchar(32) DEFAULT NULL, - `accountancy_code_buy` varchar(32) DEFAULT NULL, - `revenuestamp_type` varchar(16) NOT NULL DEFAULT 'fixed', + `accountancy_code_sell` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code_buy` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `revenuestamp_type` varchar(16) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'fixed', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2204,14 +2259,14 @@ DROP TABLE IF EXISTS `llx_c_shipment_mode`; CREATE TABLE `llx_c_shipment_mode` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `code` varchar(30) NOT NULL, - `libelle` varchar(50) NOT NULL, - `description` text, - `tracking` varchar(256) NOT NULL, + `code` varchar(30) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `tracking` varchar(256) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) DEFAULT '0', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2233,13 +2288,13 @@ DROP TABLE IF EXISTS `llx_c_stcomm`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_stcomm` ( `id` int(11) NOT NULL, - `code` varchar(12) NOT NULL, - `libelle` varchar(30) DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `picto` varchar(128) DEFAULT NULL, + `picto` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_c_stcomm` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2262,20 +2317,20 @@ DROP TABLE IF EXISTS `llx_c_tva`; CREATE TABLE `llx_c_tva` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_pays` int(11) NOT NULL, - `code` varchar(10) DEFAULT '', + `code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `taux` double NOT NULL, - `localtax1` varchar(20) DEFAULT NULL, - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', - `localtax2` varchar(20) DEFAULT NULL, - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + `localtax2` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `recuperableonly` int(11) NOT NULL DEFAULT '0', - `note` varchar(128) DEFAULT NULL, + `note` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `accountancy_code_sell` varchar(32) DEFAULT NULL, - `accountancy_code_buy` varchar(32) DEFAULT NULL, + `accountancy_code_sell` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code_buy` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_tva_id` (`fk_pays`,`code`,`taux`,`recuperableonly`) -) ENGINE=InnoDB AUTO_INCREMENT=2477 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2477 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2297,16 +2352,16 @@ DROP TABLE IF EXISTS `llx_c_type_contact`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_type_contact` ( `rowid` int(11) NOT NULL, - `element` varchar(30) NOT NULL, - `source` varchar(8) NOT NULL DEFAULT 'external', - `code` varchar(32) NOT NULL, - `libelle` varchar(64) NOT NULL, + `element` varchar(30) COLLATE utf8_unicode_ci NOT NULL, + `source` varchar(8) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'external', + `code` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_type_contact_id` (`element`,`source`,`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2328,15 +2383,15 @@ DROP TABLE IF EXISTS `llx_c_type_fees`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_type_fees` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(12) NOT NULL, - `label` varchar(30) DEFAULT NULL, - `accountancy_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `uk_c_type_fees` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2358,12 +2413,12 @@ DROP TABLE IF EXISTS `llx_c_type_resource`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_type_resource` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(32) NOT NULL, - `label` varchar(64) NOT NULL, + `code` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_type_resource_id` (`label`,`code`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2385,15 +2440,15 @@ DROP TABLE IF EXISTS `llx_c_typent`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_typent` ( `id` int(11) NOT NULL, - `code` varchar(12) NOT NULL, - `libelle` varchar(64) DEFAULT NULL, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_country` int(11) DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', - `module` varchar(32) DEFAULT NULL, + `module` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `uk_c_typent` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2415,13 +2470,13 @@ DROP TABLE IF EXISTS `llx_c_units`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_units` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(3) DEFAULT NULL, - `label` varchar(50) DEFAULT NULL, - `short_label` varchar(5) DEFAULT NULL, + `code` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `short_label` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_c_units_code` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2443,11 +2498,11 @@ DROP TABLE IF EXISTS `llx_c_ziptown`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_c_ziptown` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(5) DEFAULT NULL, + `code` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_county` int(11) DEFAULT NULL, `fk_pays` int(11) NOT NULL DEFAULT '0', - `zip` varchar(10) NOT NULL, - `town` varchar(255) NOT NULL, + `zip` varchar(10) COLLATE utf8_unicode_ci NOT NULL, + `town` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_ziptown_fk_pays` (`zip`,`town`,`fk_pays`), @@ -2456,7 +2511,7 @@ CREATE TABLE `llx_c_ziptown` ( KEY `idx_c_ziptown_zip` (`zip`), CONSTRAINT `fk_c_ziptown_fk_county` FOREIGN KEY (`fk_county`) REFERENCES `llx_c_departements` (`rowid`), CONSTRAINT `fk_c_ziptown_fk_pays` FOREIGN KEY (`fk_pays`) REFERENCES `llx_c_pays` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2478,19 +2533,19 @@ DROP TABLE IF EXISTS `llx_categorie`; CREATE TABLE `llx_categorie` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_parent` int(11) NOT NULL DEFAULT '0', - `label` varchar(255) NOT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `type` tinyint(4) NOT NULL DEFAULT '1', `entity` int(11) NOT NULL DEFAULT '1', - `description` text, + `description` text COLLATE utf8_unicode_ci, `fk_soc` int(11) DEFAULT NULL, `visible` tinyint(4) NOT NULL DEFAULT '1', - `import_key` varchar(14) DEFAULT NULL, - `color` varchar(8) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `color` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_categorie_ref` (`entity`,`fk_parent`,`label`,`type`), KEY `idx_categorie_type` (`type`), KEY `idx_categorie_label` (`label`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2513,13 +2568,13 @@ DROP TABLE IF EXISTS `llx_categorie_account`; CREATE TABLE `llx_categorie_account` ( `fk_categorie` int(11) NOT NULL, `fk_account` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_account`), KEY `idx_categorie_account_fk_categorie` (`fk_categorie`), KEY `idx_categorie_account_fk_account` (`fk_account`), CONSTRAINT `fk_categorie_account_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_account_fk_account` FOREIGN KEY (`fk_account`) REFERENCES `llx_bank_account` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2543,7 +2598,7 @@ CREATE TABLE `llx_categorie_association` ( `fk_categorie_fille` int(11) NOT NULL, UNIQUE KEY `uk_categorie_association` (`fk_categorie_mere`,`fk_categorie_fille`), UNIQUE KEY `uk_categorie_association_fk_categorie_fille` (`fk_categorie_fille`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2566,13 +2621,13 @@ DROP TABLE IF EXISTS `llx_categorie_contact`; CREATE TABLE `llx_categorie_contact` ( `fk_categorie` int(11) NOT NULL, `fk_socpeople` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_socpeople`), KEY `idx_categorie_contact_fk_categorie` (`fk_categorie`), KEY `idx_categorie_contact_fk_socpeople` (`fk_socpeople`), CONSTRAINT `fk_categorie_contact_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_contact_fk_socpeople` FOREIGN KEY (`fk_socpeople`) REFERENCES `llx_socpeople` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2595,13 +2650,13 @@ DROP TABLE IF EXISTS `llx_categorie_fournisseur`; CREATE TABLE `llx_categorie_fournisseur` ( `fk_categorie` int(11) NOT NULL, `fk_soc` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_soc`), KEY `idx_categorie_fournisseur_fk_categorie` (`fk_categorie`), KEY `idx_categorie_fournisseur_fk_societe` (`fk_soc`), CONSTRAINT `fk_categorie_fournisseur_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_fournisseur_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2610,7 +2665,7 @@ CREATE TABLE `llx_categorie_fournisseur` ( LOCK TABLES `llx_categorie_fournisseur` WRITE; /*!40000 ALTER TABLE `llx_categorie_fournisseur` DISABLE KEYS */; -INSERT INTO `llx_categorie_fournisseur` VALUES (1,2,NULL),(1,10,NULL),(9,2,NULL); +INSERT INTO `llx_categorie_fournisseur` VALUES (1,2,NULL),(1,10,NULL); /*!40000 ALTER TABLE `llx_categorie_fournisseur` ENABLE KEYS */; UNLOCK TABLES; @@ -2624,13 +2679,13 @@ DROP TABLE IF EXISTS `llx_categorie_lang`; CREATE TABLE `llx_categorie_lang` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_category` int(11) NOT NULL DEFAULT '0', - `lang` varchar(5) NOT NULL DEFAULT '0', - `label` varchar(255) NOT NULL, - `description` text, + `lang` varchar(5) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_category_lang` (`fk_category`,`lang`), CONSTRAINT `fk_category_lang_fk_category` FOREIGN KEY (`fk_category`) REFERENCES `llx_categorie` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2657,7 +2712,7 @@ CREATE TABLE `llx_categorie_member` ( KEY `idx_categorie_member_fk_member` (`fk_member`), CONSTRAINT `fk_categorie_member_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_member_member_rowid` FOREIGN KEY (`fk_member`) REFERENCES `llx_adherent` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2679,13 +2734,13 @@ DROP TABLE IF EXISTS `llx_categorie_product`; CREATE TABLE `llx_categorie_product` ( `fk_categorie` int(11) NOT NULL, `fk_product` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_product`), KEY `idx_categorie_product_fk_categorie` (`fk_categorie`), KEY `idx_categorie_product_fk_product` (`fk_product`), CONSTRAINT `fk_categorie_product_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_product_product_rowid` FOREIGN KEY (`fk_product`) REFERENCES `llx_product` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2708,14 +2763,14 @@ DROP TABLE IF EXISTS `llx_categorie_project`; CREATE TABLE `llx_categorie_project` ( `fk_categorie` int(11) NOT NULL, `fk_project` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_project`), KEY `idx_categorie_project_fk_categorie` (`fk_categorie`), KEY `idx_categorie_project_fk_project` (`fk_project`), CONSTRAINT `fk_categorie_project_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_project_fk_project` FOREIGN KEY (`fk_project`) REFERENCES `llx_projet` (`rowid`), CONSTRAINT `fk_categorie_project_fk_project_rowid` FOREIGN KEY (`fk_project`) REFERENCES `llx_projet` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2737,13 +2792,13 @@ DROP TABLE IF EXISTS `llx_categorie_societe`; CREATE TABLE `llx_categorie_societe` ( `fk_categorie` int(11) NOT NULL, `fk_soc` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_soc`), KEY `idx_categorie_societe_fk_categorie` (`fk_categorie`), KEY `idx_categorie_societe_fk_societe` (`fk_soc`), CONSTRAINT `fk_categorie_societe_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_societe_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2752,7 +2807,7 @@ CREATE TABLE `llx_categorie_societe` ( LOCK TABLES `llx_categorie_societe` WRITE; /*!40000 ALTER TABLE `llx_categorie_societe` DISABLE KEYS */; -INSERT INTO `llx_categorie_societe` VALUES (2,2,NULL),(2,19,NULL),(12,10,NULL),(12,11,NULL),(14,11,NULL); +INSERT INTO `llx_categorie_societe` VALUES (12,10,NULL),(12,11,NULL),(14,11,NULL); /*!40000 ALTER TABLE `llx_categorie_societe` ENABLE KEYS */; UNLOCK TABLES; @@ -2766,13 +2821,13 @@ DROP TABLE IF EXISTS `llx_categorie_user`; CREATE TABLE `llx_categorie_user` ( `fk_categorie` int(11) NOT NULL, `fk_user` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_categorie`,`fk_user`), KEY `idx_categorie_user_fk_categorie` (`fk_categorie`), KEY `idx_categorie_user_fk_user` (`fk_user`), CONSTRAINT `fk_categorie_user_categorie_rowid` FOREIGN KEY (`fk_categorie`) REFERENCES `llx_categorie` (`rowid`), CONSTRAINT `fk_categorie_user_fk_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2795,10 +2850,10 @@ CREATE TABLE `llx_categories_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_categories_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2820,7 +2875,7 @@ DROP TABLE IF EXISTS `llx_chargesociales`; CREATE TABLE `llx_chargesociales` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `date_ech` datetime NOT NULL, - `libelle` varchar(80) NOT NULL, + `libelle` varchar(80) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `fk_type` int(11) NOT NULL, `fk_account` int(11) DEFAULT NULL, @@ -2831,14 +2886,14 @@ CREATE TABLE `llx_chargesociales` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `date_creation` datetime DEFAULT NULL, `date_valid` datetime DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `fk_user_valid` int(11) DEFAULT NULL, - `ref` varchar(16) DEFAULT NULL, + `ref` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_projet` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2863,11 +2918,11 @@ CREATE TABLE `llx_commande` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_soc` int(11) NOT NULL, `fk_projet` int(11) DEFAULT NULL, - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(255) DEFAULT NULL, - `ref_client` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_client` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_creation` datetime DEFAULT NULL, `date_valid` datetime DEFAULT NULL, `date_cloture` datetime DEFAULT NULL, @@ -2887,12 +2942,12 @@ CREATE TABLE `llx_commande` ( `localtax2` double(24,8) DEFAULT '0.00000000', `total_ht` double(24,8) DEFAULT '0.00000000', `total_ttc` double(24,8) DEFAULT '0.00000000', - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `facture` tinyint(4) DEFAULT '0', `fk_account` int(11) DEFAULT NULL, - `fk_currency` varchar(3) DEFAULT NULL, + `fk_currency` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_cond_reglement` int(11) DEFAULT NULL, `fk_mode_reglement` int(11) DEFAULT NULL, `date_livraison` date DEFAULT NULL, @@ -2901,17 +2956,17 @@ CREATE TABLE `llx_commande` ( `fk_availability` int(11) DEFAULT NULL, `fk_input_reason` int(11) DEFAULT NULL, `fk_delivery_address` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_commande_ref` (`ref`,`entity`), KEY `idx_commande_fk_soc` (`fk_soc`), @@ -2926,7 +2981,7 @@ CREATE TABLE `llx_commande` ( CONSTRAINT `fk_commande_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_commande_fk_user_cloture` FOREIGN KEY (`fk_user_cloture`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_commande_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=93 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=93 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2950,10 +3005,10 @@ CREATE TABLE `llx_commande_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_commande_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -2976,10 +3031,10 @@ CREATE TABLE `llx_commande_fournisseur` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_soc` int(11) NOT NULL, - `ref` varchar(255) DEFAULT NULL, + `ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `ref_supplier` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_supplier` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_projet` int(11) DEFAULT '0', `date_creation` datetime DEFAULT NULL, `date_valid` datetime DEFAULT NULL, @@ -3002,31 +3057,31 @@ CREATE TABLE `llx_commande_fournisseur` ( `localtax2` double(24,8) DEFAULT '0.00000000', `total_ht` double(24,8) DEFAULT '0.00000000', `total_ttc` double(24,8) DEFAULT '0.00000000', - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_input_method` int(11) DEFAULT '0', `fk_cond_reglement` int(11) DEFAULT '0', `fk_mode_reglement` int(11) DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_livraison` datetime DEFAULT NULL, `fk_account` int(11) DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_commande_fournisseur_ref` (`ref`,`fk_soc`,`entity`), KEY `idx_commande_fournisseur_fk_soc` (`fk_soc`), KEY `billed` (`billed`), CONSTRAINT `fk_commande_fournisseur_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3055,15 +3110,15 @@ CREATE TABLE `llx_commande_fournisseur_dispatch` ( `fk_entrepot` int(11) DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, `datec` datetime DEFAULT NULL, - `comment` varchar(255) DEFAULT NULL, + `comment` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `status` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `batch` varchar(30) DEFAULT NULL, + `batch` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `eatby` date DEFAULT NULL, `sellby` date DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_commande_fournisseur_dispatch_fk_commande` (`fk_commande`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3086,10 +3141,10 @@ CREATE TABLE `llx_commande_fournisseur_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_commande_fournisseur_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3115,9 +3170,9 @@ CREATE TABLE `llx_commande_fournisseur_log` ( `fk_commande` int(11) NOT NULL, `fk_statut` smallint(6) NOT NULL, `fk_user` int(11) NOT NULL, - `comment` varchar(255) DEFAULT NULL, + `comment` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3142,15 +3197,15 @@ CREATE TABLE `llx_commande_fournisseurdet` ( `fk_commande` int(11) NOT NULL, `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `ref` varchar(50) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `description` text, + `ref` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `tva_tx` double(6,3) DEFAULT '0.000', - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `remise` double DEFAULT '0', @@ -3164,12 +3219,12 @@ CREATE TABLE `llx_commande_fournisseurdet` ( `date_start` datetime DEFAULT NULL, `date_end` datetime DEFAULT NULL, `info_bits` int(11) DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `special_code` int(11) DEFAULT '0', `rang` int(11) DEFAULT '0', `fk_unit` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -3177,7 +3232,7 @@ CREATE TABLE `llx_commande_fournisseurdet` ( PRIMARY KEY (`rowid`), KEY `fk_commande_fournisseurdet_fk_unit` (`fk_unit`), CONSTRAINT `fk_commande_fournisseurdet_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3201,10 +3256,10 @@ CREATE TABLE `llx_commande_fournisseurdet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_commande_fournisseurdet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3228,14 +3283,14 @@ CREATE TABLE `llx_commandedet` ( `fk_commande` int(11) DEFAULT NULL, `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `description` text, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `tva_tx` double(6,3) DEFAULT NULL, - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT NULL, - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT NULL, - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `remise` double DEFAULT '0', @@ -3255,11 +3310,11 @@ CREATE TABLE `llx_commandedet` ( `buy_price_ht` double(24,8) DEFAULT '0.00000000', `special_code` int(10) unsigned DEFAULT '0', `rang` int(11) DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_commandefourndet` int(11) DEFAULT NULL, `fk_unit` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -3270,7 +3325,7 @@ CREATE TABLE `llx_commandedet` ( KEY `fk_commandedet_fk_unit` (`fk_unit`), CONSTRAINT `fk_commandedet_fk_commande` FOREIGN KEY (`fk_commande`) REFERENCES `llx_commande` (`rowid`), CONSTRAINT `fk_commandedet_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=292 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=292 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3294,10 +3349,10 @@ CREATE TABLE `llx_commandedet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_commandedet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3339,6 +3394,37 @@ LOCK TABLES `llx_comment` WRITE; /*!40000 ALTER TABLE `llx_comment` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_cond_reglement` +-- + +DROP TABLE IF EXISTS `llx_cond_reglement`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_cond_reglement` ( + `rowid` int(11) NOT NULL, + `code` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + `sortorder` smallint(6) DEFAULT NULL, + `active` tinyint(4) DEFAULT '1', + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `libelle_facture` text COLLATE utf8_unicode_ci, + `fdm` tinyint(4) DEFAULT NULL, + `nbjour` smallint(6) DEFAULT NULL, + `decalage` smallint(6) DEFAULT NULL, + PRIMARY KEY (`rowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_cond_reglement` +-- + +LOCK TABLES `llx_cond_reglement` WRITE; +/*!40000 ALTER TABLE `llx_cond_reglement` DISABLE KEYS */; +INSERT INTO `llx_cond_reglement` VALUES (1,'RECEP',1,1,'A réception','Réception de facture',0,0,NULL),(2,'30D',2,1,'30 jours','Réglement à 30 jours',0,30,NULL),(3,'30DENDMONTH',3,1,'30 jours fin de mois','Réglement à 30 jours fin de mois',1,30,NULL),(4,'60D',4,1,'60 jours','Réglement à 60 jours',0,60,NULL),(5,'60DENDMONTH',5,1,'60 jours fin de mois','Réglement à 60 jours fin de mois',1,60,NULL); +/*!40000 ALTER TABLE `llx_cond_reglement` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_const` -- @@ -3348,16 +3434,16 @@ DROP TABLE IF EXISTS `llx_const`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_const` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `value` text NOT NULL, - `type` varchar(6) DEFAULT NULL, + `value` text COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, `visible` tinyint(4) NOT NULL DEFAULT '1', - `note` text, + `note` text COLLATE utf8_unicode_ci, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_const` (`name`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=6570 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6588 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3366,7 +3452,7 @@ CREATE TABLE `llx_const` ( LOCK TABLES `llx_const` WRITE; /*!40000 ALTER TABLE `llx_const` DISABLE KEYS */; -INSERT INTO `llx_const` VALUES (8,'MAIN_UPLOAD_DOC',0,'2048','chaine',0,'Max size for file upload (0 means no upload allowed)','2010-07-08 11:17:57'),(9,'MAIN_SEARCHFORM_SOCIETE',0,'1','yesno',0,'Show form for quick company search','2010-07-08 11:17:57'),(10,'MAIN_SEARCHFORM_CONTACT',0,'1','yesno',0,'Show form for quick contact search','2010-07-08 11:17:57'),(11,'MAIN_SEARCHFORM_PRODUITSERVICE',0,'1','yesno',0,'Show form for quick product search','2010-07-08 11:17:58'),(12,'MAIN_SEARCHFORM_ADHERENT',0,'1','yesno',0,'Show form for quick member search','2010-07-08 11:17:58'),(16,'MAIN_SIZE_LISTE_LIMIT',0,'25','chaine',0,'Longueur maximum des listes','2010-07-08 11:17:58'),(29,'MAIN_DELAY_NOT_ACTIVATED_SERVICES',1,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services à activer','2010-07-08 11:17:58'),(33,'SOCIETE_NOLIST_COURRIER',0,'1','yesno',0,'Liste les fichiers du repertoire courrier','2010-07-08 11:17:58'),(36,'ADHERENT_MAIL_REQUIRED',1,'1','yesno',0,'EMail required to create a new member','2010-07-08 11:17:58'),(37,'ADHERENT_MAIL_FROM',1,'adherents@domain.com','chaine',0,'Sender EMail for automatic emails','2010-07-08 11:17:58'),(38,'ADHERENT_MAIL_RESIL',1,'Your subscription has been resiliated.\r\nWe hope to see you soon again','texte',0,'Mail resiliation','2010-07-08 11:17:58'),(39,'ADHERENT_MAIL_VALID',1,'Your subscription has been validated.\r\nThis is a remind of your personal information :\r\n\r\n%INFOS%\r\n\r\n','texte',0,'Mail de validation','2010-07-08 11:17:59'),(40,'ADHERENT_MAIL_COTIS',1,'Hello %PRENOM%,\r\nThanks for your subscription.\r\nThis email confirms that your subscription has been received and processed.\r\n\r\n','texte',0,'Mail de validation de cotisation','2010-07-08 11:17:59'),(41,'ADHERENT_MAIL_VALID_SUBJECT',1,'Your subscription has been validated','chaine',0,'Sujet du mail de validation','2010-07-08 11:17:59'),(42,'ADHERENT_MAIL_RESIL_SUBJECT',1,'Resiliating your subscription','chaine',0,'Sujet du mail de resiliation','2010-07-08 11:17:59'),(43,'ADHERENT_MAIL_COTIS_SUBJECT',1,'Receipt of your subscription','chaine',0,'Sujet du mail de validation de cotisation','2010-07-08 11:17:59'),(44,'MAILING_EMAIL_FROM',1,'dolibarr@domain.com','chaine',0,'EMail emmetteur pour les envois d emailings','2010-07-08 11:17:59'),(45,'ADHERENT_USE_MAILMAN',1,'0','yesno',0,'Utilisation de Mailman','2010-07-08 11:17:59'),(46,'ADHERENT_MAILMAN_UNSUB_URL',1,'http://lists.domain.com/cgi-bin/mailman/admin/%LISTE%/members?adminpw=%MAILMAN_ADMINPW%&user=%EMAIL%','chaine',0,'Url de desinscription aux listes mailman','2010-07-08 11:17:59'),(47,'ADHERENT_MAILMAN_URL',1,'http://lists.domain.com/cgi-bin/mailman/admin/%LISTE%/members?adminpw=%MAILMAN_ADMINPW%&send_welcome_msg_to_this_batch=1&subscribees=%EMAIL%','chaine',0,'Url pour les inscriptions mailman','2010-07-08 11:17:59'),(48,'ADHERENT_MAILMAN_LISTS',1,'test-test,test-test2','chaine',0,'Listes auxquelles inscrire les nouveaux adherents','2010-07-08 11:17:59'),(49,'ADHERENT_MAILMAN_ADMINPW',1,'','chaine',0,'Mot de passe Admin des liste mailman','2010-07-08 11:17:59'),(50,'ADHERENT_MAILMAN_SERVER',1,'lists.domain.com','chaine',0,'Serveur hebergeant les interfaces d Admin des listes mailman','2010-07-08 11:17:59'),(51,'ADHERENT_MAILMAN_LISTS_COTISANT',1,'','chaine',0,'Liste(s) auxquelles les nouveaux cotisants sont inscris automatiquement','2010-07-08 11:17:59'),(52,'ADHERENT_USE_SPIP',1,'0','yesno',0,'Utilisation de SPIP ?','2010-07-08 11:17:59'),(53,'ADHERENT_USE_SPIP_AUTO',1,'0','yesno',0,'Utilisation de SPIP automatiquement','2010-07-08 11:17:59'),(54,'ADHERENT_SPIP_USER',1,'user','chaine',0,'user spip','2010-07-08 11:17:59'),(55,'ADHERENT_SPIP_PASS',1,'pass','chaine',0,'Pass de connection','2010-07-08 11:17:59'),(56,'ADHERENT_SPIP_SERVEUR',1,'localhost','chaine',0,'serveur spip','2010-07-08 11:17:59'),(57,'ADHERENT_SPIP_DB',1,'spip','chaine',0,'db spip','2010-07-08 11:17:59'),(58,'ADHERENT_CARD_HEADER_TEXT',1,'%ANNEE%','chaine',0,'Texte imprime sur le haut de la carte adherent','2010-07-08 11:17:59'),(59,'ADHERENT_CARD_FOOTER_TEXT',1,'Association AZERTY','chaine',0,'Texte imprime sur le bas de la carte adherent','2010-07-08 11:17:59'),(61,'FCKEDITOR_ENABLE_USER',1,'1','yesno',0,'Activation fckeditor sur notes utilisateurs','2010-07-08 11:17:59'),(62,'FCKEDITOR_ENABLE_SOCIETE',1,'1','yesno',0,'Activation fckeditor sur notes societe','2010-07-08 11:17:59'),(63,'FCKEDITOR_ENABLE_PRODUCTDESC',1,'1','yesno',0,'Activation fckeditor sur notes produits','2010-07-08 11:17:59'),(64,'FCKEDITOR_ENABLE_MEMBER',1,'1','yesno',0,'Activation fckeditor sur notes adherent','2010-07-08 11:17:59'),(65,'FCKEDITOR_ENABLE_MAILING',1,'1','yesno',0,'Activation fckeditor sur emailing','2010-07-08 11:17:59'),(67,'DON_ADDON_MODEL',1,'html_cerfafr','chaine',0,'','2010-07-08 11:18:00'),(68,'PROPALE_ADDON',1,'mod_propale_marbre','chaine',0,'','2010-07-08 11:18:00'),(69,'PROPALE_ADDON_PDF',1,'azur','chaine',0,'','2010-07-08 11:18:00'),(70,'COMMANDE_ADDON',1,'mod_commande_marbre','chaine',0,'','2010-07-08 11:18:00'),(71,'COMMANDE_ADDON_PDF',1,'einstein','chaine',0,'','2010-07-08 11:18:00'),(72,'COMMANDE_SUPPLIER_ADDON',1,'mod_commande_fournisseur_muguet','chaine',0,'','2010-07-08 11:18:00'),(73,'COMMANDE_SUPPLIER_ADDON_PDF',1,'muscadet','chaine',0,'','2010-07-08 11:18:00'),(74,'EXPEDITION_ADDON',1,'enlevement','chaine',0,'','2010-07-08 11:18:00'),(76,'FICHEINTER_ADDON',1,'pacific','chaine',0,'','2010-07-08 11:18:00'),(77,'FICHEINTER_ADDON_PDF',1,'soleil','chaine',0,'','2010-07-08 11:18:00'),(79,'FACTURE_ADDON_PDF',1,'crabe','chaine',0,'','2010-07-08 11:18:00'),(80,'PROPALE_VALIDITY_DURATION',1,'15','chaine',0,'Durée de validitée des propales','2010-07-08 11:18:00'),(230,'COMPANY_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/thirdparties','chaine',0,NULL,'2010-07-08 11:26:20'),(238,'LIVRAISON_ADDON_PDF',1,'typhon','chaine',0,'Nom du gestionnaire de generation des commandes en PDF','2010-07-08 11:26:27'),(239,'LIVRAISON_ADDON_NUMBER',1,'mod_livraison_jade','chaine',0,'Nom du gestionnaire de numerotation des bons de livraison','2013-03-20 13:17:36'),(245,'FACTURE_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/invoices','chaine',0,NULL,'2010-07-08 11:28:53'),(249,'DON_FORM',1,'html_cerfafr','chaine',0,'Nom du gestionnaire de formulaire de dons','2017-09-06 16:12:22'),(254,'ADHERENT_BANK_ACCOUNT',1,'','chaine',0,'ID du Compte banquaire utilise','2010-07-08 11:29:05'),(255,'ADHERENT_BANK_CATEGORIE',1,'','chaine',0,'ID de la categorie banquaire des cotisations','2010-07-08 11:29:05'),(256,'ADHERENT_ETIQUETTE_TYPE',1,'L7163','chaine',0,'Type d etiquette (pour impression de planche d etiquette)','2010-07-08 11:29:05'),(269,'PROJECT_ADDON_PDF',1,'baleine','chaine',0,'Nom du gestionnaire de generation des projets en PDF','2010-07-08 11:29:33'),(270,'PROJECT_ADDON',1,'mod_project_simple','chaine',0,'Nom du gestionnaire de numerotation des projets','2010-07-08 11:29:33'),(368,'STOCK_USERSTOCK_AUTOCREATE',1,'1','chaine',0,'','2010-07-08 22:44:59'),(369,'EXPEDITION_ADDON_PDF',1,'merou','chaine',0,'','2010-07-08 22:58:07'),(377,'FACTURE_ADDON',1,'mod_facture_terre','chaine',0,'','2010-07-08 23:08:12'),(380,'ADHERENT_CARD_TEXT',1,'%TYPE% n° %ID%\r\n%PRENOM% %NOM%\r\n<%EMAIL%>\r\n%ADRESSE%\r\n%CP% %VILLE%\r\n%PAYS%','',0,'Texte imprime sur la carte adherent','2010-07-08 23:14:46'),(381,'ADHERENT_CARD_TEXT_RIGHT',1,'aaa','',0,'','2010-07-08 23:14:55'),(385,'PRODUIT_USE_SEARCH_TO_SELECT',1,'1','chaine',0,'','2010-07-08 23:22:19'),(386,'STOCK_CALCULATE_ON_SHIPMENT',1,'1','chaine',0,'','2010-07-08 23:23:21'),(387,'STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER',1,'1','chaine',0,'','2010-07-08 23:23:26'),(392,'MAIN_AGENDA_XCAL_EXPORTKEY',1,'dolibarr','chaine',0,'','2010-07-08 23:27:50'),(393,'MAIN_AGENDA_EXPORT_PAST_DELAY',1,'100','chaine',0,'','2010-07-08 23:27:50'),(610,'CASHDESK_ID_THIRDPARTY',1,'7','chaine',0,'','2010-07-11 17:08:18'),(611,'CASHDESK_ID_BANKACCOUNT_CASH',1,'3','chaine',0,'','2010-07-11 17:08:18'),(612,'CASHDESK_ID_BANKACCOUNT_CHEQUE',1,'1','chaine',0,'','2010-07-11 17:08:18'),(613,'CASHDESK_ID_BANKACCOUNT_CB',1,'1','chaine',0,'','2010-07-11 17:08:18'),(614,'CASHDESK_ID_WAREHOUSE',1,'2','chaine',0,'','2010-07-11 17:08:18'),(660,'LDAP_USER_DN',1,'ou=users,dc=my-domain,dc=com','chaine',0,NULL,'2010-07-18 10:25:27'),(661,'LDAP_GROUP_DN',1,'ou=groups,dc=my-domain,dc=com','chaine',0,NULL,'2010-07-18 10:25:27'),(662,'LDAP_FILTER_CONNECTION',1,'&(objectClass=user)(objectCategory=person)','chaine',0,NULL,'2010-07-18 10:25:27'),(663,'LDAP_FIELD_LOGIN',1,'uid','chaine',0,NULL,'2010-07-18 10:25:27'),(664,'LDAP_FIELD_FULLNAME',1,'cn','chaine',0,NULL,'2010-07-18 10:25:27'),(665,'LDAP_FIELD_NAME',1,'sn','chaine',0,NULL,'2010-07-18 10:25:27'),(666,'LDAP_FIELD_FIRSTNAME',1,'givenname','chaine',0,NULL,'2010-07-18 10:25:27'),(667,'LDAP_FIELD_MAIL',1,'mail','chaine',0,NULL,'2010-07-18 10:25:27'),(668,'LDAP_FIELD_PHONE',1,'telephonenumber','chaine',0,NULL,'2010-07-18 10:25:27'),(669,'LDAP_FIELD_FAX',1,'facsimiletelephonenumber','chaine',0,NULL,'2010-07-18 10:25:27'),(670,'LDAP_FIELD_MOBILE',1,'mobile','chaine',0,NULL,'2010-07-18 10:25:27'),(671,'LDAP_SERVER_TYPE',1,'openldap','chaine',0,'','2010-07-18 10:25:46'),(672,'LDAP_SERVER_PROTOCOLVERSION',1,'3','chaine',0,'','2010-07-18 10:25:47'),(673,'LDAP_SERVER_HOST',1,'localhost','chaine',0,'','2010-07-18 10:25:47'),(674,'LDAP_SERVER_PORT',1,'389','chaine',0,'','2010-07-18 10:25:47'),(675,'LDAP_SERVER_USE_TLS',1,'0','chaine',0,'','2010-07-18 10:25:47'),(676,'LDAP_SYNCHRO_ACTIVE',1,'dolibarr2ldap','chaine',0,'','2010-07-18 10:25:47'),(677,'LDAP_CONTACT_ACTIVE',1,'1','chaine',0,'','2010-07-18 10:25:47'),(678,'LDAP_MEMBER_ACTIVE',1,'1','chaine',0,'','2010-07-18 10:25:47'),(974,'MAIN_MODULE_WORKFLOW_TRIGGERS',1,'1','chaine',0,NULL,'2011-07-18 18:02:20'),(975,'WORKFLOW_PROPAL_AUTOCREATE_ORDER',1,'1','chaine',0,'','2011-07-18 18:02:24'),(980,'PRELEVEMENT_NUMERO_NATIONAL_EMETTEUR',1,'1234567','chaine',0,'','2011-07-18 18:05:50'),(983,'FACTURE_RIB_NUMBER',1,'1','chaine',0,'','2011-07-18 18:35:14'),(984,'FACTURE_CHQ_NUMBER',1,'1','chaine',0,'','2011-07-18 18:35:14'),(1016,'GOOGLE_DUPLICATE_INTO_GCAL',1,'1','chaine',0,'','2011-07-18 21:40:20'),(1152,'SOCIETE_CODECLIENT_ADDON',1,'mod_codeclient_monkey','chaine',0,'','2011-07-29 20:50:02'),(1231,'MAIN_UPLOAD_DOC',1,'2048','chaine',0,'','2011-07-29 21:04:00'),(1234,'MAIN_UMASK',1,'0664','chaine',0,'','2011-07-29 21:04:11'),(1240,'MAIN_LOGEVENTS_USER_LOGIN',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1241,'MAIN_LOGEVENTS_USER_LOGIN_FAILED',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1242,'MAIN_LOGEVENTS_USER_LOGOUT',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1243,'MAIN_LOGEVENTS_USER_CREATE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1244,'MAIN_LOGEVENTS_USER_MODIFY',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1245,'MAIN_LOGEVENTS_USER_NEW_PASSWORD',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1246,'MAIN_LOGEVENTS_USER_ENABLEDISABLE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1247,'MAIN_LOGEVENTS_USER_DELETE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1248,'MAIN_LOGEVENTS_GROUP_CREATE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1249,'MAIN_LOGEVENTS_GROUP_MODIFY',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1250,'MAIN_LOGEVENTS_GROUP_DELETE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1251,'MAIN_BOXES_MAXLINES',1,'5','',0,'','2011-07-29 21:05:42'),(1482,'EXPEDITION_ADDON_NUMBER',1,'mod_expedition_safor','chaine',0,'Nom du gestionnaire de numerotation des expeditions','2011-08-05 17:53:11'),(1490,'CONTRACT_ADDON',1,'mod_contract_serpis','chaine',0,'Nom du gestionnaire de numerotation des contrats','2011-08-05 18:11:58'),(1677,'COMMANDE_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/orders','chaine',0,NULL,'2012-12-08 13:11:02'),(1698,'PRODUCT_CODEPRODUCT_ADDON',1,'mod_codeproduct_leopard','yesno',0,'Module to control product codes','2012-12-08 13:11:25'),(1724,'PROPALE_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/proposals','chaine',0,NULL,'2012-12-08 13:17:14'),(1730,'OPENSTREETMAP_ENABLE_MAPS',1,'1','chaine',0,'','2012-12-08 13:22:47'),(1731,'OPENSTREETMAP_ENABLE_MAPS_CONTACTS',1,'1','chaine',0,'','2012-12-08 13:22:47'),(1732,'OPENSTREETMAP_ENABLE_MAPS_MEMBERS',1,'1','chaine',0,'','2012-12-08 13:22:47'),(1733,'OPENSTREETMAP_MAPS_ZOOM_LEVEL',1,'15','chaine',0,'','2012-12-08 13:22:47'),(1742,'MAIN_MAIL_EMAIL_FROM',2,'dolibarr-robot@domain.com','chaine',0,'EMail emetteur pour les emails automatiques Dolibarr','2012-12-08 14:08:14'),(1743,'MAIN_MENU_STANDARD',2,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs internes','2013-02-11 19:43:54'),(1744,'MAIN_MENUFRONT_STANDARD',2,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs externes','2013-02-11 19:43:54'),(1745,'MAIN_MENU_SMARTPHONE',2,'iphone_backoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs internes','2012-12-08 14:08:14'),(1746,'MAIN_MENUFRONT_SMARTPHONE',2,'iphone_frontoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs externes','2012-12-08 14:08:14'),(1747,'MAIN_THEME',2,'eldy','chaine',0,'Default theme','2012-12-08 14:08:14'),(1748,'MAIN_DELAY_ACTIONS_TODO',2,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur actions planifiées non réalisées','2012-12-08 14:08:14'),(1749,'MAIN_DELAY_ORDERS_TO_PROCESS',2,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes clients non traitées','2012-12-08 14:08:14'),(1750,'MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS',2,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes fournisseurs non traitées','2012-12-08 14:08:14'),(1751,'MAIN_DELAY_PROPALS_TO_CLOSE',2,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales à cloturer','2012-12-08 14:08:14'),(1752,'MAIN_DELAY_PROPALS_TO_BILL',2,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales non facturées','2012-12-08 14:08:14'),(1753,'MAIN_DELAY_CUSTOMER_BILLS_UNPAYED',2,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures client impayées','2012-12-08 14:08:14'),(1754,'MAIN_DELAY_SUPPLIER_BILLS_TO_PAY',2,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures fournisseur impayées','2012-12-08 14:08:14'),(1755,'MAIN_DELAY_NOT_ACTIVATED_SERVICES',2,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services à activer','2012-12-08 14:08:14'),(1756,'MAIN_DELAY_RUNNING_SERVICES',2,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services expirés','2012-12-08 14:08:14'),(1757,'MAIN_DELAY_MEMBERS',2,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur cotisations adhérent en retard','2012-12-08 14:08:14'),(1758,'MAIN_DELAY_TRANSACTIONS_TO_CONCILIATE',2,'62','chaine',0,'Tolérance de retard avant alerte (en jours) sur rapprochements bancaires à faire','2012-12-08 14:08:14'),(1759,'MAILING_EMAIL_FROM',2,'dolibarr@domain.com','chaine',0,'EMail emmetteur pour les envois d emailings','2012-12-08 14:08:14'),(1760,'MAIN_INFO_SOCIETE_COUNTRY',3,'1:FR:France','chaine',0,'','2013-02-26 21:56:28'),(1761,'MAIN_INFO_SOCIETE_NOM',3,'bbb','chaine',0,'','2012-12-08 14:08:20'),(1762,'MAIN_INFO_SOCIETE_STATE',3,'0','chaine',0,'','2013-02-27 14:20:27'),(1763,'MAIN_MONNAIE',3,'EUR','chaine',0,'','2012-12-08 14:08:20'),(1764,'MAIN_LANG_DEFAULT',3,'auto','chaine',0,'','2012-12-08 14:08:20'),(1765,'MAIN_MAIL_EMAIL_FROM',3,'dolibarr-robot@domain.com','chaine',0,'EMail emetteur pour les emails automatiques Dolibarr','2012-12-08 14:08:20'),(1766,'MAIN_MENU_STANDARD',3,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs internes','2013-02-11 19:43:54'),(1767,'MAIN_MENUFRONT_STANDARD',3,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs externes','2013-02-11 19:43:54'),(1768,'MAIN_MENU_SMARTPHONE',3,'iphone_backoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs internes','2012-12-08 14:08:20'),(1769,'MAIN_MENUFRONT_SMARTPHONE',3,'iphone_frontoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs externes','2012-12-08 14:08:20'),(1770,'MAIN_THEME',3,'eldy','chaine',0,'Default theme','2012-12-08 14:08:20'),(1771,'MAIN_DELAY_ACTIONS_TODO',3,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur actions planifiées non réalisées','2012-12-08 14:08:20'),(1772,'MAIN_DELAY_ORDERS_TO_PROCESS',3,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes clients non traitées','2012-12-08 14:08:20'),(1773,'MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS',3,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes fournisseurs non traitées','2012-12-08 14:08:20'),(1774,'MAIN_DELAY_PROPALS_TO_CLOSE',3,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales à cloturer','2012-12-08 14:08:20'),(1775,'MAIN_DELAY_PROPALS_TO_BILL',3,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales non facturées','2012-12-08 14:08:20'),(1776,'MAIN_DELAY_CUSTOMER_BILLS_UNPAYED',3,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures client impayées','2012-12-08 14:08:20'),(1777,'MAIN_DELAY_SUPPLIER_BILLS_TO_PAY',3,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures fournisseur impayées','2012-12-08 14:08:20'),(1778,'MAIN_DELAY_NOT_ACTIVATED_SERVICES',3,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services à activer','2012-12-08 14:08:20'),(1779,'MAIN_DELAY_RUNNING_SERVICES',3,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services expirés','2012-12-08 14:08:20'),(1780,'MAIN_DELAY_MEMBERS',3,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur cotisations adhérent en retard','2012-12-08 14:08:20'),(1781,'MAIN_DELAY_TRANSACTIONS_TO_CONCILIATE',3,'62','chaine',0,'Tolérance de retard avant alerte (en jours) sur rapprochements bancaires à faire','2012-12-08 14:08:20'),(1782,'MAILING_EMAIL_FROM',3,'dolibarr@domain.com','chaine',0,'EMail emmetteur pour les envois d emailings','2012-12-08 14:08:20'),(1803,'SYSLOG_FILE',1,'DOL_DATA_ROOT/dolibarr.log','chaine',0,'','2012-12-08 14:15:08'),(1804,'SYSLOG_HANDLERS',1,'[\"mod_syslog_file\"]','chaine',0,'','2012-12-08 14:15:08'),(1805,'MAIN_MODULE_SKINCOLOREDITOR',3,'1',NULL,0,NULL,'2012-12-08 14:35:40'),(1806,'MAIN_MODULE_SKINCOLOREDITOR_TABS_0',3,'user:+tabskincoloreditors:ColorEditor:skincoloreditor@skincoloreditor:/skincoloreditor/usercolors.php?id=__ID__','chaine',0,NULL,'2012-12-08 14:35:40'),(1922,'PAYPAL_API_SANDBOX',1,'1','chaine',0,'','2012-12-12 12:11:05'),(1923,'PAYPAL_API_USER',1,'seller_1355312017_biz_api1.nltechno.com','chaine',0,'','2012-12-12 12:11:05'),(1924,'PAYPAL_API_PASSWORD',1,'1355312040','chaine',0,'','2012-12-12 12:11:05'),(1925,'PAYPAL_API_SIGNATURE',1,'AXqqdsWBzvfn0q5iNmbuiDv1y.3EAXIMWyl4C5KvDReR9HDwwAd6dQ4Q','chaine',0,'','2012-12-12 12:11:05'),(1926,'PAYPAL_API_INTEGRAL_OR_PAYPALONLY',1,'integral','chaine',0,'','2012-12-12 12:11:05'),(1927,'PAYPAL_SECURITY_TOKEN',1,'50c82fab36bb3b6aa83e2a50691803b2','chaine',0,'','2012-12-12 12:11:05'),(1928,'PAYPAL_SECURITY_TOKEN_UNIQUE',1,'0','chaine',0,'','2012-12-12 12:11:05'),(1929,'PAYPAL_ADD_PAYMENT_URL',1,'1','chaine',0,'','2012-12-12 12:11:05'),(1980,'MAIN_PDF_FORMAT',1,'EUA4','chaine',0,'','2012-12-12 19:58:05'),(1981,'MAIN_PROFID1_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1982,'MAIN_PROFID2_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1983,'MAIN_PROFID3_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1984,'MAIN_PROFID4_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1985,'MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT',1,'0','chaine',0,'','2012-12-12 19:58:05'),(2251,'FCKEDITOR_TEST',1,'Test
\r\n\"\"fdfs','chaine',0,'','2012-12-19 19:12:24'),(2293,'SYSTEMTOOLS_MYSQLDUMP',1,'/usr/bin/mysqldump','chaine',0,'','2012-12-27 02:02:00'),(2835,'MAIN_USE_CONNECT_TIMEOUT',1,'10','chaine',0,'','2013-01-16 19:28:50'),(2836,'MAIN_USE_RESPONSE_TIMEOUT',1,'30','chaine',0,'','2013-01-16 19:28:50'),(2837,'MAIN_PROXY_USE',1,'0','chaine',0,'','2013-01-16 19:28:50'),(2838,'MAIN_PROXY_HOST',1,'localhost','chaine',0,'','2013-01-16 19:28:50'),(2839,'MAIN_PROXY_PORT',1,'8080','chaine',0,'','2013-01-16 19:28:50'),(2840,'MAIN_PROXY_USER',1,'aaa','chaine',0,'','2013-01-16 19:28:50'),(2841,'MAIN_PROXY_PASS',1,'bbb','chaine',0,'','2013-01-16 19:28:50'),(2848,'OVHSMS_NICK',1,'BN196-OVH','chaine',0,'','2013-01-16 19:32:36'),(2849,'OVHSMS_PASS',1,'bigone-10','chaine',0,'','2013-01-16 19:32:36'),(2850,'OVHSMS_SOAPURL',1,'https://www.ovh.com/soapi/soapi-re-1.55.wsdl','chaine',0,'','2013-01-16 19:32:36'),(2854,'THEME_ELDY_RGB',1,'bfbf00','chaine',0,'','2013-01-18 10:02:53'),(2855,'THEME_ELDY_ENABLE_PERSONALIZED',1,'0','chaine',0,'','2013-01-18 10:02:55'),(2858,'MAIN_SESSION_TIMEOUT',1,'2000','chaine',0,'','2013-01-19 17:01:53'),(2862,'TICKET_ADDON',1,'mod_ticket_avenc','chaine',0,'Nom du gestionnaire de numerotation des tickets','2013-01-19 17:16:10'),(2867,'FACSIM_ADDON',1,'mod_facsim_alcoy','chaine',0,'','2013-01-19 17:16:25'),(2868,'POS_SERVICES',1,'0','chaine',0,'','2013-01-19 17:16:51'),(2869,'POS_USE_TICKETS',1,'1','chaine',0,'','2013-01-19 17:16:51'),(2870,'POS_MAX_TTC',1,'100','chaine',0,'','2013-01-19 17:16:51'),(3190,'MAIN_MODULE_HOLIDAY',2,'1',NULL,0,NULL,'2013-02-01 08:52:34'),(3191,'MAIN_MODULE_HOLIDAY_TABS_0',2,'user:+paidholidays:CPTitreMenu:holiday:$user->rights->holiday->write:/holiday/index.php?mainmenu=holiday&id=__ID__','chaine',0,NULL,'2013-02-01 08:52:34'),(3195,'INVOICE_SUPPLIER_ADDON_PDF',1,'canelle','chaine',0,'','2013-02-10 19:50:27'),(3199,'MAIN_FORCE_RELOAD_PAGE',1,'1','chaine',0,NULL,'2013-02-12 16:22:55'),(3217,'MAIN_PDF_TITLE_BACKGROUND_COLOR',1,'240,240,240','chaine',1,'','2013-02-13 15:18:02'),(3223,'OVH_THIRDPARTY_IMPORT',1,'2','chaine',0,'','2013-02-13 16:20:18'),(3241,'COMPANY_USE_SEARCH_TO_SELECT',1,'2','chaine',0,'','2013-02-17 14:33:39'),(3409,'AGENDA_USE_EVENT_TYPE',1,'1','chaine',0,'','2013-02-27 18:12:24'),(3886,'MAIN_REMOVE_INSTALL_WARNING',1,'1','chaine',1,'','2013-03-02 18:32:50'),(4013,'MAIN_DELAY_ACTIONS_TODO',1,'7','chaine',0,'','2013-03-06 08:59:12'),(4014,'MAIN_DELAY_PROPALS_TO_CLOSE',1,'31','chaine',0,'','2013-03-06 08:59:12'),(4015,'MAIN_DELAY_PROPALS_TO_BILL',1,'7','chaine',0,'','2013-03-06 08:59:12'),(4016,'MAIN_DELAY_ORDERS_TO_PROCESS',1,'2','chaine',0,'','2013-03-06 08:59:12'),(4017,'MAIN_DELAY_CUSTOMER_BILLS_UNPAYED',1,'31','chaine',0,'','2013-03-06 08:59:12'),(4018,'MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS',1,'7','chaine',0,'','2013-03-06 08:59:12'),(4019,'MAIN_DELAY_SUPPLIER_BILLS_TO_PAY',1,'2','chaine',0,'','2013-03-06 08:59:12'),(4020,'MAIN_DELAY_RUNNING_SERVICES',1,'-15','chaine',0,'','2013-03-06 08:59:12'),(4021,'MAIN_DELAY_TRANSACTIONS_TO_CONCILIATE',1,'62','chaine',0,'','2013-03-06 08:59:13'),(4022,'MAIN_DELAY_MEMBERS',1,'31','chaine',0,'','2013-03-06 08:59:13'),(4023,'MAIN_DISABLE_METEO',1,'0','chaine',0,'','2013-03-06 08:59:13'),(4044,'ADHERENT_VAT_FOR_SUBSCRIPTIONS',1,'0','',0,'','2013-03-06 16:06:38'),(4047,'ADHERENT_BANK_USE',1,'bankviainvoice','',0,'','2013-03-06 16:12:30'),(4049,'PHPSANE_SCANIMAGE',1,'/usr/bin/scanimage','chaine',0,'','2013-03-06 21:54:13'),(4050,'PHPSANE_PNMTOJPEG',1,'/usr/bin/pnmtojpeg','chaine',0,'','2013-03-06 21:54:13'),(4051,'PHPSANE_PNMTOTIFF',1,'/usr/bin/pnmtotiff','chaine',0,'','2013-03-06 21:54:13'),(4052,'PHPSANE_OCR',1,'/usr/bin/gocr','chaine',0,'','2013-03-06 21:54:13'),(4548,'ECM_AUTO_TREE_ENABLED',1,'1','chaine',0,'','2013-03-10 15:57:21'),(4579,'MAIN_MODULE_AGENDA',2,'1',NULL,0,NULL,'2013-03-13 15:29:19'),(4580,'MAIN_AGENDA_ACTIONAUTO_COMPANY_CREATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4581,'MAIN_AGENDA_ACTIONAUTO_CONTRACT_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4582,'MAIN_AGENDA_ACTIONAUTO_PROPAL_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4583,'MAIN_AGENDA_ACTIONAUTO_PROPAL_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4584,'MAIN_AGENDA_ACTIONAUTO_ORDER_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4585,'MAIN_AGENDA_ACTIONAUTO_ORDER_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4586,'MAIN_AGENDA_ACTIONAUTO_BILL_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4587,'MAIN_AGENDA_ACTIONAUTO_BILL_PAYED',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4588,'MAIN_AGENDA_ACTIONAUTO_BILL_CANCEL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4589,'MAIN_AGENDA_ACTIONAUTO_BILL_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4590,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4591,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4592,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4593,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4594,'MAIN_AGENDA_ACTIONAUTO_BILL_UNVALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4595,'MAIN_MODULE_GOOGLE',2,'1',NULL,0,NULL,'2013-03-13 15:29:47'),(4596,'MAIN_MODULE_GOOGLE_TABS_0',2,'agenda:+gcal:MenuAgendaGoogle:google@google:$conf->google->enabled && $conf->global->GOOGLE_ENABLE_AGENDA:/google/index.php','chaine',0,NULL,'2013-03-13 15:29:47'),(4597,'MAIN_MODULE_GOOGLE_TABS_1',2,'user:+gsetup:GoogleUserConf:google@google:$conf->google->enabled && $conf->global->GOOGLE_DUPLICATE_INTO_GCAL:/google/admin/google_calsync_user.php?id=__ID__','chaine',0,NULL,'2013-03-13 15:29:47'),(4598,'MAIN_MODULE_GOOGLE_TRIGGERS',2,'1','chaine',0,NULL,'2013-03-13 15:29:47'),(4599,'MAIN_MODULE_GOOGLE_HOOKS',2,'[\"toprightmenu\"]','chaine',0,NULL,'2013-03-13 15:29:47'),(4688,'GOOGLE_ENABLE_AGENDA',2,'1','chaine',0,'','2013-03-13 15:36:29'),(4689,'GOOGLE_AGENDA_NAME1',2,'eldy','chaine',0,'','2013-03-13 15:36:29'),(4690,'GOOGLE_AGENDA_SRC1',2,'eldy10@mail.com','chaine',0,'','2013-03-13 15:36:29'),(4691,'GOOGLE_AGENDA_COLOR1',2,'BE6D00','chaine',0,'','2013-03-13 15:36:29'),(4692,'GOOGLE_AGENDA_COLOR2',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4693,'GOOGLE_AGENDA_COLOR3',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4694,'GOOGLE_AGENDA_COLOR4',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4695,'GOOGLE_AGENDA_COLOR5',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4696,'GOOGLE_AGENDA_TIMEZONE',2,'Europe/Paris','chaine',0,'','2013-03-13 15:36:29'),(4697,'GOOGLE_AGENDA_NB',2,'5','chaine',0,'','2013-03-13 15:36:29'),(4725,'SOCIETE_CODECLIENT_ADDON',2,'mod_codeclient_leopard','chaine',0,'Module to control third parties codes','2013-03-13 20:21:35'),(4726,'SOCIETE_CODECOMPTA_ADDON',2,'mod_codecompta_panicum','chaine',0,'Module to control third parties codes','2013-03-13 20:21:35'),(4727,'SOCIETE_FISCAL_MONTH_START',2,'','chaine',0,'Mettre le numero du mois du debut d\\\'annee fiscale, ex: 9 pour septembre','2013-03-13 20:21:35'),(4728,'MAIN_SEARCHFORM_SOCIETE',2,'1','yesno',0,'Show form for quick company search','2013-03-13 20:21:35'),(4729,'MAIN_SEARCHFORM_CONTACT',2,'1','yesno',0,'Show form for quick contact search','2013-03-13 20:21:35'),(4730,'COMPANY_ADDON_PDF_ODT_PATH',2,'DOL_DATA_ROOT/doctemplates/thirdparties','chaine',0,NULL,'2013-03-13 20:21:35'),(4743,'MAIN_MODULE_CLICKTODIAL',2,'1',NULL,0,NULL,'2013-03-13 20:30:28'),(4744,'MAIN_MODULE_NOTIFICATION',2,'1',NULL,0,NULL,'2013-03-13 20:30:34'),(4745,'MAIN_MODULE_WEBSERVICES',2,'1',NULL,0,NULL,'2013-03-13 20:30:41'),(4746,'MAIN_MODULE_PROPALE',2,'1',NULL,0,NULL,'2013-03-13 20:32:38'),(4747,'PROPALE_ADDON_PDF',2,'azur','chaine',0,'Nom du gestionnaire de generation des propales en PDF','2013-03-13 20:32:38'),(4748,'PROPALE_ADDON',2,'mod_propale_marbre','chaine',0,'Nom du gestionnaire de numerotation des propales','2013-03-13 20:32:38'),(4749,'PROPALE_VALIDITY_DURATION',2,'15','chaine',0,'Duration of validity of business proposals','2013-03-13 20:32:38'),(4750,'PROPALE_ADDON_PDF_ODT_PATH',2,'DOL_DATA_ROOT/doctemplates/proposals','chaine',0,NULL,'2013-03-13 20:32:38'),(4752,'MAIN_MODULE_TAX',2,'1',NULL,0,NULL,'2013-03-13 20:32:47'),(4753,'MAIN_MODULE_DON',2,'1',NULL,0,NULL,'2013-03-13 20:32:54'),(4754,'DON_ADDON_MODEL',2,'html_cerfafr','chaine',0,'Nom du gestionnaire de generation de recu de dons','2013-03-13 20:32:54'),(4755,'POS_USE_TICKETS',2,'1','chaine',0,'','2013-03-13 20:33:09'),(4756,'POS_MAX_TTC',2,'100','chaine',0,'','2013-03-13 20:33:09'),(4757,'MAIN_MODULE_POS',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4758,'TICKET_ADDON',2,'mod_ticket_avenc','chaine',0,'Nom du gestionnaire de numerotation des tickets','2013-03-13 20:33:09'),(4759,'MAIN_MODULE_BANQUE',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4760,'MAIN_MODULE_FACTURE',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4761,'FACTURE_ADDON_PDF',2,'crabe','chaine',0,'Name of PDF model of invoice','2013-03-13 20:33:09'),(4762,'FACTURE_ADDON',2,'mod_facture_terre','chaine',0,'Name of numbering numerotation rules of invoice','2013-03-13 20:33:09'),(4763,'FACTURE_ADDON_PDF_ODT_PATH',2,'DOL_DATA_ROOT/doctemplates/invoices','chaine',0,NULL,'2013-03-13 20:33:09'),(4764,'MAIN_MODULE_SOCIETE',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4765,'MAIN_MODULE_PRODUCT',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4766,'PRODUCT_CODEPRODUCT_ADDON',2,'mod_codeproduct_leopard','chaine',0,'Module to control product codes','2013-03-13 20:33:09'),(4767,'MAIN_SEARCHFORM_PRODUITSERVICE',2,'1','yesno',0,'Show form for quick product search','2013-03-13 20:33:09'),(4772,'FACSIM_ADDON',2,'mod_facsim_alcoy','chaine',0,'','2013-03-13 20:33:32'),(4773,'MAIN_MODULE_MAILING',2,'1',NULL,0,NULL,'2013-03-13 20:33:37'),(4774,'MAIN_MODULE_OPENSURVEY',2,'1',NULL,0,NULL,'2013-03-13 20:33:42'),(4782,'AGENDA_USE_EVENT_TYPE',2,'1','chaine',0,'','2013-03-13 20:53:36'),(4884,'AGENDA_DISABLE_EXT',2,'1','chaine',0,'','2013-03-13 22:03:40'),(4928,'COMMANDE_SUPPLIER_ADDON_NUMBER',1,'mod_commande_fournisseur_muguet','chaine',0,'Nom du gestionnaire de numerotation des commandes fournisseur','2013-03-22 09:24:29'),(4929,'INVOICE_SUPPLIER_ADDON_NUMBER',1,'mod_facture_fournisseur_cactus','chaine',0,'Nom du gestionnaire de numerotation des factures fournisseur','2013-03-22 09:24:29'),(5001,'MAIN_CRON_KEY',0,'bc54582fe30d5d4a830c6f582ec28810','chaine',0,'','2013-03-23 17:54:53'),(5009,'CRON_KEY',0,'2c2e755c20be2014098f629865598006','chaine',0,'','2013-03-23 18:06:24'),(5139,'SOCIETE_ADD_REF_IN_LIST',1,'','yesno',0,'Display customer ref into select list','2013-09-08 23:06:08'),(5150,'PROJECT_TASK_ADDON_PDF',1,'','chaine',0,'Name of PDF/ODT tasks manager class','2013-09-08 23:06:14'),(5151,'PROJECT_TASK_ADDON',1,'mod_task_simple','chaine',0,'Name of Numbering Rule task manager class','2013-09-08 23:06:14'),(5152,'PROJECT_TASK_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/tasks','chaine',0,'','2013-09-08 23:06:14'),(5239,'BOOKMARKS_SHOW_IN_MENU',1,'10','chaine',0,'','2014-03-02 15:42:26'),(5271,'DONATION_ART200',1,'','yesno',0,'Option Française - Eligibilité Art200 du CGI','2014-12-21 12:51:28'),(5272,'DONATION_ART238',1,'','yesno',0,'Option Française - Eligibilité Art238 bis du CGI','2014-12-21 12:51:28'),(5273,'DONATION_ART885',1,'','yesno',0,'Option Française - Eligibilité Art885-0 V bis du CGI','2014-12-21 12:51:28'),(5274,'DONATION_MESSAGE',1,'Thank you','chaine',0,'Message affiché sur le récépissé de versements ou dons','2014-12-21 12:51:28'),(5288,'DONATION_ACCOUNTINGACCOUNT',1,'7581','chaine',0,'Compte comptable de remise des versements ou dons','2015-07-19 13:41:21'),(5349,'MAIN_SEARCHFORM_CONTACT',1,'1','chaine',0,'','2015-10-03 10:11:33'),(5351,'MAIN_SEARCHFORM_PRODUITSERVICE',1,'1','chaine',0,'','2015-10-03 10:11:33'),(5352,'MAIN_SEARCHFORM_PRODUITSERVICE_SUPPLIER',1,'0','chaine',0,'','2015-10-03 10:11:33'),(5353,'MAIN_SEARCHFORM_ADHERENT',1,'1','chaine',0,'','2015-10-03 10:11:33'),(5354,'MAIN_SEARCHFORM_PROJECT',1,'0','chaine',0,'','2015-10-03 10:11:33'),(5394,'FCKEDITOR_ENABLE_DETAILS',1,'1','yesno',0,'WYSIWIG for products details lines for all entities','2015-11-04 15:27:44'),(5395,'FCKEDITOR_ENABLE_USERSIGN',1,'1','yesno',0,'WYSIWIG for user signature','2015-11-04 15:27:44'),(5396,'FCKEDITOR_ENABLE_MAIL',1,'1','yesno',0,'WYSIWIG for products details lines for all entities','2015-11-04 15:27:44'),(5398,'CATEGORIE_RECURSIV_ADD',1,'','yesno',0,'Affect parent categories','2015-11-04 15:27:46'),(5403,'MAIN_MODULE_FCKEDITOR',1,'1',NULL,0,NULL,'2015-11-04 15:41:40'),(5404,'MAIN_MODULE_CATEGORIE',1,'1',NULL,0,NULL,'2015-11-04 15:41:43'),(5415,'EXPEDITION_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/shipment','chaine',0,NULL,'2015-11-15 22:38:28'),(5416,'LIVRAISON_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/delivery','chaine',0,NULL,'2015-11-15 22:38:28'),(5419,'MAIN_MODULE_CASHDESK',1,'1',NULL,0,NULL,'2015-11-15 22:38:33'),(5426,'MAIN_MODULE_PROJET',1,'1',NULL,0,NULL,'2015-11-15 22:38:44'),(5427,'PROJECT_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/projects','chaine',0,NULL,'2015-11-15 22:38:44'),(5428,'PROJECT_USE_OPPORTUNIES',1,'1','chaine',0,NULL,'2015-11-15 22:38:44'),(5430,'MAIN_MODULE_EXPORT',1,'1',NULL,0,NULL,'2015-11-15 22:38:56'),(5431,'MAIN_MODULE_IMPORT',1,'1',NULL,0,NULL,'2015-11-15 22:38:58'),(5432,'MAIN_MODULE_MAILING',1,'1',NULL,0,NULL,'2015-11-15 22:39:00'),(5434,'EXPENSEREPORT_ADDON_PDF',1,'standard','chaine',0,'Name of manager to build PDF expense reports documents','2015-11-15 22:39:05'),(5436,'SALARIES_ACCOUNTING_ACCOUNT_PAYMENT',1,'421','chaine',0,NULL,'2015-11-15 22:39:08'),(5437,'SALARIES_ACCOUNTING_ACCOUNT_CHARGE',1,'641','chaine',0,NULL,'2015-11-15 22:39:08'),(5441,'ADHERENT_ETIQUETTE_TEXT',1,'%FULLNAME%\n%ADDRESS%\n%ZIP% %TOWN%\n%COUNTRY%','texte',0,'Text to print on member address sheets','2015-11-15 22:39:17'),(5443,'MAIN_MODULE_PRELEVEMENT',1,'1',NULL,0,NULL,'2015-11-15 22:39:33'),(5453,'MAIN_MODULE_CONTRAT',1,'1',NULL,0,NULL,'2015-11-15 22:39:52'),(5455,'MAIN_MODULE_FICHEINTER',1,'1',NULL,0,NULL,'2015-11-15 22:39:56'),(5459,'MAIN_MODULE_PAYPAL',1,'1',NULL,0,NULL,'2015-11-15 22:41:02'),(5460,'MAIN_MODULE_MARGIN',1,'1',NULL,0,NULL,'2015-11-15 22:41:47'),(5461,'MAIN_MODULE_MARGIN_TABS_0',1,'product:+margin:Margins:margins:$user->rights->margins->liretous:/margin/tabs/productMargins.php?id=__ID__','chaine',0,NULL,'2015-11-15 22:41:47'),(5462,'MAIN_MODULE_MARGIN_TABS_1',1,'thirdparty:+margin:Margins:margins:empty($user->societe_id) && $user->rights->margins->liretous && ($object->client > 0):/margin/tabs/thirdpartyMargins.php?socid=__ID__','chaine',0,NULL,'2015-11-15 22:41:47'),(5463,'MAIN_MODULE_PROPALE',1,'1',NULL,0,NULL,'2015-11-15 22:41:47'),(5483,'GENBARCODE_BARCODETYPE_THIRDPARTY',1,'6','chaine',0,'','2016-01-16 15:49:43'),(5484,'PRODUIT_DEFAULT_BARCODE_TYPE',1,'2','chaine',0,'','2016-01-16 15:49:46'),(5539,'PRODUCT_USE_OLD_PATH_FOR_PHOTO',0,'0','chaine',1,'Use old path for products images','2016-01-22 13:34:23'),(5541,'MODULE_GOOGLE_DEBUG',1,'0','chaine',1,'','2016-01-22 13:34:57'),(5586,'MAIN_DELAY_EXPENSEREPORTS_TO_PAY',1,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur les notes de frais impayées','2016-01-22 17:28:18'),(5587,'MAIN_FIX_FOR_BUGGED_MTA',1,'1','chaine',1,'Set constant to fix email ending from PHP with some linux ike system','2016-01-22 17:28:18'),(5590,'MAIN_VERSION_LAST_INSTALL',0,'3.8.3','chaine',0,'Dolibarr version when install','2016-01-22 17:28:42'),(5604,'MAIN_INFO_SOCIETE_LOGO',1,'mybigcompany.png','chaine',0,'','2016-01-22 17:33:49'),(5605,'MAIN_INFO_SOCIETE_LOGO_SMALL',1,'mybigcompany_small.png','chaine',0,'','2016-01-22 17:33:49'),(5606,'MAIN_INFO_SOCIETE_LOGO_MINI',1,'mybigcompany_mini.png','chaine',0,'','2016-01-22 17:33:49'),(5612,'MAIN_ENABLE_LOG_TO_HTML',0,'0','chaine',1,'If this option is set to 1, it is possible to see log output at end of HTML sources by adding paramater logtohtml=1 on URL','2016-03-13 10:54:45'),(5614,'MAIN_SIZE_SHORTLISTE_LIMIT',1,'4','chaine',0,'Longueur maximum des listes courtes (fiche client)','2016-03-13 10:54:46'),(5626,'MAIN_MODULE_SUPPLIERPROPOSAL',1,'1',NULL,0,NULL,'2016-07-30 11:13:20'),(5627,'SUPPLIER_PROPOSAL_ADDON_PDF',1,'aurore','chaine',0,'Name of submodule to generate PDF for supplier quotation request','2016-07-30 11:13:20'),(5628,'SUPPLIER_PROPOSAL_ADDON',1,'mod_supplier_proposal_marbre','chaine',0,'Name of submodule to number supplier quotation request','2016-07-30 11:13:20'),(5629,'SUPPLIER_PROPOSAL_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/supplier_proposal','chaine',0,NULL,'2016-07-30 11:13:20'),(5632,'MAIN_MODULE_RESOURCE',1,'1',NULL,0,NULL,'2016-07-30 11:13:32'),(5633,'MAIN_MODULE_API',1,'1',NULL,0,NULL,'2016-07-30 11:13:54'),(5634,'MAIN_MODULE_WEBSERVICES',1,'1',NULL,0,NULL,'2016-07-30 11:13:56'),(5635,'WEBSERVICES_KEY',1,'dolibarrkey','chaine',0,'','2016-07-30 11:14:04'),(5638,'MAIN_MODULE_EXTERNALRSS',1,'1',NULL,0,NULL,'2016-07-30 11:15:04'),(5639,'EXTERNAL_RSS_TITLE_1',1,'Dolibarr.org News','chaine',0,'','2016-07-30 11:15:25'),(5640,'EXTERNAL_RSS_URLRSS_1',1,'https://www.dolibarr.org/rss','chaine',0,'','2016-07-30 11:15:25'),(5642,'SOCIETE_CODECOMPTA_ADDON',1,'mod_codecompta_aquarium','chaine',0,'','2016-07-30 11:16:42'),(5707,'CASHDESK_NO_DECREASE_STOCK',1,'1','chaine',0,'','2016-07-30 13:38:11'),(5708,'MAIN_MODULE_PRODUCTBATCH',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5710,'MAIN_MODULE_STOCK',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5711,'MAIN_MODULE_PRODUCT',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5712,'MAIN_MODULE_EXPEDITION',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5808,'MARGIN_TYPE',1,'costprice','chaine',0,'','2016-07-30 16:32:18'),(5809,'DISPLAY_MARGIN_RATES',1,'1','chaine',0,'','2016-07-30 16:32:20'),(5810,'MAIN_FEATURES_LEVEL',0,'0','chaine',1,'Level of features to show (0=stable only, 1=stable+experimental, 2=stable+experimental+development','2016-07-30 18:36:15'),(5813,'USER_PASSWORD_PATTERN',1,'8;1;1;1;3;1','chaine',0,'','2016-07-31 16:04:58'),(5814,'MAIN_MODULE_EXPENSEREPORT',1,'1',NULL,0,NULL,'2016-07-31 21:14:32'),(5830,'LOAN_ACCOUNTING_ACCOUNT_CAPITAL',1,'164','chaine',0,NULL,'2017-01-29 15:11:51'),(5831,'LOAN_ACCOUNTING_ACCOUNT_INSURANCE',1,'6162','chaine',0,NULL,'2017-01-29 15:11:51'),(5833,'ACCOUNTING_EXPORT_SEPARATORCSV',1,',','string',0,NULL,'2017-01-29 15:11:56'),(5834,'ACCOUNTING_ACCOUNT_SUSPENSE',1,'471','chaine',0,NULL,'2017-01-29 15:11:56'),(5839,'ACCOUNTING_ACCOUNT_TRANSFER_CASH',1,'58','chaine',0,NULL,'2017-01-29 15:11:56'),(5840,'CHARTOFACCOUNTS',1,'2','chaine',0,NULL,'2017-01-29 15:11:56'),(5841,'ACCOUNTING_EXPORT_MODELCSV',1,'1','chaine',0,NULL,'2017-01-29 15:11:56'),(5842,'ACCOUNTING_LENGTH_GACCOUNT',1,'','chaine',0,NULL,'2017-01-29 15:11:56'),(5843,'ACCOUNTING_LENGTH_AACCOUNT',1,'','chaine',0,NULL,'2017-01-29 15:11:56'),(5844,'ACCOUNTING_LIST_SORT_VENTILATION_TODO',1,'1','yesno',0,NULL,'2017-01-29 15:11:56'),(5845,'ACCOUNTING_LIST_SORT_VENTILATION_DONE',1,'1','yesno',0,NULL,'2017-01-29 15:11:56'),(5846,'ACCOUNTING_EXPORT_DATE',1,'%d%m%Y','chaine',0,NULL,'2017-01-29 15:11:56'),(5848,'ACCOUNTING_EXPORT_FORMAT',1,'csv','chaine',0,NULL,'2017-01-29 15:11:56'),(5853,'MAIN_MODULE_WORKFLOW',1,'1',NULL,0,NULL,'2017-01-29 15:12:12'),(5854,'MAIN_MODULE_NOTIFICATION',1,'1',NULL,0,NULL,'2017-01-29 15:12:35'),(5855,'MAIN_MODULE_OAUTH',1,'1',NULL,0,NULL,'2017-01-29 15:12:41'),(5856,'MAIN_MODULE_PRINTING',1,'1',NULL,0,NULL,'2017-01-29 15:12:44'),(5883,'MAILING_LIMIT_SENDBYWEB',0,'15','chaine',1,'Number of targets to defined packet size when sending mass email','2017-01-29 17:36:33'),(5884,'MAIN_MAIL_DEBUG',1,'0','chaine',1,'','2017-01-29 18:53:02'),(5885,'MAIN_SOAP_DEBUG',1,'0','chaine',1,'','2017-01-29 18:53:02'),(5887,'PROJECT_USE_OPPORTUNITIES',1,'1','chaine',0,'','2017-02-01 12:23:56'),(5888,'PROJECT_HIDE_TASKS',1,'1','chaine',0,'','2017-02-01 12:23:56'),(5889,'MAIN_AGENDA_ACTIONAUTO_COMPANY_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5890,'MAIN_AGENDA_ACTIONAUTO_COMPANY_CREATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5891,'MAIN_AGENDA_ACTIONAUTO_PROPAL_CLOSE_REFUSED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5892,'MAIN_AGENDA_ACTIONAUTO_PROPAL_CLOSE_SIGNED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5893,'MAIN_AGENDA_ACTIONAUTO_PROPAL_CLASSIFY_BILLED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5894,'MAIN_AGENDA_ACTIONAUTO_PROPAL_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5895,'MAIN_AGENDA_ACTIONAUTO_PROPAL_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5896,'MAIN_AGENDA_ACTIONAUTO_ORDER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5897,'MAIN_AGENDA_ACTIONAUTO_ORDER_CLOSE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5898,'MAIN_AGENDA_ACTIONAUTO_ORDER_CANCEL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5899,'MAIN_AGENDA_ACTIONAUTO_ORDER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5900,'MAIN_AGENDA_ACTIONAUTO_ORDER_CLASSIFY_BILLED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5901,'MAIN_AGENDA_ACTIONAUTO_BILL_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5902,'MAIN_AGENDA_ACTIONAUTO_BILL_PAYED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5903,'MAIN_AGENDA_ACTIONAUTO_BILL_CANCEL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5904,'MAIN_AGENDA_ACTIONAUTO_BILL_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5905,'MAIN_AGENDA_ACTIONAUTO_BILL_UNVALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5906,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5907,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_APPROVE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5908,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_RECEIVE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5909,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_SUBMIT',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5910,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_REFUSE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5911,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_CLASSIFY_BILLED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5912,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5913,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_UNVALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5914,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5915,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_PAYED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5916,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5917,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_CANCELED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5918,'MAIN_AGENDA_ACTIONAUTO_CONTRACT_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5919,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_REOPEN',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5920,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5921,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5922,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5923,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5924,'MAIN_AGENDA_ACTIONAUTO_MEMBER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5925,'MAIN_AGENDA_ACTIONAUTO_MEMBER_SUBSCRIPTION',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5926,'MAIN_AGENDA_ACTIONAUTO_MEMBER_MODIFY',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5927,'MAIN_AGENDA_ACTIONAUTO_MEMBER_RESILIATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5928,'MAIN_AGENDA_ACTIONAUTO_MEMBER_DELETE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5929,'MAIN_AGENDA_ACTIONAUTO_PROJECT_CREATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5930,'MAIN_AGENDA_ACTIONAUTO_PROJECT_DELETE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5931,'DATABASE_PWD_ENCRYPTED',1,'1','chaine',0,'','2017-02-01 15:06:04'),(5932,'MAIN_DISABLE_ALL_MAILS',1,'0','chaine',0,'','2017-02-01 15:09:09'),(5933,'MAIN_MAIL_SENDMODE',1,'mail','chaine',0,'','2017-02-01 15:09:09'),(5934,'MAIN_MAIL_SMTP_PORT',1,'465','chaine',0,'','2017-02-01 15:09:09'),(5935,'MAIN_MAIL_SMTP_SERVER',1,'smtp.mail.com','chaine',0,'','2017-02-01 15:09:09'),(5936,'MAIN_MAIL_SMTPS_ID',1,'eldy10@mail.com','chaine',0,'','2017-02-01 15:09:09'),(5937,'MAIN_MAIL_SMTPS_PW',1,'bidonge','chaine',0,'','2017-02-01 15:09:09'),(5938,'MAIN_MAIL_EMAIL_FROM',1,'robot@example.com','chaine',0,'','2017-02-01 15:09:09'),(5939,'MAIN_MAIL_DEFAULT_FROMTYPE',1,'user','chaine',0,'','2017-02-01 15:09:09'),(5940,'PRELEVEMENT_ID_BANKACCOUNT',1,'1','chaine',0,'','2017-02-06 04:04:47'),(5941,'PRELEVEMENT_ICS',1,'ICS123456','chaine',0,'','2017-02-06 04:04:47'),(5942,'PRELEVEMENT_USER',1,'1','chaine',0,'','2017-02-06 04:04:47'),(5943,'BANKADDON_PDF',1,'sepamandate','chaine',0,'','2017-02-06 04:13:52'),(5947,'CHEQUERECEIPTS_THYME_MASK',1,'CHK{yy}{mm}-{0000@1}','chaine',0,'','2017-02-06 04:16:27'),(5948,'MAIN_MODULE_LOAN',1,'1',NULL,0,NULL,'2017-02-06 19:19:05'),(5954,'MAIN_SUBMODULE_EXPEDITION',1,'1','chaine',0,'','2017-02-06 23:57:37'),(5963,'MAIN_MODULE_BANQUE',1,'1',NULL,0,NULL,'2017-02-07 18:56:12'),(5964,'MAIN_MODULE_TAX',1,'1',NULL,0,NULL,'2017-02-07 18:56:12'),(5996,'CABINETMED_RHEUMATOLOGY_ON',1,'0','texte',0,'','2017-02-12 19:20:04'),(5999,'MAIN_SEARCHFORM_SOCIETE',1,'1','texte',0,'','2017-02-12 19:20:04'),(6000,'CABINETMED_BANK_PATIENT_REQUIRED',1,'0','texte',0,'','2017-02-12 19:20:04'),(6004,'MAIN_MODULE_MULTICOMPANY_CSS',1,'[\"\\/multicompany\\/css\\/multicompany.css.php\"]','chaine',0,NULL,'2017-02-15 17:17:11'),(6019,'MAIN_INFO_SOCIETE_COUNTRY',2,'1:FR:France','chaine',0,'','2017-02-15 17:18:22'),(6020,'MAIN_INFO_SOCIETE_NOM',2,'MySecondCompany','chaine',0,'','2017-02-15 17:18:22'),(6021,'MAIN_INFO_SOCIETE_STATE',2,'0','chaine',0,'','2017-02-15 17:18:22'),(6022,'MAIN_MONNAIE',2,'EUR','chaine',0,'','2017-02-15 17:18:22'),(6023,'MAIN_LANG_DEFAULT',2,'auto','chaine',0,'','2017-02-15 17:18:22'),(6032,'MAIN_MODULE_MULTICURRENCY',1,'1',NULL,0,NULL,'2017-02-15 17:29:59'),(6047,'MAIN_MODULE_SYSLOG',0,'1',NULL,0,NULL,'2017-02-15 22:36:58'),(6048,'SYSLOG_FACILITY',0,'LOG_USER','chaine',0,'','2017-02-15 22:37:01'),(6049,'SYSLOG_FIREPHP_INCLUDEPATH',0,'/home/ldestailleur/git/dolibarr_5.0/htdocs/includes/firephp/firephp-core/lib/','chaine',0,'','2017-02-15 22:37:01'),(6050,'SYSLOG_FILE',0,'DOL_DATA_ROOT/dolibarr.log','chaine',0,'','2017-02-15 22:37:01'),(6051,'SYSLOG_CHROMEPHP_INCLUDEPATH',0,'/home/ldestailleur/git/dolibarr_5.0/htdocs/includes/ccampbell/chromephp/','chaine',0,'','2017-02-15 22:37:01'),(6052,'SYSLOG_HANDLERS',0,'[\"mod_syslog_file\"]','chaine',0,'','2017-02-15 22:37:01'),(6054,'SYSLOG_LEVEL',0,'7','chaine',0,'','2017-02-15 22:37:21'),(6074,'CABINETMED_DELAY_TO_LOCK_RECORD',1,'','chaine',1,'Number of days before locking edit of consultation','2017-02-21 00:04:15'),(6092,'MAIN_SIZE_SHORTLIST_LIMIT',0,'3','chaine',0,'Max length for small lists (tabs)','2017-05-12 09:02:38'),(6099,'MAIN_MODULE_SKYPE',1,'1',NULL,0,NULL,'2017-05-12 09:03:51'),(6100,'MAIN_MODULE_GRAVATAR',1,'1',NULL,0,NULL,'2017-05-12 09:03:54'),(6101,'MAIN_MODULE_ACCOUNTING',1,'1',NULL,0,NULL,'2017-05-12 09:14:30'),(6102,'PRODUCT_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/products','chaine',0,'','2017-08-27 13:29:07'),(6103,'CONTRACT_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/contracts','chaine',0,'','2017-08-27 13:29:07'),(6104,'USERGROUP_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/usergroups','chaine',0,'','2017-08-27 13:29:07'),(6105,'USER_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/users','chaine',0,'','2017-08-27 13:29:07'),(6106,'MAIN_ENABLE_OVERWRITE_TRANSLATION',1,'1','chaine',0,'Enable overwrote of translation','2017-08-27 13:29:07'),(6108,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6109,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_CLASSIFY_BILLED',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6110,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_CLASSIFY_UNBILLED',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6111,'MAIN_AGENDA_ACTIONAUTO_PRODUCT_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6112,'MAIN_AGENDA_ACTIONAUTO_PRODUCT_MODIFY',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6113,'MAIN_AGENDA_ACTIONAUTO_PRODUCT_DELETE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6114,'MAIN_AGENDA_ACTIONAUTO_PROJECT_MODIFY',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6115,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6116,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_VALIDATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6117,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_APPROVE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6118,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_PAYED',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6119,'MAIN_AGENDA_ACTIONAUTO_HOLIDAY_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6120,'MAIN_AGENDA_ACTIONAUTO_HOLIDAY_VALIDATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6121,'MAIN_AGENDA_ACTIONAUTO_HOLIDAY_APPROVE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6137,'MAIN_LANG_DEFAULT',1,'auto','chaine',0,'','2017-08-28 10:19:58'),(6138,'MAIN_MULTILANGS',1,'1','chaine',0,'','2017-08-28 10:19:58'),(6139,'MAIN_THEME',1,'eldy','chaine',0,'','2017-08-28 10:19:58'),(6140,'THEME_ELDY_USE_HOVER',1,'edf4fb','chaine',0,'','2017-08-28 10:19:58'),(6141,'MAIN_SIZE_LISTE_LIMIT',1,'25','chaine',0,'','2017-08-28 10:19:59'),(6142,'MAIN_SIZE_SHORTLIST_LIMIT',1,'3','chaine',0,'','2017-08-28 10:19:59'),(6143,'MAIN_DISABLE_JAVASCRIPT',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6144,'MAIN_BUTTON_HIDE_UNAUTHORIZED',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6145,'MAIN_START_WEEK',1,'1','chaine',0,'','2017-08-28 10:19:59'),(6146,'MAIN_DEFAULT_WORKING_DAYS',1,'1-5','chaine',0,'','2017-08-28 10:19:59'),(6147,'MAIN_DEFAULT_WORKING_HOURS',1,'9-18','chaine',0,'','2017-08-28 10:19:59'),(6148,'MAIN_SHOW_LOGO',1,'1','chaine',0,'','2017-08-28 10:19:59'),(6149,'MAIN_FIRSTNAME_NAME_POSITION',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6150,'MAIN_HELPCENTER_DISABLELINK',0,'1','chaine',0,'','2017-08-28 10:19:59'),(6151,'MAIN_HOME',1,'__(NoteSomeFeaturesAreDisabled)__
\r\n
\r\n__(SomeTranslationAreUncomplete)__
','chaine',0,'','2017-08-28 10:19:59'),(6152,'MAIN_HELP_DISABLELINK',0,'0','chaine',0,'','2017-08-28 10:19:59'),(6153,'MAIN_BUGTRACK_ENABLELINK',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6353,'MAIN_MENU_STANDARD',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6354,'MAIN_MENU_SMARTPHONE',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6355,'MAIN_MENUFRONT_STANDARD',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6356,'MAIN_MENUFRONT_SMARTPHONE',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6377,'COMMANDE_SAPHIR_MASK',1,'{yy}{mm}{000}{ttt}','chaine',0,'','2017-09-06 07:56:25'),(6461,'MAIN_INFO_SOCIETE_COUNTRY',1,'117:IN:India','chaine',0,'','2017-09-06 08:51:11'),(6462,'MAIN_INFO_SOCIETE_NOM',1,'MyBigCompany','chaine',0,'','2017-09-06 08:51:11'),(6463,'MAIN_INFO_SOCIETE_ADDRESS',1,'21 Jump street..ll..ee \"','chaine',0,'','2017-09-06 08:51:11'),(6464,'MAIN_INFO_SOCIETE_TOWN',1,'MyTown','chaine',0,'','2017-09-06 08:51:12'),(6465,'MAIN_INFO_SOCIETE_ZIP',1,'75500','chaine',0,'','2017-09-06 08:51:12'),(6466,'MAIN_INFO_SOCIETE_STATE',1,'290','chaine',0,'','2017-09-06 08:51:12'),(6467,'MAIN_MONNAIE',1,'EUR','chaine',0,'','2017-09-06 08:51:12'),(6468,'MAIN_INFO_SOCIETE_TEL',1,'09123123','chaine',0,'','2017-09-06 08:51:12'),(6469,'MAIN_INFO_SOCIETE_FAX',1,'09123124','chaine',0,'','2017-09-06 08:51:12'),(6470,'MAIN_INFO_SOCIETE_MAIL',1,'myemail@mybigcompany.com','chaine',0,'','2017-09-06 08:51:12'),(6471,'MAIN_INFO_SOCIETE_WEB',1,'https://www.dolibarr.org','chaine',0,'','2017-09-06 08:51:12'),(6472,'MAIN_INFO_SOCIETE_NOTE',1,'This is note about my company\r\n\"ee\"','chaine',0,'','2017-09-06 08:51:12'),(6473,'MAIN_INFO_SOCIETE_GENCOD',1,'1234567890','chaine',0,'','2017-09-06 08:51:12'),(6474,'MAIN_INFO_SOCIETE_MANAGERS',1,'Zack Zeceo','chaine',0,'','2017-09-06 08:51:12'),(6475,'MAIN_INFO_CAPITAL',1,'10000','chaine',0,'','2017-09-06 08:51:12'),(6476,'MAIN_INFO_SOCIETE_FORME_JURIDIQUE',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6477,'MAIN_INFO_SIREN',1,'123456','chaine',0,'','2017-09-06 08:51:12'),(6478,'MAIN_INFO_SIRET',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6479,'MAIN_INFO_APE',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6480,'MAIN_INFO_RCS',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6481,'MAIN_INFO_PROFID5',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6482,'MAIN_INFO_TVAINTRA',1,'FR1234567','chaine',0,'','2017-09-06 08:51:12'),(6483,'MAIN_INFO_SOCIETE_OBJECT',1,'A company demo to show how Dolibarr ERP CRM is wonderfull','chaine',0,'','2017-09-06 08:51:12'),(6484,'SOCIETE_FISCAL_MONTH_START',1,'4','chaine',0,'','2017-09-06 08:51:12'),(6485,'FACTURE_TVAOPTION',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6486,'FACTURE_LOCAL_TAX1_OPTION',1,'localtax1on','chaine',0,'','2017-09-06 08:51:12'),(6487,'FACTURE_LOCAL_TAX2_OPTION',1,'localtax2on','chaine',0,'','2017-09-06 08:51:12'),(6488,'MAIN_INFO_VALUE_LOCALTAX1',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6489,'MAIN_INFO_LOCALTAX_CALC1',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6490,'MAIN_INFO_VALUE_LOCALTAX2',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6491,'MAIN_INFO_LOCALTAX_CALC2',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6518,'GOOGLE_DUPLICATE_INTO_THIRDPARTIES',1,'1','chaine',0,'','2017-09-06 19:43:57'),(6519,'GOOGLE_DUPLICATE_INTO_CONTACTS',1,'0','chaine',0,'','2017-09-06 19:43:57'),(6520,'GOOGLE_TAG_PREFIX',1,'Dolibarr (Thirdparties)','chaine',0,'','2017-09-06 19:43:57'),(6521,'GOOGLE_TAG_PREFIX_CONTACTS',1,'Dolibarr (Contacts/Addresses)','chaine',0,'','2017-09-06 19:43:57'),(6522,'GOOGLE_ENABLE_AGENDA',1,'1','chaine',0,'','2017-09-06 19:44:12'),(6523,'GOOGLE_AGENDA_COLOR1',1,'1B887A','chaine',0,'','2017-09-06 19:44:12'),(6524,'GOOGLE_AGENDA_COLOR2',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6525,'GOOGLE_AGENDA_COLOR3',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6526,'GOOGLE_AGENDA_COLOR4',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6527,'GOOGLE_AGENDA_COLOR5',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6528,'GOOGLE_AGENDA_TIMEZONE',1,'Europe/Paris','chaine',0,'','2017-09-06 19:44:12'),(6529,'GOOGLE_AGENDA_NB',1,'5','chaine',0,'','2017-09-06 19:44:12'),(6543,'MAIN_SMS_DEBUG',0,'1','chaine',1,'This is to enable OVH SMS debug','2017-09-06 19:44:34'),(6545,'MAIN_MODULE_AGENDA',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6546,'MAIN_MODULE_BARCODE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6547,'MAIN_MODULE_CRON',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6548,'MAIN_MODULE_COMMANDE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6549,'MAIN_MODULE_DON',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6550,'MAIN_MODULE_ECM',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6551,'MAIN_MODULE_FACTURE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6552,'MAIN_MODULE_FOURNISSEUR',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6553,'MAIN_MODULE_HOLIDAY',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6554,'MAIN_MODULE_HOLIDAY_TABS_0',1,'user:+paidholidays:CPTitreMenu:holiday:$user->rights->holiday->read:/holiday/list.php?mainmenu=hrm&id=__ID__','chaine',0,NULL,'2018-01-19 11:17:54'),(6555,'MAIN_MODULE_OPENSURVEY',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6556,'MAIN_MODULE_SOCIETE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6557,'MAIN_MODULE_SERVICE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6558,'MAIN_MODULE_USER',0,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6559,'MAIN_MODULE_SALARIES',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"82.240.38.230\"}','2018-01-19 11:17:54'),(6560,'MAIN_VERSION_LAST_UPGRADE',0,'7.0.0-beta','chaine',0,'Dolibarr version for last upgrade','2018-01-19 11:17:56'),(6562,'BLOCKEDLOG_ENTITY_FINGERPRINT',1,'b63e359ffca54d5c2bab869916eaf23d4a736703028ccbf77ce1167c5f830e7b','chaine',0,'Numeric Unique Fingerprint','2018-01-19 11:27:15'),(6563,'MAIN_MODULE_BLOCKEDLOG',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:27:15'),(6564,'BLOCKEDLOG_DISABLE_NOT_ALLOWED_FOR_COUNTRY',1,'FR','chaine',0,'This is list of country code where the module may be mandatory','2018-01-19 11:27:15'),(6565,'MAIN_MODULE_BOOKMARK',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:27:34'),(6566,'MAIN_MODULE_ADHERENT',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:27:56'),(6567,'ADHERENT_ADDON_PDF',1,'standard','chaine',0,'Name of PDF model of member','2018-01-19 11:27:56'),(6568,'MAIN_MODULE_VARIANTS',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:28:04'),(6569,'MAIN_MODULE_STRIPE',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:28:17'); +INSERT INTO `llx_const` VALUES (8,'MAIN_UPLOAD_DOC',0,'2048','chaine',0,'Max size for file upload (0 means no upload allowed)','2010-07-08 11:17:57'),(9,'MAIN_SEARCHFORM_SOCIETE',0,'1','yesno',0,'Show form for quick company search','2010-07-08 11:17:57'),(10,'MAIN_SEARCHFORM_CONTACT',0,'1','yesno',0,'Show form for quick contact search','2010-07-08 11:17:57'),(11,'MAIN_SEARCHFORM_PRODUITSERVICE',0,'1','yesno',0,'Show form for quick product search','2010-07-08 11:17:58'),(12,'MAIN_SEARCHFORM_ADHERENT',0,'1','yesno',0,'Show form for quick member search','2010-07-08 11:17:58'),(16,'MAIN_SIZE_LISTE_LIMIT',0,'25','chaine',0,'Longueur maximum des listes','2010-07-08 11:17:58'),(29,'MAIN_DELAY_NOT_ACTIVATED_SERVICES',1,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services à activer','2010-07-08 11:17:58'),(33,'SOCIETE_NOLIST_COURRIER',0,'1','yesno',0,'Liste les fichiers du repertoire courrier','2010-07-08 11:17:58'),(36,'ADHERENT_MAIL_REQUIRED',1,'1','yesno',0,'EMail required to create a new member','2010-07-08 11:17:58'),(37,'ADHERENT_MAIL_FROM',1,'adherents@domain.com','chaine',0,'Sender EMail for automatic emails','2010-07-08 11:17:58'),(38,'ADHERENT_MAIL_RESIL',1,'Your subscription has been resiliated.\r\nWe hope to see you soon again','texte',0,'Mail resiliation','2010-07-08 11:17:58'),(39,'ADHERENT_MAIL_VALID',1,'Your subscription has been validated.\r\nThis is a remind of your personal information :\r\n\r\n%INFOS%\r\n\r\n','texte',0,'Mail de validation','2010-07-08 11:17:59'),(40,'ADHERENT_MAIL_COTIS',1,'Hello %PRENOM%,\r\nThanks for your subscription.\r\nThis email confirms that your subscription has been received and processed.\r\n\r\n','texte',0,'Mail de validation de cotisation','2010-07-08 11:17:59'),(41,'ADHERENT_MAIL_VALID_SUBJECT',1,'Your subscription has been validated','chaine',0,'Sujet du mail de validation','2010-07-08 11:17:59'),(42,'ADHERENT_MAIL_RESIL_SUBJECT',1,'Resiliating your subscription','chaine',0,'Sujet du mail de resiliation','2010-07-08 11:17:59'),(43,'ADHERENT_MAIL_COTIS_SUBJECT',1,'Receipt of your subscription','chaine',0,'Sujet du mail de validation de cotisation','2010-07-08 11:17:59'),(44,'MAILING_EMAIL_FROM',1,'dolibarr@domain.com','chaine',0,'EMail emmetteur pour les envois d emailings','2010-07-08 11:17:59'),(45,'ADHERENT_USE_MAILMAN',1,'0','yesno',0,'Utilisation de Mailman','2010-07-08 11:17:59'),(46,'ADHERENT_MAILMAN_UNSUB_URL',1,'http://lists.domain.com/cgi-bin/mailman/admin/%LISTE%/members?adminpw=%MAILMAN_ADMINPW%&user=%EMAIL%','chaine',0,'Url de desinscription aux listes mailman','2010-07-08 11:17:59'),(47,'ADHERENT_MAILMAN_URL',1,'http://lists.domain.com/cgi-bin/mailman/admin/%LISTE%/members?adminpw=%MAILMAN_ADMINPW%&send_welcome_msg_to_this_batch=1&subscribees=%EMAIL%','chaine',0,'Url pour les inscriptions mailman','2010-07-08 11:17:59'),(48,'ADHERENT_MAILMAN_LISTS',1,'test-test,test-test2','chaine',0,'Listes auxquelles inscrire les nouveaux adherents','2010-07-08 11:17:59'),(49,'ADHERENT_MAILMAN_ADMINPW',1,'','chaine',0,'Mot de passe Admin des liste mailman','2010-07-08 11:17:59'),(50,'ADHERENT_MAILMAN_SERVER',1,'lists.domain.com','chaine',0,'Serveur hebergeant les interfaces d Admin des listes mailman','2010-07-08 11:17:59'),(51,'ADHERENT_MAILMAN_LISTS_COTISANT',1,'','chaine',0,'Liste(s) auxquelles les nouveaux cotisants sont inscris automatiquement','2010-07-08 11:17:59'),(52,'ADHERENT_USE_SPIP',1,'0','yesno',0,'Utilisation de SPIP ?','2010-07-08 11:17:59'),(53,'ADHERENT_USE_SPIP_AUTO',1,'0','yesno',0,'Utilisation de SPIP automatiquement','2010-07-08 11:17:59'),(54,'ADHERENT_SPIP_USER',1,'user','chaine',0,'user spip','2010-07-08 11:17:59'),(55,'ADHERENT_SPIP_PASS',1,'pass','chaine',0,'Pass de connection','2010-07-08 11:17:59'),(56,'ADHERENT_SPIP_SERVEUR',1,'localhost','chaine',0,'serveur spip','2010-07-08 11:17:59'),(57,'ADHERENT_SPIP_DB',1,'spip','chaine',0,'db spip','2010-07-08 11:17:59'),(58,'ADHERENT_CARD_HEADER_TEXT',1,'%ANNEE%','chaine',0,'Texte imprime sur le haut de la carte adherent','2010-07-08 11:17:59'),(59,'ADHERENT_CARD_FOOTER_TEXT',1,'Association AZERTY','chaine',0,'Texte imprime sur le bas de la carte adherent','2010-07-08 11:17:59'),(61,'FCKEDITOR_ENABLE_USER',1,'1','yesno',0,'Activation fckeditor sur notes utilisateurs','2010-07-08 11:17:59'),(62,'FCKEDITOR_ENABLE_SOCIETE',1,'1','yesno',0,'Activation fckeditor sur notes societe','2010-07-08 11:17:59'),(63,'FCKEDITOR_ENABLE_PRODUCTDESC',1,'1','yesno',0,'Activation fckeditor sur notes produits','2010-07-08 11:17:59'),(64,'FCKEDITOR_ENABLE_MEMBER',1,'1','yesno',0,'Activation fckeditor sur notes adherent','2010-07-08 11:17:59'),(65,'FCKEDITOR_ENABLE_MAILING',1,'1','yesno',0,'Activation fckeditor sur emailing','2010-07-08 11:17:59'),(67,'DON_ADDON_MODEL',1,'html_cerfafr','chaine',0,'','2010-07-08 11:18:00'),(68,'PROPALE_ADDON',1,'mod_propale_marbre','chaine',0,'','2010-07-08 11:18:00'),(69,'PROPALE_ADDON_PDF',1,'azur','chaine',0,'','2010-07-08 11:18:00'),(70,'COMMANDE_ADDON',1,'mod_commande_marbre','chaine',0,'','2010-07-08 11:18:00'),(71,'COMMANDE_ADDON_PDF',1,'einstein','chaine',0,'','2010-07-08 11:18:00'),(72,'COMMANDE_SUPPLIER_ADDON',1,'mod_commande_fournisseur_muguet','chaine',0,'','2010-07-08 11:18:00'),(73,'COMMANDE_SUPPLIER_ADDON_PDF',1,'muscadet','chaine',0,'','2010-07-08 11:18:00'),(74,'EXPEDITION_ADDON',1,'enlevement','chaine',0,'','2010-07-08 11:18:00'),(76,'FICHEINTER_ADDON',1,'pacific','chaine',0,'','2010-07-08 11:18:00'),(77,'FICHEINTER_ADDON_PDF',1,'soleil','chaine',0,'','2010-07-08 11:18:00'),(79,'FACTURE_ADDON_PDF',1,'crabe','chaine',0,'','2010-07-08 11:18:00'),(80,'PROPALE_VALIDITY_DURATION',1,'15','chaine',0,'Durée de validitée des propales','2010-07-08 11:18:00'),(230,'COMPANY_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/thirdparties','chaine',0,NULL,'2010-07-08 11:26:20'),(238,'LIVRAISON_ADDON_PDF',1,'typhon','chaine',0,'Nom du gestionnaire de generation des commandes en PDF','2010-07-08 11:26:27'),(239,'LIVRAISON_ADDON_NUMBER',1,'mod_livraison_jade','chaine',0,'Nom du gestionnaire de numerotation des bons de livraison','2013-03-20 13:17:36'),(245,'FACTURE_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/invoices','chaine',0,NULL,'2010-07-08 11:28:53'),(249,'DON_FORM',1,'html_cerfafr','chaine',0,'Nom du gestionnaire de formulaire de dons','2017-09-06 16:12:22'),(254,'ADHERENT_BANK_ACCOUNT',1,'','chaine',0,'ID du Compte banquaire utilise','2010-07-08 11:29:05'),(255,'ADHERENT_BANK_CATEGORIE',1,'','chaine',0,'ID de la categorie banquaire des cotisations','2010-07-08 11:29:05'),(256,'ADHERENT_ETIQUETTE_TYPE',1,'L7163','chaine',0,'Type d etiquette (pour impression de planche d etiquette)','2010-07-08 11:29:05'),(269,'PROJECT_ADDON_PDF',1,'baleine','chaine',0,'Nom du gestionnaire de generation des projets en PDF','2010-07-08 11:29:33'),(270,'PROJECT_ADDON',1,'mod_project_simple','chaine',0,'Nom du gestionnaire de numerotation des projets','2010-07-08 11:29:33'),(368,'STOCK_USERSTOCK_AUTOCREATE',1,'1','chaine',0,'','2010-07-08 22:44:59'),(369,'EXPEDITION_ADDON_PDF',1,'merou','chaine',0,'','2010-07-08 22:58:07'),(377,'FACTURE_ADDON',1,'mod_facture_terre','chaine',0,'','2010-07-08 23:08:12'),(380,'ADHERENT_CARD_TEXT',1,'%TYPE% n° %ID%\r\n%PRENOM% %NOM%\r\n<%EMAIL%>\r\n%ADRESSE%\r\n%CP% %VILLE%\r\n%PAYS%','',0,'Texte imprime sur la carte adherent','2010-07-08 23:14:46'),(381,'ADHERENT_CARD_TEXT_RIGHT',1,'aaa','',0,'','2010-07-08 23:14:55'),(385,'PRODUIT_USE_SEARCH_TO_SELECT',1,'1','chaine',0,'','2010-07-08 23:22:19'),(386,'STOCK_CALCULATE_ON_SHIPMENT',1,'1','chaine',0,'','2010-07-08 23:23:21'),(387,'STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER',1,'1','chaine',0,'','2010-07-08 23:23:26'),(392,'MAIN_AGENDA_XCAL_EXPORTKEY',1,'dolibarr','chaine',0,'','2010-07-08 23:27:50'),(393,'MAIN_AGENDA_EXPORT_PAST_DELAY',1,'100','chaine',0,'','2010-07-08 23:27:50'),(610,'CASHDESK_ID_THIRDPARTY',1,'7','chaine',0,'','2010-07-11 17:08:18'),(611,'CASHDESK_ID_BANKACCOUNT_CASH',1,'3','chaine',0,'','2010-07-11 17:08:18'),(612,'CASHDESK_ID_BANKACCOUNT_CHEQUE',1,'1','chaine',0,'','2010-07-11 17:08:18'),(613,'CASHDESK_ID_BANKACCOUNT_CB',1,'1','chaine',0,'','2010-07-11 17:08:18'),(614,'CASHDESK_ID_WAREHOUSE',1,'2','chaine',0,'','2010-07-11 17:08:18'),(660,'LDAP_USER_DN',1,'ou=users,dc=my-domain,dc=com','chaine',0,NULL,'2010-07-18 10:25:27'),(661,'LDAP_GROUP_DN',1,'ou=groups,dc=my-domain,dc=com','chaine',0,NULL,'2010-07-18 10:25:27'),(662,'LDAP_FILTER_CONNECTION',1,'&(objectClass=user)(objectCategory=person)','chaine',0,NULL,'2010-07-18 10:25:27'),(663,'LDAP_FIELD_LOGIN',1,'uid','chaine',0,NULL,'2010-07-18 10:25:27'),(664,'LDAP_FIELD_FULLNAME',1,'cn','chaine',0,NULL,'2010-07-18 10:25:27'),(665,'LDAP_FIELD_NAME',1,'sn','chaine',0,NULL,'2010-07-18 10:25:27'),(666,'LDAP_FIELD_FIRSTNAME',1,'givenname','chaine',0,NULL,'2010-07-18 10:25:27'),(667,'LDAP_FIELD_MAIL',1,'mail','chaine',0,NULL,'2010-07-18 10:25:27'),(668,'LDAP_FIELD_PHONE',1,'telephonenumber','chaine',0,NULL,'2010-07-18 10:25:27'),(669,'LDAP_FIELD_FAX',1,'facsimiletelephonenumber','chaine',0,NULL,'2010-07-18 10:25:27'),(670,'LDAP_FIELD_MOBILE',1,'mobile','chaine',0,NULL,'2010-07-18 10:25:27'),(671,'LDAP_SERVER_TYPE',1,'openldap','chaine',0,'','2010-07-18 10:25:46'),(672,'LDAP_SERVER_PROTOCOLVERSION',1,'3','chaine',0,'','2010-07-18 10:25:47'),(673,'LDAP_SERVER_HOST',1,'localhost','chaine',0,'','2010-07-18 10:25:47'),(674,'LDAP_SERVER_PORT',1,'389','chaine',0,'','2010-07-18 10:25:47'),(675,'LDAP_SERVER_USE_TLS',1,'0','chaine',0,'','2010-07-18 10:25:47'),(676,'LDAP_SYNCHRO_ACTIVE',1,'dolibarr2ldap','chaine',0,'','2010-07-18 10:25:47'),(677,'LDAP_CONTACT_ACTIVE',1,'1','chaine',0,'','2010-07-18 10:25:47'),(678,'LDAP_MEMBER_ACTIVE',1,'1','chaine',0,'','2010-07-18 10:25:47'),(974,'MAIN_MODULE_WORKFLOW_TRIGGERS',1,'1','chaine',0,NULL,'2011-07-18 18:02:20'),(975,'WORKFLOW_PROPAL_AUTOCREATE_ORDER',1,'1','chaine',0,'','2011-07-18 18:02:24'),(980,'PRELEVEMENT_NUMERO_NATIONAL_EMETTEUR',1,'1234567','chaine',0,'','2011-07-18 18:05:50'),(983,'FACTURE_RIB_NUMBER',1,'1','chaine',0,'','2011-07-18 18:35:14'),(984,'FACTURE_CHQ_NUMBER',1,'1','chaine',0,'','2011-07-18 18:35:14'),(1016,'GOOGLE_DUPLICATE_INTO_GCAL',1,'1','chaine',0,'','2011-07-18 21:40:20'),(1152,'SOCIETE_CODECLIENT_ADDON',1,'mod_codeclient_monkey','chaine',0,'','2011-07-29 20:50:02'),(1231,'MAIN_UPLOAD_DOC',1,'2048','chaine',0,'','2011-07-29 21:04:00'),(1234,'MAIN_UMASK',1,'0664','chaine',0,'','2011-07-29 21:04:11'),(1240,'MAIN_LOGEVENTS_USER_LOGIN',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1241,'MAIN_LOGEVENTS_USER_LOGIN_FAILED',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1242,'MAIN_LOGEVENTS_USER_LOGOUT',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1243,'MAIN_LOGEVENTS_USER_CREATE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1244,'MAIN_LOGEVENTS_USER_MODIFY',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1245,'MAIN_LOGEVENTS_USER_NEW_PASSWORD',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1246,'MAIN_LOGEVENTS_USER_ENABLEDISABLE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1247,'MAIN_LOGEVENTS_USER_DELETE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1248,'MAIN_LOGEVENTS_GROUP_CREATE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1249,'MAIN_LOGEVENTS_GROUP_MODIFY',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1250,'MAIN_LOGEVENTS_GROUP_DELETE',1,'1','chaine',0,'','2011-07-29 21:05:01'),(1251,'MAIN_BOXES_MAXLINES',1,'5','',0,'','2011-07-29 21:05:42'),(1482,'EXPEDITION_ADDON_NUMBER',1,'mod_expedition_safor','chaine',0,'Nom du gestionnaire de numerotation des expeditions','2011-08-05 17:53:11'),(1490,'CONTRACT_ADDON',1,'mod_contract_serpis','chaine',0,'Nom du gestionnaire de numerotation des contrats','2011-08-05 18:11:58'),(1677,'COMMANDE_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/orders','chaine',0,NULL,'2012-12-08 13:11:02'),(1698,'PRODUCT_CODEPRODUCT_ADDON',1,'mod_codeproduct_leopard','yesno',0,'Module to control product codes','2012-12-08 13:11:25'),(1724,'PROPALE_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/proposals','chaine',0,NULL,'2012-12-08 13:17:14'),(1730,'OPENSTREETMAP_ENABLE_MAPS',1,'1','chaine',0,'','2012-12-08 13:22:47'),(1731,'OPENSTREETMAP_ENABLE_MAPS_CONTACTS',1,'1','chaine',0,'','2012-12-08 13:22:47'),(1732,'OPENSTREETMAP_ENABLE_MAPS_MEMBERS',1,'1','chaine',0,'','2012-12-08 13:22:47'),(1733,'OPENSTREETMAP_MAPS_ZOOM_LEVEL',1,'15','chaine',0,'','2012-12-08 13:22:47'),(1742,'MAIN_MAIL_EMAIL_FROM',2,'dolibarr-robot@domain.com','chaine',0,'EMail emetteur pour les emails automatiques Dolibarr','2012-12-08 14:08:14'),(1743,'MAIN_MENU_STANDARD',2,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs internes','2013-02-11 19:43:54'),(1744,'MAIN_MENUFRONT_STANDARD',2,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs externes','2013-02-11 19:43:54'),(1745,'MAIN_MENU_SMARTPHONE',2,'iphone_backoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs internes','2012-12-08 14:08:14'),(1746,'MAIN_MENUFRONT_SMARTPHONE',2,'iphone_frontoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs externes','2012-12-08 14:08:14'),(1747,'MAIN_THEME',2,'eldy','chaine',0,'Default theme','2012-12-08 14:08:14'),(1748,'MAIN_DELAY_ACTIONS_TODO',2,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur actions planifiées non réalisées','2012-12-08 14:08:14'),(1749,'MAIN_DELAY_ORDERS_TO_PROCESS',2,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes clients non traitées','2012-12-08 14:08:14'),(1750,'MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS',2,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes fournisseurs non traitées','2012-12-08 14:08:14'),(1751,'MAIN_DELAY_PROPALS_TO_CLOSE',2,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales à cloturer','2012-12-08 14:08:14'),(1752,'MAIN_DELAY_PROPALS_TO_BILL',2,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales non facturées','2012-12-08 14:08:14'),(1753,'MAIN_DELAY_CUSTOMER_BILLS_UNPAYED',2,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures client impayées','2012-12-08 14:08:14'),(1754,'MAIN_DELAY_SUPPLIER_BILLS_TO_PAY',2,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures fournisseur impayées','2012-12-08 14:08:14'),(1755,'MAIN_DELAY_NOT_ACTIVATED_SERVICES',2,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services à activer','2012-12-08 14:08:14'),(1756,'MAIN_DELAY_RUNNING_SERVICES',2,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services expirés','2012-12-08 14:08:14'),(1757,'MAIN_DELAY_MEMBERS',2,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur cotisations adhérent en retard','2012-12-08 14:08:14'),(1758,'MAIN_DELAY_TRANSACTIONS_TO_CONCILIATE',2,'62','chaine',0,'Tolérance de retard avant alerte (en jours) sur rapprochements bancaires à faire','2012-12-08 14:08:14'),(1759,'MAILING_EMAIL_FROM',2,'dolibarr@domain.com','chaine',0,'EMail emmetteur pour les envois d emailings','2012-12-08 14:08:14'),(1760,'MAIN_INFO_SOCIETE_COUNTRY',3,'1:FR:France','chaine',0,'','2013-02-26 21:56:28'),(1761,'MAIN_INFO_SOCIETE_NOM',3,'bbb','chaine',0,'','2012-12-08 14:08:20'),(1762,'MAIN_INFO_SOCIETE_STATE',3,'0','chaine',0,'','2013-02-27 14:20:27'),(1763,'MAIN_MONNAIE',3,'EUR','chaine',0,'','2012-12-08 14:08:20'),(1764,'MAIN_LANG_DEFAULT',3,'auto','chaine',0,'','2012-12-08 14:08:20'),(1765,'MAIN_MAIL_EMAIL_FROM',3,'dolibarr-robot@domain.com','chaine',0,'EMail emetteur pour les emails automatiques Dolibarr','2012-12-08 14:08:20'),(1766,'MAIN_MENU_STANDARD',3,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs internes','2013-02-11 19:43:54'),(1767,'MAIN_MENUFRONT_STANDARD',3,'eldy_menu.php','chaine',0,'Module de gestion de la barre de menu du haut pour utilisateurs externes','2013-02-11 19:43:54'),(1768,'MAIN_MENU_SMARTPHONE',3,'iphone_backoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs internes','2012-12-08 14:08:20'),(1769,'MAIN_MENUFRONT_SMARTPHONE',3,'iphone_frontoffice.php','chaine',0,'Module de gestion de la barre de menu smartphone pour utilisateurs externes','2012-12-08 14:08:20'),(1770,'MAIN_THEME',3,'eldy','chaine',0,'Default theme','2012-12-08 14:08:20'),(1771,'MAIN_DELAY_ACTIONS_TODO',3,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur actions planifiées non réalisées','2012-12-08 14:08:20'),(1772,'MAIN_DELAY_ORDERS_TO_PROCESS',3,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes clients non traitées','2012-12-08 14:08:20'),(1773,'MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS',3,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur commandes fournisseurs non traitées','2012-12-08 14:08:20'),(1774,'MAIN_DELAY_PROPALS_TO_CLOSE',3,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales à cloturer','2012-12-08 14:08:20'),(1775,'MAIN_DELAY_PROPALS_TO_BILL',3,'7','chaine',0,'Tolérance de retard avant alerte (en jours) sur propales non facturées','2012-12-08 14:08:20'),(1776,'MAIN_DELAY_CUSTOMER_BILLS_UNPAYED',3,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures client impayées','2012-12-08 14:08:20'),(1777,'MAIN_DELAY_SUPPLIER_BILLS_TO_PAY',3,'2','chaine',0,'Tolérance de retard avant alerte (en jours) sur factures fournisseur impayées','2012-12-08 14:08:20'),(1778,'MAIN_DELAY_NOT_ACTIVATED_SERVICES',3,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services à activer','2012-12-08 14:08:20'),(1779,'MAIN_DELAY_RUNNING_SERVICES',3,'0','chaine',0,'Tolérance de retard avant alerte (en jours) sur services expirés','2012-12-08 14:08:20'),(1780,'MAIN_DELAY_MEMBERS',3,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur cotisations adhérent en retard','2012-12-08 14:08:20'),(1781,'MAIN_DELAY_TRANSACTIONS_TO_CONCILIATE',3,'62','chaine',0,'Tolérance de retard avant alerte (en jours) sur rapprochements bancaires à faire','2012-12-08 14:08:20'),(1782,'MAILING_EMAIL_FROM',3,'dolibarr@domain.com','chaine',0,'EMail emmetteur pour les envois d emailings','2012-12-08 14:08:20'),(1803,'SYSLOG_FILE',1,'DOL_DATA_ROOT/dolibarr.log','chaine',0,'','2012-12-08 14:15:08'),(1804,'SYSLOG_HANDLERS',1,'[\"mod_syslog_file\"]','chaine',0,'','2012-12-08 14:15:08'),(1805,'MAIN_MODULE_SKINCOLOREDITOR',3,'1',NULL,0,NULL,'2012-12-08 14:35:40'),(1806,'MAIN_MODULE_SKINCOLOREDITOR_TABS_0',3,'user:+tabskincoloreditors:ColorEditor:skincoloreditor@skincoloreditor:/skincoloreditor/usercolors.php?id=__ID__','chaine',0,NULL,'2012-12-08 14:35:40'),(1922,'PAYPAL_API_SANDBOX',1,'1','chaine',0,'','2012-12-12 12:11:05'),(1923,'PAYPAL_API_USER',1,'seller_1355312017_biz_api1.nltechno.com','chaine',0,'','2012-12-12 12:11:05'),(1924,'PAYPAL_API_PASSWORD',1,'1355312040','chaine',0,'','2012-12-12 12:11:05'),(1925,'PAYPAL_API_SIGNATURE',1,'AXqqdsWBzvfn0q5iNmbuiDv1y.3EAXIMWyl4C5KvDReR9HDwwAd6dQ4Q','chaine',0,'','2012-12-12 12:11:05'),(1926,'PAYPAL_API_INTEGRAL_OR_PAYPALONLY',1,'integral','chaine',0,'','2012-12-12 12:11:05'),(1927,'PAYPAL_SECURITY_TOKEN',1,'50c82fab36bb3b6aa83e2a50691803b2','chaine',0,'','2012-12-12 12:11:05'),(1928,'PAYPAL_SECURITY_TOKEN_UNIQUE',1,'0','chaine',0,'','2012-12-12 12:11:05'),(1929,'PAYPAL_ADD_PAYMENT_URL',1,'1','chaine',0,'','2012-12-12 12:11:05'),(1980,'MAIN_PDF_FORMAT',1,'EUA4','chaine',0,'','2012-12-12 19:58:05'),(1981,'MAIN_PROFID1_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1982,'MAIN_PROFID2_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1983,'MAIN_PROFID3_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1984,'MAIN_PROFID4_IN_ADDRESS',1,'0','chaine',0,'','2012-12-12 19:58:05'),(1985,'MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT',1,'0','chaine',0,'','2012-12-12 19:58:05'),(2251,'FCKEDITOR_TEST',1,'Test
\r\n\"\"fdfs','chaine',0,'','2012-12-19 19:12:24'),(2293,'SYSTEMTOOLS_MYSQLDUMP',1,'/usr/bin/mysqldump','chaine',0,'','2012-12-27 02:02:00'),(2835,'MAIN_USE_CONNECT_TIMEOUT',1,'10','chaine',0,'','2013-01-16 19:28:50'),(2836,'MAIN_USE_RESPONSE_TIMEOUT',1,'30','chaine',0,'','2013-01-16 19:28:50'),(2837,'MAIN_PROXY_USE',1,'0','chaine',0,'','2013-01-16 19:28:50'),(2838,'MAIN_PROXY_HOST',1,'localhost','chaine',0,'','2013-01-16 19:28:50'),(2839,'MAIN_PROXY_PORT',1,'8080','chaine',0,'','2013-01-16 19:28:50'),(2840,'MAIN_PROXY_USER',1,'aaa','chaine',0,'','2013-01-16 19:28:50'),(2841,'MAIN_PROXY_PASS',1,'bbb','chaine',0,'','2013-01-16 19:28:50'),(2848,'OVHSMS_NICK',1,'BN196-OVH','chaine',0,'','2013-01-16 19:32:36'),(2849,'OVHSMS_PASS',1,'bigone-10','chaine',0,'','2013-01-16 19:32:36'),(2850,'OVHSMS_SOAPURL',1,'https://www.ovh.com/soapi/soapi-re-1.55.wsdl','chaine',0,'','2013-01-16 19:32:36'),(2854,'THEME_ELDY_RGB',1,'bfbf00','chaine',0,'','2013-01-18 10:02:53'),(2855,'THEME_ELDY_ENABLE_PERSONALIZED',1,'0','chaine',0,'','2013-01-18 10:02:55'),(2858,'MAIN_SESSION_TIMEOUT',1,'2000','chaine',0,'','2013-01-19 17:01:53'),(2862,'TICKET_ADDON',1,'mod_ticket_avenc','chaine',0,'Nom du gestionnaire de numerotation des tickets','2013-01-19 17:16:10'),(2867,'FACSIM_ADDON',1,'mod_facsim_alcoy','chaine',0,'','2013-01-19 17:16:25'),(2868,'POS_SERVICES',1,'0','chaine',0,'','2013-01-19 17:16:51'),(2869,'POS_USE_TICKETS',1,'1','chaine',0,'','2013-01-19 17:16:51'),(2870,'POS_MAX_TTC',1,'100','chaine',0,'','2013-01-19 17:16:51'),(3190,'MAIN_MODULE_HOLIDAY',2,'1',NULL,0,NULL,'2013-02-01 08:52:34'),(3191,'MAIN_MODULE_HOLIDAY_TABS_0',2,'user:+paidholidays:CPTitreMenu:holiday:$user->rights->holiday->write:/holiday/index.php?mainmenu=holiday&id=__ID__','chaine',0,NULL,'2013-02-01 08:52:34'),(3195,'INVOICE_SUPPLIER_ADDON_PDF',1,'canelle','chaine',0,'','2013-02-10 19:50:27'),(3199,'MAIN_FORCE_RELOAD_PAGE',1,'1','chaine',0,NULL,'2013-02-12 16:22:55'),(3217,'MAIN_PDF_TITLE_BACKGROUND_COLOR',1,'240,240,240','chaine',1,'','2013-02-13 15:18:02'),(3223,'OVH_THIRDPARTY_IMPORT',1,'2','chaine',0,'','2013-02-13 16:20:18'),(3241,'COMPANY_USE_SEARCH_TO_SELECT',1,'2','chaine',0,'','2013-02-17 14:33:39'),(3409,'AGENDA_USE_EVENT_TYPE',1,'1','chaine',0,'','2013-02-27 18:12:24'),(3886,'MAIN_REMOVE_INSTALL_WARNING',1,'1','chaine',1,'','2013-03-02 18:32:50'),(4013,'MAIN_DELAY_ACTIONS_TODO',1,'7','chaine',0,'','2013-03-06 08:59:12'),(4014,'MAIN_DELAY_PROPALS_TO_CLOSE',1,'31','chaine',0,'','2013-03-06 08:59:12'),(4015,'MAIN_DELAY_PROPALS_TO_BILL',1,'7','chaine',0,'','2013-03-06 08:59:12'),(4016,'MAIN_DELAY_ORDERS_TO_PROCESS',1,'2','chaine',0,'','2013-03-06 08:59:12'),(4017,'MAIN_DELAY_CUSTOMER_BILLS_UNPAYED',1,'31','chaine',0,'','2013-03-06 08:59:12'),(4018,'MAIN_DELAY_SUPPLIER_ORDERS_TO_PROCESS',1,'7','chaine',0,'','2013-03-06 08:59:12'),(4019,'MAIN_DELAY_SUPPLIER_BILLS_TO_PAY',1,'2','chaine',0,'','2013-03-06 08:59:12'),(4020,'MAIN_DELAY_RUNNING_SERVICES',1,'-15','chaine',0,'','2013-03-06 08:59:12'),(4021,'MAIN_DELAY_TRANSACTIONS_TO_CONCILIATE',1,'62','chaine',0,'','2013-03-06 08:59:13'),(4022,'MAIN_DELAY_MEMBERS',1,'31','chaine',0,'','2013-03-06 08:59:13'),(4023,'MAIN_DISABLE_METEO',1,'0','chaine',0,'','2013-03-06 08:59:13'),(4044,'ADHERENT_VAT_FOR_SUBSCRIPTIONS',1,'0','',0,'','2013-03-06 16:06:38'),(4047,'ADHERENT_BANK_USE',1,'bankviainvoice','',0,'','2013-03-06 16:12:30'),(4049,'PHPSANE_SCANIMAGE',1,'/usr/bin/scanimage','chaine',0,'','2013-03-06 21:54:13'),(4050,'PHPSANE_PNMTOJPEG',1,'/usr/bin/pnmtojpeg','chaine',0,'','2013-03-06 21:54:13'),(4051,'PHPSANE_PNMTOTIFF',1,'/usr/bin/pnmtotiff','chaine',0,'','2013-03-06 21:54:13'),(4052,'PHPSANE_OCR',1,'/usr/bin/gocr','chaine',0,'','2013-03-06 21:54:13'),(4548,'ECM_AUTO_TREE_ENABLED',1,'1','chaine',0,'','2013-03-10 15:57:21'),(4579,'MAIN_MODULE_AGENDA',2,'1',NULL,0,NULL,'2013-03-13 15:29:19'),(4580,'MAIN_AGENDA_ACTIONAUTO_COMPANY_CREATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4581,'MAIN_AGENDA_ACTIONAUTO_CONTRACT_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4582,'MAIN_AGENDA_ACTIONAUTO_PROPAL_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4583,'MAIN_AGENDA_ACTIONAUTO_PROPAL_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4584,'MAIN_AGENDA_ACTIONAUTO_ORDER_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4585,'MAIN_AGENDA_ACTIONAUTO_ORDER_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4586,'MAIN_AGENDA_ACTIONAUTO_BILL_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4587,'MAIN_AGENDA_ACTIONAUTO_BILL_PAYED',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4588,'MAIN_AGENDA_ACTIONAUTO_BILL_CANCEL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4589,'MAIN_AGENDA_ACTIONAUTO_BILL_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4590,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4591,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4592,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_VALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4593,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_SENTBYMAIL',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4594,'MAIN_AGENDA_ACTIONAUTO_BILL_UNVALIDATE',2,'1','chaine',0,NULL,'2013-03-13 15:29:19'),(4595,'MAIN_MODULE_GOOGLE',2,'1',NULL,0,NULL,'2013-03-13 15:29:47'),(4596,'MAIN_MODULE_GOOGLE_TABS_0',2,'agenda:+gcal:MenuAgendaGoogle:google@google:$conf->google->enabled && $conf->global->GOOGLE_ENABLE_AGENDA:/google/index.php','chaine',0,NULL,'2013-03-13 15:29:47'),(4597,'MAIN_MODULE_GOOGLE_TABS_1',2,'user:+gsetup:GoogleUserConf:google@google:$conf->google->enabled && $conf->global->GOOGLE_DUPLICATE_INTO_GCAL:/google/admin/google_calsync_user.php?id=__ID__','chaine',0,NULL,'2013-03-13 15:29:47'),(4598,'MAIN_MODULE_GOOGLE_TRIGGERS',2,'1','chaine',0,NULL,'2013-03-13 15:29:47'),(4599,'MAIN_MODULE_GOOGLE_HOOKS',2,'[\"toprightmenu\"]','chaine',0,NULL,'2013-03-13 15:29:47'),(4688,'GOOGLE_ENABLE_AGENDA',2,'1','chaine',0,'','2013-03-13 15:36:29'),(4689,'GOOGLE_AGENDA_NAME1',2,'eldy','chaine',0,'','2013-03-13 15:36:29'),(4690,'GOOGLE_AGENDA_SRC1',2,'eldy10@mail.com','chaine',0,'','2013-03-13 15:36:29'),(4691,'GOOGLE_AGENDA_COLOR1',2,'BE6D00','chaine',0,'','2013-03-13 15:36:29'),(4692,'GOOGLE_AGENDA_COLOR2',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4693,'GOOGLE_AGENDA_COLOR3',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4694,'GOOGLE_AGENDA_COLOR4',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4695,'GOOGLE_AGENDA_COLOR5',2,'7A367A','chaine',0,'','2013-03-13 15:36:29'),(4696,'GOOGLE_AGENDA_TIMEZONE',2,'Europe/Paris','chaine',0,'','2013-03-13 15:36:29'),(4697,'GOOGLE_AGENDA_NB',2,'5','chaine',0,'','2013-03-13 15:36:29'),(4725,'SOCIETE_CODECLIENT_ADDON',2,'mod_codeclient_leopard','chaine',0,'Module to control third parties codes','2013-03-13 20:21:35'),(4726,'SOCIETE_CODECOMPTA_ADDON',2,'mod_codecompta_panicum','chaine',0,'Module to control third parties codes','2013-03-13 20:21:35'),(4727,'SOCIETE_FISCAL_MONTH_START',2,'','chaine',0,'Mettre le numero du mois du debut d\\\'annee fiscale, ex: 9 pour septembre','2013-03-13 20:21:35'),(4728,'MAIN_SEARCHFORM_SOCIETE',2,'1','yesno',0,'Show form for quick company search','2013-03-13 20:21:35'),(4729,'MAIN_SEARCHFORM_CONTACT',2,'1','yesno',0,'Show form for quick contact search','2013-03-13 20:21:35'),(4730,'COMPANY_ADDON_PDF_ODT_PATH',2,'DOL_DATA_ROOT/doctemplates/thirdparties','chaine',0,NULL,'2013-03-13 20:21:35'),(4743,'MAIN_MODULE_CLICKTODIAL',2,'1',NULL,0,NULL,'2013-03-13 20:30:28'),(4744,'MAIN_MODULE_NOTIFICATION',2,'1',NULL,0,NULL,'2013-03-13 20:30:34'),(4745,'MAIN_MODULE_WEBSERVICES',2,'1',NULL,0,NULL,'2013-03-13 20:30:41'),(4746,'MAIN_MODULE_PROPALE',2,'1',NULL,0,NULL,'2013-03-13 20:32:38'),(4747,'PROPALE_ADDON_PDF',2,'azur','chaine',0,'Nom du gestionnaire de generation des propales en PDF','2013-03-13 20:32:38'),(4748,'PROPALE_ADDON',2,'mod_propale_marbre','chaine',0,'Nom du gestionnaire de numerotation des propales','2013-03-13 20:32:38'),(4749,'PROPALE_VALIDITY_DURATION',2,'15','chaine',0,'Duration of validity of business proposals','2013-03-13 20:32:38'),(4750,'PROPALE_ADDON_PDF_ODT_PATH',2,'DOL_DATA_ROOT/doctemplates/proposals','chaine',0,NULL,'2013-03-13 20:32:38'),(4752,'MAIN_MODULE_TAX',2,'1',NULL,0,NULL,'2013-03-13 20:32:47'),(4753,'MAIN_MODULE_DON',2,'1',NULL,0,NULL,'2013-03-13 20:32:54'),(4754,'DON_ADDON_MODEL',2,'html_cerfafr','chaine',0,'Nom du gestionnaire de generation de recu de dons','2013-03-13 20:32:54'),(4755,'POS_USE_TICKETS',2,'1','chaine',0,'','2013-03-13 20:33:09'),(4756,'POS_MAX_TTC',2,'100','chaine',0,'','2013-03-13 20:33:09'),(4757,'MAIN_MODULE_POS',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4758,'TICKET_ADDON',2,'mod_ticket_avenc','chaine',0,'Nom du gestionnaire de numerotation des tickets','2013-03-13 20:33:09'),(4759,'MAIN_MODULE_BANQUE',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4760,'MAIN_MODULE_FACTURE',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4761,'FACTURE_ADDON_PDF',2,'crabe','chaine',0,'Name of PDF model of invoice','2013-03-13 20:33:09'),(4762,'FACTURE_ADDON',2,'mod_facture_terre','chaine',0,'Name of numbering numerotation rules of invoice','2013-03-13 20:33:09'),(4763,'FACTURE_ADDON_PDF_ODT_PATH',2,'DOL_DATA_ROOT/doctemplates/invoices','chaine',0,NULL,'2013-03-13 20:33:09'),(4764,'MAIN_MODULE_SOCIETE',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4765,'MAIN_MODULE_PRODUCT',2,'1',NULL,0,NULL,'2013-03-13 20:33:09'),(4766,'PRODUCT_CODEPRODUCT_ADDON',2,'mod_codeproduct_leopard','chaine',0,'Module to control product codes','2013-03-13 20:33:09'),(4767,'MAIN_SEARCHFORM_PRODUITSERVICE',2,'1','yesno',0,'Show form for quick product search','2013-03-13 20:33:09'),(4772,'FACSIM_ADDON',2,'mod_facsim_alcoy','chaine',0,'','2013-03-13 20:33:32'),(4773,'MAIN_MODULE_MAILING',2,'1',NULL,0,NULL,'2013-03-13 20:33:37'),(4774,'MAIN_MODULE_OPENSURVEY',2,'1',NULL,0,NULL,'2013-03-13 20:33:42'),(4782,'AGENDA_USE_EVENT_TYPE',2,'1','chaine',0,'','2013-03-13 20:53:36'),(4884,'AGENDA_DISABLE_EXT',2,'1','chaine',0,'','2013-03-13 22:03:40'),(4928,'COMMANDE_SUPPLIER_ADDON_NUMBER',1,'mod_commande_fournisseur_muguet','chaine',0,'Nom du gestionnaire de numerotation des commandes fournisseur','2013-03-22 09:24:29'),(4929,'INVOICE_SUPPLIER_ADDON_NUMBER',1,'mod_facture_fournisseur_cactus','chaine',0,'Nom du gestionnaire de numerotation des factures fournisseur','2013-03-22 09:24:29'),(5001,'MAIN_CRON_KEY',0,'bc54582fe30d5d4a830c6f582ec28810','chaine',0,'','2013-03-23 17:54:53'),(5009,'CRON_KEY',0,'2c2e755c20be2014098f629865598006','chaine',0,'','2013-03-23 18:06:24'),(5139,'SOCIETE_ADD_REF_IN_LIST',1,'','yesno',0,'Display customer ref into select list','2013-09-08 23:06:08'),(5150,'PROJECT_TASK_ADDON_PDF',1,'','chaine',0,'Name of PDF/ODT tasks manager class','2013-09-08 23:06:14'),(5151,'PROJECT_TASK_ADDON',1,'mod_task_simple','chaine',0,'Name of Numbering Rule task manager class','2013-09-08 23:06:14'),(5152,'PROJECT_TASK_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/tasks','chaine',0,'','2013-09-08 23:06:14'),(5239,'BOOKMARKS_SHOW_IN_MENU',1,'10','chaine',0,'','2014-03-02 15:42:26'),(5271,'DONATION_ART200',1,'','yesno',0,'Option Française - Eligibilité Art200 du CGI','2014-12-21 12:51:28'),(5272,'DONATION_ART238',1,'','yesno',0,'Option Française - Eligibilité Art238 bis du CGI','2014-12-21 12:51:28'),(5273,'DONATION_ART885',1,'','yesno',0,'Option Française - Eligibilité Art885-0 V bis du CGI','2014-12-21 12:51:28'),(5274,'DONATION_MESSAGE',1,'Thank you','chaine',0,'Message affiché sur le récépissé de versements ou dons','2014-12-21 12:51:28'),(5288,'DONATION_ACCOUNTINGACCOUNT',1,'7581','chaine',0,'Compte comptable de remise des versements ou dons','2015-07-19 13:41:21'),(5349,'MAIN_SEARCHFORM_CONTACT',1,'1','chaine',0,'','2015-10-03 10:11:33'),(5351,'MAIN_SEARCHFORM_PRODUITSERVICE',1,'1','chaine',0,'','2015-10-03 10:11:33'),(5352,'MAIN_SEARCHFORM_PRODUITSERVICE_SUPPLIER',1,'0','chaine',0,'','2015-10-03 10:11:33'),(5353,'MAIN_SEARCHFORM_ADHERENT',1,'1','chaine',0,'','2015-10-03 10:11:33'),(5354,'MAIN_SEARCHFORM_PROJECT',1,'0','chaine',0,'','2015-10-03 10:11:33'),(5394,'FCKEDITOR_ENABLE_DETAILS',1,'1','yesno',0,'WYSIWIG for products details lines for all entities','2015-11-04 15:27:44'),(5395,'FCKEDITOR_ENABLE_USERSIGN',1,'1','yesno',0,'WYSIWIG for user signature','2015-11-04 15:27:44'),(5396,'FCKEDITOR_ENABLE_MAIL',1,'1','yesno',0,'WYSIWIG for products details lines for all entities','2015-11-04 15:27:44'),(5398,'CATEGORIE_RECURSIV_ADD',1,'','yesno',0,'Affect parent categories','2015-11-04 15:27:46'),(5403,'MAIN_MODULE_FCKEDITOR',1,'1',NULL,0,NULL,'2015-11-04 15:41:40'),(5404,'MAIN_MODULE_CATEGORIE',1,'1',NULL,0,NULL,'2015-11-04 15:41:43'),(5415,'EXPEDITION_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/shipment','chaine',0,NULL,'2015-11-15 22:38:28'),(5416,'LIVRAISON_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/delivery','chaine',0,NULL,'2015-11-15 22:38:28'),(5419,'MAIN_MODULE_CASHDESK',1,'1',NULL,0,NULL,'2015-11-15 22:38:33'),(5426,'MAIN_MODULE_PROJET',1,'1',NULL,0,NULL,'2015-11-15 22:38:44'),(5427,'PROJECT_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/projects','chaine',0,NULL,'2015-11-15 22:38:44'),(5428,'PROJECT_USE_OPPORTUNIES',1,'1','chaine',0,NULL,'2015-11-15 22:38:44'),(5430,'MAIN_MODULE_EXPORT',1,'1',NULL,0,NULL,'2015-11-15 22:38:56'),(5431,'MAIN_MODULE_IMPORT',1,'1',NULL,0,NULL,'2015-11-15 22:38:58'),(5432,'MAIN_MODULE_MAILING',1,'1',NULL,0,NULL,'2015-11-15 22:39:00'),(5434,'EXPENSEREPORT_ADDON_PDF',1,'standard','chaine',0,'Name of manager to build PDF expense reports documents','2015-11-15 22:39:05'),(5436,'SALARIES_ACCOUNTING_ACCOUNT_PAYMENT',1,'421','chaine',0,NULL,'2015-11-15 22:39:08'),(5437,'SALARIES_ACCOUNTING_ACCOUNT_CHARGE',1,'641','chaine',0,NULL,'2015-11-15 22:39:08'),(5441,'ADHERENT_ETIQUETTE_TEXT',1,'%FULLNAME%\n%ADDRESS%\n%ZIP% %TOWN%\n%COUNTRY%','texte',0,'Text to print on member address sheets','2015-11-15 22:39:17'),(5443,'MAIN_MODULE_PRELEVEMENT',1,'1',NULL,0,NULL,'2015-11-15 22:39:33'),(5453,'MAIN_MODULE_CONTRAT',1,'1',NULL,0,NULL,'2015-11-15 22:39:52'),(5455,'MAIN_MODULE_FICHEINTER',1,'1',NULL,0,NULL,'2015-11-15 22:39:56'),(5459,'MAIN_MODULE_PAYPAL',1,'1',NULL,0,NULL,'2015-11-15 22:41:02'),(5460,'MAIN_MODULE_MARGIN',1,'1',NULL,0,NULL,'2015-11-15 22:41:47'),(5461,'MAIN_MODULE_MARGIN_TABS_0',1,'product:+margin:Margins:margins:$user->rights->margins->liretous:/margin/tabs/productMargins.php?id=__ID__','chaine',0,NULL,'2015-11-15 22:41:47'),(5462,'MAIN_MODULE_MARGIN_TABS_1',1,'thirdparty:+margin:Margins:margins:empty($user->societe_id) && $user->rights->margins->liretous && ($object->client > 0):/margin/tabs/thirdpartyMargins.php?socid=__ID__','chaine',0,NULL,'2015-11-15 22:41:47'),(5463,'MAIN_MODULE_PROPALE',1,'1',NULL,0,NULL,'2015-11-15 22:41:47'),(5483,'GENBARCODE_BARCODETYPE_THIRDPARTY',1,'6','chaine',0,'','2016-01-16 15:49:43'),(5484,'PRODUIT_DEFAULT_BARCODE_TYPE',1,'2','chaine',0,'','2016-01-16 15:49:46'),(5539,'PRODUCT_USE_OLD_PATH_FOR_PHOTO',0,'0','chaine',1,'Use old path for products images','2016-01-22 13:34:23'),(5541,'MODULE_GOOGLE_DEBUG',1,'0','chaine',1,'','2016-01-22 13:34:57'),(5586,'MAIN_DELAY_EXPENSEREPORTS_TO_PAY',1,'31','chaine',0,'Tolérance de retard avant alerte (en jours) sur les notes de frais impayées','2016-01-22 17:28:18'),(5587,'MAIN_FIX_FOR_BUGGED_MTA',1,'1','chaine',1,'Set constant to fix email ending from PHP with some linux ike system','2016-01-22 17:28:18'),(5590,'MAIN_VERSION_LAST_INSTALL',0,'3.8.3','chaine',0,'Dolibarr version when install','2016-01-22 17:28:42'),(5604,'MAIN_INFO_SOCIETE_LOGO',1,'mybigcompany.png','chaine',0,'','2016-01-22 17:33:49'),(5605,'MAIN_INFO_SOCIETE_LOGO_SMALL',1,'mybigcompany_small.png','chaine',0,'','2016-01-22 17:33:49'),(5606,'MAIN_INFO_SOCIETE_LOGO_MINI',1,'mybigcompany_mini.png','chaine',0,'','2016-01-22 17:33:49'),(5612,'MAIN_ENABLE_LOG_TO_HTML',0,'0','chaine',1,'If this option is set to 1, it is possible to see log output at end of HTML sources by adding paramater logtohtml=1 on URL','2016-03-13 10:54:45'),(5614,'MAIN_SIZE_SHORTLISTE_LIMIT',1,'4','chaine',0,'Longueur maximum des listes courtes (fiche client)','2016-03-13 10:54:46'),(5626,'MAIN_MODULE_SUPPLIERPROPOSAL',1,'1',NULL,0,NULL,'2016-07-30 11:13:20'),(5627,'SUPPLIER_PROPOSAL_ADDON_PDF',1,'aurore','chaine',0,'Name of submodule to generate PDF for supplier quotation request','2016-07-30 11:13:20'),(5628,'SUPPLIER_PROPOSAL_ADDON',1,'mod_supplier_proposal_marbre','chaine',0,'Name of submodule to number supplier quotation request','2016-07-30 11:13:20'),(5629,'SUPPLIER_PROPOSAL_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/supplier_proposal','chaine',0,NULL,'2016-07-30 11:13:20'),(5632,'MAIN_MODULE_RESOURCE',1,'1',NULL,0,NULL,'2016-07-30 11:13:32'),(5633,'MAIN_MODULE_API',1,'1',NULL,0,NULL,'2016-07-30 11:13:54'),(5634,'MAIN_MODULE_WEBSERVICES',1,'1',NULL,0,NULL,'2016-07-30 11:13:56'),(5635,'WEBSERVICES_KEY',1,'dolibarrkey','chaine',0,'','2016-07-30 11:14:04'),(5638,'MAIN_MODULE_EXTERNALRSS',1,'1',NULL,0,NULL,'2016-07-30 11:15:04'),(5639,'EXTERNAL_RSS_TITLE_1',1,'Dolibarr.org News','chaine',0,'','2016-07-30 11:15:25'),(5640,'EXTERNAL_RSS_URLRSS_1',1,'https://www.dolibarr.org/rss','chaine',0,'','2016-07-30 11:15:25'),(5642,'SOCIETE_CODECOMPTA_ADDON',1,'mod_codecompta_aquarium','chaine',0,'','2016-07-30 11:16:42'),(5707,'CASHDESK_NO_DECREASE_STOCK',1,'1','chaine',0,'','2016-07-30 13:38:11'),(5708,'MAIN_MODULE_PRODUCTBATCH',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5710,'MAIN_MODULE_STOCK',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5711,'MAIN_MODULE_PRODUCT',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5712,'MAIN_MODULE_EXPEDITION',1,'1',NULL,0,NULL,'2016-07-30 13:38:11'),(5808,'MARGIN_TYPE',1,'costprice','chaine',0,'','2016-07-30 16:32:18'),(5809,'DISPLAY_MARGIN_RATES',1,'1','chaine',0,'','2016-07-30 16:32:20'),(5810,'MAIN_FEATURES_LEVEL',0,'0','chaine',1,'Level of features to show (0=stable only, 1=stable+experimental, 2=stable+experimental+development','2016-07-30 18:36:15'),(5813,'USER_PASSWORD_PATTERN',1,'8;1;1;1;3;1','chaine',0,'','2016-07-31 16:04:58'),(5814,'MAIN_MODULE_EXPENSEREPORT',1,'1',NULL,0,NULL,'2016-07-31 21:14:32'),(5830,'LOAN_ACCOUNTING_ACCOUNT_CAPITAL',1,'164','chaine',0,NULL,'2017-01-29 15:11:51'),(5831,'LOAN_ACCOUNTING_ACCOUNT_INSURANCE',1,'6162','chaine',0,NULL,'2017-01-29 15:11:51'),(5833,'ACCOUNTING_EXPORT_SEPARATORCSV',1,',','string',0,NULL,'2017-01-29 15:11:56'),(5834,'ACCOUNTING_ACCOUNT_SUSPENSE',1,'471','chaine',0,NULL,'2017-01-29 15:11:56'),(5839,'ACCOUNTING_ACCOUNT_TRANSFER_CASH',1,'58','chaine',0,NULL,'2017-01-29 15:11:56'),(5840,'CHARTOFACCOUNTS',1,'2','chaine',0,NULL,'2017-01-29 15:11:56'),(5841,'ACCOUNTING_EXPORT_MODELCSV',1,'1','chaine',0,NULL,'2017-01-29 15:11:56'),(5842,'ACCOUNTING_LENGTH_GACCOUNT',1,'','chaine',0,NULL,'2017-01-29 15:11:56'),(5843,'ACCOUNTING_LENGTH_AACCOUNT',1,'','chaine',0,NULL,'2017-01-29 15:11:56'),(5844,'ACCOUNTING_LIST_SORT_VENTILATION_TODO',1,'1','yesno',0,NULL,'2017-01-29 15:11:56'),(5845,'ACCOUNTING_LIST_SORT_VENTILATION_DONE',1,'1','yesno',0,NULL,'2017-01-29 15:11:56'),(5846,'ACCOUNTING_EXPORT_DATE',1,'%d%m%Y','chaine',0,NULL,'2017-01-29 15:11:56'),(5848,'ACCOUNTING_EXPORT_FORMAT',1,'csv','chaine',0,NULL,'2017-01-29 15:11:56'),(5853,'MAIN_MODULE_WORKFLOW',1,'1',NULL,0,NULL,'2017-01-29 15:12:12'),(5854,'MAIN_MODULE_NOTIFICATION',1,'1',NULL,0,NULL,'2017-01-29 15:12:35'),(5855,'MAIN_MODULE_OAUTH',1,'1',NULL,0,NULL,'2017-01-29 15:12:41'),(5856,'MAIN_MODULE_PRINTING',1,'1',NULL,0,NULL,'2017-01-29 15:12:44'),(5883,'MAILING_LIMIT_SENDBYWEB',0,'15','chaine',1,'Number of targets to defined packet size when sending mass email','2017-01-29 17:36:33'),(5884,'MAIN_MAIL_DEBUG',1,'0','chaine',1,'','2017-01-29 18:53:02'),(5885,'MAIN_SOAP_DEBUG',1,'0','chaine',1,'','2017-01-29 18:53:02'),(5887,'PROJECT_USE_OPPORTUNITIES',1,'1','chaine',0,'','2017-02-01 12:23:56'),(5888,'PROJECT_HIDE_TASKS',1,'1','chaine',0,'','2017-02-01 12:23:56'),(5889,'MAIN_AGENDA_ACTIONAUTO_COMPANY_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5890,'MAIN_AGENDA_ACTIONAUTO_COMPANY_CREATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5891,'MAIN_AGENDA_ACTIONAUTO_PROPAL_CLOSE_REFUSED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5892,'MAIN_AGENDA_ACTIONAUTO_PROPAL_CLOSE_SIGNED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5893,'MAIN_AGENDA_ACTIONAUTO_PROPAL_CLASSIFY_BILLED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5894,'MAIN_AGENDA_ACTIONAUTO_PROPAL_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5895,'MAIN_AGENDA_ACTIONAUTO_PROPAL_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5896,'MAIN_AGENDA_ACTIONAUTO_ORDER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5897,'MAIN_AGENDA_ACTIONAUTO_ORDER_CLOSE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5898,'MAIN_AGENDA_ACTIONAUTO_ORDER_CANCEL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5899,'MAIN_AGENDA_ACTIONAUTO_ORDER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5900,'MAIN_AGENDA_ACTIONAUTO_ORDER_CLASSIFY_BILLED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5901,'MAIN_AGENDA_ACTIONAUTO_BILL_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5902,'MAIN_AGENDA_ACTIONAUTO_BILL_PAYED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5903,'MAIN_AGENDA_ACTIONAUTO_BILL_CANCEL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5904,'MAIN_AGENDA_ACTIONAUTO_BILL_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5905,'MAIN_AGENDA_ACTIONAUTO_BILL_UNVALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5906,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5907,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_APPROVE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5908,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_RECEIVE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5909,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_SUBMIT',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5910,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_REFUSE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5911,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_CLASSIFY_BILLED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5912,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5913,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_UNVALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5914,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5915,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_PAYED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5916,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5917,'MAIN_AGENDA_ACTIONAUTO_BILL_SUPPLIER_CANCELED',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5918,'MAIN_AGENDA_ACTIONAUTO_CONTRACT_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5919,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_REOPEN',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5920,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5921,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5922,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5923,'MAIN_AGENDA_ACTIONAUTO_SHIPPING_SENTBYMAIL',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5924,'MAIN_AGENDA_ACTIONAUTO_MEMBER_VALIDATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5925,'MAIN_AGENDA_ACTIONAUTO_MEMBER_SUBSCRIPTION',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5926,'MAIN_AGENDA_ACTIONAUTO_MEMBER_MODIFY',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5927,'MAIN_AGENDA_ACTIONAUTO_MEMBER_RESILIATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5928,'MAIN_AGENDA_ACTIONAUTO_MEMBER_DELETE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5929,'MAIN_AGENDA_ACTIONAUTO_PROJECT_CREATE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5930,'MAIN_AGENDA_ACTIONAUTO_PROJECT_DELETE',1,'1','chaine',0,'','2017-02-01 14:48:55'),(5931,'DATABASE_PWD_ENCRYPTED',1,'1','chaine',0,'','2017-02-01 15:06:04'),(5932,'MAIN_DISABLE_ALL_MAILS',1,'0','chaine',0,'','2017-02-01 15:09:09'),(5933,'MAIN_MAIL_SENDMODE',1,'mail','chaine',0,'','2017-02-01 15:09:09'),(5934,'MAIN_MAIL_SMTP_PORT',1,'465','chaine',0,'','2017-02-01 15:09:09'),(5935,'MAIN_MAIL_SMTP_SERVER',1,'smtp.mail.com','chaine',0,'','2017-02-01 15:09:09'),(5936,'MAIN_MAIL_SMTPS_ID',1,'eldy10@mail.com','chaine',0,'','2017-02-01 15:09:09'),(5937,'MAIN_MAIL_SMTPS_PW',1,'bidonge','chaine',0,'','2017-02-01 15:09:09'),(5938,'MAIN_MAIL_EMAIL_FROM',1,'robot@example.com','chaine',0,'','2017-02-01 15:09:09'),(5939,'MAIN_MAIL_DEFAULT_FROMTYPE',1,'user','chaine',0,'','2017-02-01 15:09:09'),(5940,'PRELEVEMENT_ID_BANKACCOUNT',1,'1','chaine',0,'','2017-02-06 04:04:47'),(5941,'PRELEVEMENT_ICS',1,'ICS123456','chaine',0,'','2017-02-06 04:04:47'),(5942,'PRELEVEMENT_USER',1,'1','chaine',0,'','2017-02-06 04:04:47'),(5943,'BANKADDON_PDF',1,'sepamandate','chaine',0,'','2017-02-06 04:13:52'),(5947,'CHEQUERECEIPTS_THYME_MASK',1,'CHK{yy}{mm}-{0000@1}','chaine',0,'','2017-02-06 04:16:27'),(5948,'MAIN_MODULE_LOAN',1,'1',NULL,0,NULL,'2017-02-06 19:19:05'),(5954,'MAIN_SUBMODULE_EXPEDITION',1,'1','chaine',0,'','2017-02-06 23:57:37'),(5963,'MAIN_MODULE_BANQUE',1,'1',NULL,0,NULL,'2017-02-07 18:56:12'),(5964,'MAIN_MODULE_TAX',1,'1',NULL,0,NULL,'2017-02-07 18:56:12'),(5996,'CABINETMED_RHEUMATOLOGY_ON',1,'0','texte',0,'','2017-02-12 19:20:04'),(5999,'MAIN_SEARCHFORM_SOCIETE',1,'1','texte',0,'','2017-02-12 19:20:04'),(6000,'CABINETMED_BANK_PATIENT_REQUIRED',1,'0','texte',0,'','2017-02-12 19:20:04'),(6019,'MAIN_INFO_SOCIETE_COUNTRY',2,'1:FR:France','chaine',0,'','2017-02-15 17:18:22'),(6020,'MAIN_INFO_SOCIETE_NOM',2,'MySecondCompany','chaine',0,'','2017-02-15 17:18:22'),(6021,'MAIN_INFO_SOCIETE_STATE',2,'0','chaine',0,'','2017-02-15 17:18:22'),(6022,'MAIN_MONNAIE',2,'EUR','chaine',0,'','2017-02-15 17:18:22'),(6023,'MAIN_LANG_DEFAULT',2,'auto','chaine',0,'','2017-02-15 17:18:22'),(6032,'MAIN_MODULE_MULTICURRENCY',1,'1',NULL,0,NULL,'2017-02-15 17:29:59'),(6047,'MAIN_MODULE_SYSLOG',0,'1',NULL,0,NULL,'2017-02-15 22:36:58'),(6048,'SYSLOG_FACILITY',0,'LOG_USER','chaine',0,'','2017-02-15 22:37:01'),(6049,'SYSLOG_FIREPHP_INCLUDEPATH',0,'/home/ldestailleur/git/dolibarr_5.0/htdocs/includes/firephp/firephp-core/lib/','chaine',0,'','2017-02-15 22:37:01'),(6050,'SYSLOG_FILE',0,'DOL_DATA_ROOT/dolibarr.log','chaine',0,'','2017-02-15 22:37:01'),(6051,'SYSLOG_CHROMEPHP_INCLUDEPATH',0,'/home/ldestailleur/git/dolibarr_5.0/htdocs/includes/ccampbell/chromephp/','chaine',0,'','2017-02-15 22:37:01'),(6052,'SYSLOG_HANDLERS',0,'[\"mod_syslog_file\"]','chaine',0,'','2017-02-15 22:37:01'),(6054,'SYSLOG_LEVEL',0,'7','chaine',0,'','2017-02-15 22:37:21'),(6074,'CABINETMED_DELAY_TO_LOCK_RECORD',1,'','chaine',1,'Number of days before locking edit of consultation','2017-02-21 00:04:15'),(6092,'MAIN_SIZE_SHORTLIST_LIMIT',0,'3','chaine',0,'Max length for small lists (tabs)','2017-05-12 09:02:38'),(6099,'MAIN_MODULE_SKYPE',1,'1',NULL,0,NULL,'2017-05-12 09:03:51'),(6100,'MAIN_MODULE_GRAVATAR',1,'1',NULL,0,NULL,'2017-05-12 09:03:54'),(6101,'MAIN_MODULE_ACCOUNTING',1,'1',NULL,0,NULL,'2017-05-12 09:14:30'),(6102,'PRODUCT_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/products','chaine',0,'','2017-08-27 13:29:07'),(6103,'CONTRACT_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/contracts','chaine',0,'','2017-08-27 13:29:07'),(6104,'USERGROUP_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/usergroups','chaine',0,'','2017-08-27 13:29:07'),(6105,'USER_ADDON_PDF_ODT_PATH',1,'DOL_DATA_ROOT/doctemplates/users','chaine',0,'','2017-08-27 13:29:07'),(6106,'MAIN_ENABLE_OVERWRITE_TRANSLATION',1,'1','chaine',0,'Enable overwrote of translation','2017-08-27 13:29:07'),(6108,'MAIN_AGENDA_ACTIONAUTO_ORDER_SUPPLIER_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6109,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_CLASSIFY_BILLED',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6110,'MAIN_AGENDA_ACTIONAUTO_FICHINTER_CLASSIFY_UNBILLED',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6111,'MAIN_AGENDA_ACTIONAUTO_PRODUCT_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6112,'MAIN_AGENDA_ACTIONAUTO_PRODUCT_MODIFY',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6113,'MAIN_AGENDA_ACTIONAUTO_PRODUCT_DELETE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6114,'MAIN_AGENDA_ACTIONAUTO_PROJECT_MODIFY',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6115,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6116,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_VALIDATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6117,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_APPROVE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6118,'MAIN_AGENDA_ACTIONAUTO_EXPENSE_REPORT_PAYED',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6119,'MAIN_AGENDA_ACTIONAUTO_HOLIDAY_CREATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6120,'MAIN_AGENDA_ACTIONAUTO_HOLIDAY_VALIDATE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6121,'MAIN_AGENDA_ACTIONAUTO_HOLIDAY_APPROVE',1,'1','chaine',0,NULL,'2017-08-27 13:29:14'),(6137,'MAIN_LANG_DEFAULT',1,'auto','chaine',0,'','2017-08-28 10:19:58'),(6138,'MAIN_MULTILANGS',1,'1','chaine',0,'','2017-08-28 10:19:58'),(6139,'MAIN_THEME',1,'eldy','chaine',0,'','2017-08-28 10:19:58'),(6140,'THEME_ELDY_USE_HOVER',1,'edf4fb','chaine',0,'','2017-08-28 10:19:58'),(6141,'MAIN_SIZE_LISTE_LIMIT',1,'25','chaine',0,'','2017-08-28 10:19:59'),(6142,'MAIN_SIZE_SHORTLIST_LIMIT',1,'3','chaine',0,'','2017-08-28 10:19:59'),(6143,'MAIN_DISABLE_JAVASCRIPT',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6144,'MAIN_BUTTON_HIDE_UNAUTHORIZED',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6145,'MAIN_START_WEEK',1,'1','chaine',0,'','2017-08-28 10:19:59'),(6146,'MAIN_DEFAULT_WORKING_DAYS',1,'1-5','chaine',0,'','2017-08-28 10:19:59'),(6147,'MAIN_DEFAULT_WORKING_HOURS',1,'9-18','chaine',0,'','2017-08-28 10:19:59'),(6148,'MAIN_SHOW_LOGO',1,'1','chaine',0,'','2017-08-28 10:19:59'),(6149,'MAIN_FIRSTNAME_NAME_POSITION',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6150,'MAIN_HELPCENTER_DISABLELINK',0,'1','chaine',0,'','2017-08-28 10:19:59'),(6151,'MAIN_HOME',1,'__(NoteSomeFeaturesAreDisabled)__
\r\n
\r\n__(SomeTranslationAreUncomplete)__
','chaine',0,'','2017-08-28 10:19:59'),(6152,'MAIN_HELP_DISABLELINK',0,'0','chaine',0,'','2017-08-28 10:19:59'),(6153,'MAIN_BUGTRACK_ENABLELINK',1,'0','chaine',0,'','2017-08-28 10:19:59'),(6353,'MAIN_MENU_STANDARD',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6354,'MAIN_MENU_SMARTPHONE',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6355,'MAIN_MENUFRONT_STANDARD',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6356,'MAIN_MENUFRONT_SMARTPHONE',1,'eldy_menu.php','chaine',0,'','2017-08-30 15:14:44'),(6377,'COMMANDE_SAPHIR_MASK',1,'{yy}{mm}{000}{ttt}','chaine',0,'','2017-09-06 07:56:25'),(6461,'MAIN_INFO_SOCIETE_COUNTRY',1,'117:IN:India','chaine',0,'','2017-09-06 08:51:11'),(6462,'MAIN_INFO_SOCIETE_NOM',1,'MyBigCompany','chaine',0,'','2017-09-06 08:51:11'),(6463,'MAIN_INFO_SOCIETE_ADDRESS',1,'21 Jump street..ll..ee \"','chaine',0,'','2017-09-06 08:51:11'),(6464,'MAIN_INFO_SOCIETE_TOWN',1,'MyTown','chaine',0,'','2017-09-06 08:51:12'),(6465,'MAIN_INFO_SOCIETE_ZIP',1,'75500','chaine',0,'','2017-09-06 08:51:12'),(6466,'MAIN_INFO_SOCIETE_STATE',1,'290','chaine',0,'','2017-09-06 08:51:12'),(6467,'MAIN_MONNAIE',1,'EUR','chaine',0,'','2017-09-06 08:51:12'),(6468,'MAIN_INFO_SOCIETE_TEL',1,'09123123','chaine',0,'','2017-09-06 08:51:12'),(6469,'MAIN_INFO_SOCIETE_FAX',1,'09123124','chaine',0,'','2017-09-06 08:51:12'),(6470,'MAIN_INFO_SOCIETE_MAIL',1,'myemail@mybigcompany.com','chaine',0,'','2017-09-06 08:51:12'),(6471,'MAIN_INFO_SOCIETE_WEB',1,'https://www.dolibarr.org','chaine',0,'','2017-09-06 08:51:12'),(6472,'MAIN_INFO_SOCIETE_NOTE',1,'This is note about my company\r\n\"ee\"','chaine',0,'','2017-09-06 08:51:12'),(6473,'MAIN_INFO_SOCIETE_GENCOD',1,'1234567890','chaine',0,'','2017-09-06 08:51:12'),(6474,'MAIN_INFO_SOCIETE_MANAGERS',1,'Zack Zeceo','chaine',0,'','2017-09-06 08:51:12'),(6475,'MAIN_INFO_CAPITAL',1,'10000','chaine',0,'','2017-09-06 08:51:12'),(6476,'MAIN_INFO_SOCIETE_FORME_JURIDIQUE',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6477,'MAIN_INFO_SIREN',1,'123456','chaine',0,'','2017-09-06 08:51:12'),(6478,'MAIN_INFO_SIRET',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6479,'MAIN_INFO_APE',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6480,'MAIN_INFO_RCS',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6481,'MAIN_INFO_PROFID5',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6482,'MAIN_INFO_TVAINTRA',1,'FR1234567','chaine',0,'','2017-09-06 08:51:12'),(6483,'MAIN_INFO_SOCIETE_OBJECT',1,'A company demo to show how Dolibarr ERP CRM is wonderfull','chaine',0,'','2017-09-06 08:51:12'),(6484,'SOCIETE_FISCAL_MONTH_START',1,'4','chaine',0,'','2017-09-06 08:51:12'),(6485,'FACTURE_TVAOPTION',1,'1','chaine',0,'','2017-09-06 08:51:12'),(6486,'FACTURE_LOCAL_TAX1_OPTION',1,'localtax1on','chaine',0,'','2017-09-06 08:51:12'),(6487,'FACTURE_LOCAL_TAX2_OPTION',1,'localtax2on','chaine',0,'','2017-09-06 08:51:12'),(6488,'MAIN_INFO_VALUE_LOCALTAX1',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6489,'MAIN_INFO_LOCALTAX_CALC1',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6490,'MAIN_INFO_VALUE_LOCALTAX2',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6491,'MAIN_INFO_LOCALTAX_CALC2',1,'0','chaine',0,'','2017-09-06 08:51:12'),(6518,'GOOGLE_DUPLICATE_INTO_THIRDPARTIES',1,'1','chaine',0,'','2017-09-06 19:43:57'),(6519,'GOOGLE_DUPLICATE_INTO_CONTACTS',1,'0','chaine',0,'','2017-09-06 19:43:57'),(6520,'GOOGLE_TAG_PREFIX',1,'Dolibarr (Thirdparties)','chaine',0,'','2017-09-06 19:43:57'),(6521,'GOOGLE_TAG_PREFIX_CONTACTS',1,'Dolibarr (Contacts/Addresses)','chaine',0,'','2017-09-06 19:43:57'),(6522,'GOOGLE_ENABLE_AGENDA',1,'1','chaine',0,'','2017-09-06 19:44:12'),(6523,'GOOGLE_AGENDA_COLOR1',1,'1B887A','chaine',0,'','2017-09-06 19:44:12'),(6524,'GOOGLE_AGENDA_COLOR2',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6525,'GOOGLE_AGENDA_COLOR3',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6526,'GOOGLE_AGENDA_COLOR4',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6527,'GOOGLE_AGENDA_COLOR5',1,'7A367A','chaine',0,'','2017-09-06 19:44:12'),(6528,'GOOGLE_AGENDA_TIMEZONE',1,'Europe/Paris','chaine',0,'','2017-09-06 19:44:12'),(6529,'GOOGLE_AGENDA_NB',1,'5','chaine',0,'','2017-09-06 19:44:12'),(6543,'MAIN_SMS_DEBUG',0,'1','chaine',1,'This is to enable OVH SMS debug','2017-09-06 19:44:34'),(6562,'BLOCKEDLOG_ENTITY_FINGERPRINT',1,'b63e359ffca54d5c2bab869916eaf23d4a736703028ccbf77ce1167c5f830e7b','chaine',0,'Numeric Unique Fingerprint','2018-01-19 11:27:15'),(6564,'BLOCKEDLOG_DISABLE_NOT_ALLOWED_FOR_COUNTRY',1,'FR','chaine',0,'This is list of country code where the module may be mandatory','2018-01-19 11:27:15'),(6565,'MAIN_MODULE_BOOKMARK',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:27:34'),(6566,'MAIN_MODULE_ADHERENT',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:27:56'),(6567,'ADHERENT_ADDON_PDF',1,'standard','chaine',0,'Name of PDF model of member','2018-01-19 11:27:56'),(6568,'MAIN_MODULE_VARIANTS',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:28:04'),(6569,'MAIN_MODULE_STRIPE',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"82.240.38.230\"}','2018-01-19 11:28:17'),(6570,'MAIN_MODULE_AGENDA',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6571,'MAIN_MODULE_BARCODE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6572,'MAIN_MODULE_CRON',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6573,'MAIN_MODULE_COMMANDE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6574,'MAIN_MODULE_DON',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6575,'MAIN_MODULE_ECM',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6576,'MAIN_MODULE_FACTURE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6577,'MAIN_MODULE_FOURNISSEUR',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6578,'MAIN_MODULE_HOLIDAY',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6579,'MAIN_MODULE_HOLIDAY_TABS_0',1,'user:+paidholidays:CPTitreMenu:holiday:$user->rights->holiday->read:/holiday/list.php?mainmenu=hrm&id=__ID__','chaine',0,NULL,'2018-03-16 09:54:05'),(6580,'MAIN_MODULE_OPENSURVEY',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6581,'MAIN_MODULE_SOCIETE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6582,'MAIN_MODULE_SERVICE',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6583,'MAIN_MODULE_USER',0,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6584,'MAIN_MODULE_SALARIES',1,'1',NULL,0,'{\"authorid\":0,\"ip\":\"127.0.0.1\"}','2018-03-16 09:54:05'),(6585,'MAIN_VERSION_LAST_UPGRADE',0,'7.0.1','chaine',0,'Dolibarr version for last upgrade','2018-03-16 09:54:07'),(6587,'MAIN_MODULE_BLOCKEDLOG',1,'1',NULL,0,'{\"authorid\":\"12\",\"ip\":\"127.0.0.1\"}','2018-03-16 09:57:24'); /*!40000 ALTER TABLE `llx_const` ENABLE KEYS */; UNLOCK TABLES; @@ -3379,9 +3465,9 @@ DROP TABLE IF EXISTS `llx_contrat`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_contrat` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(50) DEFAULT NULL, - `ref_ext` varchar(255) DEFAULT NULL, - `ref_supplier` varchar(50) DEFAULT NULL, + `ref` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_supplier` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, @@ -3397,21 +3483,21 @@ CREATE TABLE `llx_contrat` ( `fk_user_author` int(11) NOT NULL DEFAULT '0', `fk_user_mise_en_service` int(11) DEFAULT NULL, `fk_user_cloture` int(11) DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, - `ref_customer` varchar(50) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_customer` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_contrat_ref` (`ref`,`entity`), KEY `idx_contrat_fk_soc` (`fk_soc`), KEY `idx_contrat_fk_user_author` (`fk_user_author`), CONSTRAINT `fk_contrat_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_contrat_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3435,10 +3521,10 @@ CREATE TABLE `llx_contrat_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_contrat_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3463,8 +3549,8 @@ CREATE TABLE `llx_contratdet` ( `fk_contrat` int(11) NOT NULL, `fk_product` int(11) DEFAULT NULL, `statut` smallint(6) DEFAULT '0', - `label` text, - `description` text, + `label` text COLLATE utf8_unicode_ci, + `description` text COLLATE utf8_unicode_ci, `fk_remise_except` int(11) DEFAULT NULL, `date_commande` datetime DEFAULT NULL, `date_ouverture_prevue` datetime DEFAULT NULL, @@ -3472,11 +3558,11 @@ CREATE TABLE `llx_contratdet` ( `date_fin_validite` datetime DEFAULT NULL, `date_cloture` datetime DEFAULT NULL, `tva_tx` double(6,3) DEFAULT '0.000', - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `qty` double NOT NULL, `remise_percent` double DEFAULT '0', `subprice` double(24,8) DEFAULT '0.00000000', @@ -3494,10 +3580,10 @@ CREATE TABLE `llx_contratdet` ( `fk_user_author` int(11) NOT NULL DEFAULT '0', `fk_user_ouverture` int(11) DEFAULT NULL, `fk_user_cloture` int(11) DEFAULT NULL, - `commentaire` text, + `commentaire` text COLLATE utf8_unicode_ci, `fk_unit` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -3512,7 +3598,7 @@ CREATE TABLE `llx_contratdet` ( CONSTRAINT `fk_contratdet_fk_contrat` FOREIGN KEY (`fk_contrat`) REFERENCES `llx_contrat` (`rowid`), CONSTRAINT `fk_contratdet_fk_product` FOREIGN KEY (`fk_product`) REFERENCES `llx_product` (`rowid`), CONSTRAINT `fk_contratdet_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3536,10 +3622,10 @@ CREATE TABLE `llx_contratdet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_contratdet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3565,12 +3651,12 @@ CREATE TABLE `llx_contratdet_log` ( `date` datetime NOT NULL, `statut` smallint(6) NOT NULL, `fk_user_author` int(11) NOT NULL, - `commentaire` text, + `commentaire` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), KEY `idx_contratdet_log_fk_contratdet` (`fk_contratdet`), KEY `idx_contratdet_log_date` (`date`), CONSTRAINT `fk_contratdet_log_fk_contratdet` FOREIGN KEY (`fk_contratdet`) REFERENCES `llx_contratdet` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3598,10 +3684,10 @@ CREATE TABLE `llx_cotisation` ( `datef` date DEFAULT NULL, `cotisation` double DEFAULT NULL, `fk_bank` int(11) DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_cotisation` (`fk_adherent`,`dateadh`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3625,39 +3711,39 @@ CREATE TABLE `llx_cronjob` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, - `jobtype` varchar(10) NOT NULL, - `label` text NOT NULL, - `command` varchar(255) DEFAULT NULL, - `classesname` varchar(255) DEFAULT NULL, - `objectname` varchar(255) DEFAULT NULL, - `methodename` varchar(255) DEFAULT NULL, - `params` text, - `md5params` varchar(32) DEFAULT NULL, - `module_name` varchar(255) DEFAULT NULL, + `jobtype` varchar(10) COLLATE utf8_unicode_ci NOT NULL, + `label` text COLLATE utf8_unicode_ci NOT NULL, + `command` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `classesname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `objectname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `methodename` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `params` text COLLATE utf8_unicode_ci, + `md5params` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `module_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `priority` int(11) DEFAULT '0', `datelastrun` datetime DEFAULT NULL, `datenextrun` datetime DEFAULT NULL, `datestart` datetime DEFAULT NULL, `dateend` datetime DEFAULT NULL, `datelastresult` datetime DEFAULT NULL, - `lastresult` text, - `lastoutput` text, - `unitfrequency` varchar(255) NOT NULL DEFAULT '3600', + `lastresult` text COLLATE utf8_unicode_ci, + `lastoutput` text COLLATE utf8_unicode_ci, + `unitfrequency` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '3600', `frequency` int(11) NOT NULL DEFAULT '0', `nbrun` int(11) DEFAULT NULL, `status` int(11) NOT NULL DEFAULT '1', `fk_user_author` int(11) DEFAULT NULL, `fk_user_mod` int(11) DEFAULT NULL, - `note` text, - `libname` varchar(255) DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, + `libname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) DEFAULT '0', `maxrun` int(11) NOT NULL DEFAULT '0', `autodelete` int(11) DEFAULT '0', `fk_mailing` int(11) DEFAULT NULL, - `test` varchar(255) DEFAULT '1', + `test` varchar(255) COLLATE utf8_unicode_ci DEFAULT '1', `processing` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3666,7 +3752,7 @@ CREATE TABLE `llx_cronjob` ( LOCK TABLES `llx_cronjob` WRITE; /*!40000 ALTER TABLE `llx_cronjob` DISABLE KEYS */; -INSERT INTO `llx_cronjob` VALUES (1,'2013-03-23 18:18:39','2013-03-23 19:18:39','command','aaa','aaaa','','','','','','',0,NULL,NULL,'2013-03-23 19:18:00',NULL,NULL,NULL,NULL,'3600',3600,0,0,1,1,'',NULL,0,0,0,NULL,'1',0),(32,'2018-01-19 11:17:54','2018-01-19 11:17:54','method','SendEmailsReminders',NULL,'comm/action/class/actioncomm.class.php','ActionComm','sendEmailsReminder',NULL,NULL,'agenda',10,NULL,NULL,'2018-01-19 11:17:54',NULL,NULL,NULL,NULL,'60',10,NULL,1,NULL,NULL,'SendEMailsReminder',NULL,1,0,0,NULL,'1',0),(33,'2018-01-19 11:17:54','2018-01-19 11:17:54','method','PurgeDeleteTemporaryFilesShort',NULL,'core/class/utils.class.php','Utils','purgeFiles',NULL,NULL,'cron',50,NULL,NULL,'2018-01-19 11:17:54',NULL,NULL,NULL,NULL,'604800',2,NULL,1,NULL,NULL,'PurgeDeleteTemporaryFiles',NULL,1,0,0,NULL,'1',0),(34,'2018-01-19 11:17:54','2018-01-19 11:17:54','method','MakeLocalDatabaseDumpShort',NULL,'core/class/utils.class.php','Utils','dumpDatabase','none,auto,1,auto,10',NULL,'cron',90,NULL,NULL,'2018-01-19 11:17:54',NULL,NULL,NULL,NULL,'604800',1,NULL,0,NULL,NULL,'MakeLocalDatabaseDump',NULL,1,0,0,NULL,'1',0),(35,'2018-01-19 11:17:54','2018-01-19 11:17:54','method','RecurringInvoices',NULL,'compta/facture/class/facture-rec.class.php','FactureRec','createRecurringInvoices',NULL,NULL,'facture',50,NULL,NULL,'2018-01-19 11:17:54',NULL,NULL,NULL,NULL,'86400',1,NULL,1,NULL,NULL,'Generate recurring invoices',NULL,1,0,0,NULL,'1',0); +INSERT INTO `llx_cronjob` VALUES (1,'2013-03-23 18:18:39','2013-03-23 19:18:39','command','aaa','aaaa','','','','','','',0,NULL,NULL,'2013-03-23 19:18:00',NULL,NULL,NULL,NULL,'3600',3600,0,0,1,1,'',NULL,0,0,0,NULL,'1',0),(36,'2018-03-16 09:54:05','2018-03-16 13:54:05','method','SendEmailsReminders',NULL,'comm/action/class/actioncomm.class.php','ActionComm','sendEmailsReminder',NULL,NULL,'agenda',10,NULL,NULL,'2018-03-16 13:54:05',NULL,NULL,NULL,NULL,'60',10,NULL,1,NULL,NULL,'SendEMailsReminder',NULL,1,0,0,NULL,'1',0),(37,'2018-03-16 09:54:05','2018-03-16 13:54:05','method','PurgeDeleteTemporaryFilesShort',NULL,'core/class/utils.class.php','Utils','purgeFiles',NULL,NULL,'cron',50,NULL,NULL,'2018-03-16 13:54:05',NULL,NULL,NULL,NULL,'604800',2,NULL,1,NULL,NULL,'PurgeDeleteTemporaryFiles',NULL,1,0,0,NULL,'1',0),(38,'2018-03-16 09:54:05','2018-03-16 13:54:05','method','MakeLocalDatabaseDumpShort',NULL,'core/class/utils.class.php','Utils','dumpDatabase','none,auto,1,auto,10',NULL,'cron',90,NULL,NULL,'2018-03-16 13:54:05',NULL,NULL,NULL,NULL,'604800',1,NULL,0,NULL,NULL,'MakeLocalDatabaseDump',NULL,1,0,0,NULL,'1',0),(39,'2018-03-16 09:54:05','2018-03-16 13:54:05','method','RecurringInvoices',NULL,'compta/facture/class/facture-rec.class.php','FactureRec','createRecurringInvoices',NULL,NULL,'facture',50,NULL,NULL,'2018-03-16 13:54:05',NULL,NULL,NULL,NULL,'86400',1,NULL,1,NULL,NULL,'Generate recurring invoices',NULL,1,0,0,NULL,'1',0); /*!40000 ALTER TABLE `llx_cronjob` ENABLE KEYS */; UNLOCK TABLES; @@ -3708,7 +3794,7 @@ DROP TABLE IF EXISTS `llx_deplacement`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_deplacement` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(30) DEFAULT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime NOT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -3716,16 +3802,16 @@ CREATE TABLE `llx_deplacement` ( `fk_user` int(11) NOT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, - `type` varchar(12) NOT NULL, + `type` varchar(12) COLLATE utf8_unicode_ci NOT NULL, `fk_statut` int(11) NOT NULL DEFAULT '1', `km` double DEFAULT NULL, `fk_soc` int(11) DEFAULT NULL, `fk_projet` int(11) DEFAULT '0', - `note_private` text, - `note_public` text, - `extraparams` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3747,14 +3833,14 @@ DROP TABLE IF EXISTS `llx_document_model`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_document_model` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `nom` varchar(50) DEFAULT NULL, + `nom` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `type` varchar(20) NOT NULL, - `libelle` varchar(255) DEFAULT NULL, - `description` text, + `type` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_document_model` (`nom`,`type`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=300 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=304 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3763,7 +3849,7 @@ CREATE TABLE `llx_document_model` ( LOCK TABLES `llx_document_model` WRITE; /*!40000 ALTER TABLE `llx_document_model` DISABLE KEYS */; -INSERT INTO `llx_document_model` VALUES (9,'merou',1,'shipping',NULL,NULL),(181,'generic_invoice_odt',1,'invoice','ODT templates','FACTURE_ADDON_PDF_ODT_PATH'),(193,'canelle2',1,'invoice_supplier','canelle2',NULL),(195,'canelle',1,'invoice_supplier','canelle',NULL),(198,'azur',2,'propal',NULL,NULL),(199,'html_cerfafr',2,'donation',NULL,NULL),(200,'crabe',2,'invoice',NULL,NULL),(201,'generic_odt',1,'company','ODT templates','COMPANY_ADDON_PDF_ODT_PATH'),(250,'baleine',1,'project',NULL,NULL),(255,'soleil',1,'ficheinter',NULL,NULL),(256,'azur',1,'propal',NULL,NULL),(270,'aurore',1,'supplier_proposal',NULL,NULL),(273,'beluga',1,'project','beluga',NULL),(274,'rouget',1,'shipping',NULL,NULL),(275,'typhon',1,'delivery',NULL,NULL),(278,'standard',1,'expensereport',NULL,NULL),(281,'sepamandate',1,'bankaccount','sepamandate',NULL),(294,'einstein',1,'order',NULL,NULL),(296,'crabe',1,'invoice',NULL,NULL),(297,'muscadet',1,'order_supplier',NULL,NULL),(298,'html_cerfafr',1,'donation',NULL,NULL),(299,'standard',1,'member',NULL,NULL); +INSERT INTO `llx_document_model` VALUES (9,'merou',1,'shipping',NULL,NULL),(181,'generic_invoice_odt',1,'invoice','ODT templates','FACTURE_ADDON_PDF_ODT_PATH'),(193,'canelle2',1,'invoice_supplier','canelle2',NULL),(195,'canelle',1,'invoice_supplier','canelle',NULL),(198,'azur',2,'propal',NULL,NULL),(199,'html_cerfafr',2,'donation',NULL,NULL),(200,'crabe',2,'invoice',NULL,NULL),(201,'generic_odt',1,'company','ODT templates','COMPANY_ADDON_PDF_ODT_PATH'),(250,'baleine',1,'project',NULL,NULL),(255,'soleil',1,'ficheinter',NULL,NULL),(256,'azur',1,'propal',NULL,NULL),(270,'aurore',1,'supplier_proposal',NULL,NULL),(273,'beluga',1,'project','beluga',NULL),(274,'rouget',1,'shipping',NULL,NULL),(275,'typhon',1,'delivery',NULL,NULL),(278,'standard',1,'expensereport',NULL,NULL),(281,'sepamandate',1,'bankaccount','sepamandate',NULL),(299,'standard',1,'member',NULL,NULL),(300,'einstein',1,'order',NULL,NULL),(301,'html_cerfafr',1,'donation',NULL,NULL),(302,'crabe',1,'invoice',NULL,NULL),(303,'muscadet',1,'order_supplier',NULL,NULL); /*!40000 ALTER TABLE `llx_document_model` ENABLE KEYS */; UNLOCK TABLES; @@ -3776,7 +3862,7 @@ DROP TABLE IF EXISTS `llx_don`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_don` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(30) DEFAULT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_statut` smallint(6) NOT NULL DEFAULT '0', @@ -3785,29 +3871,29 @@ CREATE TABLE `llx_don` ( `amount` double(24,8) DEFAULT NULL, `fk_payment` int(11) DEFAULT NULL, `paid` smallint(6) NOT NULL DEFAULT '0', - `firstname` varchar(50) DEFAULT NULL, - `lastname` varchar(50) DEFAULT NULL, - `societe` varchar(50) DEFAULT NULL, - `address` text, - `zip` varchar(10) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, - `country` varchar(50) DEFAULT NULL, + `firstname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `lastname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `societe` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` text COLLATE utf8_unicode_ci, + `zip` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `country` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_country` int(11) NOT NULL, - `email` varchar(255) DEFAULT NULL, - `phone` varchar(24) DEFAULT NULL, - `phone_mobile` varchar(24) DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone_mobile` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, `public` smallint(6) NOT NULL DEFAULT '1', `fk_projet` int(11) DEFAULT NULL, `fk_user_author` int(11) NOT NULL, `fk_user_valid` int(11) DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `date_valid` datetime DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3831,10 +3917,10 @@ CREATE TABLE `llx_don_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_don_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3855,25 +3941,25 @@ DROP TABLE IF EXISTS `llx_ecm_directories`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_ecm_directories` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `label` varchar(64) NOT NULL, + `label` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `fk_parent` int(11) DEFAULT NULL, - `description` varchar(255) NOT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `cachenbofdoc` int(11) NOT NULL DEFAULT '0', - `fullpath` varchar(750) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `fullpath` varchar(750) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_c` datetime DEFAULT NULL, `date_m` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_c` int(11) DEFAULT NULL, `fk_user_m` int(11) DEFAULT NULL, - `acl` text, + `acl` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_ecm_directories` (`label`,`fk_parent`,`entity`), KEY `idx_ecm_directories_fk_user_c` (`fk_user_c`), KEY `idx_ecm_directories_fk_user_m` (`fk_user_m`), CONSTRAINT `fk_ecm_directories_fk_user_c` FOREIGN KEY (`fk_user_c`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_ecm_directories_fk_user_m` FOREIGN KEY (`fk_user_m`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3895,31 +3981,31 @@ DROP TABLE IF EXISTS `llx_ecm_files`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_ecm_files` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(128) DEFAULT NULL, - `label` varchar(128) NOT NULL, - `share` varchar(128) DEFAULT NULL, + `ref` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `share` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `filename` varchar(255) NOT NULL, - `filepath` varchar(255) DEFAULT NULL, - `fullpath_orig` varchar(750) DEFAULT NULL, - `description` text, - `keywords` text, - `cover` text, - `gen_or_uploaded` varchar(12) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `filename` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `filepath` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `fullpath_orig` varchar(750) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, + `keywords` text COLLATE utf8_unicode_ci, + `cover` text COLLATE utf8_unicode_ci, + `gen_or_uploaded` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_c` datetime DEFAULT NULL, `date_m` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_c` int(11) DEFAULT NULL, `fk_user_m` int(11) DEFAULT NULL, - `acl` text, + `acl` text COLLATE utf8_unicode_ci, `position` int(11) DEFAULT NULL, - `keyword` varchar(750) DEFAULT NULL, - `src_object_type` varchar(32) DEFAULT NULL, + `keyword` varchar(750) COLLATE utf8_unicode_ci DEFAULT NULL, + `src_object_type` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `src_object_id` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_ecm_files` (`filepath`,`filename`,`entity`), KEY `idx_ecm_files_label` (`label`) -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -3928,44 +4014,10 @@ CREATE TABLE `llx_ecm_files` ( LOCK TABLES `llx_ecm_files` WRITE; /*!40000 ALTER TABLE `llx_ecm_files` DISABLE KEYS */; -INSERT INTO `llx_ecm_files` VALUES (1,NULL,'6ff09d1c53ef83fe622b02a320bcfa52',NULL,1,'FA1107-0019.pdf','facture/FA1107-0019','/home/ldestailleur/git/dolibarr_6.0/documents/facture/FA1107-0019/FA1107-0019.pdf','',NULL,NULL,'unknown',NULL,'2017-08-30 15:53:34','2017-08-30 11:53:34',18,NULL,NULL,1,NULL,NULL,NULL),(2,NULL,'a6c8a0f04af73e4dfc059006d7a5f55a',NULL,1,'FA1107-0019_invoice.odt','facture/FA1107-0019','/home/ldestailleur/git/dolibarr_6.0/documents/facture/FA1107-0019/FA1107-0019_invoice.odt','',NULL,NULL,'unknown',NULL,'2017-08-30 15:53:34','2017-08-30 11:53:34',18,NULL,NULL,2,NULL,NULL,NULL),(3,NULL,'24e96a4a0da25d1ac5049ea46d031d3a',NULL,1,'FA1107-0019-depotFacture-1418-INH-N000289-20170720.pdf','facture/FA1107-0019','depotFacture-1418-INH-N000289-20170720.pdf','',NULL,NULL,'uploaded',NULL,'2017-08-30 15:54:45','2017-08-30 11:54:45',18,NULL,NULL,3,NULL,NULL,NULL),(4,NULL,'91a42a4e2c77e826562c83fa84f6fccd',NULL,1,'CO7001-0027-acces-coopinfo.txt','commande/CO7001-0027','acces-coopinfo.txt','',NULL,NULL,'uploaded',NULL,'2017-08-30 16:02:33','2017-08-30 12:02:33',18,NULL,NULL,1,NULL,NULL,NULL),(5,NULL,'3a9283622bae3469fd90eefd90eb53ea',NULL,1,'FA1601-0024.pdf','facture/FA1601-0024','/home/ldestailleur/git/dolibarr_6.0/documents/facture/FA1601-0024/FA1601-0024.pdf','',NULL,NULL,'unknown',NULL,'2017-08-30 16:23:01','2017-08-30 12:23:01',12,NULL,NULL,1,NULL,NULL,NULL),(6,NULL,'24e96a4a0da25d1ac5049ea46d031d3a',NULL,1,'FA1601-0024-depotFacture-1418-INH-N000289-20170720.pdf','facture/FA1601-0024','depotFacture-1418-INH-N000289-20170720.pdf','',NULL,NULL,'uploaded',NULL,'2017-08-30 16:23:14','2017-08-30 12:23:14',12,NULL,NULL,2,NULL,NULL,NULL),(7,NULL,'d41d8cd98f00b204e9800998ecf8427e',NULL,1,'Exxxqqqw','produit/COMP-XP4523','/home/ldestailleur/git/dolibarr_6.0/documents/produit/COMP-XP4523/Exxxqqqw','',NULL,NULL,'unknown',NULL,'2017-08-30 19:03:15','2017-08-30 15:04:02',12,NULL,NULL,2,NULL,NULL,NULL),(8,NULL,'8245ba8e8e345655f06cd904d7d54f73',NULL,1,'compxp4523product.jpg','produit/COMP-XP4523','/home/ldestailleur/git/dolibarr_6.0/documents/produit/COMP-XP4523/compxp4523product.jpg','',NULL,NULL,'unknown',NULL,'2017-08-30 19:03:15','2017-08-30 15:04:02',12,NULL,NULL,1,NULL,NULL,NULL),(9,NULL,'d32552ee874c82b9f0ccab4f309b4b61',NULL,1,'dolihelp.ico','produit/COMP-XP4523','/home/ldestailleur/git/dolibarr_6.0/documents/produit/COMP-XP4523/dolihelp.ico','',NULL,NULL,'unknown',NULL,'2017-08-30 19:03:15','2017-08-30 15:03:15',12,NULL,NULL,3,NULL,NULL,NULL),(10,'afef987559622d6334fdc4a9a134c435','ccd46bbf3ab6c78588a0ba775106258f',NULL,1,'rolluproduct.jpg','produit/ROLLUPABC','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/ROLLUPABC/rolluproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(11,'a8bbc6c6daea9a4dd58d6fb37a77a030','2f1f2ea4b1b4eb9f25ba440c7870ffcd',NULL,1,'dolicloud_logo.png','produit/DOLICLOUD','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLICLOUD/dolicloud_logo.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(12,'bd0951e23023b22ad1cd21fe33ee46bf','03f0be1249e56fd0b3dd02d5545e3675',NULL,1,'applepieproduct.jpg','produit/CAKECONTRIB','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/CAKECONTRIB/applepieproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(13,'e9e029e2d2bbd014162c0b385ab8739a','b7446fb7b54a3085ff7167e2c5b370fd',NULL,1,'pearpieproduct.jpg','produit/PEARPIE','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/PEARPIE/pearpieproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(14,'14eea962fb99dc6dd8ca4474c519f837','973b1603b5eb01aac97eb2d911f4c341',NULL,1,'pinkdressproduct.jpg','produit/PINKDRESS','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/PINKDRESS/pinkdressproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(15,'83616b4ec45e835526144ce114979f49','2adadd910fe97a07bd5be0f1f27f2d28',NULL,1,'dolidroid_114x114.png','produit/DOLIDROID','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLIDROID/dolidroid_114x114.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(16,'6b11123918f12440cac5fa3c3190d2d6','8a0bc12d0e579a5a56e299a1a128ba80',NULL,1,'dolidroid_512x512_en.png','produit/DOLIDROID','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLIDROID/dolidroid_512x512_en.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,2,NULL,NULL,NULL),(17,'5c0ceed2d113af84868710c940e1a921','246708ef260d601dcfa367a6b3258ecf',NULL,1,'dolidroid_screenshot_home_720x1280.png','produit/DOLIDROID','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLIDROID/dolidroid_screenshot_home_720x1280.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,3,NULL,NULL,NULL),(18,'1972b3da7908b3e08247e6e23bb7bdc3','03f0be1249e56fd0b3dd02d5545e3675',NULL,1,'applepieproduct.jpg','produit/APPLEPIE','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/APPLEPIE/applepieproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL); +INSERT INTO `llx_ecm_files` VALUES (1,NULL,'6ff09d1c53ef83fe622b02a320bcfa52',NULL,1,'FA1107-0019.pdf','facture/FA1107-0019','/home/ldestailleur/git/dolibarr_6.0/documents/facture/FA1107-0019/FA1107-0019.pdf','',NULL,NULL,'unknown',NULL,'2017-08-30 15:53:34','2017-08-30 11:53:34',18,NULL,NULL,1,NULL,NULL,NULL),(2,NULL,'a6c8a0f04af73e4dfc059006d7a5f55a',NULL,1,'FA1107-0019_invoice.odt','facture/FA1107-0019','/home/ldestailleur/git/dolibarr_6.0/documents/facture/FA1107-0019/FA1107-0019_invoice.odt','',NULL,NULL,'unknown',NULL,'2017-08-30 15:53:34','2017-08-30 11:53:34',18,NULL,NULL,2,NULL,NULL,NULL),(3,NULL,'24e96a4a0da25d1ac5049ea46d031d3a',NULL,1,'FA1107-0019-depotFacture-1418-INH-N000289-20170720.pdf','facture/FA1107-0019','depotFacture-1418-INH-N000289-20170720.pdf','',NULL,NULL,'uploaded',NULL,'2017-08-30 15:54:45','2017-08-30 11:54:45',18,NULL,NULL,3,NULL,NULL,NULL),(4,NULL,'91a42a4e2c77e826562c83fa84f6fccd',NULL,1,'CO7001-0027-acces-coopinfo.txt','commande/CO7001-0027','acces-coopinfo.txt','',NULL,NULL,'uploaded',NULL,'2017-08-30 16:02:33','2017-08-30 12:02:33',18,NULL,NULL,1,NULL,NULL,NULL),(5,'5fe17a68b2f6a73e6326f77fa7b6586c','a60cad66c6da948eb08d5b939f3516ff',NULL,1,'FA1601-0024.pdf','facture/FA1601-0024','','',NULL,NULL,'generated',NULL,'2017-08-30 16:23:01','2018-03-16 09:59:31',12,12,NULL,1,NULL,NULL,NULL),(6,NULL,'24e96a4a0da25d1ac5049ea46d031d3a',NULL,1,'FA1601-0024-depotFacture-1418-INH-N000289-20170720.pdf','facture/FA1601-0024','depotFacture-1418-INH-N000289-20170720.pdf','',NULL,NULL,'uploaded',NULL,'2017-08-30 16:23:14','2017-08-30 12:23:14',12,NULL,NULL,2,NULL,NULL,NULL),(7,NULL,'d41d8cd98f00b204e9800998ecf8427e',NULL,1,'Exxxqqqw','produit/COMP-XP4523','/home/ldestailleur/git/dolibarr_6.0/documents/produit/COMP-XP4523/Exxxqqqw','',NULL,NULL,'unknown',NULL,'2017-08-30 19:03:15','2017-08-30 15:04:02',12,NULL,NULL,2,NULL,NULL,NULL),(8,NULL,'8245ba8e8e345655f06cd904d7d54f73',NULL,1,'compxp4523product.jpg','produit/COMP-XP4523','/home/ldestailleur/git/dolibarr_6.0/documents/produit/COMP-XP4523/compxp4523product.jpg','',NULL,NULL,'unknown',NULL,'2017-08-30 19:03:15','2017-08-30 15:04:02',12,NULL,NULL,1,NULL,NULL,NULL),(9,NULL,'d32552ee874c82b9f0ccab4f309b4b61',NULL,1,'dolihelp.ico','produit/COMP-XP4523','/home/ldestailleur/git/dolibarr_6.0/documents/produit/COMP-XP4523/dolihelp.ico','',NULL,NULL,'unknown',NULL,'2017-08-30 19:03:15','2017-08-30 15:03:15',12,NULL,NULL,3,NULL,NULL,NULL),(10,'afef987559622d6334fdc4a9a134c435','ccd46bbf3ab6c78588a0ba775106258f',NULL,1,'rolluproduct.jpg','produit/ROLLUPABC','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/ROLLUPABC/rolluproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(11,'a8bbc6c6daea9a4dd58d6fb37a77a030','2f1f2ea4b1b4eb9f25ba440c7870ffcd',NULL,1,'dolicloud_logo.png','produit/DOLICLOUD','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLICLOUD/dolicloud_logo.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(12,'bd0951e23023b22ad1cd21fe33ee46bf','03f0be1249e56fd0b3dd02d5545e3675',NULL,1,'applepieproduct.jpg','produit/CAKECONTRIB','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/CAKECONTRIB/applepieproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(13,'e9e029e2d2bbd014162c0b385ab8739a','b7446fb7b54a3085ff7167e2c5b370fd',NULL,1,'pearpieproduct.jpg','produit/PEARPIE','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/PEARPIE/pearpieproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(14,'14eea962fb99dc6dd8ca4474c519f837','973b1603b5eb01aac97eb2d911f4c341',NULL,1,'pinkdressproduct.jpg','produit/PINKDRESS','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/PINKDRESS/pinkdressproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(15,'83616b4ec45e835526144ce114979f49','2adadd910fe97a07bd5be0f1f27f2d28',NULL,1,'dolidroid_114x114.png','produit/DOLIDROID','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLIDROID/dolidroid_114x114.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL),(16,'6b11123918f12440cac5fa3c3190d2d6','8a0bc12d0e579a5a56e299a1a128ba80',NULL,1,'dolidroid_512x512_en.png','produit/DOLIDROID','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLIDROID/dolidroid_512x512_en.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,2,NULL,NULL,NULL),(17,'5c0ceed2d113af84868710c940e1a921','246708ef260d601dcfa367a6b3258ecf',NULL,1,'dolidroid_screenshot_home_720x1280.png','produit/DOLIDROID','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/DOLIDROID/dolidroid_screenshot_home_720x1280.png','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,3,NULL,NULL,NULL),(18,'1972b3da7908b3e08247e6e23bb7bdc3','03f0be1249e56fd0b3dd02d5545e3675',NULL,1,'applepieproduct.jpg','produit/APPLEPIE','/home/dolibarr/demo.dolibarr.org/dolibarr_documents/produit/APPLEPIE/applepieproduct.jpg','',NULL,NULL,'unknown',NULL,'2018-01-19 11:23:16','2018-01-19 11:23:16',12,NULL,NULL,1,NULL,NULL,NULL); /*!40000 ALTER TABLE `llx_ecm_files` ENABLE KEYS */; UNLOCK TABLES; --- --- Table structure for table `llx_ecommerce_category` --- - -DROP TABLE IF EXISTS `llx_ecommerce_category`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `llx_ecommerce_category` ( - `rowid` int(11) NOT NULL AUTO_INCREMENT, - `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `type` tinyint(4) NOT NULL DEFAULT '1', - `description` text COLLATE utf8_unicode_ci, - `fk_category` int(11) NOT NULL, - `fk_site` int(11) NOT NULL, - `remote_id` int(11) NOT NULL, - `remote_parent_id` int(11) DEFAULT NULL, - `last_update` datetime DEFAULT NULL, - PRIMARY KEY (`rowid`), - UNIQUE KEY `uk_ecommerce_category_fk_site_fk_category` (`fk_site`,`fk_category`), - KEY `idx_ecommerce_category_fk_category` (`fk_category`), - KEY `idx_ecommerce_category_fk_site` (`fk_site`) -) ENGINE=InnoDB AUTO_INCREMENT=2260 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Table transition remote site - Dolibarr'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `llx_ecommerce_category` --- - -LOCK TABLES `llx_ecommerce_category` WRITE; -/*!40000 ALTER TABLE `llx_ecommerce_category` DISABLE KEYS */; -INSERT INTO `llx_ecommerce_category` VALUES (1951,'Root Catalog',0,'',1750,1,3,1,'2007-12-05 04:38:59'),(1952,'Parquets',0,'',1751,1,35,3,'2016-01-19 09:35:20'),(1953,'Parquets massifs exotiques',0,'',1752,1,36,35,'2016-01-19 09:35:44'),(1954,'Parquets massifs salle de bain',0,'',1753,1,135,35,'2016-01-19 09:36:03'),(1955,'Parquets massifs bruts',0,'',1754,1,43,35,'2016-01-19 09:36:24'),(1956,'Parquets massifs naturels',0,'',1755,1,46,35,'2016-01-19 09:36:47'),(1957,'Parquets massifs tendances',0,'',1756,1,136,35,'2016-01-19 09:37:04'),(1958,'Parquets massifs vieillis',0,'',1757,1,227,35,'2016-01-19 09:37:23'),(1959,'Parquets contrecollés classiques',0,'',1758,1,51,35,'2016-01-19 09:37:49'),(1960,'Parquets contrecollés tendance',0,'',1759,1,54,35,'2016-01-19 09:38:09'),(1961,'Parquets contrecollés sols chauffants & rafraîchissants',0,'',1760,1,57,35,'2016-01-19 09:38:35'),(1962,'Parquets contrecollés vieillis',0,'',1761,1,279,35,'2016-01-19 09:38:57'),(1963,'Terrasses',0,'',1762,1,80,3,'2017-03-15 09:46:37'),(1964,'Lames terrasse Bois Exotique',0,'',1763,1,81,80,'2017-03-15 15:34:21'),(1965,'Ipé',0,'',1764,1,461,81,'2017-03-15 09:55:42'),(1966,'Cumaru',0,'',1765,1,462,81,'2017-03-15 09:57:55'),(1967,'Terrasses bois français',0,'',1766,1,84,80,'2015-10-07 15:51:30'),(1968,'Lames Terrasse Bois Composite',0,'',1767,1,86,80,'2017-03-15 14:33:51'),(1969,'Lames Bois Composite',0,'',1768,1,463,86,'2017-03-15 10:24:30'),(1970,'Fixations Lames Composites',0,'',1769,1,464,86,'2017-03-15 10:26:53'),(1971,'Accessoires terrasses',0,'',1770,1,286,80,'2017-03-15 10:58:40'),(1972,'FORET ÉTAGÉ SPAX',0,'',1771,1,396,286,'2017-03-15 10:57:07'),(1973,'CALES ET FEUTRES POUR LAMBOURDE',0,'',1772,1,459,286,'2017-03-15 10:55:50'),(1974,'Feutre Géotextile',0,'',1773,1,466,80,'2017-03-20 15:15:32'),(1975,'Cales pour Lambourdes',0,'',1774,1,394,80,'2017-03-16 13:47:30'),(1976,'Lambourdes Pour Terrasse',0,'',1775,1,465,80,'2017-03-15 10:36:30'),(1977,'Vis Terrasse Inox SPAX',0,'',1776,1,366,80,'2017-03-15 14:26:33'),(1978,'Fixations Terrasse',0,'',1777,1,365,80,'2017-03-20 09:18:10'),(1979,'Plots Pour Terrasse',0,'',1778,1,367,80,'2017-03-16 13:09:09'),(1980,'Redresseur De Lames',0,'',1779,1,368,80,'2017-03-16 13:26:27'),(1981,'Espaceur et gabarit',0,'',1780,1,370,80,'2017-03-16 13:37:38'),(1982,'Forets et Embouts',0,'',1781,1,369,80,'2017-03-16 13:53:06'),(1983,'BANDE D\'ÉTANCHÉITÉ',0,'',1782,1,395,80,'2017-03-15 14:15:22'),(1984,'Luminaires extérieurs terrasses et jardins',0,'',1783,1,290,80,'2017-03-16 13:58:11'),(1985,'ÉCLAIRAGE Á POSER AU SOL',0,'',1784,1,374,290,'2017-03-15 10:59:39'),(1986,'ÉCLAIRAGE MURAL',0,'',1785,1,375,290,'2017-03-15 10:59:44'),(1987,'ÉCLAIRAGE D\'ORIENTATION',0,'',1786,1,376,290,'2017-03-15 11:00:02'),(1988,'Eclairage Design Collection',0,'',1787,1,377,290,'2015-03-05 18:25:35'),(1989,'SPOTS',0,'',1788,1,378,290,'2017-03-15 11:00:08'),(1990,'TRANSFORMATEURS',0,'',1789,1,372,290,'2017-03-15 11:00:14'),(1991,'CÂBLES ET RALLONGES',0,'',1790,1,373,290,'2017-03-15 11:00:24'),(1992,'PROGRAMMATEURS, DÉTECTEURS, TÉLÉCOMMANDES',0,'',1791,1,379,290,'2017-03-15 11:00:31'),(1993,'Revêtements sol & mur',0,'',1792,1,60,3,'2017-02-01 10:13:13'),(1994,'Dalles (cuir, béton ciré...)',0,'',1793,1,63,60,'2017-02-01 13:50:13'),(1995,'Dalles/lames plombantes',0,'',1794,1,401,60,'2017-02-01 13:50:20'),(1996,'Sols en bois',0,'',1795,1,61,60,'2017-02-01 13:50:59'),(1997,'Sols en liège',0,'',1796,1,294,60,'2017-02-01 13:50:52'),(1998,'Stratifiés classiques',0,'',1797,1,68,60,'2016-01-18 17:43:44'),(1999,'Stratifiés tendances',0,'',1798,1,72,60,'2016-01-18 17:44:37'),(2000,'Stratifiés salles de bain',0,'',1799,1,76,60,'2016-01-18 17:45:57'),(2001,'Stratifiés économiques',0,'',1800,1,78,60,'2016-01-18 17:45:32'),(2002,'Stratifiés colle intégrée',0,'',1801,1,421,60,'2016-03-24 10:36:21'),(2003,'Vinyles Click',0,'',1802,1,400,60,'2016-01-18 17:46:15'),(2004,'Vinyles',0,'',1803,1,283,60,'2015-10-29 16:09:39'),(2005,'Lambris',0,'',1804,1,89,60,'2017-02-01 10:13:57'),(2006,'Lambris massifs pin',0,'',1805,1,90,89,'2017-02-01 10:15:07'),(2007,'Accessoires lambris',0,'',1806,1,302,89,'2017-02-01 10:15:13'),(2008,'Entretien',0,'',1807,1,323,3,'2016-01-18 17:08:34'),(2009,'Toutes surfaces',0,'',1808,1,335,323,'2016-03-31 12:14:59'),(2010,'Parquet ciré',0,'',1809,1,359,323,'2016-03-31 12:21:35'),(2011,'Parquet huilé',0,'',1810,1,337,323,'2016-03-31 12:26:55'),(2012,'Parquet vitrifié',0,'',1811,1,339,323,'2016-03-31 12:31:26'),(2013,'Sols stratifié - Vinyl',0,'',1812,1,340,323,'2016-03-31 12:40:36'),(2014,'Terrasse',0,'',1813,1,341,323,'2016-01-18 17:10:11'),(2015,'SATURATEURS ENVIRONNEMENT',0,'',1814,1,342,341,'2017-02-01 14:30:22'),(2016,'SATURATEURS BOIS',0,'',1815,1,343,341,'2017-02-01 14:30:30'),(2017,'SATURATEURS MONOCOUCHE',0,'',1816,1,344,341,'2017-02-01 14:30:41'),(2018,'SATURATEURS OPAQUES',0,'',1817,1,345,341,'2017-02-01 14:33:53'),(2019,'NETTOYANTS - DÉGRISEURS',0,'',1818,1,346,341,'2017-02-01 14:31:20'),(2020,'RÉNOVATEURS COMPOSITES',0,'',1819,1,347,341,'2017-02-01 14:31:33'),(2021,'SYSTÈME ANTI UV',0,'',1820,1,348,341,'2017-02-01 14:32:02'),(2022,'Bardage',0,'',1821,1,349,323,'2016-01-18 17:20:26'),(2023,'HUILES BARDAGES',0,'',1822,1,350,349,'2017-02-01 14:32:18'),(2024,'LASURES OPTEM',0,'',1823,1,352,349,'2017-02-01 14:32:25'),(2025,'LASURES HPS',0,'',1824,1,353,349,'2017-02-01 14:32:32'),(2026,'LASURES ENVIRONNEMENT',0,'',1825,1,354,349,'2017-02-01 14:32:42'),(2027,'PEINTURES ENVIRONNEMENT',0,'',1826,1,355,349,'2017-02-01 14:32:51'),(2028,'SYSTÈME ANTI UV BARDAGE',0,'',1827,1,356,349,'2017-07-04 22:36:49'),(2029,'HUILES POUR TECK',0,'',1828,1,413,349,'2017-02-01 14:33:13'),(2030,'Décapants',0,'',1829,1,357,323,'2016-03-31 12:46:26'),(2031,'Décireurs',0,'',1830,1,361,323,'2016-03-31 12:52:31'),(2032,'Traitements',0,'',1831,1,358,323,'2016-03-31 12:54:22'),(2033,'Pose',0,'',1832,1,128,3,'2015-01-22 14:40:40'),(2034,'Finition',0,'',1833,1,319,3,'2016-01-18 17:25:47'),(2035,'HUILES POUR PARQUETS',0,'',1834,1,239,319,'2017-02-01 13:54:09'),(2036,'HUILES ENVIRONNEMENT',0,'',1835,1,334,239,'2017-02-01 13:55:03'),(2037,'HUILES CIRE',0,'',1836,1,241,239,'2017-02-01 13:55:42'),(2038,'HUILES TRADITIONNELLES',0,'',1837,1,321,239,'2017-02-01 13:55:53'),(2039,'HUILES DURES',0,'',1838,1,322,239,'2017-02-01 13:56:00'),(2040,'LASURES',0,'',1839,1,255,319,'2017-02-01 13:54:17'),(2041,'PEINTURES ENVIRONNEMENT LASURE',0,'',1840,1,325,255,'2017-07-04 22:37:25'),(2042,'LASURES BOIS ENVIRONNEMENT',0,'',1841,1,326,255,'2017-02-01 13:56:35'),(2043,'LASURES OPTEM',0,'',1842,1,327,255,'2017-02-01 13:56:42'),(2044,'LASURES TRADITIONNELLES',0,'',1843,1,328,255,'2017-02-01 13:56:51'),(2045,'PRÉPARATION BOIS EXTERIEUR',0,'',1844,1,329,255,'2017-02-01 13:57:12'),(2046,'VITRIFICATEURS',0,'',1845,1,240,319,'2017-02-01 13:54:27'),(2047,'VITRIFICATEURS TRADITIONNELS',0,'',1846,1,330,240,'2017-02-01 13:57:45'),(2048,'VITRIFICATEURS ENVIRONNEMENT',0,'',1847,1,331,240,'2017-02-01 13:58:00'),(2049,'VITRIFICATEURS OCÉANIC®',0,'',1848,1,332,240,'2017-02-01 13:59:10'),(2050,'VITRIFICATEURS ESCALIERS',0,'',1849,1,333,240,'2017-02-01 13:59:40'),(2051,'VITRIFICATEURS BI COMPOSANTS',0,'',1850,1,404,240,'2017-02-01 13:59:51'),(2052,'Béton - Pavés briques',0,'',1851,1,300,319,'2016-03-25 09:44:34'),(2053,'Peintures sol',0,'',1852,1,271,319,'2016-03-25 09:47:36'),(2054,'Peintures tableau d\'école',0,'',1853,1,360,319,'2016-03-25 09:59:01'),(2055,'Peinture décorative aqua',0,'',1854,1,426,319,'2016-03-25 10:08:28'),(2056,'Primaire fond dur',0,'',1855,1,320,319,'2016-03-25 10:10:34'),(2057,'Teintes parquets - boiseries',0,'',1856,1,324,319,'2016-03-25 10:14:55'),(2058,'Vernis bois',0,'',1857,1,296,319,'2016-03-25 10:16:20'),(2059,'Parquet ciré',0,'',1858,1,414,319,'2016-03-31 12:57:15'),(2060,'Peinture rénovation multi supports',0,'',1859,1,435,319,'2016-10-17 15:33:29'),(2061,'Accessoires disabled',0,'',1860,1,98,3,'2017-07-04 23:32:02'),(2062,'Produits de finitions',0,'',1861,1,108,98,'2015-01-21 11:05:10'),(2063,'Ventes flash',0,'',1862,1,129,3,'2017-02-17 11:21:37'),(2064,'Marques',0,'',1863,1,151,3,'2015-10-16 09:15:18'),(2065,'Blanchon',0,'',1864,1,161,151,'2015-12-14 17:42:03'),(2066,'Colles',0,'',1865,1,263,161,'2015-10-14 17:04:55'),(2067,'Décapants',0,'',1866,1,268,161,'2016-01-18 14:33:19'),(2068,'Décireurs',0,'',1867,1,303,161,'2016-01-18 14:33:39'),(2069,'Dégriseurs bois',0,'',1868,1,250,161,'2016-01-18 14:34:12'),(2070,'Diluants',0,'',1869,1,298,161,'2016-01-22 15:03:39'),(2071,'Encaustique et Cire',0,'',1870,1,416,161,'2016-01-21 09:21:28'),(2072,'Fonds durs',0,'',1871,1,257,161,'2016-01-22 15:04:00'),(2073,'Huiles bardage',0,'',1872,1,351,161,'2016-01-18 14:35:20'),(2074,'Huile-cire dure : Solid\'Oil',0,'',1873,1,249,161,'2016-01-22 15:04:58'),(2075,'Huiles cires',0,'',1874,1,264,161,'2016-01-22 15:05:27'),(2076,'Huile cire béton',0,'',1875,1,254,161,'2016-01-18 14:36:33'),(2077,'Huiles environnement pour parquet',0,'',1876,1,244,161,'2016-01-22 15:06:30'),(2078,'Huile d\'entretien',0,'',1877,1,248,161,'2016-01-11 16:55:17'),(2079,'Huile pour Teck',0,'',1878,1,259,161,'2016-01-18 14:37:06'),(2080,'Lasures bois très longue durée',0,'',1879,1,256,161,'2016-01-18 14:37:20'),(2081,'Lasures OPTEM',0,'',1880,1,316,161,'2016-01-18 14:37:37'),(2082,'Liant mastic à bois',0,'',1881,1,308,161,'2016-01-18 14:37:54'),(2083,'Nettoyants et rénovateurs',0,'',1882,1,253,161,'2016-01-18 14:38:18'),(2084,'Peinture bois très longue durée',0,'',1883,1,315,161,'2016-01-13 10:25:14'),(2085,'Peinture ciment bois',0,'',1884,1,270,161,'2016-01-18 14:38:40'),(2086,'Préparation bois extérieur',0,'',1885,1,317,161,'2016-01-18 14:39:05'),(2087,'Peinture décorative aqua',0,'',1886,1,425,161,'2016-03-01 14:36:39'),(2088,'Prim\' Huile',0,'',1887,1,246,161,'2016-01-18 14:39:19'),(2089,'Saturateur pour terrasse',0,'',1888,1,247,161,'2016-02-24 17:24:11'),(2090,'Savon Naturel des parquets huilés',0,'',1889,1,245,161,'2016-01-18 14:40:23'),(2091,'Système incolore anti-UV',0,'',1890,1,260,161,'2016-01-18 14:41:09'),(2092,'Teintes',0,'',1891,1,258,161,'2016-01-18 14:41:34'),(2093,'Traitement des bois - Trait\'plus -Trait\'découp',0,'',1892,1,251,161,'2016-01-18 14:42:00'),(2094,'Vitrificateurs environnement Blanchon',0,'',1893,1,243,161,'2016-01-18 14:42:24'),(2095,'Vitrificateurs parquet (VP)',0,'',1894,1,261,161,'2016-02-02 14:00:59'),(2096,'Vitrificateurs parquet Océanic®',0,'',1895,1,265,161,'2016-01-19 11:00:23'),(2097,'Vitrificateurs parquet SVP aqua',0,'',1896,1,405,161,'2015-12-04 13:49:33'),(2098,'Vernis bois',0,'',1897,1,297,161,'2016-02-02 14:02:02'),(2099,'Peinture rénovation',0,'',1898,1,436,161,'2016-10-18 14:49:46'),(2100,'Bugal',0,'',1899,1,280,151,'2016-03-26 10:23:08'),(2101,'Bona',0,'',1900,1,269,151,'2016-03-26 10:24:58'),(2102,'Balai Bona spay mop parquet',0,'',1901,1,312,269,'2016-01-18 14:46:36'),(2103,'Balai Bona spay mop sol dur',0,'',1902,1,311,269,'2016-01-18 14:46:56'),(2104,'Entretien parquet vitrifié',0,'',1903,1,306,269,'2016-01-18 14:47:20'),(2105,'Entretien parquet huilé',0,'',1904,1,307,269,'2015-12-01 11:03:24'),(2106,'Savon pour parquet huilé',0,'',1905,1,406,269,'2015-12-02 14:06:24'),(2107,'Huile d\'entretien parquet huilé',0,'',1906,1,407,269,'2015-12-02 15:45:26'),(2108,'Bostik',0,'',1907,1,162,151,'2011-04-08 13:05:04'),(2109,'Chêne de l\'Est',0,'',1908,1,155,151,'2016-03-26 10:25:53'),(2110,'Poussière d\'argile',0,'',1909,1,164,155,'2016-01-18 14:47:59'),(2111,'Plancher d\'autrefois',0,'',1910,1,165,155,'2016-01-18 14:48:11'),(2112,'Privilège Café',0,'',1911,1,166,155,'2016-01-18 14:48:31'),(2113,'Gamme Initiale',0,'',1912,1,423,155,'2016-11-02 11:30:10'),(2114,'Gamme Nature',0,'',1913,1,437,155,'2016-11-02 11:30:22'),(2115,'Cepam',0,'',1914,1,158,151,'2015-07-30 14:04:49'),(2116,'Natural',0,'',1915,1,187,158,'2014-07-02 13:42:36'),(2117,'Grand large',0,'',1916,1,188,158,'2014-07-02 13:42:46'),(2118,'Bord à bord',0,'',1917,1,189,158,'2014-07-02 13:42:57'),(2119,'Clean green',0,'',1918,1,288,151,'2016-03-26 10:28:14'),(2120,'Design by BP',0,'',1919,1,152,151,'2016-03-26 10:28:19'),(2121,'Massif brut',0,'',1920,1,228,152,'2016-01-18 14:49:17'),(2122,'Massif huilé naturel',0,'',1921,1,229,152,'2016-01-18 14:49:33'),(2123,'Lambris',0,'',1922,1,231,152,'2016-01-18 14:49:42'),(2124,'Massifs exotiques',0,'',1923,1,232,152,'2015-12-15 14:03:23'),(2125,'Massifs salle de bain',0,'',1924,1,233,152,'2016-01-18 14:50:01'),(2126,'Médoc 5G',0,'',1925,1,234,152,'2014-04-09 10:25:52'),(2127,'Colosse 5G',0,'',1926,1,235,152,'2014-04-09 10:27:24'),(2128,'Terrasses bois composite',0,'',1927,1,236,152,'2016-01-18 14:50:24'),(2129,'Terrasses bois',0,'',1928,1,237,152,'2016-01-18 14:50:42'),(2130,'Stratifié',0,'',1929,1,238,152,'2016-01-18 14:50:56'),(2131,'Emfi',0,'',1930,1,224,151,'2016-02-05 10:16:51'),(2132,'Cabbani',0,'',1931,1,156,151,'2017-02-21 14:18:31'),(2133,'Collection 02 (Finition Huilée)',0,'',1932,1,192,156,'2017-02-21 15:20:28'),(2134,'Collection 01 (Finition Vernie)',0,'',1933,1,193,156,'2017-02-21 14:58:31'),(2135,'Collection 03 (Finition Vernie)',0,'',1934,1,412,156,'2017-02-22 13:14:49'),(2136,'Garden lights',0,'',1935,1,289,151,'2016-03-26 10:32:34'),(2137,'Transformateurs',0,'',1936,1,380,289,'2015-03-11 16:43:54'),(2138,'Câbles et rallonges',0,'',1937,1,381,289,'2015-03-11 16:44:02'),(2139,'Programmateurs, détecteurs, télécommandes',0,'',1938,1,382,289,'2015-03-11 16:44:09'),(2140,'Eclairage à poser au sol',0,'',1939,1,383,289,'2015-03-11 16:44:17'),(2141,'Eclairage mural',0,'',1940,1,384,289,'2015-03-11 16:44:24'),(2142,'Eclairage d\'orientation',0,'',1941,1,385,289,'2015-03-11 16:44:31'),(2143,'SPOTS',0,'',1942,1,386,289,'2015-03-11 16:44:38'),(2144,'Eclairage Design Collection',0,'',1943,1,387,289,'2015-03-11 16:46:51'),(2145,'Pièces détachées',0,'',1944,1,388,289,'2015-03-11 16:48:03'),(2146,'Hapax',0,'',1945,1,273,151,'2016-02-05 13:14:38'),(2147,'Haro',0,'',1946,1,163,151,'2016-01-14 16:39:17'),(2148,'A l\'Anglaise Série 4000',0,'',1947,1,217,163,'2016-02-25 10:30:24'),(2149,'Toscana Série 3000',0,'',1948,1,218,163,'2016-02-25 10:32:27'),(2150,'Planche large à l\'ancienne - Série 4000',0,'',1949,1,287,163,'2016-01-18 14:58:07'),(2151,'Planche large à l\'ancienne - Série 3000',0,'',1950,1,219,163,'2017-03-21 16:19:37'),(2152,'Célénio Papyrus',0,'',1951,1,222,163,'2016-01-18 14:58:53'),(2153,'Célénio Athos',0,'',1952,1,223,163,'2016-01-18 14:59:06'),(2154,'Célénio Atrium',0,'',1953,1,226,163,'2016-01-18 14:59:19'),(2155,'Gran Via',0,'',1954,1,424,163,'2016-02-25 10:06:45'),(2156,'Tritty 100',0,'',1955,1,242,163,'2016-02-23 15:24:42'),(2157,'Tritty 100 - Silent CT',0,'',1956,1,422,163,'2016-02-01 17:23:41'),(2158,'Tritty 75',0,'',1957,1,285,163,'2016-01-11 16:47:33'),(2159,'Tritty 75 - Silent CT',0,'',1958,1,420,163,'2016-02-01 15:22:18'),(2160,'Haro Sols en liège',0,'',1959,1,293,163,'2017-07-04 22:35:39'),(2161,'Tritty 90',0,'',1960,1,448,163,'2017-01-09 10:28:29'),(2162,'Tritty 90 - Silent CT',0,'',1961,1,449,163,'2017-01-09 10:33:13'),(2163,'Tritty 90 - Silent Pro',0,'',1962,1,450,163,'2017-01-09 10:34:36'),(2164,'A l\'anglaise Série 3500',0,'',1963,1,451,163,'2017-01-10 08:50:28'),(2165,'Planche large à l\'ancienne - Série 3500',0,'',1964,1,469,163,'2017-03-22 16:43:12'),(2166,'Lamett',0,'',1965,1,272,151,'2016-03-26 10:44:05'),(2167,'Atlanta',0,'',1966,1,274,272,'2015-03-04 17:24:47'),(2168,'Cézanne',0,'',1967,1,362,272,'2016-01-27 10:27:55'),(2169,'Cuba',0,'',1968,1,364,272,'2016-01-18 17:01:24'),(2170,'Dijon',0,'',1969,1,313,272,'2016-01-27 13:14:13'),(2171,'New york',0,'',1970,1,275,272,'2014-07-02 13:23:40'),(2172,'Oslo',0,'',1971,1,276,272,'2016-01-27 14:26:50'),(2173,'Country',0,'',1972,1,277,272,'2014-07-02 13:26:51'),(2174,'Farm',0,'',1973,1,278,272,'2015-11-18 16:38:07'),(2175,'Impérial',0,'',1974,1,230,272,'2016-01-18 17:02:26'),(2176,'Nature',0,'',1975,1,282,272,'2014-07-02 13:27:21'),(2177,'Brussels',0,'',1976,1,284,272,'2017-02-15 09:14:11'),(2178,'Palace',0,'',1977,1,363,272,'2016-01-18 17:02:52'),(2179,'Royal',0,'',1978,1,371,272,'2016-01-18 17:03:06'),(2180,'Classica',0,'',1979,1,391,272,'2017-02-15 09:14:07'),(2181,'Sapphire',0,'',1980,1,392,272,'2017-02-15 09:14:00'),(2182,'Bolero',0,'',1981,1,393,272,'2017-02-15 09:13:47'),(2183,'Listone Giordano',0,'',1982,1,154,151,'2016-01-18 14:31:31'),(2184,'Linéa 70',0,'',1983,1,195,154,'2016-01-18 17:04:34'),(2185,'Piano 90',0,'',1984,1,196,154,'2016-01-18 17:04:55'),(2186,'Makita',0,'',1985,1,403,151,'2015-11-04 13:40:24'),(2187,'Objectflor',0,'',1986,1,402,151,'2015-11-02 17:45:40'),(2188,'Parqueterie Berrichonne',0,'',1987,1,153,151,'2016-01-26 16:54:20'),(2189,'Confluence 90',0,'',1988,1,211,153,'2017-03-03 16:30:22'),(2190,'Terrasse',0,'',1989,1,214,153,'2016-01-14 14:58:31'),(2191,'Nativ 11',0,'',1990,1,215,153,'2017-02-22 16:43:16'),(2192,'Class clic 140',0,'',1991,1,216,153,'2017-03-03 16:29:58'),(2193,'Confluence 140',0,'',1992,1,417,153,'2017-03-03 16:30:06'),(2194,'Confluence 170',0,'',1993,1,418,153,'2017-03-03 16:30:12'),(2195,'Confluence 190',0,'',1994,1,419,153,'2017-03-03 16:30:17'),(2196,'Nativ 14',0,'',1995,1,460,153,'2017-03-01 10:42:26'),(2197,'Quick Step',0,'',1996,1,157,151,'2016-01-18 14:32:33'),(2198,'Ambient Click',0,'',1997,1,399,157,'2017-01-24 14:15:35'),(2199,'Arte',0,'',1998,1,184,157,'2016-03-26 11:03:17'),(2200,'Balance Click',0,'',1999,1,398,157,'2015-12-14 13:51:44'),(2201,'Classic',0,'',2000,1,181,157,'2016-01-18 17:06:14'),(2202,'Creo',0,'',2001,1,309,157,'2016-01-18 17:06:24'),(2203,'Vogue',0,'',2002,1,175,157,'2015-04-07 12:47:45'),(2204,'Eligna',0,'',2003,1,177,157,'2016-01-18 17:06:36'),(2205,'Eligna Wide',0,'',2004,1,262,157,'2016-01-18 17:06:46'),(2206,'Elite',0,'',2005,1,179,157,'2016-01-18 17:06:57'),(2207,'Impressive',0,'',2006,1,304,157,'2017-04-10 14:14:08'),(2208,'Impressive Ultra',0,'',2007,1,305,157,'2017-04-10 14:13:54'),(2209,'Lagune',0,'',2008,1,180,157,'2016-01-18 17:07:29'),(2210,'Largo',0,'',2009,1,174,157,'2016-01-18 17:07:39'),(2211,'Perspective',0,'',2010,1,178,157,'2016-01-18 17:07:47'),(2212,'Perspective Wide',0,'',2011,1,310,157,'2016-01-18 17:07:58'),(2213,'Imperio',0,'',2012,1,438,157,'2016-11-16 10:56:09'),(2214,'Palazzo',0,'',2013,1,439,157,'2016-11-16 10:58:35'),(2215,'Castello',0,'',2014,1,441,157,'2016-11-16 11:04:00'),(2216,'Villa',0,'',2015,1,442,157,'2016-11-16 11:03:35'),(2217,'Compact',0,'',2016,1,444,157,'2016-11-16 11:06:42'),(2218,'Balance Click Plus',0,'',2017,1,452,157,'2017-01-23 16:58:07'),(2219,'Ambient Click Plus',0,'',2018,1,453,157,'2017-01-24 14:15:57'),(2220,'Pulse Click',0,'',2019,1,454,157,'2017-01-24 16:18:34'),(2221,'Pulse Click Plus',0,'',2020,1,455,157,'2017-01-25 10:43:20'),(2222,'Majestic',0,'',2021,1,470,157,'2017-04-10 14:17:26'),(2223,'Solidfloor',0,'',2022,1,292,151,'2016-01-18 14:32:45'),(2224,'Spax',0,'',2023,1,281,151,'2016-11-30 10:04:04'),(2225,'Verniland',0,'',2024,1,160,151,'2015-11-04 13:38:41'),(2226,'Virginia',0,'',2025,1,167,160,'2014-07-02 13:41:49'),(2227,'Callao',0,'',2026,1,169,160,'2014-07-02 13:42:00'),(2228,'Sapin brossé large',0,'',2027,1,170,160,'2014-07-02 13:42:10'),(2229,'Duo color',0,'',2028,1,173,160,'2014-07-02 13:42:23'),(2230,'Accessoires lambris',0,'',2029,1,301,160,'2014-07-25 09:30:55'),(2231,'Woca',0,'',2030,1,427,151,'2016-04-14 12:24:43'),(2232,'Box d\'entretien des parquets huilés',0,'',2031,1,432,427,'2016-03-14 17:33:10'),(2233,'Détachant',0,'',2032,1,430,427,'2016-03-14 17:27:05'),(2234,'Huile d\'entretien',0,'',2033,1,429,427,'2016-03-15 17:01:35'),(2235,'Huile d\'extérieur',0,'',2034,1,433,427,'2016-03-14 17:58:24'),(2236,'Nettoyant intensif',0,'',2035,1,431,427,'2016-03-14 17:30:25'),(2237,'Oil Care',0,'',2036,1,434,427,'2016-04-01 14:54:00'),(2238,'Savon naturel des parquets huilés',0,'',2037,1,428,427,'2016-04-01 15:38:07'),(2239,'L\'outil parfait',0,'',2038,1,446,151,'2016-11-30 10:44:20'),(2240,'Lalegno',0,'',2039,1,456,151,'2017-02-01 13:14:31'),(2241,'Fiberdeck',0,'',2040,1,458,151,'2017-02-15 10:16:05'),(2242,'Dressing',0,'',2041,1,447,3,'2016-11-30 15:52:29'),(2243,'Kendoors',0,'',2042,1,471,447,'2017-05-31 08:03:58'),(2244,'Accessoires',0,'',2043,1,457,3,'2017-02-01 14:25:08'),(2245,'LAMBOURDES & TASSEAUX',0,'',2044,1,99,457,'2017-03-21 17:03:37'),(2246,'COLLES & MASTICS',0,'',2045,1,107,457,'2017-03-21 17:03:42'),(2247,'SOUS-COUCHES',0,'',2046,1,100,457,'2017-02-01 14:26:19'),(2248,'BARRES DE SEUIL',0,'',2047,1,225,457,'2017-02-01 14:26:26'),(2249,'PLINTHES',0,'',2048,1,103,457,'2017-02-01 14:26:33'),(2250,'SOUS-PROFILÉ INCIZO',0,'',2049,1,397,457,'2017-02-01 14:27:04'),(2251,'ACCESSOIRES TERRASSES',0,'',2050,1,140,457,'2017-02-01 14:27:16'),(2252,'ACCESSOIRES LUMINAIRES',0,'',2051,1,291,457,'2017-02-01 14:27:24'),(2253,'CONTRES PLINTHES',0,'',2052,1,408,457,'2017-02-01 14:27:56'),(2254,'CHAMPLATS',0,'',2053,1,409,457,'2017-02-01 14:27:41'),(2255,'QUARTS DE ROND',0,'',2054,1,410,457,'2017-02-01 14:27:50'),(2256,'NEZ DE MARCHE',0,'',2055,1,411,457,'2017-02-01 14:28:02'),(2257,'OUTILLAGE PARQUET',0,'',2056,1,116,457,'2017-02-01 14:28:13'),(2258,'Brosses & Spalters',0,'',2057,1,467,457,'2017-03-21 16:54:05'),(2259,'Rouleaux & Montures',0,'',2058,1,468,457,'2017-03-21 17:03:14'); -/*!40000 ALTER TABLE `llx_ecommerce_category` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `llx_element_contact` -- @@ -3985,7 +4037,7 @@ CREATE TABLE `llx_element_contact` ( KEY `fk_element_contact_fk_c_type_contact` (`fk_c_type_contact`), KEY `idx_element_contact_fk_socpeople` (`fk_socpeople`), CONSTRAINT `fk_element_contact_fk_c_type_contact` FOREIGN KEY (`fk_c_type_contact`) REFERENCES `llx_c_type_contact` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4008,13 +4060,13 @@ DROP TABLE IF EXISTS `llx_element_element`; CREATE TABLE `llx_element_element` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_source` int(11) NOT NULL, - `sourcetype` varchar(32) NOT NULL, + `sourcetype` varchar(32) COLLATE utf8_unicode_ci NOT NULL, `fk_target` int(11) NOT NULL, - `targettype` varchar(32) NOT NULL, + `targettype` varchar(32) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `idx_element_element_idx1` (`fk_source`,`sourcetype`,`fk_target`,`targettype`), KEY `idx_element_element_fk_target` (`fk_target`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4027,6 +4079,33 @@ INSERT INTO `llx_element_element` VALUES (4,1,'order_supplier',20,'invoice_suppl /*!40000 ALTER TABLE `llx_element_element` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_element_lock` +-- + +DROP TABLE IF EXISTS `llx_element_lock`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_element_lock` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `fk_element` int(11) NOT NULL, + `elementtype` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `datel` datetime DEFAULT NULL, + `datem` datetime DEFAULT NULL, + `sessionid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`rowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_element_lock` +-- + +LOCK TABLES `llx_element_lock` WRITE; +/*!40000 ALTER TABLE `llx_element_lock` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_element_lock` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_element_resources` -- @@ -4037,9 +4116,9 @@ DROP TABLE IF EXISTS `llx_element_resources`; CREATE TABLE `llx_element_resources` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `element_id` int(11) DEFAULT NULL, - `element_type` varchar(64) DEFAULT NULL, + `element_type` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, `resource_id` int(11) DEFAULT NULL, - `resource_type` varchar(64) DEFAULT NULL, + `resource_type` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, `busy` int(11) DEFAULT NULL, `mandatory` int(11) DEFAULT NULL, `fk_user_create` int(11) DEFAULT NULL, @@ -4048,7 +4127,7 @@ CREATE TABLE `llx_element_resources` ( PRIMARY KEY (`rowid`), UNIQUE KEY `idx_element_resources_idx1` (`resource_id`,`resource_type`,`element_id`,`element_type`), KEY `idx_element_element_element_id` (`element_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4070,13 +4149,13 @@ DROP TABLE IF EXISTS `llx_element_tag`; CREATE TABLE `llx_element_tag` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `lang` varchar(5) NOT NULL, - `tag` varchar(255) NOT NULL, + `lang` varchar(5) COLLATE utf8_unicode_ci NOT NULL, + `tag` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `fk_element` int(11) NOT NULL, - `element` varchar(64) NOT NULL, + `element` varchar(64) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_element_tag` (`entity`,`lang`,`tag`,`fk_element`,`element`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4088,6 +4167,70 @@ LOCK TABLES `llx_element_tag` WRITE; /*!40000 ALTER TABLE `llx_element_tag` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_emailsenderprofile` +-- + +DROP TABLE IF EXISTS `llx_emailsenderprofile`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_emailsenderprofile` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `ref` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `entity` int(11) NOT NULL DEFAULT '1', + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `amount` double(24,8) DEFAULT NULL, + `note_public` mediumtext COLLATE utf8_unicode_ci, + `note_private` mediumtext COLLATE utf8_unicode_ci, + `date_creation` datetime NOT NULL, + `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `fk_user_creat` int(11) NOT NULL, + `fk_user_modif` int(11) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `status` int(11) DEFAULT NULL, + PRIMARY KEY (`rowid`), + KEY `idx_emailsenderprofile_rowid` (`rowid`), + KEY `idx_emailsenderprofile_ref` (`ref`), + KEY `idx_emailsenderprofile_entity` (`entity`), + KEY `idx_emailsenderprofile_import_key` (`import_key`), + KEY `idx_emailsenderprofile_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_emailsenderprofile` +-- + +LOCK TABLES `llx_emailsenderprofile` WRITE; +/*!40000 ALTER TABLE `llx_emailsenderprofile` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_emailsenderprofile` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `llx_emailsenderprofile_extrafields` +-- + +DROP TABLE IF EXISTS `llx_emailsenderprofile_extrafields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_emailsenderprofile_extrafields` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `fk_object` int(11) NOT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`rowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_emailsenderprofile_extrafields` +-- + +LOCK TABLES `llx_emailsenderprofile_extrafields` WRITE; +/*!40000 ALTER TABLE `llx_emailsenderprofile_extrafields` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_emailsenderprofile_extrafields` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_entity_extrafields` -- @@ -4124,22 +4267,22 @@ CREATE TABLE `llx_entrepot` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `ref` varchar(255) DEFAULT NULL, + `ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `description` text, - `lieu` varchar(64) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `zip` varchar(10) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, + `lieu` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `zip` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_departement` int(11) DEFAULT NULL, `fk_pays` int(11) DEFAULT '0', `statut` tinyint(4) DEFAULT '1', `fk_user_author` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_parent` int(11) DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_entrepot_label` (`ref`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4162,23 +4305,23 @@ DROP TABLE IF EXISTS `llx_establishment`; CREATE TABLE `llx_establishment` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `name` varchar(50) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `zip` varchar(25) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, + `name` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `zip` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_state` int(11) DEFAULT '0', `fk_country` int(11) DEFAULT '0', - `profid1` varchar(20) DEFAULT NULL, - `profid2` varchar(20) DEFAULT NULL, - `profid3` varchar(20) DEFAULT NULL, - `phone` varchar(20) DEFAULT NULL, + `profid1` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `profid2` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `profid3` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_author` int(11) NOT NULL, `fk_user_mod` int(11) DEFAULT NULL, `datec` datetime NOT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `status` tinyint(4) DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4201,9 +4344,9 @@ CREATE TABLE `llx_event_element` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_source` int(11) NOT NULL, `fk_target` int(11) NOT NULL, - `targettype` varchar(32) NOT NULL, + `targettype` varchar(32) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4225,17 +4368,17 @@ DROP TABLE IF EXISTS `llx_events`; CREATE TABLE `llx_events` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `type` varchar(32) NOT NULL, + `type` varchar(32) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `dateevent` datetime DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, - `description` varchar(250) NOT NULL, - `ip` varchar(250) DEFAULT NULL, - `user_agent` varchar(255) DEFAULT NULL, + `description` varchar(250) COLLATE utf8_unicode_ci NOT NULL, + `ip` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL, + `user_agent` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_object` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_events_dateevent` (`dateevent`) -) ENGINE=InnoDB AUTO_INCREMENT=877 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=879 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4244,7 +4387,7 @@ CREATE TABLE `llx_events` ( LOCK TABLES `llx_events` WRITE; /*!40000 ALTER TABLE `llx_events` DISABLE KEYS */; -INSERT INTO `llx_events` VALUES (30,'2011-07-18 18:23:06','USER_LOGOUT',1,'2011-07-18 20:23:06',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(31,'2011-07-18 18:23:12','USER_LOGIN_FAILED',1,'2011-07-18 20:23:12',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(32,'2011-07-18 18:23:17','USER_LOGIN',1,'2011-07-18 20:23:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(33,'2011-07-18 20:10:51','USER_LOGIN_FAILED',1,'2011-07-18 22:10:51',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(34,'2011-07-18 20:10:55','USER_LOGIN',1,'2011-07-18 22:10:55',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(35,'2011-07-18 21:18:57','USER_LOGIN',1,'2011-07-18 23:18:57',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(36,'2011-07-20 10:34:10','USER_LOGIN',1,'2011-07-20 12:34:10',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(37,'2011-07-20 12:36:44','USER_LOGIN',1,'2011-07-20 14:36:44',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(38,'2011-07-20 13:20:51','USER_LOGIN_FAILED',1,'2011-07-20 15:20:51',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(39,'2011-07-20 13:20:54','USER_LOGIN',1,'2011-07-20 15:20:54',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(40,'2011-07-20 15:03:46','USER_LOGIN_FAILED',1,'2011-07-20 17:03:46',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(41,'2011-07-20 15:03:55','USER_LOGIN',1,'2011-07-20 17:03:55',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(42,'2011-07-20 18:05:05','USER_LOGIN_FAILED',1,'2011-07-20 20:05:05',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(43,'2011-07-20 18:05:08','USER_LOGIN',1,'2011-07-20 20:05:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(44,'2011-07-20 21:08:53','USER_LOGIN_FAILED',1,'2011-07-20 23:08:53',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(45,'2011-07-20 21:08:56','USER_LOGIN',1,'2011-07-20 23:08:56',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(46,'2011-07-21 01:26:12','USER_LOGIN',1,'2011-07-21 03:26:12',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(47,'2011-07-21 22:35:45','USER_LOGIN_FAILED',1,'2011-07-22 00:35:45',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(48,'2011-07-21 22:35:49','USER_LOGIN',1,'2011-07-22 00:35:49',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(49,'2011-07-26 23:09:47','USER_LOGIN_FAILED',1,'2011-07-27 01:09:47',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(50,'2011-07-26 23:09:50','USER_LOGIN',1,'2011-07-27 01:09:50',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(51,'2011-07-27 17:02:27','USER_LOGIN_FAILED',1,'2011-07-27 19:02:27',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(52,'2011-07-27 17:02:32','USER_LOGIN',1,'2011-07-27 19:02:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(53,'2011-07-27 23:33:37','USER_LOGIN_FAILED',1,'2011-07-28 01:33:37',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(54,'2011-07-27 23:33:41','USER_LOGIN',1,'2011-07-28 01:33:41',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(55,'2011-07-28 18:20:36','USER_LOGIN_FAILED',1,'2011-07-28 20:20:36',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(56,'2011-07-28 18:20:38','USER_LOGIN',1,'2011-07-28 20:20:38',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(57,'2011-07-28 20:13:30','USER_LOGIN_FAILED',1,'2011-07-28 22:13:30',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(58,'2011-07-28 20:13:34','USER_LOGIN',1,'2011-07-28 22:13:34',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(59,'2011-07-28 20:22:51','USER_LOGIN',1,'2011-07-28 22:22:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(60,'2011-07-28 23:05:06','USER_LOGIN',1,'2011-07-29 01:05:06',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(61,'2011-07-29 20:15:50','USER_LOGIN_FAILED',1,'2011-07-29 22:15:50',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(62,'2011-07-29 20:15:53','USER_LOGIN',1,'2011-07-29 22:15:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(68,'2011-07-29 20:51:01','USER_LOGOUT',1,'2011-07-29 22:51:01',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(69,'2011-07-29 20:51:05','USER_LOGIN',1,'2011-07-29 22:51:05',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(70,'2011-07-30 08:46:20','USER_LOGIN_FAILED',1,'2011-07-30 10:46:20',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(71,'2011-07-30 08:46:38','USER_LOGIN_FAILED',1,'2011-07-30 10:46:38',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(72,'2011-07-30 08:46:42','USER_LOGIN',1,'2011-07-30 10:46:42',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(73,'2011-07-30 10:05:12','USER_LOGIN_FAILED',1,'2011-07-30 12:05:12',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(74,'2011-07-30 10:05:15','USER_LOGIN',1,'2011-07-30 12:05:15',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(75,'2011-07-30 12:15:46','USER_LOGIN',1,'2011-07-30 14:15:46',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(76,'2011-07-31 22:19:30','USER_LOGIN',1,'2011-08-01 00:19:30',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(77,'2011-07-31 23:32:52','USER_LOGIN',1,'2011-08-01 01:32:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(78,'2011-08-01 01:24:50','USER_LOGIN_FAILED',1,'2011-08-01 03:24:50',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(79,'2011-08-01 01:24:54','USER_LOGIN',1,'2011-08-01 03:24:54',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(80,'2011-08-01 19:31:36','USER_LOGIN_FAILED',1,'2011-08-01 21:31:35',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(81,'2011-08-01 19:31:39','USER_LOGIN',1,'2011-08-01 21:31:39',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(82,'2011-08-01 20:01:36','USER_LOGIN',1,'2011-08-01 22:01:36',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(83,'2011-08-01 20:52:54','USER_LOGIN_FAILED',1,'2011-08-01 22:52:54',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(84,'2011-08-01 20:52:58','USER_LOGIN',1,'2011-08-01 22:52:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(85,'2011-08-01 21:17:28','USER_LOGIN_FAILED',1,'2011-08-01 23:17:28',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(86,'2011-08-01 21:17:31','USER_LOGIN',1,'2011-08-01 23:17:31',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(87,'2011-08-04 11:55:17','USER_LOGIN',1,'2011-08-04 13:55:17',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(88,'2011-08-04 20:19:03','USER_LOGIN_FAILED',1,'2011-08-04 22:19:03',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(89,'2011-08-04 20:19:07','USER_LOGIN',1,'2011-08-04 22:19:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(90,'2011-08-05 17:51:42','USER_LOGIN_FAILED',1,'2011-08-05 19:51:42',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(91,'2011-08-05 17:51:47','USER_LOGIN',1,'2011-08-05 19:51:47',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(92,'2011-08-05 17:56:03','USER_LOGIN',1,'2011-08-05 19:56:03',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(93,'2011-08-05 17:59:10','USER_LOGIN',1,'2011-08-05 19:59:10',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30',NULL),(94,'2011-08-05 18:01:58','USER_LOGIN',1,'2011-08-05 20:01:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30',NULL),(95,'2011-08-05 19:59:56','USER_LOGIN',1,'2011-08-05 21:59:56',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(96,'2011-08-06 18:33:22','USER_LOGIN',1,'2011-08-06 20:33:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(97,'2011-08-07 00:56:59','USER_LOGIN',1,'2011-08-07 02:56:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(98,'2011-08-07 22:49:14','USER_LOGIN',1,'2011-08-08 00:49:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(99,'2011-08-07 23:05:18','USER_LOGOUT',1,'2011-08-08 01:05:18',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(105,'2011-08-08 00:41:09','USER_LOGIN',1,'2011-08-08 02:41:09',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(106,'2011-08-08 11:58:55','USER_LOGIN',1,'2011-08-08 13:58:55',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(107,'2011-08-08 14:35:48','USER_LOGIN',1,'2011-08-08 16:35:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(108,'2011-08-08 14:36:31','USER_LOGOUT',1,'2011-08-08 16:36:31',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(109,'2011-08-08 14:38:28','USER_LOGIN',1,'2011-08-08 16:38:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(110,'2011-08-08 14:39:02','USER_LOGOUT',1,'2011-08-08 16:39:02',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(111,'2011-08-08 14:39:10','USER_LOGIN',1,'2011-08-08 16:39:10',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(112,'2011-08-08 14:39:28','USER_LOGOUT',1,'2011-08-08 16:39:28',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(113,'2011-08-08 14:39:37','USER_LOGIN',1,'2011-08-08 16:39:37',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(114,'2011-08-08 14:50:02','USER_LOGOUT',1,'2011-08-08 16:50:02',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(115,'2011-08-08 14:51:45','USER_LOGIN_FAILED',1,'2011-08-08 16:51:45',NULL,'Identifiants login ou mot de passe incorrects - login=','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(116,'2011-08-08 14:51:52','USER_LOGIN',1,'2011-08-08 16:51:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(117,'2011-08-08 15:09:54','USER_LOGOUT',1,'2011-08-08 17:09:54',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(118,'2011-08-08 15:10:19','USER_LOGIN_FAILED',1,'2011-08-08 17:10:19',NULL,'Identifiants login ou mot de passe incorrects - login=','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(119,'2011-08-08 15:10:28','USER_LOGIN',1,'2011-08-08 17:10:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(121,'2011-08-08 15:14:58','USER_LOGOUT',1,'2011-08-08 17:14:58',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(122,'2011-08-08 15:15:00','USER_LOGIN_FAILED',1,'2011-08-08 17:15:00',NULL,'Identifiants login ou mot de passe incorrects - login=','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(123,'2011-08-08 15:17:57','USER_LOGIN',1,'2011-08-08 17:17:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(124,'2011-08-08 15:35:56','USER_LOGOUT',1,'2011-08-08 17:35:56',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(125,'2011-08-08 15:36:05','USER_LOGIN',1,'2011-08-08 17:36:05',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(126,'2011-08-08 17:32:42','USER_LOGIN',1,'2011-08-08 19:32:42',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(127,'2012-12-08 13:49:37','USER_LOGOUT',1,'2012-12-08 14:49:37',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(128,'2012-12-08 13:49:42','USER_LOGIN',1,'2012-12-08 14:49:42',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(129,'2012-12-08 13:50:12','USER_LOGOUT',1,'2012-12-08 14:50:12',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(130,'2012-12-08 13:50:14','USER_LOGIN',1,'2012-12-08 14:50:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(131,'2012-12-08 13:50:17','USER_LOGOUT',1,'2012-12-08 14:50:17',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(132,'2012-12-08 13:52:47','USER_LOGIN',1,'2012-12-08 14:52:47',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(133,'2012-12-08 13:53:08','USER_MODIFY',1,'2012-12-08 14:53:08',1,'User admin modified','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(134,'2012-12-08 14:08:45','USER_LOGOUT',1,'2012-12-08 15:08:45',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(135,'2012-12-08 14:09:09','USER_LOGIN',1,'2012-12-08 15:09:09',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(136,'2012-12-08 14:11:43','USER_LOGOUT',1,'2012-12-08 15:11:43',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(137,'2012-12-08 14:11:45','USER_LOGIN',1,'2012-12-08 15:11:45',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(138,'2012-12-08 14:22:53','USER_LOGOUT',1,'2012-12-08 15:22:53',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(139,'2012-12-08 14:22:54','USER_LOGIN',1,'2012-12-08 15:22:54',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(140,'2012-12-08 14:23:10','USER_LOGOUT',1,'2012-12-08 15:23:10',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(141,'2012-12-08 14:23:11','USER_LOGIN',1,'2012-12-08 15:23:11',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(142,'2012-12-08 14:23:49','USER_LOGOUT',1,'2012-12-08 15:23:49',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(143,'2012-12-08 14:23:50','USER_LOGIN',1,'2012-12-08 15:23:50',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(144,'2012-12-08 14:28:08','USER_LOGOUT',1,'2012-12-08 15:28:08',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(145,'2012-12-08 14:35:15','USER_LOGIN',1,'2012-12-08 15:35:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(146,'2012-12-08 14:35:18','USER_LOGOUT',1,'2012-12-08 15:35:18',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(147,'2012-12-08 14:36:07','USER_LOGIN',1,'2012-12-08 15:36:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(148,'2012-12-08 14:36:09','USER_LOGOUT',1,'2012-12-08 15:36:09',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(149,'2012-12-08 14:36:41','USER_LOGIN',1,'2012-12-08 15:36:41',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(150,'2012-12-08 15:59:13','USER_LOGIN',1,'2012-12-08 16:59:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(151,'2012-12-09 11:49:52','USER_LOGIN',1,'2012-12-09 12:49:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(152,'2012-12-09 13:46:31','USER_LOGIN',1,'2012-12-09 14:46:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(153,'2012-12-09 19:03:14','USER_LOGIN',1,'2012-12-09 20:03:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(154,'2012-12-10 00:16:31','USER_LOGIN',1,'2012-12-10 01:16:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(170,'2012-12-11 22:03:31','USER_LOGIN',1,'2012-12-11 23:03:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(171,'2012-12-12 00:32:39','USER_LOGIN',1,'2012-12-12 01:32:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(172,'2012-12-12 10:49:59','USER_LOGIN',1,'2012-12-12 11:49:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(175,'2012-12-12 10:57:40','USER_MODIFY',1,'2012-12-12 11:57:40',1,'Modification utilisateur admin','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(176,'2012-12-12 13:29:15','USER_LOGIN',1,'2012-12-12 14:29:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(177,'2012-12-12 13:30:15','USER_LOGIN',1,'2012-12-12 14:30:15',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(178,'2012-12-12 13:40:08','USER_LOGOUT',1,'2012-12-12 14:40:08',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(179,'2012-12-12 13:40:10','USER_LOGIN',1,'2012-12-12 14:40:10',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(180,'2012-12-12 13:40:26','USER_MODIFY',1,'2012-12-12 14:40:26',1,'Modification utilisateur admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(181,'2012-12-12 13:40:34','USER_LOGOUT',1,'2012-12-12 14:40:34',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(182,'2012-12-12 13:42:23','USER_LOGIN',1,'2012-12-12 14:42:23',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(183,'2012-12-12 13:43:02','USER_NEW_PASSWORD',1,'2012-12-12 14:43:02',NULL,'Changement mot de passe de admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(184,'2012-12-12 13:43:25','USER_LOGOUT',1,'2012-12-12 14:43:25',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(185,'2012-12-12 13:43:27','USER_LOGIN_FAILED',1,'2012-12-12 14:43:27',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(186,'2012-12-12 13:43:30','USER_LOGIN',1,'2012-12-12 14:43:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(187,'2012-12-12 14:52:11','USER_LOGIN',1,'2012-12-12 15:52:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(188,'2012-12-12 17:53:00','USER_LOGIN_FAILED',1,'2012-12-12 18:53:00',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(189,'2012-12-12 17:53:07','USER_LOGIN_FAILED',1,'2012-12-12 18:53:07',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(190,'2012-12-12 17:53:51','USER_NEW_PASSWORD',1,'2012-12-12 18:53:51',NULL,'Changement mot de passe de admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(191,'2012-12-12 17:54:00','USER_LOGIN',1,'2012-12-12 18:54:00',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(192,'2012-12-12 17:54:10','USER_NEW_PASSWORD',1,'2012-12-12 18:54:10',1,'Changement mot de passe de admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(193,'2012-12-12 17:54:10','USER_MODIFY',1,'2012-12-12 18:54:10',1,'Modification utilisateur admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(194,'2012-12-12 18:57:09','USER_LOGIN',1,'2012-12-12 19:57:09',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(195,'2012-12-12 23:04:08','USER_LOGIN',1,'2012-12-13 00:04:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(196,'2012-12-17 20:03:14','USER_LOGIN',1,'2012-12-17 21:03:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(197,'2012-12-17 21:18:45','USER_LOGIN',1,'2012-12-17 22:18:45',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(198,'2012-12-17 22:30:08','USER_LOGIN',1,'2012-12-17 23:30:08',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(199,'2012-12-18 23:32:03','USER_LOGIN',1,'2012-12-19 00:32:03',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(200,'2012-12-19 09:38:03','USER_LOGIN',1,'2012-12-19 10:38:03',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(201,'2012-12-19 11:23:35','USER_LOGIN',1,'2012-12-19 12:23:35',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(202,'2012-12-19 12:46:22','USER_LOGIN',1,'2012-12-19 13:46:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(214,'2012-12-19 19:11:31','USER_LOGIN',1,'2012-12-19 20:11:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(215,'2012-12-21 16:36:57','USER_LOGIN',1,'2012-12-21 17:36:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(216,'2012-12-21 16:38:43','USER_NEW_PASSWORD',1,'2012-12-21 17:38:43',1,'Changement mot de passe de adupont','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(217,'2012-12-21 16:38:43','USER_MODIFY',1,'2012-12-21 17:38:43',1,'Modification utilisateur adupont','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(218,'2012-12-21 16:38:51','USER_LOGOUT',1,'2012-12-21 17:38:51',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(219,'2012-12-21 16:38:55','USER_LOGIN',1,'2012-12-21 17:38:55',3,'(UserLogged,adupont)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(220,'2012-12-21 16:48:18','USER_LOGOUT',1,'2012-12-21 17:48:18',3,'(UserLogoff,adupont)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(221,'2012-12-21 16:48:20','USER_LOGIN',1,'2012-12-21 17:48:20',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(222,'2012-12-26 18:28:18','USER_LOGIN',1,'2012-12-26 19:28:18',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(223,'2012-12-26 20:00:24','USER_LOGIN',1,'2012-12-26 21:00:24',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(224,'2012-12-27 01:10:27','USER_LOGIN',1,'2012-12-27 02:10:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(225,'2012-12-28 19:12:08','USER_LOGIN',1,'2012-12-28 20:12:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(226,'2012-12-28 20:16:58','USER_LOGIN',1,'2012-12-28 21:16:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(227,'2012-12-29 14:35:46','USER_LOGIN',1,'2012-12-29 15:35:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(228,'2012-12-29 14:37:59','USER_LOGOUT',1,'2012-12-29 15:37:59',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(229,'2012-12-29 14:38:00','USER_LOGIN',1,'2012-12-29 15:38:00',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(230,'2012-12-29 17:16:48','USER_LOGIN',1,'2012-12-29 18:16:48',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(231,'2012-12-31 12:02:59','USER_LOGIN',1,'2012-12-31 13:02:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(232,'2013-01-02 20:32:51','USER_LOGIN',1,'2013-01-02 21:32:51',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0',NULL),(233,'2013-01-02 20:58:59','USER_LOGIN',1,'2013-01-02 21:58:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(234,'2013-01-03 09:25:07','USER_LOGIN',1,'2013-01-03 10:25:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(235,'2013-01-03 19:39:31','USER_LOGIN',1,'2013-01-03 20:39:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(236,'2013-01-04 22:40:19','USER_LOGIN',1,'2013-01-04 23:40:19',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(237,'2013-01-05 12:59:59','USER_LOGIN',1,'2013-01-05 13:59:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(238,'2013-01-05 15:28:52','USER_LOGIN',1,'2013-01-05 16:28:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(239,'2013-01-05 17:02:08','USER_LOGIN',1,'2013-01-05 18:02:08',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(240,'2013-01-06 12:13:33','USER_LOGIN',1,'2013-01-06 13:13:33',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(241,'2013-01-07 01:21:15','USER_LOGIN',1,'2013-01-07 02:21:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(242,'2013-01-07 01:46:31','USER_LOGOUT',1,'2013-01-07 02:46:31',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(243,'2013-01-07 19:54:50','USER_LOGIN',1,'2013-01-07 20:54:50',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(244,'2013-01-08 21:55:01','USER_LOGIN',1,'2013-01-08 22:55:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(245,'2013-01-09 11:13:28','USER_LOGIN',1,'2013-01-09 12:13:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(246,'2013-01-10 18:30:46','USER_LOGIN',1,'2013-01-10 19:30:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(247,'2013-01-11 18:03:26','USER_LOGIN',1,'2013-01-11 19:03:26',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(248,'2013-01-12 11:15:04','USER_LOGIN',1,'2013-01-12 12:15:04',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(249,'2013-01-12 14:42:44','USER_LOGIN',1,'2013-01-12 15:42:44',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(250,'2013-01-13 12:07:17','USER_LOGIN',1,'2013-01-13 13:07:17',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(251,'2013-01-13 17:37:58','USER_LOGIN',1,'2013-01-13 18:37:58',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(252,'2013-01-13 19:24:21','USER_LOGIN',1,'2013-01-13 20:24:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(253,'2013-01-13 19:29:19','USER_LOGOUT',1,'2013-01-13 20:29:19',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(254,'2013-01-13 21:39:39','USER_LOGIN',1,'2013-01-13 22:39:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(255,'2013-01-14 00:52:21','USER_LOGIN',1,'2013-01-14 01:52:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(256,'2013-01-16 11:34:31','USER_LOGIN',1,'2013-01-16 12:34:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(257,'2013-01-16 15:36:21','USER_LOGIN',1,'2013-01-16 16:36:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(258,'2013-01-16 19:17:36','USER_LOGIN',1,'2013-01-16 20:17:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(259,'2013-01-16 19:48:08','GROUP_CREATE',1,'2013-01-16 20:48:08',1,'Création groupe ggg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(260,'2013-01-16 21:48:53','USER_LOGIN',1,'2013-01-16 22:48:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(261,'2013-01-17 19:55:53','USER_LOGIN',1,'2013-01-17 20:55:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(262,'2013-01-18 09:48:01','USER_LOGIN',1,'2013-01-18 10:48:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(263,'2013-01-18 13:22:36','USER_LOGIN',1,'2013-01-18 14:22:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(264,'2013-01-18 16:10:23','USER_LOGIN',1,'2013-01-18 17:10:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(265,'2013-01-18 17:41:40','USER_LOGIN',1,'2013-01-18 18:41:40',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(266,'2013-01-19 14:33:48','USER_LOGIN',1,'2013-01-19 15:33:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(267,'2013-01-19 16:47:43','USER_LOGIN',1,'2013-01-19 17:47:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(268,'2013-01-19 16:59:43','USER_LOGIN',1,'2013-01-19 17:59:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(269,'2013-01-19 17:00:22','USER_LOGIN',1,'2013-01-19 18:00:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(270,'2013-01-19 17:04:16','USER_LOGOUT',1,'2013-01-19 18:04:16',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(271,'2013-01-19 17:04:18','USER_LOGIN',1,'2013-01-19 18:04:18',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(272,'2013-01-20 00:34:19','USER_LOGIN',1,'2013-01-20 01:34:19',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(273,'2013-01-21 11:54:17','USER_LOGIN',1,'2013-01-21 12:54:17',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(274,'2013-01-21 13:48:15','USER_LOGIN',1,'2013-01-21 14:48:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(275,'2013-01-21 14:30:22','USER_LOGIN',1,'2013-01-21 15:30:22',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(276,'2013-01-21 15:10:46','USER_LOGIN',1,'2013-01-21 16:10:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(277,'2013-01-21 17:27:43','USER_LOGIN',1,'2013-01-21 18:27:43',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(278,'2013-01-21 21:48:15','USER_LOGIN',1,'2013-01-21 22:48:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(279,'2013-01-21 21:50:42','USER_LOGIN',1,'2013-01-21 22:50:42',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(280,'2013-01-23 09:28:26','USER_LOGIN',1,'2013-01-23 10:28:26',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(281,'2013-01-23 13:21:57','USER_LOGIN',1,'2013-01-23 14:21:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(282,'2013-01-23 16:52:00','USER_LOGOUT',1,'2013-01-23 17:52:00',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(283,'2013-01-23 16:52:05','USER_LOGIN_FAILED',1,'2013-01-23 17:52:05',NULL,'Bad value for login or password - login=bbb','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(284,'2013-01-23 16:52:09','USER_LOGIN',1,'2013-01-23 17:52:09',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(285,'2013-01-23 16:52:27','USER_CREATE',1,'2013-01-23 17:52:27',1,'Création utilisateur aaa','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(286,'2013-01-23 16:52:27','USER_NEW_PASSWORD',1,'2013-01-23 17:52:27',1,'Changement mot de passe de aaa','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(287,'2013-01-23 16:52:37','USER_CREATE',1,'2013-01-23 17:52:37',1,'Création utilisateur bbb','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(288,'2013-01-23 16:52:37','USER_NEW_PASSWORD',1,'2013-01-23 17:52:37',1,'Changement mot de passe de bbb','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(289,'2013-01-23 16:53:15','USER_LOGOUT',1,'2013-01-23 17:53:15',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(290,'2013-01-23 16:53:20','USER_LOGIN',1,'2013-01-23 17:53:20',4,'(UserLogged,aaa)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(291,'2013-01-23 19:16:58','USER_LOGIN',1,'2013-01-23 20:16:58',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(292,'2013-01-26 10:54:07','USER_LOGIN',1,'2013-01-26 11:54:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(293,'2013-01-29 10:15:36','USER_LOGIN',1,'2013-01-29 11:15:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(294,'2013-01-30 17:42:50','USER_LOGIN',1,'2013-01-30 18:42:50',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(295,'2013-02-01 08:49:55','USER_LOGIN',1,'2013-02-01 09:49:55',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(296,'2013-02-01 08:51:57','USER_LOGOUT',1,'2013-02-01 09:51:57',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(297,'2013-02-01 08:52:39','USER_LOGIN',1,'2013-02-01 09:52:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(298,'2013-02-01 21:03:01','USER_LOGIN',1,'2013-02-01 22:03:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(299,'2013-02-10 19:48:39','USER_LOGIN',1,'2013-02-10 20:48:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(300,'2013-02-10 20:46:48','USER_LOGIN',1,'2013-02-10 21:46:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(301,'2013-02-10 21:39:23','USER_LOGIN',1,'2013-02-10 22:39:23',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(302,'2013-02-11 19:00:13','USER_LOGIN',1,'2013-02-11 20:00:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(303,'2013-02-11 19:43:44','USER_LOGIN_FAILED',1,'2013-02-11 20:43:44',NULL,'Unknown column \'u.fk_user\' in \'field list\'','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(304,'2013-02-11 19:44:01','USER_LOGIN',1,'2013-02-11 20:44:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(305,'2013-02-12 00:27:35','USER_LOGIN',1,'2013-02-12 01:27:35',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(306,'2013-02-12 00:27:38','USER_LOGOUT',1,'2013-02-12 01:27:38',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(307,'2013-02-12 00:28:07','USER_LOGIN',1,'2013-02-12 01:28:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(308,'2013-02-12 00:28:09','USER_LOGOUT',1,'2013-02-12 01:28:09',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(309,'2013-02-12 00:28:26','USER_LOGIN',1,'2013-02-12 01:28:26',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(310,'2013-02-12 00:28:30','USER_LOGOUT',1,'2013-02-12 01:28:30',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(311,'2013-02-12 12:42:15','USER_LOGIN',1,'2013-02-12 13:42:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(312,'2013-02-12 13:46:16','USER_LOGIN',1,'2013-02-12 14:46:16',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(313,'2013-02-12 14:54:28','USER_LOGIN',1,'2013-02-12 15:54:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(314,'2013-02-12 16:04:46','USER_LOGIN',1,'2013-02-12 17:04:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(315,'2013-02-13 14:02:43','USER_LOGIN',1,'2013-02-13 15:02:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(316,'2013-02-13 14:48:30','USER_LOGIN',1,'2013-02-13 15:48:30',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(317,'2013-02-13 17:44:53','USER_LOGIN',1,'2013-02-13 18:44:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(318,'2013-02-15 08:44:36','USER_LOGIN',1,'2013-02-15 09:44:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(319,'2013-02-15 08:53:20','USER_LOGIN',1,'2013-02-15 09:53:20',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(320,'2013-02-16 19:10:28','USER_LOGIN',1,'2013-02-16 20:10:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(321,'2013-02-16 19:22:40','USER_CREATE',1,'2013-02-16 20:22:40',1,'Création utilisateur aaab','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(322,'2013-02-16 19:22:40','USER_NEW_PASSWORD',1,'2013-02-16 20:22:40',1,'Changement mot de passe de aaab','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(323,'2013-02-16 19:48:15','USER_CREATE',1,'2013-02-16 20:48:15',1,'Création utilisateur zzz','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(324,'2013-02-16 19:48:15','USER_NEW_PASSWORD',1,'2013-02-16 20:48:15',1,'Changement mot de passe de zzz','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(325,'2013-02-16 19:50:08','USER_CREATE',1,'2013-02-16 20:50:08',1,'Création utilisateur zzzg','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(326,'2013-02-16 19:50:08','USER_NEW_PASSWORD',1,'2013-02-16 20:50:08',1,'Changement mot de passe de zzzg','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(327,'2013-02-16 21:20:03','USER_LOGIN',1,'2013-02-16 22:20:03',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(328,'2013-02-17 14:30:51','USER_LOGIN',1,'2013-02-17 15:30:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(329,'2013-02-17 17:21:22','USER_LOGIN',1,'2013-02-17 18:21:22',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(330,'2013-02-17 17:48:43','USER_MODIFY',1,'2013-02-17 18:48:43',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(331,'2013-02-17 17:48:47','USER_MODIFY',1,'2013-02-17 18:48:47',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(332,'2013-02-17 17:48:51','USER_MODIFY',1,'2013-02-17 18:48:51',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(333,'2013-02-17 17:48:56','USER_MODIFY',1,'2013-02-17 18:48:56',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(334,'2013-02-18 22:00:01','USER_LOGIN',1,'2013-02-18 23:00:01',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(335,'2013-02-19 08:19:52','USER_LOGIN',1,'2013-02-19 09:19:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(336,'2013-02-19 22:00:52','USER_LOGIN',1,'2013-02-19 23:00:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(337,'2013-02-20 09:34:52','USER_LOGIN',1,'2013-02-20 10:34:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(338,'2013-02-20 13:12:28','USER_LOGIN',1,'2013-02-20 14:12:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(339,'2013-02-20 17:19:44','USER_LOGIN',1,'2013-02-20 18:19:44',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(340,'2013-02-20 19:07:21','USER_MODIFY',1,'2013-02-20 20:07:21',1,'Modification utilisateur adupont','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(341,'2013-02-20 19:47:17','USER_LOGIN',1,'2013-02-20 20:47:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(342,'2013-02-20 19:48:01','USER_MODIFY',1,'2013-02-20 20:48:01',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(343,'2013-02-21 08:27:07','USER_LOGIN',1,'2013-02-21 09:27:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(344,'2013-02-23 13:34:13','USER_LOGIN',1,'2013-02-23 14:34:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(345,'2013-02-24 01:06:41','USER_LOGIN_FAILED',1,'2013-02-24 02:06:41',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(346,'2013-02-24 01:06:45','USER_LOGIN_FAILED',1,'2013-02-24 02:06:45',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(347,'2013-02-24 01:06:55','USER_LOGIN_FAILED',1,'2013-02-24 02:06:55',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(348,'2013-02-24 01:07:03','USER_LOGIN_FAILED',1,'2013-02-24 02:07:03',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(349,'2013-02-24 01:07:21','USER_LOGIN_FAILED',1,'2013-02-24 02:07:21',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(350,'2013-02-24 01:08:12','USER_LOGIN_FAILED',1,'2013-02-24 02:08:12',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(351,'2013-02-24 01:08:42','USER_LOGIN_FAILED',1,'2013-02-24 02:08:42',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(352,'2013-02-24 01:08:50','USER_LOGIN_FAILED',1,'2013-02-24 02:08:50',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(353,'2013-02-24 01:09:08','USER_LOGIN_FAILED',1,'2013-02-24 02:09:08',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(354,'2013-02-24 01:09:42','USER_LOGIN_FAILED',1,'2013-02-24 02:09:42',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(355,'2013-02-24 01:09:50','USER_LOGIN_FAILED',1,'2013-02-24 02:09:50',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(356,'2013-02-24 01:10:05','USER_LOGIN_FAILED',1,'2013-02-24 02:10:05',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(357,'2013-02-24 01:10:22','USER_LOGIN_FAILED',1,'2013-02-24 02:10:22',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(358,'2013-02-24 01:10:30','USER_LOGIN_FAILED',1,'2013-02-24 02:10:30',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(359,'2013-02-24 01:10:56','USER_LOGIN_FAILED',1,'2013-02-24 02:10:56',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(360,'2013-02-24 01:11:26','USER_LOGIN_FAILED',1,'2013-02-24 02:11:26',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(361,'2013-02-24 01:12:06','USER_LOGIN_FAILED',1,'2013-02-24 02:12:06',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(362,'2013-02-24 01:21:14','USER_LOGIN_FAILED',1,'2013-02-24 02:21:14',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(363,'2013-02-24 01:21:25','USER_LOGIN_FAILED',1,'2013-02-24 02:21:25',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(364,'2013-02-24 01:21:54','USER_LOGIN_FAILED',1,'2013-02-24 02:21:54',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(365,'2013-02-24 01:22:14','USER_LOGIN_FAILED',1,'2013-02-24 02:22:14',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(366,'2013-02-24 01:22:37','USER_LOGIN_FAILED',1,'2013-02-24 02:22:37',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(367,'2013-02-24 01:23:01','USER_LOGIN_FAILED',1,'2013-02-24 02:23:01',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(368,'2013-02-24 01:23:39','USER_LOGIN_FAILED',1,'2013-02-24 02:23:39',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(369,'2013-02-24 01:24:04','USER_LOGIN_FAILED',1,'2013-02-24 02:24:04',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(370,'2013-02-24 01:24:39','USER_LOGIN_FAILED',1,'2013-02-24 02:24:39',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(371,'2013-02-24 01:25:01','USER_LOGIN_FAILED',1,'2013-02-24 02:25:01',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(372,'2013-02-24 01:25:12','USER_LOGIN_FAILED',1,'2013-02-24 02:25:12',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(373,'2013-02-24 01:27:30','USER_LOGIN_FAILED',1,'2013-02-24 02:27:30',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(374,'2013-02-24 01:28:00','USER_LOGIN_FAILED',1,'2013-02-24 02:28:00',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(375,'2013-02-24 01:28:35','USER_LOGIN_FAILED',1,'2013-02-24 02:28:35',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(376,'2013-02-24 01:29:03','USER_LOGIN_FAILED',1,'2013-02-24 02:29:03',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(377,'2013-02-24 01:29:55','USER_LOGIN_FAILED',1,'2013-02-24 02:29:55',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(378,'2013-02-24 01:32:40','USER_LOGIN_FAILED',1,'2013-02-24 02:32:40',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(379,'2013-02-24 01:39:33','USER_LOGIN_FAILED',1,'2013-02-24 02:39:33',NULL,'Identifiants login ou mot de passe incorrects - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(380,'2013-02-24 01:39:38','USER_LOGIN_FAILED',1,'2013-02-24 02:39:38',NULL,'Identifiants login ou mot de passe incorrects - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(381,'2013-02-24 01:39:47','USER_LOGIN_FAILED',1,'2013-02-24 02:39:47',NULL,'Identifiants login ou mot de passe incorrects - login=lmkm','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(382,'2013-02-24 01:40:54','USER_LOGIN_FAILED',1,'2013-02-24 02:40:54',NULL,'Identifiants login ou mot de passe incorrects - login=lmkm','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(383,'2013-02-24 01:47:57','USER_LOGIN_FAILED',1,'2013-02-24 02:47:57',NULL,'Identifiants login ou mot de passe incorrects - login=lmkm','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(384,'2013-02-24 01:48:05','USER_LOGIN_FAILED',1,'2013-02-24 02:48:05',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(385,'2013-02-24 01:48:07','USER_LOGIN_FAILED',1,'2013-02-24 02:48:07',NULL,'Unknown column \'u.lastname\' in \'field list\'','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(386,'2013-02-24 01:48:35','USER_LOGIN',1,'2013-02-24 02:48:35',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(387,'2013-02-24 01:56:32','USER_LOGIN',1,'2013-02-24 02:56:32',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',NULL),(388,'2013-02-24 02:05:55','USER_LOGOUT',1,'2013-02-24 03:05:55',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(389,'2013-02-24 02:39:52','USER_LOGIN',1,'2013-02-24 03:39:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(390,'2013-02-24 02:51:10','USER_LOGOUT',1,'2013-02-24 03:51:10',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(391,'2013-02-24 12:46:41','USER_LOGIN',1,'2013-02-24 13:46:41',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(392,'2013-02-24 12:46:52','USER_LOGOUT',1,'2013-02-24 13:46:52',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(393,'2013-02-24 12:46:56','USER_LOGIN',1,'2013-02-24 13:46:56',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(394,'2013-02-24 12:47:56','USER_LOGOUT',1,'2013-02-24 13:47:56',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(395,'2013-02-24 12:48:00','USER_LOGIN',1,'2013-02-24 13:48:00',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(396,'2013-02-24 12:48:11','USER_LOGOUT',1,'2013-02-24 13:48:11',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(397,'2013-02-24 12:48:32','USER_LOGIN',1,'2013-02-24 13:48:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(398,'2013-02-24 12:52:22','USER_LOGOUT',1,'2013-02-24 13:52:22',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(399,'2013-02-24 12:52:27','USER_LOGIN',1,'2013-02-24 13:52:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(400,'2013-02-24 12:52:54','USER_LOGOUT',1,'2013-02-24 13:52:54',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(401,'2013-02-24 12:52:59','USER_LOGIN',1,'2013-02-24 13:52:59',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(402,'2013-02-24 12:55:39','USER_LOGOUT',1,'2013-02-24 13:55:39',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(403,'2013-02-24 12:55:59','USER_LOGIN',1,'2013-02-24 13:55:59',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(404,'2013-02-24 12:56:07','USER_LOGOUT',1,'2013-02-24 13:56:07',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(405,'2013-02-24 12:56:23','USER_LOGIN',1,'2013-02-24 13:56:23',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(406,'2013-02-24 12:56:46','USER_LOGOUT',1,'2013-02-24 13:56:46',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(407,'2013-02-24 12:58:30','USER_LOGIN',1,'2013-02-24 13:58:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(408,'2013-02-24 12:58:33','USER_LOGOUT',1,'2013-02-24 13:58:33',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(409,'2013-02-24 12:58:51','USER_LOGIN',1,'2013-02-24 13:58:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(410,'2013-02-24 12:58:58','USER_LOGOUT',1,'2013-02-24 13:58:58',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(411,'2013-02-24 13:18:53','USER_LOGIN',1,'2013-02-24 14:18:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(412,'2013-02-24 13:19:52','USER_LOGOUT',1,'2013-02-24 14:19:52',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(413,'2013-02-24 15:39:31','USER_LOGIN_FAILED',1,'2013-02-24 16:39:31',NULL,'ErrorBadValueForCode - login=admin','127.0.0.1',NULL,NULL),(414,'2013-02-24 15:42:07','USER_LOGIN',1,'2013-02-24 16:42:07',1,'(UserLogged,admin)','127.0.0.1',NULL,NULL),(415,'2013-02-24 15:42:52','USER_LOGOUT',1,'2013-02-24 16:42:52',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(416,'2013-02-24 16:04:21','USER_LOGIN',1,'2013-02-24 17:04:21',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',NULL),(417,'2013-02-24 16:11:28','USER_LOGIN_FAILED',1,'2013-02-24 17:11:28',NULL,'ErrorBadValueForCode - login=admin','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(418,'2013-02-24 16:11:37','USER_LOGIN',1,'2013-02-24 17:11:37',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(419,'2013-02-24 16:36:52','USER_LOGOUT',1,'2013-02-24 17:36:52',1,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',NULL),(420,'2013-02-24 16:40:37','USER_LOGIN',1,'2013-02-24 17:40:37',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(421,'2013-02-24 16:57:16','USER_LOGIN',1,'2013-02-24 17:57:16',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 - 2131034114',NULL),(422,'2013-02-24 17:01:30','USER_LOGOUT',1,'2013-02-24 18:01:30',1,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 - 2131034114',NULL),(423,'2013-02-24 17:02:33','USER_LOGIN',1,'2013-02-24 18:02:33',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(424,'2013-02-24 17:14:22','USER_LOGOUT',1,'2013-02-24 18:14:22',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(425,'2013-02-24 17:15:07','USER_LOGIN_FAILED',1,'2013-02-24 18:15:07',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(426,'2013-02-24 17:15:20','USER_LOGIN',1,'2013-02-24 18:15:20',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(427,'2013-02-24 17:20:14','USER_LOGIN',1,'2013-02-24 18:20:14',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(428,'2013-02-24 17:20:51','USER_LOGIN',1,'2013-02-24 18:20:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(429,'2013-02-24 17:20:54','USER_LOGOUT',1,'2013-02-24 18:20:54',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(430,'2013-02-24 17:21:19','USER_LOGIN',1,'2013-02-24 18:21:19',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(431,'2013-02-24 17:32:35','USER_LOGIN',1,'2013-02-24 18:32:35',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 - 2131034114',NULL),(432,'2013-02-24 18:28:48','USER_LOGIN',1,'2013-02-24 19:28:48',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(433,'2013-02-24 18:29:27','USER_LOGOUT',1,'2013-02-24 19:29:27',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(434,'2013-02-24 18:29:32','USER_LOGIN',1,'2013-02-24 19:29:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(435,'2013-02-24 20:13:13','USER_LOGOUT',1,'2013-02-24 21:13:13',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(436,'2013-02-24 20:13:17','USER_LOGIN',1,'2013-02-24 21:13:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(437,'2013-02-25 08:57:16','USER_LOGIN',1,'2013-02-25 09:57:16',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(438,'2013-02-25 08:57:59','USER_LOGOUT',1,'2013-02-25 09:57:59',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(439,'2013-02-25 09:15:02','USER_LOGIN',1,'2013-02-25 10:15:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(440,'2013-02-25 09:15:50','USER_LOGOUT',1,'2013-02-25 10:15:50',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(441,'2013-02-25 09:15:57','USER_LOGIN',1,'2013-02-25 10:15:57',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(442,'2013-02-25 09:16:12','USER_LOGOUT',1,'2013-02-25 10:16:12',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(443,'2013-02-25 09:16:19','USER_LOGIN',1,'2013-02-25 10:16:19',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(444,'2013-02-25 09:16:25','USER_LOGOUT',1,'2013-02-25 10:16:25',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(445,'2013-02-25 09:16:39','USER_LOGIN_FAILED',1,'2013-02-25 10:16:39',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(446,'2013-02-25 09:16:42','USER_LOGIN_FAILED',1,'2013-02-25 10:16:42',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(447,'2013-02-25 09:16:54','USER_LOGIN_FAILED',1,'2013-02-25 10:16:54',NULL,'Identificadors d'usuari o contrasenya incorrectes - login=gfdg','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(448,'2013-02-25 09:17:53','USER_LOGIN',1,'2013-02-25 10:17:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(449,'2013-02-25 09:18:37','USER_LOGOUT',1,'2013-02-25 10:18:37',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(450,'2013-02-25 09:18:41','USER_LOGIN',1,'2013-02-25 10:18:41',4,'(UserLogged,aaa)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(451,'2013-02-25 09:18:47','USER_LOGOUT',1,'2013-02-25 10:18:47',4,'(UserLogoff,aaa)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(452,'2013-02-25 10:05:34','USER_LOGIN',1,'2013-02-25 11:05:34',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(453,'2013-02-26 21:51:40','USER_LOGIN',1,'2013-02-26 22:51:40',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(454,'2013-02-26 23:30:06','USER_LOGIN',1,'2013-02-27 00:30:06',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(455,'2013-02-27 14:13:11','USER_LOGIN',1,'2013-02-27 15:13:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(456,'2013-02-27 18:12:06','USER_LOGIN_FAILED',1,'2013-02-27 19:12:06',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(457,'2013-02-27 18:12:10','USER_LOGIN',1,'2013-02-27 19:12:10',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(458,'2013-02-27 20:20:08','USER_LOGIN',1,'2013-02-27 21:20:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(459,'2013-03-01 22:12:03','USER_LOGIN',1,'2013-03-01 23:12:03',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(460,'2013-03-02 11:45:50','USER_LOGIN',1,'2013-03-02 12:45:50',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(461,'2013-03-02 15:53:51','USER_LOGIN_FAILED',1,'2013-03-02 16:53:51',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(462,'2013-03-02 15:53:53','USER_LOGIN',1,'2013-03-02 16:53:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(463,'2013-03-02 18:32:32','USER_LOGIN',1,'2013-03-02 19:32:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(464,'2013-03-02 22:59:36','USER_LOGIN',1,'2013-03-02 23:59:36',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(465,'2013-03-03 16:26:26','USER_LOGIN',1,'2013-03-03 17:26:26',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(466,'2013-03-03 22:50:27','USER_LOGIN',1,'2013-03-03 23:50:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(467,'2013-03-04 08:29:27','USER_LOGIN',1,'2013-03-04 09:29:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(468,'2013-03-04 18:27:28','USER_LOGIN',1,'2013-03-04 19:27:28',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; NP06)',NULL),(469,'2013-03-04 19:27:23','USER_LOGIN',1,'2013-03-04 20:27:23',1,'(UserLogged,admin)','192.168.0.254','Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)',NULL),(470,'2013-03-04 19:35:14','USER_LOGIN',1,'2013-03-04 20:35:14',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(471,'2013-03-04 19:55:49','USER_LOGIN',1,'2013-03-04 20:55:49',1,'(UserLogged,admin)','192.168.0.254','Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',NULL),(472,'2013-03-04 21:16:13','USER_LOGIN',1,'2013-03-04 22:16:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(473,'2013-03-05 10:17:30','USER_LOGIN',1,'2013-03-05 11:17:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(474,'2013-03-05 11:02:43','USER_LOGIN',1,'2013-03-05 12:02:43',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(475,'2013-03-05 23:14:39','USER_LOGIN',1,'2013-03-06 00:14:39',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(476,'2013-03-06 08:58:57','USER_LOGIN',1,'2013-03-06 09:58:57',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(477,'2013-03-06 14:29:40','USER_LOGIN',1,'2013-03-06 15:29:40',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(478,'2013-03-06 21:53:02','USER_LOGIN',1,'2013-03-06 22:53:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(479,'2013-03-07 21:14:39','USER_LOGIN',1,'2013-03-07 22:14:39',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(480,'2013-03-08 00:06:05','USER_LOGIN',1,'2013-03-08 01:06:05',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(481,'2013-03-08 01:38:13','USER_LOGIN',1,'2013-03-08 02:38:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(482,'2013-03-08 08:59:50','USER_LOGIN',1,'2013-03-08 09:59:50',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(483,'2013-03-09 12:08:51','USER_LOGIN',1,'2013-03-09 13:08:51',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(484,'2013-03-09 15:19:53','USER_LOGIN',1,'2013-03-09 16:19:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(495,'2013-03-09 18:06:21','USER_LOGIN',1,'2013-03-09 19:06:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(496,'2013-03-09 20:01:24','USER_LOGIN',1,'2013-03-09 21:01:24',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(497,'2013-03-09 23:36:45','USER_LOGIN',1,'2013-03-10 00:36:45',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(498,'2013-03-10 14:37:13','USER_LOGIN',1,'2013-03-10 15:37:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(499,'2013-03-10 17:54:12','USER_LOGIN',1,'2013-03-10 18:54:12',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(500,'2013-03-11 08:57:09','USER_LOGIN',1,'2013-03-11 09:57:09',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(501,'2013-03-11 22:05:13','USER_LOGIN',1,'2013-03-11 23:05:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(502,'2013-03-12 08:34:27','USER_LOGIN',1,'2013-03-12 09:34:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(503,'2013-03-13 09:11:02','USER_LOGIN',1,'2013-03-13 10:11:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(504,'2013-03-13 10:02:11','USER_LOGIN',1,'2013-03-13 11:02:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(505,'2013-03-13 13:20:58','USER_LOGIN',1,'2013-03-13 14:20:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(506,'2013-03-13 16:19:28','USER_LOGIN',1,'2013-03-13 17:19:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(507,'2013-03-13 18:34:30','USER_LOGIN',1,'2013-03-13 19:34:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(508,'2013-03-14 08:25:02','USER_LOGIN',1,'2013-03-14 09:25:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(509,'2013-03-14 19:15:22','USER_LOGIN',1,'2013-03-14 20:15:22',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(510,'2013-03-14 21:58:53','USER_LOGIN',1,'2013-03-14 22:58:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(511,'2013-03-14 21:58:59','USER_LOGOUT',1,'2013-03-14 22:58:59',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(512,'2013-03-14 21:59:07','USER_LOGIN',1,'2013-03-14 22:59:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(513,'2013-03-14 22:58:22','USER_LOGOUT',1,'2013-03-14 23:58:22',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(514,'2013-03-14 23:00:25','USER_LOGIN',1,'2013-03-15 00:00:25',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(515,'2013-03-16 12:14:28','USER_LOGIN',1,'2013-03-16 13:14:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(516,'2013-03-16 16:09:01','USER_LOGIN',1,'2013-03-16 17:09:01',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(517,'2013-03-16 16:57:11','USER_LOGIN',1,'2013-03-16 17:57:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(518,'2013-03-16 19:31:31','USER_LOGIN',1,'2013-03-16 20:31:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(519,'2013-03-17 17:44:39','USER_LOGIN',1,'2013-03-17 18:44:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(520,'2013-03-17 20:40:57','USER_LOGIN',1,'2013-03-17 21:40:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(521,'2013-03-17 23:14:05','USER_LOGIN',1,'2013-03-18 00:14:05',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(522,'2013-03-17 23:28:47','USER_LOGOUT',1,'2013-03-18 00:28:47',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(523,'2013-03-17 23:28:54','USER_LOGIN',1,'2013-03-18 00:28:54',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(524,'2013-03-18 17:37:30','USER_LOGIN',1,'2013-03-18 18:37:30',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(525,'2013-03-18 18:11:37','USER_LOGIN',1,'2013-03-18 19:11:37',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(526,'2013-03-19 08:35:08','USER_LOGIN',1,'2013-03-19 09:35:08',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(527,'2013-03-19 09:20:23','USER_LOGIN',1,'2013-03-19 10:20:23',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(528,'2013-03-20 13:17:13','USER_LOGIN',1,'2013-03-20 14:17:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(529,'2013-03-20 14:44:31','USER_LOGIN',1,'2013-03-20 15:44:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(530,'2013-03-20 18:24:25','USER_LOGIN',1,'2013-03-20 19:24:25',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(531,'2013-03-20 19:15:54','USER_LOGIN',1,'2013-03-20 20:15:54',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(532,'2013-03-21 18:40:47','USER_LOGIN',1,'2013-03-21 19:40:47',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(533,'2013-03-21 21:42:24','USER_LOGIN',1,'2013-03-21 22:42:24',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(534,'2013-03-22 08:39:23','USER_LOGIN',1,'2013-03-22 09:39:23',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(535,'2013-03-23 13:04:55','USER_LOGIN',1,'2013-03-23 14:04:55',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(536,'2013-03-23 15:47:43','USER_LOGIN',1,'2013-03-23 16:47:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(537,'2013-03-23 22:56:36','USER_LOGIN',1,'2013-03-23 23:56:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(538,'2013-03-24 01:22:32','USER_LOGIN',1,'2013-03-24 02:22:32',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(539,'2013-03-24 14:40:42','USER_LOGIN',1,'2013-03-24 15:40:42',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(540,'2013-03-24 15:30:26','USER_LOGOUT',1,'2013-03-24 16:30:26',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(541,'2013-03-24 15:30:29','USER_LOGIN',1,'2013-03-24 16:30:29',2,'(UserLogged,demo)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(542,'2013-03-24 15:49:40','USER_LOGOUT',1,'2013-03-24 16:49:40',2,'(UserLogoff,demo)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(543,'2013-03-24 15:49:48','USER_LOGIN',1,'2013-03-24 16:49:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(544,'2013-03-24 15:52:35','USER_MODIFY',1,'2013-03-24 16:52:35',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(545,'2013-03-24 15:52:52','USER_MODIFY',1,'2013-03-24 16:52:52',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(546,'2013-03-24 15:53:09','USER_MODIFY',1,'2013-03-24 16:53:09',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(547,'2013-03-24 15:53:23','USER_MODIFY',1,'2013-03-24 16:53:23',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(548,'2013-03-24 16:00:04','USER_MODIFY',1,'2013-03-24 17:00:04',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(549,'2013-03-24 16:01:50','USER_MODIFY',1,'2013-03-24 17:01:50',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(550,'2013-03-24 16:10:14','USER_MODIFY',1,'2013-03-24 17:10:14',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(551,'2013-03-24 16:55:13','USER_LOGIN',1,'2013-03-24 17:55:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(552,'2013-03-24 17:44:29','USER_LOGIN',1,'2013-03-24 18:44:29',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(553,'2013-09-08 23:06:26','USER_LOGIN',1,'2013-09-09 01:06:26',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36',NULL),(554,'2013-10-21 22:32:28','USER_LOGIN',1,'2013-10-22 00:32:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.66 Safari/537.36',NULL),(555,'2013-10-21 22:32:48','USER_LOGIN',1,'2013-10-22 00:32:48',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.66 Safari/537.36',NULL),(556,'2013-11-07 00:01:51','USER_LOGIN',1,'2013-11-07 01:01:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.114 Safari/537.36',NULL),(557,'2014-03-02 15:21:07','USER_LOGIN',1,'2014-03-02 16:21:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(558,'2014-03-02 15:36:53','USER_LOGIN',1,'2014-03-02 16:36:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(559,'2014-03-02 18:54:23','USER_LOGIN',1,'2014-03-02 19:54:23',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(560,'2014-03-02 19:11:17','USER_LOGIN',1,'2014-03-02 20:11:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(561,'2014-03-03 18:19:24','USER_LOGIN',1,'2014-03-03 19:19:24',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(562,'2014-12-21 12:51:38','USER_LOGIN',1,'2014-12-21 13:51:38',1,'(UserLogged,admin) - TZ=1;TZString=CET;Screen=1920x969','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',NULL),(563,'2014-12-21 19:52:09','USER_LOGIN',1,'2014-12-21 20:52:09',1,'(UserLogged,admin) - TZ=1;TZString=CET;Screen=1920x969','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',NULL),(566,'2015-10-03 08:49:43','USER_NEW_PASSWORD',1,'2015-10-03 10:49:43',1,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(567,'2015-10-03 08:49:43','USER_MODIFY',1,'2015-10-03 10:49:43',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(568,'2015-10-03 09:03:12','USER_MODIFY',1,'2015-10-03 11:03:12',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(569,'2015-10-03 09:03:42','USER_MODIFY',1,'2015-10-03 11:03:42',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(570,'2015-10-03 09:07:36','USER_MODIFY',1,'2015-10-03 11:07:36',1,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(571,'2015-10-03 09:08:58','USER_NEW_PASSWORD',1,'2015-10-03 11:08:58',1,'Password change for pcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(572,'2015-10-03 09:08:58','USER_MODIFY',1,'2015-10-03 11:08:58',1,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(573,'2015-10-03 09:09:23','USER_MODIFY',1,'2015-10-03 11:09:23',1,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(574,'2015-10-03 09:11:04','USER_NEW_PASSWORD',1,'2015-10-03 11:11:04',1,'Password change for athestudent','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(575,'2015-10-03 09:11:04','USER_MODIFY',1,'2015-10-03 11:11:04',1,'User athestudent modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(576,'2015-10-03 09:11:53','USER_MODIFY',1,'2015-10-03 11:11:53',1,'User abookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(577,'2015-10-03 09:42:12','USER_LOGIN_FAILED',1,'2015-10-03 11:42:11',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(578,'2015-10-03 09:42:19','USER_LOGIN_FAILED',1,'2015-10-03 11:42:19',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(579,'2015-10-03 09:42:42','USER_LOGIN_FAILED',1,'2015-10-03 11:42:42',NULL,'Bad value for login or password - login=aeinstein','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(580,'2015-10-03 09:43:50','USER_LOGIN',1,'2015-10-03 11:43:50',1,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x788','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(581,'2015-10-03 09:44:44','GROUP_MODIFY',1,'2015-10-03 11:44:44',1,'Group Sale representatives modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(582,'2015-10-03 09:46:25','GROUP_CREATE',1,'2015-10-03 11:46:25',1,'Group Management created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(583,'2015-10-03 09:46:46','GROUP_CREATE',1,'2015-10-03 11:46:46',1,'Group Scientists created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(584,'2015-10-03 09:47:41','USER_CREATE',1,'2015-10-03 11:47:41',1,'User mcurie created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(585,'2015-10-03 09:47:41','USER_NEW_PASSWORD',1,'2015-10-03 11:47:41',1,'Password change for mcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(586,'2015-10-03 09:47:53','USER_MODIFY',1,'2015-10-03 11:47:53',1,'User mcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(587,'2015-10-03 09:48:32','USER_DELETE',1,'2015-10-03 11:48:32',1,'User bbb removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(588,'2015-10-03 09:48:52','USER_MODIFY',1,'2015-10-03 11:48:52',1,'User bookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(589,'2015-10-03 10:01:28','USER_MODIFY',1,'2015-10-03 12:01:28',1,'User bookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(590,'2015-10-03 10:01:39','USER_MODIFY',1,'2015-10-03 12:01:39',1,'User bookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(591,'2015-10-05 06:32:38','USER_LOGIN_FAILED',1,'2015-10-05 08:32:38',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(592,'2015-10-05 06:32:44','USER_LOGIN',1,'2015-10-05 08:32:44',1,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(593,'2015-10-05 07:07:52','USER_CREATE',1,'2015-10-05 09:07:52',1,'User atheceo created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(594,'2015-10-05 07:07:52','USER_NEW_PASSWORD',1,'2015-10-05 09:07:52',1,'Password change for atheceo','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(595,'2015-10-05 07:09:08','USER_NEW_PASSWORD',1,'2015-10-05 09:09:08',1,'Password change for aeinstein','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(596,'2015-10-05 07:09:08','USER_MODIFY',1,'2015-10-05 09:09:08',1,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(597,'2015-10-05 07:09:46','USER_CREATE',1,'2015-10-05 09:09:46',1,'User admin created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(598,'2015-10-05 07:09:46','USER_NEW_PASSWORD',1,'2015-10-05 09:09:46',1,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(599,'2015-10-05 07:10:20','USER_MODIFY',1,'2015-10-05 09:10:20',1,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(600,'2015-10-05 07:10:48','USER_MODIFY',1,'2015-10-05 09:10:48',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(601,'2015-10-05 07:11:22','USER_NEW_PASSWORD',1,'2015-10-05 09:11:22',1,'Password change for bbookkeeper','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(602,'2015-10-05 07:11:22','USER_MODIFY',1,'2015-10-05 09:11:22',1,'User bbookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(603,'2015-10-05 07:12:37','USER_MODIFY',1,'2015-10-05 09:12:37',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(604,'2015-10-05 07:13:27','USER_MODIFY',1,'2015-10-05 09:13:27',1,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(605,'2015-10-05 07:13:52','USER_MODIFY',1,'2015-10-05 09:13:52',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(606,'2015-10-05 07:14:35','USER_LOGOUT',1,'2015-10-05 09:14:35',1,'(UserLogoff,aeinstein)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(607,'2015-10-05 07:14:40','USER_LOGIN_FAILED',1,'2015-10-05 09:14:40',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(608,'2015-10-05 07:14:44','USER_LOGIN_FAILED',1,'2015-10-05 09:14:44',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(609,'2015-10-05 07:14:49','USER_LOGIN',1,'2015-10-05 09:14:49',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(610,'2015-10-05 07:57:18','USER_MODIFY',1,'2015-10-05 09:57:18',12,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(611,'2015-10-05 08:06:54','USER_LOGOUT',1,'2015-10-05 10:06:54',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(612,'2015-10-05 08:07:03','USER_LOGIN',1,'2015-10-05 10:07:03',11,'(UserLogged,atheceo) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(613,'2015-10-05 19:18:46','USER_LOGIN',1,'2015-10-05 21:18:46',11,'(UserLogged,atheceo) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(614,'2015-10-05 19:29:35','USER_CREATE',1,'2015-10-05 21:29:35',11,'User ccommercy created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(615,'2015-10-05 19:29:35','USER_NEW_PASSWORD',1,'2015-10-05 21:29:35',11,'Password change for ccommercy','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(616,'2015-10-05 19:30:13','GROUP_CREATE',1,'2015-10-05 21:30:13',11,'Group Commercial created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(617,'2015-10-05 19:31:37','USER_NEW_PASSWORD',1,'2015-10-05 21:31:37',11,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(618,'2015-10-05 19:31:37','USER_MODIFY',1,'2015-10-05 21:31:37',11,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(619,'2015-10-05 19:32:00','USER_MODIFY',1,'2015-10-05 21:32:00',11,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(620,'2015-10-05 19:33:33','USER_CREATE',1,'2015-10-05 21:33:33',11,'User sscientol created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(621,'2015-10-05 19:33:33','USER_NEW_PASSWORD',1,'2015-10-05 21:33:33',11,'Password change for sscientol','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(622,'2015-10-05 19:33:47','USER_NEW_PASSWORD',1,'2015-10-05 21:33:47',11,'Password change for mcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(623,'2015-10-05 19:33:47','USER_MODIFY',1,'2015-10-05 21:33:47',11,'User mcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(624,'2015-10-05 19:34:23','USER_NEW_PASSWORD',1,'2015-10-05 21:34:23',11,'Password change for pcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(625,'2015-10-05 19:34:23','USER_MODIFY',1,'2015-10-05 21:34:23',11,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(626,'2015-10-05 19:34:42','USER_MODIFY',1,'2015-10-05 21:34:42',11,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(627,'2015-10-05 19:36:06','USER_NEW_PASSWORD',1,'2015-10-05 21:36:06',11,'Password change for ccommercy','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(628,'2015-10-05 19:36:06','USER_MODIFY',1,'2015-10-05 21:36:06',11,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(629,'2015-10-05 19:36:57','USER_NEW_PASSWORD',1,'2015-10-05 21:36:57',11,'Password change for atheceo','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(630,'2015-10-05 19:36:57','USER_MODIFY',1,'2015-10-05 21:36:57',11,'User atheceo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(631,'2015-10-05 19:37:27','USER_LOGOUT',1,'2015-10-05 21:37:27',11,'(UserLogoff,atheceo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(632,'2015-10-05 19:37:35','USER_LOGIN_FAILED',1,'2015-10-05 21:37:35',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(633,'2015-10-05 19:37:39','USER_LOGIN_FAILED',1,'2015-10-05 21:37:39',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(634,'2015-10-05 19:37:44','USER_LOGIN_FAILED',1,'2015-10-05 21:37:44',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(635,'2015-10-05 19:37:49','USER_LOGIN_FAILED',1,'2015-10-05 21:37:49',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(636,'2015-10-05 19:38:12','USER_LOGIN_FAILED',1,'2015-10-05 21:38:12',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(637,'2015-10-05 19:40:48','USER_LOGIN_FAILED',1,'2015-10-05 21:40:48',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(638,'2015-10-05 19:40:55','USER_LOGIN',1,'2015-10-05 21:40:55',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(639,'2015-10-05 19:43:34','USER_MODIFY',1,'2015-10-05 21:43:34',12,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(640,'2015-10-05 19:45:43','USER_CREATE',1,'2015-10-05 21:45:43',12,'User aaa created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(641,'2015-10-05 19:45:43','USER_NEW_PASSWORD',1,'2015-10-05 21:45:43',12,'Password change for aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(642,'2015-10-05 19:46:18','USER_DELETE',1,'2015-10-05 21:46:18',12,'User aaa removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(643,'2015-10-05 19:47:09','USER_MODIFY',1,'2015-10-05 21:47:09',12,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(644,'2015-10-05 19:47:22','USER_MODIFY',1,'2015-10-05 21:47:22',12,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(645,'2015-10-05 19:52:05','USER_MODIFY',1,'2015-10-05 21:52:05',12,'User sscientol modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(646,'2015-10-05 19:52:23','USER_MODIFY',1,'2015-10-05 21:52:23',12,'User bbookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(647,'2015-10-05 19:54:54','USER_NEW_PASSWORD',1,'2015-10-05 21:54:54',12,'Password change for zzeceo','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(648,'2015-10-05 19:54:54','USER_MODIFY',1,'2015-10-05 21:54:54',12,'User zzeceo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(649,'2015-10-05 19:57:02','USER_MODIFY',1,'2015-10-05 21:57:02',12,'User zzeceo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(650,'2015-10-05 19:57:57','USER_NEW_PASSWORD',1,'2015-10-05 21:57:57',12,'Password change for pcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(651,'2015-10-05 19:57:57','USER_MODIFY',1,'2015-10-05 21:57:57',12,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(652,'2015-10-05 19:59:42','USER_NEW_PASSWORD',1,'2015-10-05 21:59:42',12,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(653,'2015-10-05 19:59:42','USER_MODIFY',1,'2015-10-05 21:59:42',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(654,'2015-10-05 20:00:21','USER_MODIFY',1,'2015-10-05 22:00:21',12,'User adminx modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(655,'2015-10-05 20:05:36','USER_MODIFY',1,'2015-10-05 22:05:36',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(656,'2015-10-05 20:06:25','USER_MODIFY',1,'2015-10-05 22:06:25',12,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(657,'2015-10-05 20:07:18','USER_MODIFY',1,'2015-10-05 22:07:18',12,'User mcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(658,'2015-10-05 20:07:36','USER_MODIFY',1,'2015-10-05 22:07:36',12,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(659,'2015-10-05 20:08:34','USER_MODIFY',1,'2015-10-05 22:08:34',12,'User bbookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(660,'2015-10-05 20:47:52','USER_CREATE',1,'2015-10-05 22:47:52',12,'User cc1 created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(661,'2015-10-05 20:47:52','USER_NEW_PASSWORD',1,'2015-10-05 22:47:52',12,'Password change for cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(662,'2015-10-05 20:47:55','USER_LOGOUT',1,'2015-10-05 22:47:55',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(663,'2015-10-05 20:48:08','USER_LOGIN',1,'2015-10-05 22:48:08',11,'(UserLogged,zzeceo) - TZ=1;TZString=Europe/Berlin;Screen=1590x434','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(664,'2015-10-05 20:48:39','USER_CREATE',1,'2015-10-05 22:48:39',11,'User cc2 created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(665,'2015-10-05 20:48:39','USER_NEW_PASSWORD',1,'2015-10-05 22:48:39',11,'Password change for cc2','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(666,'2015-10-05 20:48:59','USER_NEW_PASSWORD',1,'2015-10-05 22:48:59',11,'Password change for cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(667,'2015-10-05 20:48:59','USER_MODIFY',1,'2015-10-05 22:48:59',11,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(668,'2015-10-05 21:06:36','USER_LOGOUT',1,'2015-10-05 23:06:35',11,'(UserLogoff,zzeceo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(669,'2015-10-05 21:06:44','USER_LOGIN_FAILED',1,'2015-10-05 23:06:44',NULL,'Bad value for login or password - login=cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(670,'2015-10-05 21:07:12','USER_LOGIN_FAILED',1,'2015-10-05 23:07:12',NULL,'Bad value for login or password - login=cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(671,'2015-10-05 21:07:19','USER_LOGIN_FAILED',1,'2015-10-05 23:07:19',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(672,'2015-10-05 21:07:27','USER_LOGIN_FAILED',1,'2015-10-05 23:07:27',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(673,'2015-10-05 21:07:32','USER_LOGIN',1,'2015-10-05 23:07:32',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(674,'2015-10-05 21:12:28','USER_NEW_PASSWORD',1,'2015-10-05 23:12:28',12,'Password change for cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(675,'2015-10-05 21:12:28','USER_MODIFY',1,'2015-10-05 23:12:28',12,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(676,'2015-10-05 21:13:00','USER_CREATE',1,'2015-10-05 23:13:00',12,'User aaa created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(677,'2015-10-05 21:13:00','USER_NEW_PASSWORD',1,'2015-10-05 23:13:00',12,'Password change for aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(678,'2015-10-05 21:13:40','USER_DELETE',1,'2015-10-05 23:13:40',12,'User aaa removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(679,'2015-10-05 21:14:47','USER_LOGOUT',1,'2015-10-05 23:14:47',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(680,'2015-10-05 21:14:56','USER_LOGIN',1,'2015-10-05 23:14:56',16,'(UserLogged,cc1) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(681,'2015-10-05 21:15:56','USER_LOGOUT',1,'2015-10-05 23:15:56',16,'(UserLogoff,cc1)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(682,'2015-10-05 21:16:06','USER_LOGIN',1,'2015-10-05 23:16:06',17,'(UserLogged,cc2) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(683,'2015-10-05 21:37:25','USER_LOGOUT',1,'2015-10-05 23:37:25',17,'(UserLogoff,cc2)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(684,'2015-10-05 21:37:31','USER_LOGIN',1,'2015-10-05 23:37:31',16,'(UserLogged,cc1) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(685,'2015-10-05 21:43:53','USER_LOGOUT',1,'2015-10-05 23:43:53',16,'(UserLogoff,cc1)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(686,'2015-10-05 21:44:00','USER_LOGIN',1,'2015-10-05 23:44:00',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(687,'2015-10-05 21:46:17','USER_LOGOUT',1,'2015-10-05 23:46:17',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(688,'2015-10-05 21:46:24','USER_LOGIN',1,'2015-10-05 23:46:24',16,'(UserLogged,cc1) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(689,'2015-11-04 15:17:06','USER_LOGIN',1,'2015-11-04 16:17:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(690,'2015-11-15 22:04:04','USER_LOGIN',1,'2015-11-15 23:04:04',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(691,'2015-11-15 22:23:45','USER_MODIFY',1,'2015-11-15 23:23:45',12,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(692,'2015-11-15 22:24:22','USER_MODIFY',1,'2015-11-15 23:24:22',12,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(693,'2015-11-15 22:24:53','USER_MODIFY',1,'2015-11-15 23:24:53',12,'User cc2 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(694,'2015-11-15 22:25:17','USER_MODIFY',1,'2015-11-15 23:25:17',12,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(695,'2015-11-15 22:45:37','USER_LOGOUT',1,'2015-11-15 23:45:37',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(696,'2015-11-18 13:41:02','USER_LOGIN',1,'2015-11-18 14:41:02',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(697,'2015-11-18 14:23:35','USER_LOGIN',1,'2015-11-18 15:23:35',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(698,'2015-11-18 15:15:46','USER_LOGOUT',1,'2015-11-18 16:15:46',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(699,'2015-11-18 15:15:51','USER_LOGIN',1,'2015-11-18 16:15:51',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(700,'2015-11-30 17:52:08','USER_LOGIN',1,'2015-11-30 18:52:08',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(701,'2016-01-10 16:45:43','USER_LOGIN',1,'2016-01-10 17:45:43',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(702,'2016-01-10 16:45:52','USER_LOGOUT',1,'2016-01-10 17:45:52',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(703,'2016-01-10 16:46:06','USER_LOGIN',1,'2016-01-10 17:46:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(704,'2016-01-16 14:53:47','USER_LOGIN',1,'2016-01-16 15:53:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(705,'2016-01-16 15:04:29','USER_LOGOUT',1,'2016-01-16 16:04:29',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(706,'2016-01-16 15:04:40','USER_LOGIN',1,'2016-01-16 16:04:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(707,'2016-01-22 09:33:26','USER_LOGIN',1,'2016-01-22 10:33:26',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(708,'2016-01-22 09:35:19','USER_LOGOUT',1,'2016-01-22 10:35:19',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(709,'2016-01-22 09:35:29','USER_LOGIN',1,'2016-01-22 10:35:29',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(710,'2016-01-22 10:47:34','USER_CREATE',1,'2016-01-22 11:47:34',12,'User aaa created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(711,'2016-01-22 10:47:34','USER_NEW_PASSWORD',1,'2016-01-22 11:47:34',12,'Password change for aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(712,'2016-01-22 12:07:56','USER_LOGIN',1,'2016-01-22 13:07:56',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(713,'2016-01-22 12:36:25','USER_NEW_PASSWORD',1,'2016-01-22 13:36:25',12,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(714,'2016-01-22 12:36:25','USER_MODIFY',1,'2016-01-22 13:36:25',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(715,'2016-01-22 12:56:32','USER_MODIFY',1,'2016-01-22 13:56:32',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(716,'2016-01-22 12:58:05','USER_MODIFY',1,'2016-01-22 13:58:05',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(717,'2016-01-22 13:01:02','USER_MODIFY',1,'2016-01-22 14:01:02',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(718,'2016-01-22 13:01:18','USER_MODIFY',1,'2016-01-22 14:01:18',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(719,'2016-01-22 13:13:42','USER_MODIFY',1,'2016-01-22 14:13:42',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(720,'2016-01-22 13:15:20','USER_DELETE',1,'2016-01-22 14:15:20',12,'User aaa removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(721,'2016-01-22 13:19:21','USER_LOGOUT',1,'2016-01-22 14:19:21',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(722,'2016-01-22 13:19:32','USER_LOGIN',1,'2016-01-22 14:19:32',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(723,'2016-01-22 13:19:51','USER_LOGOUT',1,'2016-01-22 14:19:51',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(724,'2016-01-22 13:20:01','USER_LOGIN',1,'2016-01-22 14:20:01',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(725,'2016-01-22 13:28:22','USER_LOGOUT',1,'2016-01-22 14:28:22',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(726,'2016-01-22 13:28:35','USER_LOGIN',1,'2016-01-22 14:28:35',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(727,'2016-01-22 13:33:54','USER_LOGOUT',1,'2016-01-22 14:33:54',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(728,'2016-01-22 13:34:05','USER_LOGIN',1,'2016-01-22 14:34:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(729,'2016-01-22 13:51:46','USER_MODIFY',1,'2016-01-22 14:51:46',12,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(730,'2016-01-22 16:20:12','USER_LOGIN',1,'2016-01-22 17:20:12',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(731,'2016-01-22 16:20:22','USER_LOGOUT',1,'2016-01-22 17:20:22',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(732,'2016-01-22 16:20:36','USER_LOGIN',1,'2016-01-22 17:20:36',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(733,'2016-01-22 16:27:02','USER_CREATE',1,'2016-01-22 17:27:02',12,'User ldestailleur created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(734,'2016-01-22 16:27:02','USER_NEW_PASSWORD',1,'2016-01-22 17:27:02',12,'Password change for ldestailleur','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(735,'2016-01-22 16:28:34','USER_MODIFY',1,'2016-01-22 17:28:34',12,'User ldestailleur modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(736,'2016-01-22 16:30:01','USER_ENABLEDISABLE',1,'2016-01-22 17:30:01',12,'User cc2 activated','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(737,'2016-01-22 17:11:06','USER_LOGIN',1,'2016-01-22 18:11:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(738,'2016-01-22 18:00:02','USER_DELETE',1,'2016-01-22 19:00:02',12,'User zzz removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(739,'2016-01-22 18:01:40','USER_DELETE',1,'2016-01-22 19:01:40',12,'User aaab removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(740,'2016-01-22 18:01:52','USER_DELETE',1,'2016-01-22 19:01:52',12,'User zzzg removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(741,'2016-03-13 10:54:59','USER_LOGIN',1,'2016-03-13 14:54:59',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x971','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36',NULL),(742,'2016-07-30 11:13:10','USER_LOGIN',1,'2016-07-30 15:13:10',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(743,'2016-07-30 12:50:23','USER_CREATE',1,'2016-07-30 16:50:23',12,'User eldy created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(744,'2016-07-30 12:50:23','USER_CREATE',1,'2016-07-30 16:50:23',12,'User eldy created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(745,'2016-07-30 12:50:23','USER_NEW_PASSWORD',1,'2016-07-30 16:50:23',12,'Password change for eldy','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(746,'2016-07-30 12:50:38','USER_MODIFY',1,'2016-07-30 16:50:38',12,'User eldy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(747,'2016-07-30 12:50:54','USER_DELETE',1,'2016-07-30 16:50:54',12,'User eldy removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(748,'2016-07-30 12:51:23','USER_NEW_PASSWORD',1,'2016-07-30 16:51:23',12,'Password change for ldestailleur','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(749,'2016-07-30 12:51:23','USER_MODIFY',1,'2016-07-30 16:51:23',12,'User ldestailleur modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(750,'2016-07-30 18:26:58','USER_LOGIN',1,'2016-07-30 22:26:58',18,'(UserLogged,ldestailleur) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(751,'2016-07-30 18:27:40','USER_LOGOUT',1,'2016-07-30 22:27:40',18,'(UserLogoff,ldestailleur)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(752,'2016-07-30 18:27:47','USER_LOGIN',1,'2016-07-30 22:27:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(753,'2016-07-30 19:00:00','USER_LOGOUT',1,'2016-07-30 23:00:00',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(754,'2016-07-30 19:00:04','USER_LOGIN',1,'2016-07-30 23:00:04',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(755,'2016-07-30 19:00:14','USER_LOGOUT',1,'2016-07-30 23:00:14',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(756,'2016-07-30 19:00:19','USER_LOGIN',1,'2016-07-30 23:00:19',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(757,'2016-07-30 19:00:43','USER_LOGOUT',1,'2016-07-30 23:00:43',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(758,'2016-07-30 19:00:48','USER_LOGIN',1,'2016-07-30 23:00:48',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(759,'2016-07-30 19:03:52','USER_LOGOUT',1,'2016-07-30 23:03:52',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(760,'2016-07-30 19:03:57','USER_LOGIN_FAILED',1,'2016-07-30 23:03:57',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(761,'2016-07-30 19:03:59','USER_LOGIN',1,'2016-07-30 23:03:59',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(762,'2016-07-30 19:04:13','USER_LOGOUT',1,'2016-07-30 23:04:13',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(763,'2016-07-30 19:04:17','USER_LOGIN',1,'2016-07-30 23:04:17',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(764,'2016-07-30 19:04:26','USER_LOGOUT',1,'2016-07-30 23:04:26',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(765,'2016-07-30 19:04:31','USER_LOGIN',1,'2016-07-30 23:04:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(766,'2016-07-30 19:10:50','USER_LOGOUT',1,'2016-07-30 23:10:50',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(767,'2016-07-30 19:10:54','USER_LOGIN',1,'2016-07-30 23:10:54',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(768,'2016-07-31 10:15:52','USER_LOGIN',1,'2016-07-31 14:15:52',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Lynx/2.8.8pre.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/2.12.23',NULL),(769,'2016-07-31 10:16:27','USER_LOGIN',1,'2016-07-31 14:16:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(770,'2016-07-31 10:32:14','USER_LOGIN',1,'2016-07-31 14:32:14',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Lynx/2.8.8pre.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/2.12.23',NULL),(771,'2016-07-31 10:36:28','USER_LOGIN',1,'2016-07-31 14:36:28',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Links (2.8; Linux 3.19.0-46-generic x86_64; GNU C 4.8.2; text)',NULL),(772,'2016-07-31 10:40:10','USER_LOGIN',1,'2016-07-31 14:40:10',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Links (2.8; Linux 3.19.0-46-generic x86_64; GNU C 4.8.2; text)',NULL),(773,'2016-07-31 10:54:16','USER_LOGIN',1,'2016-07-31 14:54:16',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Lynx/2.8.8pre.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/2.12.23',NULL),(774,'2016-07-31 12:52:52','USER_LOGIN',1,'2016-07-31 16:52:52',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x592','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(775,'2016-07-31 13:25:33','USER_LOGOUT',1,'2016-07-31 17:25:33',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(776,'2016-07-31 13:26:32','USER_LOGIN',1,'2016-07-31 17:26:32',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1280x751','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(777,'2016-07-31 14:13:57','USER_LOGOUT',1,'2016-07-31 18:13:57',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(778,'2016-07-31 14:14:04','USER_LOGIN',1,'2016-07-31 18:14:04',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(779,'2016-07-31 16:04:35','USER_LOGIN',1,'2016-07-31 20:04:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(780,'2016-07-31 21:14:14','USER_LOGIN',1,'2016-08-01 01:14:14',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(781,'2017-01-29 15:14:05','USER_LOGOUT',1,'2017-01-29 19:14:05',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(782,'2017-01-29 15:34:43','USER_LOGIN',1,'2017-01-29 19:34:43',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(783,'2017-01-29 15:35:04','USER_LOGOUT',1,'2017-01-29 19:35:04',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(784,'2017-01-29 15:35:12','USER_LOGIN',1,'2017-01-29 19:35:12',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(785,'2017-01-29 15:36:43','USER_LOGOUT',1,'2017-01-29 19:36:43',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(786,'2017-01-29 15:41:21','USER_LOGIN',1,'2017-01-29 19:41:21',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(787,'2017-01-29 15:41:41','USER_LOGOUT',1,'2017-01-29 19:41:41',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(788,'2017-01-29 15:42:43','USER_LOGIN',1,'2017-01-29 19:42:43',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(789,'2017-01-29 15:43:18','USER_LOGOUT',1,'2017-01-29 19:43:18',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(790,'2017-01-29 15:46:31','USER_LOGIN',1,'2017-01-29 19:46:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(791,'2017-01-29 16:18:56','USER_LOGIN',1,'2017-01-29 20:18:56',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=360x526','192.168.0.254','Mozilla/5.0 (Linux; Android 6.0; LG-H818 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36 - DoliDroid - Android client pour Dolibarr ERP-CRM',NULL),(792,'2017-01-29 17:20:59','USER_LOGIN',1,'2017-01-29 21:20:59',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(793,'2017-01-30 11:19:40','USER_LOGIN',1,'2017-01-30 15:19:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(794,'2017-01-31 16:49:39','USER_LOGIN',1,'2017-01-31 20:49:39',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x520','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(795,'2017-02-01 10:55:23','USER_LOGIN',1,'2017-02-01 14:55:23',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(796,'2017-02-01 13:34:31','USER_LOGIN',1,'2017-02-01 17:34:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(797,'2017-02-01 14:41:26','USER_LOGIN',1,'2017-02-01 18:41:26',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(798,'2017-02-01 23:51:48','USER_LOGIN_FAILED',1,'2017-02-02 03:51:48',NULL,'Bad value for login or password - login=autologin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(799,'2017-02-01 23:52:55','USER_LOGIN',1,'2017-02-02 03:52:55',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(800,'2017-02-01 23:55:45','USER_CREATE',1,'2017-02-02 03:55:45',12,'User aboston created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(801,'2017-02-01 23:55:45','USER_NEW_PASSWORD',1,'2017-02-02 03:55:45',12,'Password change for aboston','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(802,'2017-02-01 23:56:38','USER_MODIFY',1,'2017-02-02 03:56:38',12,'User aboston modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(803,'2017-02-01 23:56:50','USER_MODIFY',1,'2017-02-02 03:56:50',12,'User aboston modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(804,'2017-02-02 01:14:44','USER_LOGIN',1,'2017-02-02 05:14:44',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(805,'2017-02-03 10:27:18','USER_LOGIN',1,'2017-02-03 14:27:18',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(806,'2017-02-04 10:22:34','USER_LOGIN',1,'2017-02-04 14:22:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x489','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(807,'2017-02-06 04:01:31','USER_LOGIN',1,'2017-02-06 08:01:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(808,'2017-02-06 10:21:32','USER_LOGIN',1,'2017-02-06 14:21:32',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(809,'2017-02-06 19:09:27','USER_LOGIN',1,'2017-02-06 23:09:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(810,'2017-02-06 23:39:17','USER_LOGIN',1,'2017-02-07 03:39:17',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(811,'2017-02-07 11:36:34','USER_LOGIN',1,'2017-02-07 15:36:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x676','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(812,'2017-02-07 18:51:53','USER_LOGIN',1,'2017-02-07 22:51:53',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(813,'2017-02-07 23:13:40','USER_LOGIN',1,'2017-02-08 03:13:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(814,'2017-02-08 09:29:12','USER_LOGIN',1,'2017-02-08 13:29:12',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(815,'2017-02-08 17:33:12','USER_LOGIN',1,'2017-02-08 21:33:12',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(816,'2017-02-09 17:30:34','USER_LOGIN',1,'2017-02-09 21:30:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(817,'2017-02-10 09:30:02','USER_LOGIN',1,'2017-02-10 13:30:02',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(818,'2017-02-10 16:16:14','USER_LOGIN',1,'2017-02-10 20:16:14',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(819,'2017-02-10 17:28:15','USER_LOGIN',1,'2017-02-10 21:28:15',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(820,'2017-02-11 12:54:03','USER_LOGIN',1,'2017-02-11 16:54:03',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(821,'2017-02-11 17:23:52','USER_LOGIN',1,'2017-02-11 21:23:52',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(822,'2017-02-12 12:44:03','USER_LOGIN',1,'2017-02-12 16:44:03',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(823,'2017-02-12 16:42:13','USER_LOGIN',1,'2017-02-12 20:42:13',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(824,'2017-02-12 19:14:18','USER_LOGIN',1,'2017-02-12 23:14:18',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(825,'2017-02-15 17:17:00','USER_LOGIN',1,'2017-02-15 21:17:00',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(826,'2017-02-15 22:02:40','USER_LOGIN',1,'2017-02-16 02:02:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(827,'2017-02-16 22:13:27','USER_LOGIN',1,'2017-02-17 02:13:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x619','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(828,'2017-02-16 23:54:04','USER_LOGIN',1,'2017-02-17 03:54:04',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(829,'2017-02-17 09:14:27','USER_LOGIN',1,'2017-02-17 13:14:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(830,'2017-02-17 12:07:05','USER_LOGIN',1,'2017-02-17 16:07:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(831,'2017-02-19 21:22:20','USER_LOGIN',1,'2017-02-20 01:22:20',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(832,'2017-02-20 09:26:47','USER_LOGIN',1,'2017-02-20 13:26:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(833,'2017-02-20 16:39:55','USER_LOGIN',1,'2017-02-20 20:39:55',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(834,'2017-02-20 16:49:00','USER_MODIFY',1,'2017-02-20 20:49:00',12,'Modification utilisateur ccommerson','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(835,'2017-02-20 17:57:15','USER_LOGIN',1,'2017-02-20 21:57:14',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(836,'2017-02-20 19:43:48','USER_LOGIN',1,'2017-02-20 23:43:48',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(837,'2017-02-21 00:04:05','USER_LOGIN',1,'2017-02-21 04:04:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(838,'2017-02-21 10:23:13','USER_LOGIN',1,'2017-02-21 14:23:13',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(839,'2017-02-21 10:30:17','USER_LOGOUT',1,'2017-02-21 14:30:17',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(840,'2017-02-21 10:30:22','USER_LOGIN',1,'2017-02-21 14:30:22',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(841,'2017-02-21 11:44:05','USER_LOGIN',1,'2017-02-21 15:44:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(842,'2017-05-12 09:02:48','USER_LOGIN',1,'2017-05-12 13:02:48',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36',NULL),(843,'2017-08-27 13:29:16','USER_LOGIN',1,'2017-08-27 17:29:16',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(844,'2017-08-28 09:11:07','USER_LOGIN',1,'2017-08-28 13:11:07',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(845,'2017-08-28 10:08:58','USER_LOGIN',1,'2017-08-28 14:08:58',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(846,'2017-08-28 10:12:46','USER_MODIFY',1,'2017-08-28 14:12:46',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(847,'2017-08-28 10:28:25','USER_LOGIN',1,'2017-08-28 14:28:25',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(848,'2017-08-28 10:28:36','USER_LOGOUT',1,'2017-08-28 14:28:36',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(849,'2017-08-28 10:34:50','USER_LOGIN',1,'2017-08-28 14:34:50',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(850,'2017-08-28 11:59:02','USER_LOGIN',1,'2017-08-28 15:59:02',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(851,'2017-08-29 09:57:34','USER_LOGIN',1,'2017-08-29 13:57:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(852,'2017-08-29 11:05:51','USER_LOGIN',1,'2017-08-29 15:05:51',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(853,'2017-08-29 14:15:58','USER_LOGIN',1,'2017-08-29 18:15:58',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(854,'2017-08-29 17:49:28','USER_LOGIN',1,'2017-08-29 21:49:28',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(855,'2017-08-30 11:53:25','USER_LOGIN',1,'2017-08-30 15:53:25',18,'(UserLogged,ldestailleur) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(856,'2017-08-30 12:19:31','USER_MODIFY',1,'2017-08-30 16:19:31',18,'Modification utilisateur ldestailleur - UserRemovedFromGroup','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(857,'2017-08-30 12:19:32','USER_MODIFY',1,'2017-08-30 16:19:32',18,'Modification utilisateur ldestailleur - UserRemovedFromGroup','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(858,'2017-08-30 12:19:33','USER_MODIFY',1,'2017-08-30 16:19:33',18,'Modification utilisateur ldestailleur - UserRemovedFromGroup','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(859,'2017-08-30 12:21:42','USER_LOGOUT',1,'2017-08-30 16:21:42',18,'(UserLogoff,ldestailleur)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(860,'2017-08-30 12:21:48','USER_LOGIN',1,'2017-08-30 16:21:48',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(861,'2017-08-30 15:02:06','USER_LOGIN',1,'2017-08-30 19:02:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(862,'2017-08-31 09:25:42','USER_LOGIN',1,'2017-08-31 13:25:42',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(863,'2017-09-04 07:51:21','USER_LOGIN',1,'2017-09-04 11:51:21',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x577','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(864,'2017-09-04 09:17:09','USER_LOGIN',1,'2017-09-04 13:17:09',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(865,'2017-09-04 13:40:28','USER_LOGIN',1,'2017-09-04 17:40:28',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(866,'2017-09-06 07:55:30','USER_LOGIN',1,'2017-09-06 11:55:30',18,'(UserLogged,ldestailleur) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(867,'2017-09-06 07:55:33','USER_LOGOUT',1,'2017-09-06 11:55:33',18,'(UserLogoff,ldestailleur)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(868,'2017-09-06 07:55:38','USER_LOGIN',1,'2017-09-06 11:55:38',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(869,'2017-09-06 16:03:38','USER_LOGIN',1,'2017-09-06 20:03:38',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(870,'2017-09-06 19:43:07','USER_LOGIN',1,'2017-09-06 23:43:07',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(871,'2018-01-19 11:18:08','USER_LOGOUT',1,'2018-01-19 11:18:08',12,'(UserLogoff,admin)','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',NULL),(872,'2018-01-19 11:18:47','USER_LOGIN',1,'2018-01-19 11:18:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x965','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',NULL),(873,'2018-01-19 11:21:41','USER_LOGIN',1,'2018-01-19 11:21:41',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x926','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(874,'2018-01-19 11:24:18','USER_NEW_PASSWORD',1,'2018-01-19 11:24:18',12,'Password change for admin','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(875,'2018-01-19 11:24:18','USER_MODIFY',1,'2018-01-19 11:24:18',12,'User admin modified','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(876,'2018-01-19 11:28:45','USER_LOGOUT',1,'2018-01-19 11:28:45',12,'(UserLogoff,admin)','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL); +INSERT INTO `llx_events` VALUES (30,'2011-07-18 18:23:06','USER_LOGOUT',1,'2011-07-18 20:23:06',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(31,'2011-07-18 18:23:12','USER_LOGIN_FAILED',1,'2011-07-18 20:23:12',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(32,'2011-07-18 18:23:17','USER_LOGIN',1,'2011-07-18 20:23:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(33,'2011-07-18 20:10:51','USER_LOGIN_FAILED',1,'2011-07-18 22:10:51',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(34,'2011-07-18 20:10:55','USER_LOGIN',1,'2011-07-18 22:10:55',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(35,'2011-07-18 21:18:57','USER_LOGIN',1,'2011-07-18 23:18:57',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(36,'2011-07-20 10:34:10','USER_LOGIN',1,'2011-07-20 12:34:10',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(37,'2011-07-20 12:36:44','USER_LOGIN',1,'2011-07-20 14:36:44',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(38,'2011-07-20 13:20:51','USER_LOGIN_FAILED',1,'2011-07-20 15:20:51',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(39,'2011-07-20 13:20:54','USER_LOGIN',1,'2011-07-20 15:20:54',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(40,'2011-07-20 15:03:46','USER_LOGIN_FAILED',1,'2011-07-20 17:03:46',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(41,'2011-07-20 15:03:55','USER_LOGIN',1,'2011-07-20 17:03:55',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(42,'2011-07-20 18:05:05','USER_LOGIN_FAILED',1,'2011-07-20 20:05:05',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(43,'2011-07-20 18:05:08','USER_LOGIN',1,'2011-07-20 20:05:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(44,'2011-07-20 21:08:53','USER_LOGIN_FAILED',1,'2011-07-20 23:08:53',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(45,'2011-07-20 21:08:56','USER_LOGIN',1,'2011-07-20 23:08:56',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(46,'2011-07-21 01:26:12','USER_LOGIN',1,'2011-07-21 03:26:12',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(47,'2011-07-21 22:35:45','USER_LOGIN_FAILED',1,'2011-07-22 00:35:45',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(48,'2011-07-21 22:35:49','USER_LOGIN',1,'2011-07-22 00:35:49',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(49,'2011-07-26 23:09:47','USER_LOGIN_FAILED',1,'2011-07-27 01:09:47',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(50,'2011-07-26 23:09:50','USER_LOGIN',1,'2011-07-27 01:09:50',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(51,'2011-07-27 17:02:27','USER_LOGIN_FAILED',1,'2011-07-27 19:02:27',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(52,'2011-07-27 17:02:32','USER_LOGIN',1,'2011-07-27 19:02:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(53,'2011-07-27 23:33:37','USER_LOGIN_FAILED',1,'2011-07-28 01:33:37',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(54,'2011-07-27 23:33:41','USER_LOGIN',1,'2011-07-28 01:33:41',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(55,'2011-07-28 18:20:36','USER_LOGIN_FAILED',1,'2011-07-28 20:20:36',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(56,'2011-07-28 18:20:38','USER_LOGIN',1,'2011-07-28 20:20:38',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(57,'2011-07-28 20:13:30','USER_LOGIN_FAILED',1,'2011-07-28 22:13:30',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(58,'2011-07-28 20:13:34','USER_LOGIN',1,'2011-07-28 22:13:34',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(59,'2011-07-28 20:22:51','USER_LOGIN',1,'2011-07-28 22:22:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(60,'2011-07-28 23:05:06','USER_LOGIN',1,'2011-07-29 01:05:06',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(61,'2011-07-29 20:15:50','USER_LOGIN_FAILED',1,'2011-07-29 22:15:50',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(62,'2011-07-29 20:15:53','USER_LOGIN',1,'2011-07-29 22:15:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(68,'2011-07-29 20:51:01','USER_LOGOUT',1,'2011-07-29 22:51:01',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(69,'2011-07-29 20:51:05','USER_LOGIN',1,'2011-07-29 22:51:05',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(70,'2011-07-30 08:46:20','USER_LOGIN_FAILED',1,'2011-07-30 10:46:20',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(71,'2011-07-30 08:46:38','USER_LOGIN_FAILED',1,'2011-07-30 10:46:38',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(72,'2011-07-30 08:46:42','USER_LOGIN',1,'2011-07-30 10:46:42',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(73,'2011-07-30 10:05:12','USER_LOGIN_FAILED',1,'2011-07-30 12:05:12',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(74,'2011-07-30 10:05:15','USER_LOGIN',1,'2011-07-30 12:05:15',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(75,'2011-07-30 12:15:46','USER_LOGIN',1,'2011-07-30 14:15:46',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(76,'2011-07-31 22:19:30','USER_LOGIN',1,'2011-08-01 00:19:30',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(77,'2011-07-31 23:32:52','USER_LOGIN',1,'2011-08-01 01:32:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(78,'2011-08-01 01:24:50','USER_LOGIN_FAILED',1,'2011-08-01 03:24:50',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(79,'2011-08-01 01:24:54','USER_LOGIN',1,'2011-08-01 03:24:54',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(80,'2011-08-01 19:31:36','USER_LOGIN_FAILED',1,'2011-08-01 21:31:35',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(81,'2011-08-01 19:31:39','USER_LOGIN',1,'2011-08-01 21:31:39',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(82,'2011-08-01 20:01:36','USER_LOGIN',1,'2011-08-01 22:01:36',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(83,'2011-08-01 20:52:54','USER_LOGIN_FAILED',1,'2011-08-01 22:52:54',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(84,'2011-08-01 20:52:58','USER_LOGIN',1,'2011-08-01 22:52:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(85,'2011-08-01 21:17:28','USER_LOGIN_FAILED',1,'2011-08-01 23:17:28',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(86,'2011-08-01 21:17:31','USER_LOGIN',1,'2011-08-01 23:17:31',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(87,'2011-08-04 11:55:17','USER_LOGIN',1,'2011-08-04 13:55:17',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(88,'2011-08-04 20:19:03','USER_LOGIN_FAILED',1,'2011-08-04 22:19:03',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(89,'2011-08-04 20:19:07','USER_LOGIN',1,'2011-08-04 22:19:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(90,'2011-08-05 17:51:42','USER_LOGIN_FAILED',1,'2011-08-05 19:51:42',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(91,'2011-08-05 17:51:47','USER_LOGIN',1,'2011-08-05 19:51:47',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(92,'2011-08-05 17:56:03','USER_LOGIN',1,'2011-08-05 19:56:03',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(93,'2011-08-05 17:59:10','USER_LOGIN',1,'2011-08-05 19:59:10',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30',NULL),(94,'2011-08-05 18:01:58','USER_LOGIN',1,'2011-08-05 20:01:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30',NULL),(95,'2011-08-05 19:59:56','USER_LOGIN',1,'2011-08-05 21:59:56',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(96,'2011-08-06 18:33:22','USER_LOGIN',1,'2011-08-06 20:33:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(97,'2011-08-07 00:56:59','USER_LOGIN',1,'2011-08-07 02:56:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(98,'2011-08-07 22:49:14','USER_LOGIN',1,'2011-08-08 00:49:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(99,'2011-08-07 23:05:18','USER_LOGOUT',1,'2011-08-08 01:05:18',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(105,'2011-08-08 00:41:09','USER_LOGIN',1,'2011-08-08 02:41:09',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(106,'2011-08-08 11:58:55','USER_LOGIN',1,'2011-08-08 13:58:55',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(107,'2011-08-08 14:35:48','USER_LOGIN',1,'2011-08-08 16:35:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(108,'2011-08-08 14:36:31','USER_LOGOUT',1,'2011-08-08 16:36:31',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(109,'2011-08-08 14:38:28','USER_LOGIN',1,'2011-08-08 16:38:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(110,'2011-08-08 14:39:02','USER_LOGOUT',1,'2011-08-08 16:39:02',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(111,'2011-08-08 14:39:10','USER_LOGIN',1,'2011-08-08 16:39:10',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(112,'2011-08-08 14:39:28','USER_LOGOUT',1,'2011-08-08 16:39:28',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(113,'2011-08-08 14:39:37','USER_LOGIN',1,'2011-08-08 16:39:37',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(114,'2011-08-08 14:50:02','USER_LOGOUT',1,'2011-08-08 16:50:02',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(115,'2011-08-08 14:51:45','USER_LOGIN_FAILED',1,'2011-08-08 16:51:45',NULL,'Identifiants login ou mot de passe incorrects - login=','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(116,'2011-08-08 14:51:52','USER_LOGIN',1,'2011-08-08 16:51:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(117,'2011-08-08 15:09:54','USER_LOGOUT',1,'2011-08-08 17:09:54',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(118,'2011-08-08 15:10:19','USER_LOGIN_FAILED',1,'2011-08-08 17:10:19',NULL,'Identifiants login ou mot de passe incorrects - login=','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(119,'2011-08-08 15:10:28','USER_LOGIN',1,'2011-08-08 17:10:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(121,'2011-08-08 15:14:58','USER_LOGOUT',1,'2011-08-08 17:14:58',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(122,'2011-08-08 15:15:00','USER_LOGIN_FAILED',1,'2011-08-08 17:15:00',NULL,'Identifiants login ou mot de passe incorrects - login=','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(123,'2011-08-08 15:17:57','USER_LOGIN',1,'2011-08-08 17:17:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(124,'2011-08-08 15:35:56','USER_LOGOUT',1,'2011-08-08 17:35:56',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(125,'2011-08-08 15:36:05','USER_LOGIN',1,'2011-08-08 17:36:05',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(126,'2011-08-08 17:32:42','USER_LOGIN',1,'2011-08-08 19:32:42',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0',NULL),(127,'2012-12-08 13:49:37','USER_LOGOUT',1,'2012-12-08 14:49:37',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(128,'2012-12-08 13:49:42','USER_LOGIN',1,'2012-12-08 14:49:42',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(129,'2012-12-08 13:50:12','USER_LOGOUT',1,'2012-12-08 14:50:12',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(130,'2012-12-08 13:50:14','USER_LOGIN',1,'2012-12-08 14:50:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(131,'2012-12-08 13:50:17','USER_LOGOUT',1,'2012-12-08 14:50:17',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(132,'2012-12-08 13:52:47','USER_LOGIN',1,'2012-12-08 14:52:47',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(133,'2012-12-08 13:53:08','USER_MODIFY',1,'2012-12-08 14:53:08',1,'User admin modified','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(134,'2012-12-08 14:08:45','USER_LOGOUT',1,'2012-12-08 15:08:45',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(135,'2012-12-08 14:09:09','USER_LOGIN',1,'2012-12-08 15:09:09',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(136,'2012-12-08 14:11:43','USER_LOGOUT',1,'2012-12-08 15:11:43',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(137,'2012-12-08 14:11:45','USER_LOGIN',1,'2012-12-08 15:11:45',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(138,'2012-12-08 14:22:53','USER_LOGOUT',1,'2012-12-08 15:22:53',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(139,'2012-12-08 14:22:54','USER_LOGIN',1,'2012-12-08 15:22:54',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(140,'2012-12-08 14:23:10','USER_LOGOUT',1,'2012-12-08 15:23:10',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(141,'2012-12-08 14:23:11','USER_LOGIN',1,'2012-12-08 15:23:11',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(142,'2012-12-08 14:23:49','USER_LOGOUT',1,'2012-12-08 15:23:49',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(143,'2012-12-08 14:23:50','USER_LOGIN',1,'2012-12-08 15:23:50',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(144,'2012-12-08 14:28:08','USER_LOGOUT',1,'2012-12-08 15:28:08',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(145,'2012-12-08 14:35:15','USER_LOGIN',1,'2012-12-08 15:35:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(146,'2012-12-08 14:35:18','USER_LOGOUT',1,'2012-12-08 15:35:18',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(147,'2012-12-08 14:36:07','USER_LOGIN',1,'2012-12-08 15:36:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(148,'2012-12-08 14:36:09','USER_LOGOUT',1,'2012-12-08 15:36:09',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(149,'2012-12-08 14:36:41','USER_LOGIN',1,'2012-12-08 15:36:41',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(150,'2012-12-08 15:59:13','USER_LOGIN',1,'2012-12-08 16:59:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(151,'2012-12-09 11:49:52','USER_LOGIN',1,'2012-12-09 12:49:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(152,'2012-12-09 13:46:31','USER_LOGIN',1,'2012-12-09 14:46:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(153,'2012-12-09 19:03:14','USER_LOGIN',1,'2012-12-09 20:03:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(154,'2012-12-10 00:16:31','USER_LOGIN',1,'2012-12-10 01:16:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(170,'2012-12-11 22:03:31','USER_LOGIN',1,'2012-12-11 23:03:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(171,'2012-12-12 00:32:39','USER_LOGIN',1,'2012-12-12 01:32:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(172,'2012-12-12 10:49:59','USER_LOGIN',1,'2012-12-12 11:49:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(175,'2012-12-12 10:57:40','USER_MODIFY',1,'2012-12-12 11:57:40',1,'Modification utilisateur admin','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(176,'2012-12-12 13:29:15','USER_LOGIN',1,'2012-12-12 14:29:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(177,'2012-12-12 13:30:15','USER_LOGIN',1,'2012-12-12 14:30:15',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(178,'2012-12-12 13:40:08','USER_LOGOUT',1,'2012-12-12 14:40:08',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(179,'2012-12-12 13:40:10','USER_LOGIN',1,'2012-12-12 14:40:10',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(180,'2012-12-12 13:40:26','USER_MODIFY',1,'2012-12-12 14:40:26',1,'Modification utilisateur admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(181,'2012-12-12 13:40:34','USER_LOGOUT',1,'2012-12-12 14:40:34',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(182,'2012-12-12 13:42:23','USER_LOGIN',1,'2012-12-12 14:42:23',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(183,'2012-12-12 13:43:02','USER_NEW_PASSWORD',1,'2012-12-12 14:43:02',NULL,'Changement mot de passe de admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(184,'2012-12-12 13:43:25','USER_LOGOUT',1,'2012-12-12 14:43:25',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(185,'2012-12-12 13:43:27','USER_LOGIN_FAILED',1,'2012-12-12 14:43:27',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(186,'2012-12-12 13:43:30','USER_LOGIN',1,'2012-12-12 14:43:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(187,'2012-12-12 14:52:11','USER_LOGIN',1,'2012-12-12 15:52:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11',NULL),(188,'2012-12-12 17:53:00','USER_LOGIN_FAILED',1,'2012-12-12 18:53:00',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(189,'2012-12-12 17:53:07','USER_LOGIN_FAILED',1,'2012-12-12 18:53:07',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(190,'2012-12-12 17:53:51','USER_NEW_PASSWORD',1,'2012-12-12 18:53:51',NULL,'Changement mot de passe de admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(191,'2012-12-12 17:54:00','USER_LOGIN',1,'2012-12-12 18:54:00',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(192,'2012-12-12 17:54:10','USER_NEW_PASSWORD',1,'2012-12-12 18:54:10',1,'Changement mot de passe de admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(193,'2012-12-12 17:54:10','USER_MODIFY',1,'2012-12-12 18:54:10',1,'Modification utilisateur admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(194,'2012-12-12 18:57:09','USER_LOGIN',1,'2012-12-12 19:57:09',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(195,'2012-12-12 23:04:08','USER_LOGIN',1,'2012-12-13 00:04:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(196,'2012-12-17 20:03:14','USER_LOGIN',1,'2012-12-17 21:03:14',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(197,'2012-12-17 21:18:45','USER_LOGIN',1,'2012-12-17 22:18:45',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(198,'2012-12-17 22:30:08','USER_LOGIN',1,'2012-12-17 23:30:08',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(199,'2012-12-18 23:32:03','USER_LOGIN',1,'2012-12-19 00:32:03',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(200,'2012-12-19 09:38:03','USER_LOGIN',1,'2012-12-19 10:38:03',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(201,'2012-12-19 11:23:35','USER_LOGIN',1,'2012-12-19 12:23:35',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(202,'2012-12-19 12:46:22','USER_LOGIN',1,'2012-12-19 13:46:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(214,'2012-12-19 19:11:31','USER_LOGIN',1,'2012-12-19 20:11:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(215,'2012-12-21 16:36:57','USER_LOGIN',1,'2012-12-21 17:36:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(216,'2012-12-21 16:38:43','USER_NEW_PASSWORD',1,'2012-12-21 17:38:43',1,'Changement mot de passe de adupont','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(217,'2012-12-21 16:38:43','USER_MODIFY',1,'2012-12-21 17:38:43',1,'Modification utilisateur adupont','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(218,'2012-12-21 16:38:51','USER_LOGOUT',1,'2012-12-21 17:38:51',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(219,'2012-12-21 16:38:55','USER_LOGIN',1,'2012-12-21 17:38:55',3,'(UserLogged,adupont)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(220,'2012-12-21 16:48:18','USER_LOGOUT',1,'2012-12-21 17:48:18',3,'(UserLogoff,adupont)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(221,'2012-12-21 16:48:20','USER_LOGIN',1,'2012-12-21 17:48:20',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(222,'2012-12-26 18:28:18','USER_LOGIN',1,'2012-12-26 19:28:18',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(223,'2012-12-26 20:00:24','USER_LOGIN',1,'2012-12-26 21:00:24',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(224,'2012-12-27 01:10:27','USER_LOGIN',1,'2012-12-27 02:10:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(225,'2012-12-28 19:12:08','USER_LOGIN',1,'2012-12-28 20:12:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(226,'2012-12-28 20:16:58','USER_LOGIN',1,'2012-12-28 21:16:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(227,'2012-12-29 14:35:46','USER_LOGIN',1,'2012-12-29 15:35:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(228,'2012-12-29 14:37:59','USER_LOGOUT',1,'2012-12-29 15:37:59',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(229,'2012-12-29 14:38:00','USER_LOGIN',1,'2012-12-29 15:38:00',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(230,'2012-12-29 17:16:48','USER_LOGIN',1,'2012-12-29 18:16:48',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(231,'2012-12-31 12:02:59','USER_LOGIN',1,'2012-12-31 13:02:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(232,'2013-01-02 20:32:51','USER_LOGIN',1,'2013-01-02 21:32:51',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0',NULL),(233,'2013-01-02 20:58:59','USER_LOGIN',1,'2013-01-02 21:58:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(234,'2013-01-03 09:25:07','USER_LOGIN',1,'2013-01-03 10:25:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(235,'2013-01-03 19:39:31','USER_LOGIN',1,'2013-01-03 20:39:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(236,'2013-01-04 22:40:19','USER_LOGIN',1,'2013-01-04 23:40:19',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(237,'2013-01-05 12:59:59','USER_LOGIN',1,'2013-01-05 13:59:59',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(238,'2013-01-05 15:28:52','USER_LOGIN',1,'2013-01-05 16:28:52',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(239,'2013-01-05 17:02:08','USER_LOGIN',1,'2013-01-05 18:02:08',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(240,'2013-01-06 12:13:33','USER_LOGIN',1,'2013-01-06 13:13:33',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(241,'2013-01-07 01:21:15','USER_LOGIN',1,'2013-01-07 02:21:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(242,'2013-01-07 01:46:31','USER_LOGOUT',1,'2013-01-07 02:46:31',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(243,'2013-01-07 19:54:50','USER_LOGIN',1,'2013-01-07 20:54:50',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(244,'2013-01-08 21:55:01','USER_LOGIN',1,'2013-01-08 22:55:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(245,'2013-01-09 11:13:28','USER_LOGIN',1,'2013-01-09 12:13:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(246,'2013-01-10 18:30:46','USER_LOGIN',1,'2013-01-10 19:30:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(247,'2013-01-11 18:03:26','USER_LOGIN',1,'2013-01-11 19:03:26',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(248,'2013-01-12 11:15:04','USER_LOGIN',1,'2013-01-12 12:15:04',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(249,'2013-01-12 14:42:44','USER_LOGIN',1,'2013-01-12 15:42:44',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(250,'2013-01-13 12:07:17','USER_LOGIN',1,'2013-01-13 13:07:17',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(251,'2013-01-13 17:37:58','USER_LOGIN',1,'2013-01-13 18:37:58',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(252,'2013-01-13 19:24:21','USER_LOGIN',1,'2013-01-13 20:24:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(253,'2013-01-13 19:29:19','USER_LOGOUT',1,'2013-01-13 20:29:19',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(254,'2013-01-13 21:39:39','USER_LOGIN',1,'2013-01-13 22:39:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(255,'2013-01-14 00:52:21','USER_LOGIN',1,'2013-01-14 01:52:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11',NULL),(256,'2013-01-16 11:34:31','USER_LOGIN',1,'2013-01-16 12:34:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(257,'2013-01-16 15:36:21','USER_LOGIN',1,'2013-01-16 16:36:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(258,'2013-01-16 19:17:36','USER_LOGIN',1,'2013-01-16 20:17:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(259,'2013-01-16 19:48:08','GROUP_CREATE',1,'2013-01-16 20:48:08',1,'Création groupe ggg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(260,'2013-01-16 21:48:53','USER_LOGIN',1,'2013-01-16 22:48:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(261,'2013-01-17 19:55:53','USER_LOGIN',1,'2013-01-17 20:55:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(262,'2013-01-18 09:48:01','USER_LOGIN',1,'2013-01-18 10:48:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(263,'2013-01-18 13:22:36','USER_LOGIN',1,'2013-01-18 14:22:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(264,'2013-01-18 16:10:23','USER_LOGIN',1,'2013-01-18 17:10:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(265,'2013-01-18 17:41:40','USER_LOGIN',1,'2013-01-18 18:41:40',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(266,'2013-01-19 14:33:48','USER_LOGIN',1,'2013-01-19 15:33:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(267,'2013-01-19 16:47:43','USER_LOGIN',1,'2013-01-19 17:47:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(268,'2013-01-19 16:59:43','USER_LOGIN',1,'2013-01-19 17:59:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(269,'2013-01-19 17:00:22','USER_LOGIN',1,'2013-01-19 18:00:22',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(270,'2013-01-19 17:04:16','USER_LOGOUT',1,'2013-01-19 18:04:16',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(271,'2013-01-19 17:04:18','USER_LOGIN',1,'2013-01-19 18:04:18',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(272,'2013-01-20 00:34:19','USER_LOGIN',1,'2013-01-20 01:34:19',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(273,'2013-01-21 11:54:17','USER_LOGIN',1,'2013-01-21 12:54:17',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(274,'2013-01-21 13:48:15','USER_LOGIN',1,'2013-01-21 14:48:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(275,'2013-01-21 14:30:22','USER_LOGIN',1,'2013-01-21 15:30:22',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(276,'2013-01-21 15:10:46','USER_LOGIN',1,'2013-01-21 16:10:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(277,'2013-01-21 17:27:43','USER_LOGIN',1,'2013-01-21 18:27:43',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(278,'2013-01-21 21:48:15','USER_LOGIN',1,'2013-01-21 22:48:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(279,'2013-01-21 21:50:42','USER_LOGIN',1,'2013-01-21 22:50:42',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17',NULL),(280,'2013-01-23 09:28:26','USER_LOGIN',1,'2013-01-23 10:28:26',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(281,'2013-01-23 13:21:57','USER_LOGIN',1,'2013-01-23 14:21:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(282,'2013-01-23 16:52:00','USER_LOGOUT',1,'2013-01-23 17:52:00',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(283,'2013-01-23 16:52:05','USER_LOGIN_FAILED',1,'2013-01-23 17:52:05',NULL,'Bad value for login or password - login=bbb','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(284,'2013-01-23 16:52:09','USER_LOGIN',1,'2013-01-23 17:52:09',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(285,'2013-01-23 16:52:27','USER_CREATE',1,'2013-01-23 17:52:27',1,'Création utilisateur aaa','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(286,'2013-01-23 16:52:27','USER_NEW_PASSWORD',1,'2013-01-23 17:52:27',1,'Changement mot de passe de aaa','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(287,'2013-01-23 16:52:37','USER_CREATE',1,'2013-01-23 17:52:37',1,'Création utilisateur bbb','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(288,'2013-01-23 16:52:37','USER_NEW_PASSWORD',1,'2013-01-23 17:52:37',1,'Changement mot de passe de bbb','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(289,'2013-01-23 16:53:15','USER_LOGOUT',1,'2013-01-23 17:53:15',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(290,'2013-01-23 16:53:20','USER_LOGIN',1,'2013-01-23 17:53:20',4,'(UserLogged,aaa)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(291,'2013-01-23 19:16:58','USER_LOGIN',1,'2013-01-23 20:16:58',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(292,'2013-01-26 10:54:07','USER_LOGIN',1,'2013-01-26 11:54:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(293,'2013-01-29 10:15:36','USER_LOGIN',1,'2013-01-29 11:15:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(294,'2013-01-30 17:42:50','USER_LOGIN',1,'2013-01-30 18:42:50',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17',NULL),(295,'2013-02-01 08:49:55','USER_LOGIN',1,'2013-02-01 09:49:55',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(296,'2013-02-01 08:51:57','USER_LOGOUT',1,'2013-02-01 09:51:57',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(297,'2013-02-01 08:52:39','USER_LOGIN',1,'2013-02-01 09:52:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(298,'2013-02-01 21:03:01','USER_LOGIN',1,'2013-02-01 22:03:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(299,'2013-02-10 19:48:39','USER_LOGIN',1,'2013-02-10 20:48:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(300,'2013-02-10 20:46:48','USER_LOGIN',1,'2013-02-10 21:46:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(301,'2013-02-10 21:39:23','USER_LOGIN',1,'2013-02-10 22:39:23',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(302,'2013-02-11 19:00:13','USER_LOGIN',1,'2013-02-11 20:00:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(303,'2013-02-11 19:43:44','USER_LOGIN_FAILED',1,'2013-02-11 20:43:44',NULL,'Unknown column \'u.fk_user\' in \'field list\'','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(304,'2013-02-11 19:44:01','USER_LOGIN',1,'2013-02-11 20:44:01',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(305,'2013-02-12 00:27:35','USER_LOGIN',1,'2013-02-12 01:27:35',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(306,'2013-02-12 00:27:38','USER_LOGOUT',1,'2013-02-12 01:27:38',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(307,'2013-02-12 00:28:07','USER_LOGIN',1,'2013-02-12 01:28:07',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(308,'2013-02-12 00:28:09','USER_LOGOUT',1,'2013-02-12 01:28:09',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(309,'2013-02-12 00:28:26','USER_LOGIN',1,'2013-02-12 01:28:26',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(310,'2013-02-12 00:28:30','USER_LOGOUT',1,'2013-02-12 01:28:30',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(311,'2013-02-12 12:42:15','USER_LOGIN',1,'2013-02-12 13:42:15',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17',NULL),(312,'2013-02-12 13:46:16','USER_LOGIN',1,'2013-02-12 14:46:16',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(313,'2013-02-12 14:54:28','USER_LOGIN',1,'2013-02-12 15:54:28',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(314,'2013-02-12 16:04:46','USER_LOGIN',1,'2013-02-12 17:04:46',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(315,'2013-02-13 14:02:43','USER_LOGIN',1,'2013-02-13 15:02:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(316,'2013-02-13 14:48:30','USER_LOGIN',1,'2013-02-13 15:48:30',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(317,'2013-02-13 17:44:53','USER_LOGIN',1,'2013-02-13 18:44:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(318,'2013-02-15 08:44:36','USER_LOGIN',1,'2013-02-15 09:44:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(319,'2013-02-15 08:53:20','USER_LOGIN',1,'2013-02-15 09:53:20',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(320,'2013-02-16 19:10:28','USER_LOGIN',1,'2013-02-16 20:10:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(321,'2013-02-16 19:22:40','USER_CREATE',1,'2013-02-16 20:22:40',1,'Création utilisateur aaab','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(322,'2013-02-16 19:22:40','USER_NEW_PASSWORD',1,'2013-02-16 20:22:40',1,'Changement mot de passe de aaab','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(323,'2013-02-16 19:48:15','USER_CREATE',1,'2013-02-16 20:48:15',1,'Création utilisateur zzz','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(324,'2013-02-16 19:48:15','USER_NEW_PASSWORD',1,'2013-02-16 20:48:15',1,'Changement mot de passe de zzz','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(325,'2013-02-16 19:50:08','USER_CREATE',1,'2013-02-16 20:50:08',1,'Création utilisateur zzzg','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(326,'2013-02-16 19:50:08','USER_NEW_PASSWORD',1,'2013-02-16 20:50:08',1,'Changement mot de passe de zzzg','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(327,'2013-02-16 21:20:03','USER_LOGIN',1,'2013-02-16 22:20:03',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(328,'2013-02-17 14:30:51','USER_LOGIN',1,'2013-02-17 15:30:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(329,'2013-02-17 17:21:22','USER_LOGIN',1,'2013-02-17 18:21:22',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(330,'2013-02-17 17:48:43','USER_MODIFY',1,'2013-02-17 18:48:43',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(331,'2013-02-17 17:48:47','USER_MODIFY',1,'2013-02-17 18:48:47',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(332,'2013-02-17 17:48:51','USER_MODIFY',1,'2013-02-17 18:48:51',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(333,'2013-02-17 17:48:56','USER_MODIFY',1,'2013-02-17 18:48:56',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(334,'2013-02-18 22:00:01','USER_LOGIN',1,'2013-02-18 23:00:01',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(335,'2013-02-19 08:19:52','USER_LOGIN',1,'2013-02-19 09:19:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(336,'2013-02-19 22:00:52','USER_LOGIN',1,'2013-02-19 23:00:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(337,'2013-02-20 09:34:52','USER_LOGIN',1,'2013-02-20 10:34:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(338,'2013-02-20 13:12:28','USER_LOGIN',1,'2013-02-20 14:12:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(339,'2013-02-20 17:19:44','USER_LOGIN',1,'2013-02-20 18:19:44',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(340,'2013-02-20 19:07:21','USER_MODIFY',1,'2013-02-20 20:07:21',1,'Modification utilisateur adupont','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(341,'2013-02-20 19:47:17','USER_LOGIN',1,'2013-02-20 20:47:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(342,'2013-02-20 19:48:01','USER_MODIFY',1,'2013-02-20 20:48:01',1,'Modification utilisateur aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(343,'2013-02-21 08:27:07','USER_LOGIN',1,'2013-02-21 09:27:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(344,'2013-02-23 13:34:13','USER_LOGIN',1,'2013-02-23 14:34:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.69 Safari/537.17',NULL),(345,'2013-02-24 01:06:41','USER_LOGIN_FAILED',1,'2013-02-24 02:06:41',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(346,'2013-02-24 01:06:45','USER_LOGIN_FAILED',1,'2013-02-24 02:06:45',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(347,'2013-02-24 01:06:55','USER_LOGIN_FAILED',1,'2013-02-24 02:06:55',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(348,'2013-02-24 01:07:03','USER_LOGIN_FAILED',1,'2013-02-24 02:07:03',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(349,'2013-02-24 01:07:21','USER_LOGIN_FAILED',1,'2013-02-24 02:07:21',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(350,'2013-02-24 01:08:12','USER_LOGIN_FAILED',1,'2013-02-24 02:08:12',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(351,'2013-02-24 01:08:42','USER_LOGIN_FAILED',1,'2013-02-24 02:08:42',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(352,'2013-02-24 01:08:50','USER_LOGIN_FAILED',1,'2013-02-24 02:08:50',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(353,'2013-02-24 01:09:08','USER_LOGIN_FAILED',1,'2013-02-24 02:09:08',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(354,'2013-02-24 01:09:42','USER_LOGIN_FAILED',1,'2013-02-24 02:09:42',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(355,'2013-02-24 01:09:50','USER_LOGIN_FAILED',1,'2013-02-24 02:09:50',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(356,'2013-02-24 01:10:05','USER_LOGIN_FAILED',1,'2013-02-24 02:10:05',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(357,'2013-02-24 01:10:22','USER_LOGIN_FAILED',1,'2013-02-24 02:10:22',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(358,'2013-02-24 01:10:30','USER_LOGIN_FAILED',1,'2013-02-24 02:10:30',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(359,'2013-02-24 01:10:56','USER_LOGIN_FAILED',1,'2013-02-24 02:10:56',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(360,'2013-02-24 01:11:26','USER_LOGIN_FAILED',1,'2013-02-24 02:11:26',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(361,'2013-02-24 01:12:06','USER_LOGIN_FAILED',1,'2013-02-24 02:12:06',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(362,'2013-02-24 01:21:14','USER_LOGIN_FAILED',1,'2013-02-24 02:21:14',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(363,'2013-02-24 01:21:25','USER_LOGIN_FAILED',1,'2013-02-24 02:21:25',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(364,'2013-02-24 01:21:54','USER_LOGIN_FAILED',1,'2013-02-24 02:21:54',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(365,'2013-02-24 01:22:14','USER_LOGIN_FAILED',1,'2013-02-24 02:22:14',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(366,'2013-02-24 01:22:37','USER_LOGIN_FAILED',1,'2013-02-24 02:22:37',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(367,'2013-02-24 01:23:01','USER_LOGIN_FAILED',1,'2013-02-24 02:23:01',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(368,'2013-02-24 01:23:39','USER_LOGIN_FAILED',1,'2013-02-24 02:23:39',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(369,'2013-02-24 01:24:04','USER_LOGIN_FAILED',1,'2013-02-24 02:24:04',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(370,'2013-02-24 01:24:39','USER_LOGIN_FAILED',1,'2013-02-24 02:24:39',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(371,'2013-02-24 01:25:01','USER_LOGIN_FAILED',1,'2013-02-24 02:25:01',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(372,'2013-02-24 01:25:12','USER_LOGIN_FAILED',1,'2013-02-24 02:25:12',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(373,'2013-02-24 01:27:30','USER_LOGIN_FAILED',1,'2013-02-24 02:27:30',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(374,'2013-02-24 01:28:00','USER_LOGIN_FAILED',1,'2013-02-24 02:28:00',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(375,'2013-02-24 01:28:35','USER_LOGIN_FAILED',1,'2013-02-24 02:28:35',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(376,'2013-02-24 01:29:03','USER_LOGIN_FAILED',1,'2013-02-24 02:29:03',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(377,'2013-02-24 01:29:55','USER_LOGIN_FAILED',1,'2013-02-24 02:29:55',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(378,'2013-02-24 01:32:40','USER_LOGIN_FAILED',1,'2013-02-24 02:32:40',NULL,'Bad value for login or password - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(379,'2013-02-24 01:39:33','USER_LOGIN_FAILED',1,'2013-02-24 02:39:33',NULL,'Identifiants login ou mot de passe incorrects - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(380,'2013-02-24 01:39:38','USER_LOGIN_FAILED',1,'2013-02-24 02:39:38',NULL,'Identifiants login ou mot de passe incorrects - login=aa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(381,'2013-02-24 01:39:47','USER_LOGIN_FAILED',1,'2013-02-24 02:39:47',NULL,'Identifiants login ou mot de passe incorrects - login=lmkm','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(382,'2013-02-24 01:40:54','USER_LOGIN_FAILED',1,'2013-02-24 02:40:54',NULL,'Identifiants login ou mot de passe incorrects - login=lmkm','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(383,'2013-02-24 01:47:57','USER_LOGIN_FAILED',1,'2013-02-24 02:47:57',NULL,'Identifiants login ou mot de passe incorrects - login=lmkm','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(384,'2013-02-24 01:48:05','USER_LOGIN_FAILED',1,'2013-02-24 02:48:05',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(385,'2013-02-24 01:48:07','USER_LOGIN_FAILED',1,'2013-02-24 02:48:07',NULL,'Unknown column \'u.lastname\' in \'field list\'','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(386,'2013-02-24 01:48:35','USER_LOGIN',1,'2013-02-24 02:48:35',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(387,'2013-02-24 01:56:32','USER_LOGIN',1,'2013-02-24 02:56:32',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',NULL),(388,'2013-02-24 02:05:55','USER_LOGOUT',1,'2013-02-24 03:05:55',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(389,'2013-02-24 02:39:52','USER_LOGIN',1,'2013-02-24 03:39:52',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(390,'2013-02-24 02:51:10','USER_LOGOUT',1,'2013-02-24 03:51:10',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(391,'2013-02-24 12:46:41','USER_LOGIN',1,'2013-02-24 13:46:41',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(392,'2013-02-24 12:46:52','USER_LOGOUT',1,'2013-02-24 13:46:52',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(393,'2013-02-24 12:46:56','USER_LOGIN',1,'2013-02-24 13:46:56',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(394,'2013-02-24 12:47:56','USER_LOGOUT',1,'2013-02-24 13:47:56',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(395,'2013-02-24 12:48:00','USER_LOGIN',1,'2013-02-24 13:48:00',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(396,'2013-02-24 12:48:11','USER_LOGOUT',1,'2013-02-24 13:48:11',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(397,'2013-02-24 12:48:32','USER_LOGIN',1,'2013-02-24 13:48:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(398,'2013-02-24 12:52:22','USER_LOGOUT',1,'2013-02-24 13:52:22',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(399,'2013-02-24 12:52:27','USER_LOGIN',1,'2013-02-24 13:52:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(400,'2013-02-24 12:52:54','USER_LOGOUT',1,'2013-02-24 13:52:54',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(401,'2013-02-24 12:52:59','USER_LOGIN',1,'2013-02-24 13:52:59',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(402,'2013-02-24 12:55:39','USER_LOGOUT',1,'2013-02-24 13:55:39',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(403,'2013-02-24 12:55:59','USER_LOGIN',1,'2013-02-24 13:55:59',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(404,'2013-02-24 12:56:07','USER_LOGOUT',1,'2013-02-24 13:56:07',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(405,'2013-02-24 12:56:23','USER_LOGIN',1,'2013-02-24 13:56:23',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(406,'2013-02-24 12:56:46','USER_LOGOUT',1,'2013-02-24 13:56:46',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(407,'2013-02-24 12:58:30','USER_LOGIN',1,'2013-02-24 13:58:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(408,'2013-02-24 12:58:33','USER_LOGOUT',1,'2013-02-24 13:58:33',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(409,'2013-02-24 12:58:51','USER_LOGIN',1,'2013-02-24 13:58:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(410,'2013-02-24 12:58:58','USER_LOGOUT',1,'2013-02-24 13:58:58',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(411,'2013-02-24 13:18:53','USER_LOGIN',1,'2013-02-24 14:18:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(412,'2013-02-24 13:19:52','USER_LOGOUT',1,'2013-02-24 14:19:52',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(413,'2013-02-24 15:39:31','USER_LOGIN_FAILED',1,'2013-02-24 16:39:31',NULL,'ErrorBadValueForCode - login=admin','127.0.0.1',NULL,NULL),(414,'2013-02-24 15:42:07','USER_LOGIN',1,'2013-02-24 16:42:07',1,'(UserLogged,admin)','127.0.0.1',NULL,NULL),(415,'2013-02-24 15:42:52','USER_LOGOUT',1,'2013-02-24 16:42:52',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(416,'2013-02-24 16:04:21','USER_LOGIN',1,'2013-02-24 17:04:21',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',NULL),(417,'2013-02-24 16:11:28','USER_LOGIN_FAILED',1,'2013-02-24 17:11:28',NULL,'ErrorBadValueForCode - login=admin','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(418,'2013-02-24 16:11:37','USER_LOGIN',1,'2013-02-24 17:11:37',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(419,'2013-02-24 16:36:52','USER_LOGOUT',1,'2013-02-24 17:36:52',1,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',NULL),(420,'2013-02-24 16:40:37','USER_LOGIN',1,'2013-02-24 17:40:37',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(421,'2013-02-24 16:57:16','USER_LOGIN',1,'2013-02-24 17:57:16',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 - 2131034114',NULL),(422,'2013-02-24 17:01:30','USER_LOGOUT',1,'2013-02-24 18:01:30',1,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 - 2131034114',NULL),(423,'2013-02-24 17:02:33','USER_LOGIN',1,'2013-02-24 18:02:33',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(424,'2013-02-24 17:14:22','USER_LOGOUT',1,'2013-02-24 18:14:22',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(425,'2013-02-24 17:15:07','USER_LOGIN_FAILED',1,'2013-02-24 18:15:07',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(426,'2013-02-24 17:15:20','USER_LOGIN',1,'2013-02-24 18:15:20',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(427,'2013-02-24 17:20:14','USER_LOGIN',1,'2013-02-24 18:20:14',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(428,'2013-02-24 17:20:51','USER_LOGIN',1,'2013-02-24 18:20:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(429,'2013-02-24 17:20:54','USER_LOGOUT',1,'2013-02-24 18:20:54',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(430,'2013-02-24 17:21:19','USER_LOGIN',1,'2013-02-24 18:21:19',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(431,'2013-02-24 17:32:35','USER_LOGIN',1,'2013-02-24 18:32:35',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 - 2131034114',NULL),(432,'2013-02-24 18:28:48','USER_LOGIN',1,'2013-02-24 19:28:48',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(433,'2013-02-24 18:29:27','USER_LOGOUT',1,'2013-02-24 19:29:27',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(434,'2013-02-24 18:29:32','USER_LOGIN',1,'2013-02-24 19:29:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7',NULL),(435,'2013-02-24 20:13:13','USER_LOGOUT',1,'2013-02-24 21:13:13',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(436,'2013-02-24 20:13:17','USER_LOGIN',1,'2013-02-24 21:13:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(437,'2013-02-25 08:57:16','USER_LOGIN',1,'2013-02-25 09:57:16',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(438,'2013-02-25 08:57:59','USER_LOGOUT',1,'2013-02-25 09:57:59',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(439,'2013-02-25 09:15:02','USER_LOGIN',1,'2013-02-25 10:15:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(440,'2013-02-25 09:15:50','USER_LOGOUT',1,'2013-02-25 10:15:50',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(441,'2013-02-25 09:15:57','USER_LOGIN',1,'2013-02-25 10:15:57',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(442,'2013-02-25 09:16:12','USER_LOGOUT',1,'2013-02-25 10:16:12',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(443,'2013-02-25 09:16:19','USER_LOGIN',1,'2013-02-25 10:16:19',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(444,'2013-02-25 09:16:25','USER_LOGOUT',1,'2013-02-25 10:16:25',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(445,'2013-02-25 09:16:39','USER_LOGIN_FAILED',1,'2013-02-25 10:16:39',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(446,'2013-02-25 09:16:42','USER_LOGIN_FAILED',1,'2013-02-25 10:16:42',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(447,'2013-02-25 09:16:54','USER_LOGIN_FAILED',1,'2013-02-25 10:16:54',NULL,'Identificadors d'usuari o contrasenya incorrectes - login=gfdg','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(448,'2013-02-25 09:17:53','USER_LOGIN',1,'2013-02-25 10:17:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(449,'2013-02-25 09:18:37','USER_LOGOUT',1,'2013-02-25 10:18:37',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(450,'2013-02-25 09:18:41','USER_LOGIN',1,'2013-02-25 10:18:41',4,'(UserLogged,aaa)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(451,'2013-02-25 09:18:47','USER_LOGOUT',1,'2013-02-25 10:18:47',4,'(UserLogoff,aaa)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(452,'2013-02-25 10:05:34','USER_LOGIN',1,'2013-02-25 11:05:34',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(453,'2013-02-26 21:51:40','USER_LOGIN',1,'2013-02-26 22:51:40',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(454,'2013-02-26 23:30:06','USER_LOGIN',1,'2013-02-27 00:30:06',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(455,'2013-02-27 14:13:11','USER_LOGIN',1,'2013-02-27 15:13:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(456,'2013-02-27 18:12:06','USER_LOGIN_FAILED',1,'2013-02-27 19:12:06',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(457,'2013-02-27 18:12:10','USER_LOGIN',1,'2013-02-27 19:12:10',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(458,'2013-02-27 20:20:08','USER_LOGIN',1,'2013-02-27 21:20:08',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(459,'2013-03-01 22:12:03','USER_LOGIN',1,'2013-03-01 23:12:03',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(460,'2013-03-02 11:45:50','USER_LOGIN',1,'2013-03-02 12:45:50',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(461,'2013-03-02 15:53:51','USER_LOGIN_FAILED',1,'2013-03-02 16:53:51',NULL,'Identifiants login ou mot de passe incorrects - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(462,'2013-03-02 15:53:53','USER_LOGIN',1,'2013-03-02 16:53:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(463,'2013-03-02 18:32:32','USER_LOGIN',1,'2013-03-02 19:32:32',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(464,'2013-03-02 22:59:36','USER_LOGIN',1,'2013-03-02 23:59:36',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(465,'2013-03-03 16:26:26','USER_LOGIN',1,'2013-03-03 17:26:26',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(466,'2013-03-03 22:50:27','USER_LOGIN',1,'2013-03-03 23:50:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(467,'2013-03-04 08:29:27','USER_LOGIN',1,'2013-03-04 09:29:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(468,'2013-03-04 18:27:28','USER_LOGIN',1,'2013-03-04 19:27:28',1,'(UserLogged,admin)','192.168.0.254','Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; NP06)',NULL),(469,'2013-03-04 19:27:23','USER_LOGIN',1,'2013-03-04 20:27:23',1,'(UserLogged,admin)','192.168.0.254','Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)',NULL),(470,'2013-03-04 19:35:14','USER_LOGIN',1,'2013-03-04 20:35:14',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(471,'2013-03-04 19:55:49','USER_LOGIN',1,'2013-03-04 20:55:49',1,'(UserLogged,admin)','192.168.0.254','Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',NULL),(472,'2013-03-04 21:16:13','USER_LOGIN',1,'2013-03-04 22:16:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(473,'2013-03-05 10:17:30','USER_LOGIN',1,'2013-03-05 11:17:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(474,'2013-03-05 11:02:43','USER_LOGIN',1,'2013-03-05 12:02:43',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(475,'2013-03-05 23:14:39','USER_LOGIN',1,'2013-03-06 00:14:39',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(476,'2013-03-06 08:58:57','USER_LOGIN',1,'2013-03-06 09:58:57',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(477,'2013-03-06 14:29:40','USER_LOGIN',1,'2013-03-06 15:29:40',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(478,'2013-03-06 21:53:02','USER_LOGIN',1,'2013-03-06 22:53:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(479,'2013-03-07 21:14:39','USER_LOGIN',1,'2013-03-07 22:14:39',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(480,'2013-03-08 00:06:05','USER_LOGIN',1,'2013-03-08 01:06:05',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(481,'2013-03-08 01:38:13','USER_LOGIN',1,'2013-03-08 02:38:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(482,'2013-03-08 08:59:50','USER_LOGIN',1,'2013-03-08 09:59:50',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(483,'2013-03-09 12:08:51','USER_LOGIN',1,'2013-03-09 13:08:51',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(484,'2013-03-09 15:19:53','USER_LOGIN',1,'2013-03-09 16:19:53',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(495,'2013-03-09 18:06:21','USER_LOGIN',1,'2013-03-09 19:06:21',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(496,'2013-03-09 20:01:24','USER_LOGIN',1,'2013-03-09 21:01:24',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(497,'2013-03-09 23:36:45','USER_LOGIN',1,'2013-03-10 00:36:45',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(498,'2013-03-10 14:37:13','USER_LOGIN',1,'2013-03-10 15:37:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(499,'2013-03-10 17:54:12','USER_LOGIN',1,'2013-03-10 18:54:12',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(500,'2013-03-11 08:57:09','USER_LOGIN',1,'2013-03-11 09:57:09',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(501,'2013-03-11 22:05:13','USER_LOGIN',1,'2013-03-11 23:05:13',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(502,'2013-03-12 08:34:27','USER_LOGIN',1,'2013-03-12 09:34:27',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(503,'2013-03-13 09:11:02','USER_LOGIN',1,'2013-03-13 10:11:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(504,'2013-03-13 10:02:11','USER_LOGIN',1,'2013-03-13 11:02:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(505,'2013-03-13 13:20:58','USER_LOGIN',1,'2013-03-13 14:20:58',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(506,'2013-03-13 16:19:28','USER_LOGIN',1,'2013-03-13 17:19:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(507,'2013-03-13 18:34:30','USER_LOGIN',1,'2013-03-13 19:34:30',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(508,'2013-03-14 08:25:02','USER_LOGIN',1,'2013-03-14 09:25:02',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(509,'2013-03-14 19:15:22','USER_LOGIN',1,'2013-03-14 20:15:22',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(510,'2013-03-14 21:58:53','USER_LOGIN',1,'2013-03-14 22:58:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(511,'2013-03-14 21:58:59','USER_LOGOUT',1,'2013-03-14 22:58:59',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(512,'2013-03-14 21:59:07','USER_LOGIN',1,'2013-03-14 22:59:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(513,'2013-03-14 22:58:22','USER_LOGOUT',1,'2013-03-14 23:58:22',1,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(514,'2013-03-14 23:00:25','USER_LOGIN',1,'2013-03-15 00:00:25',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(515,'2013-03-16 12:14:28','USER_LOGIN',1,'2013-03-16 13:14:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(516,'2013-03-16 16:09:01','USER_LOGIN',1,'2013-03-16 17:09:01',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(517,'2013-03-16 16:57:11','USER_LOGIN',1,'2013-03-16 17:57:11',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(518,'2013-03-16 19:31:31','USER_LOGIN',1,'2013-03-16 20:31:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22',NULL),(519,'2013-03-17 17:44:39','USER_LOGIN',1,'2013-03-17 18:44:39',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(520,'2013-03-17 20:40:57','USER_LOGIN',1,'2013-03-17 21:40:57',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(521,'2013-03-17 23:14:05','USER_LOGIN',1,'2013-03-18 00:14:05',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(522,'2013-03-17 23:28:47','USER_LOGOUT',1,'2013-03-18 00:28:47',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(523,'2013-03-17 23:28:54','USER_LOGIN',1,'2013-03-18 00:28:54',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(524,'2013-03-18 17:37:30','USER_LOGIN',1,'2013-03-18 18:37:30',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(525,'2013-03-18 18:11:37','USER_LOGIN',1,'2013-03-18 19:11:37',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(526,'2013-03-19 08:35:08','USER_LOGIN',1,'2013-03-19 09:35:08',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(527,'2013-03-19 09:20:23','USER_LOGIN',1,'2013-03-19 10:20:23',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(528,'2013-03-20 13:17:13','USER_LOGIN',1,'2013-03-20 14:17:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(529,'2013-03-20 14:44:31','USER_LOGIN',1,'2013-03-20 15:44:31',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(530,'2013-03-20 18:24:25','USER_LOGIN',1,'2013-03-20 19:24:25',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(531,'2013-03-20 19:15:54','USER_LOGIN',1,'2013-03-20 20:15:54',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(532,'2013-03-21 18:40:47','USER_LOGIN',1,'2013-03-21 19:40:47',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(533,'2013-03-21 21:42:24','USER_LOGIN',1,'2013-03-21 22:42:24',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(534,'2013-03-22 08:39:23','USER_LOGIN',1,'2013-03-22 09:39:23',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(535,'2013-03-23 13:04:55','USER_LOGIN',1,'2013-03-23 14:04:55',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(536,'2013-03-23 15:47:43','USER_LOGIN',1,'2013-03-23 16:47:43',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(537,'2013-03-23 22:56:36','USER_LOGIN',1,'2013-03-23 23:56:36',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(538,'2013-03-24 01:22:32','USER_LOGIN',1,'2013-03-24 02:22:32',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(539,'2013-03-24 14:40:42','USER_LOGIN',1,'2013-03-24 15:40:42',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(540,'2013-03-24 15:30:26','USER_LOGOUT',1,'2013-03-24 16:30:26',1,'(UserLogoff,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(541,'2013-03-24 15:30:29','USER_LOGIN',1,'2013-03-24 16:30:29',2,'(UserLogged,demo)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(542,'2013-03-24 15:49:40','USER_LOGOUT',1,'2013-03-24 16:49:40',2,'(UserLogoff,demo)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(543,'2013-03-24 15:49:48','USER_LOGIN',1,'2013-03-24 16:49:48',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(544,'2013-03-24 15:52:35','USER_MODIFY',1,'2013-03-24 16:52:35',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(545,'2013-03-24 15:52:52','USER_MODIFY',1,'2013-03-24 16:52:52',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(546,'2013-03-24 15:53:09','USER_MODIFY',1,'2013-03-24 16:53:09',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(547,'2013-03-24 15:53:23','USER_MODIFY',1,'2013-03-24 16:53:23',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(548,'2013-03-24 16:00:04','USER_MODIFY',1,'2013-03-24 17:00:04',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(549,'2013-03-24 16:01:50','USER_MODIFY',1,'2013-03-24 17:01:50',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(550,'2013-03-24 16:10:14','USER_MODIFY',1,'2013-03-24 17:10:14',1,'Modification utilisateur zzzg','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(551,'2013-03-24 16:55:13','USER_LOGIN',1,'2013-03-24 17:55:13',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(552,'2013-03-24 17:44:29','USER_LOGIN',1,'2013-03-24 18:44:29',1,'(UserLogged,admin)','::1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',NULL),(553,'2013-09-08 23:06:26','USER_LOGIN',1,'2013-09-09 01:06:26',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36',NULL),(554,'2013-10-21 22:32:28','USER_LOGIN',1,'2013-10-22 00:32:28',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.66 Safari/537.36',NULL),(555,'2013-10-21 22:32:48','USER_LOGIN',1,'2013-10-22 00:32:48',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.66 Safari/537.36',NULL),(556,'2013-11-07 00:01:51','USER_LOGIN',1,'2013-11-07 01:01:51',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.114 Safari/537.36',NULL),(557,'2014-03-02 15:21:07','USER_LOGIN',1,'2014-03-02 16:21:07',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(558,'2014-03-02 15:36:53','USER_LOGIN',1,'2014-03-02 16:36:53',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(559,'2014-03-02 18:54:23','USER_LOGIN',1,'2014-03-02 19:54:23',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(560,'2014-03-02 19:11:17','USER_LOGIN',1,'2014-03-02 20:11:17',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(561,'2014-03-03 18:19:24','USER_LOGIN',1,'2014-03-03 19:19:24',1,'(UserLogged,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36',NULL),(562,'2014-12-21 12:51:38','USER_LOGIN',1,'2014-12-21 13:51:38',1,'(UserLogged,admin) - TZ=1;TZString=CET;Screen=1920x969','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',NULL),(563,'2014-12-21 19:52:09','USER_LOGIN',1,'2014-12-21 20:52:09',1,'(UserLogged,admin) - TZ=1;TZString=CET;Screen=1920x969','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',NULL),(566,'2015-10-03 08:49:43','USER_NEW_PASSWORD',1,'2015-10-03 10:49:43',1,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(567,'2015-10-03 08:49:43','USER_MODIFY',1,'2015-10-03 10:49:43',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(568,'2015-10-03 09:03:12','USER_MODIFY',1,'2015-10-03 11:03:12',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(569,'2015-10-03 09:03:42','USER_MODIFY',1,'2015-10-03 11:03:42',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(570,'2015-10-03 09:07:36','USER_MODIFY',1,'2015-10-03 11:07:36',1,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(571,'2015-10-03 09:08:58','USER_NEW_PASSWORD',1,'2015-10-03 11:08:58',1,'Password change for pcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(572,'2015-10-03 09:08:58','USER_MODIFY',1,'2015-10-03 11:08:58',1,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(573,'2015-10-03 09:09:23','USER_MODIFY',1,'2015-10-03 11:09:23',1,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(574,'2015-10-03 09:11:04','USER_NEW_PASSWORD',1,'2015-10-03 11:11:04',1,'Password change for athestudent','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(575,'2015-10-03 09:11:04','USER_MODIFY',1,'2015-10-03 11:11:04',1,'User athestudent modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(576,'2015-10-03 09:11:53','USER_MODIFY',1,'2015-10-03 11:11:53',1,'User abookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(577,'2015-10-03 09:42:12','USER_LOGIN_FAILED',1,'2015-10-03 11:42:11',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(578,'2015-10-03 09:42:19','USER_LOGIN_FAILED',1,'2015-10-03 11:42:19',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(579,'2015-10-03 09:42:42','USER_LOGIN_FAILED',1,'2015-10-03 11:42:42',NULL,'Bad value for login or password - login=aeinstein','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(580,'2015-10-03 09:43:50','USER_LOGIN',1,'2015-10-03 11:43:50',1,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x788','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(581,'2015-10-03 09:44:44','GROUP_MODIFY',1,'2015-10-03 11:44:44',1,'Group Sale representatives modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(582,'2015-10-03 09:46:25','GROUP_CREATE',1,'2015-10-03 11:46:25',1,'Group Management created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(583,'2015-10-03 09:46:46','GROUP_CREATE',1,'2015-10-03 11:46:46',1,'Group Scientists created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(584,'2015-10-03 09:47:41','USER_CREATE',1,'2015-10-03 11:47:41',1,'User mcurie created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(585,'2015-10-03 09:47:41','USER_NEW_PASSWORD',1,'2015-10-03 11:47:41',1,'Password change for mcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(586,'2015-10-03 09:47:53','USER_MODIFY',1,'2015-10-03 11:47:53',1,'User mcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(587,'2015-10-03 09:48:32','USER_DELETE',1,'2015-10-03 11:48:32',1,'User bbb removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(588,'2015-10-03 09:48:52','USER_MODIFY',1,'2015-10-03 11:48:52',1,'User bookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(589,'2015-10-03 10:01:28','USER_MODIFY',1,'2015-10-03 12:01:28',1,'User bookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(590,'2015-10-03 10:01:39','USER_MODIFY',1,'2015-10-03 12:01:39',1,'User bookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(591,'2015-10-05 06:32:38','USER_LOGIN_FAILED',1,'2015-10-05 08:32:38',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(592,'2015-10-05 06:32:44','USER_LOGIN',1,'2015-10-05 08:32:44',1,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(593,'2015-10-05 07:07:52','USER_CREATE',1,'2015-10-05 09:07:52',1,'User atheceo created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(594,'2015-10-05 07:07:52','USER_NEW_PASSWORD',1,'2015-10-05 09:07:52',1,'Password change for atheceo','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(595,'2015-10-05 07:09:08','USER_NEW_PASSWORD',1,'2015-10-05 09:09:08',1,'Password change for aeinstein','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(596,'2015-10-05 07:09:08','USER_MODIFY',1,'2015-10-05 09:09:08',1,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(597,'2015-10-05 07:09:46','USER_CREATE',1,'2015-10-05 09:09:46',1,'User admin created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(598,'2015-10-05 07:09:46','USER_NEW_PASSWORD',1,'2015-10-05 09:09:46',1,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(599,'2015-10-05 07:10:20','USER_MODIFY',1,'2015-10-05 09:10:20',1,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(600,'2015-10-05 07:10:48','USER_MODIFY',1,'2015-10-05 09:10:48',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(601,'2015-10-05 07:11:22','USER_NEW_PASSWORD',1,'2015-10-05 09:11:22',1,'Password change for bbookkeeper','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(602,'2015-10-05 07:11:22','USER_MODIFY',1,'2015-10-05 09:11:22',1,'User bbookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(603,'2015-10-05 07:12:37','USER_MODIFY',1,'2015-10-05 09:12:37',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(604,'2015-10-05 07:13:27','USER_MODIFY',1,'2015-10-05 09:13:27',1,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(605,'2015-10-05 07:13:52','USER_MODIFY',1,'2015-10-05 09:13:52',1,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(606,'2015-10-05 07:14:35','USER_LOGOUT',1,'2015-10-05 09:14:35',1,'(UserLogoff,aeinstein)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(607,'2015-10-05 07:14:40','USER_LOGIN_FAILED',1,'2015-10-05 09:14:40',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(608,'2015-10-05 07:14:44','USER_LOGIN_FAILED',1,'2015-10-05 09:14:44',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(609,'2015-10-05 07:14:49','USER_LOGIN',1,'2015-10-05 09:14:49',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(610,'2015-10-05 07:57:18','USER_MODIFY',1,'2015-10-05 09:57:18',12,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(611,'2015-10-05 08:06:54','USER_LOGOUT',1,'2015-10-05 10:06:54',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(612,'2015-10-05 08:07:03','USER_LOGIN',1,'2015-10-05 10:07:03',11,'(UserLogged,atheceo) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(613,'2015-10-05 19:18:46','USER_LOGIN',1,'2015-10-05 21:18:46',11,'(UserLogged,atheceo) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(614,'2015-10-05 19:29:35','USER_CREATE',1,'2015-10-05 21:29:35',11,'User ccommercy created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(615,'2015-10-05 19:29:35','USER_NEW_PASSWORD',1,'2015-10-05 21:29:35',11,'Password change for ccommercy','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(616,'2015-10-05 19:30:13','GROUP_CREATE',1,'2015-10-05 21:30:13',11,'Group Commercial created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(617,'2015-10-05 19:31:37','USER_NEW_PASSWORD',1,'2015-10-05 21:31:37',11,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(618,'2015-10-05 19:31:37','USER_MODIFY',1,'2015-10-05 21:31:37',11,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(619,'2015-10-05 19:32:00','USER_MODIFY',1,'2015-10-05 21:32:00',11,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(620,'2015-10-05 19:33:33','USER_CREATE',1,'2015-10-05 21:33:33',11,'User sscientol created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(621,'2015-10-05 19:33:33','USER_NEW_PASSWORD',1,'2015-10-05 21:33:33',11,'Password change for sscientol','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(622,'2015-10-05 19:33:47','USER_NEW_PASSWORD',1,'2015-10-05 21:33:47',11,'Password change for mcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(623,'2015-10-05 19:33:47','USER_MODIFY',1,'2015-10-05 21:33:47',11,'User mcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(624,'2015-10-05 19:34:23','USER_NEW_PASSWORD',1,'2015-10-05 21:34:23',11,'Password change for pcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(625,'2015-10-05 19:34:23','USER_MODIFY',1,'2015-10-05 21:34:23',11,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(626,'2015-10-05 19:34:42','USER_MODIFY',1,'2015-10-05 21:34:42',11,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(627,'2015-10-05 19:36:06','USER_NEW_PASSWORD',1,'2015-10-05 21:36:06',11,'Password change for ccommercy','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(628,'2015-10-05 19:36:06','USER_MODIFY',1,'2015-10-05 21:36:06',11,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(629,'2015-10-05 19:36:57','USER_NEW_PASSWORD',1,'2015-10-05 21:36:57',11,'Password change for atheceo','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(630,'2015-10-05 19:36:57','USER_MODIFY',1,'2015-10-05 21:36:57',11,'User atheceo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(631,'2015-10-05 19:37:27','USER_LOGOUT',1,'2015-10-05 21:37:27',11,'(UserLogoff,atheceo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(632,'2015-10-05 19:37:35','USER_LOGIN_FAILED',1,'2015-10-05 21:37:35',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(633,'2015-10-05 19:37:39','USER_LOGIN_FAILED',1,'2015-10-05 21:37:39',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(634,'2015-10-05 19:37:44','USER_LOGIN_FAILED',1,'2015-10-05 21:37:44',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(635,'2015-10-05 19:37:49','USER_LOGIN_FAILED',1,'2015-10-05 21:37:49',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(636,'2015-10-05 19:38:12','USER_LOGIN_FAILED',1,'2015-10-05 21:38:12',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(637,'2015-10-05 19:40:48','USER_LOGIN_FAILED',1,'2015-10-05 21:40:48',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(638,'2015-10-05 19:40:55','USER_LOGIN',1,'2015-10-05 21:40:55',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(639,'2015-10-05 19:43:34','USER_MODIFY',1,'2015-10-05 21:43:34',12,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(640,'2015-10-05 19:45:43','USER_CREATE',1,'2015-10-05 21:45:43',12,'User aaa created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(641,'2015-10-05 19:45:43','USER_NEW_PASSWORD',1,'2015-10-05 21:45:43',12,'Password change for aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(642,'2015-10-05 19:46:18','USER_DELETE',1,'2015-10-05 21:46:18',12,'User aaa removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(643,'2015-10-05 19:47:09','USER_MODIFY',1,'2015-10-05 21:47:09',12,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(644,'2015-10-05 19:47:22','USER_MODIFY',1,'2015-10-05 21:47:22',12,'User demo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(645,'2015-10-05 19:52:05','USER_MODIFY',1,'2015-10-05 21:52:05',12,'User sscientol modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(646,'2015-10-05 19:52:23','USER_MODIFY',1,'2015-10-05 21:52:23',12,'User bbookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(647,'2015-10-05 19:54:54','USER_NEW_PASSWORD',1,'2015-10-05 21:54:54',12,'Password change for zzeceo','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(648,'2015-10-05 19:54:54','USER_MODIFY',1,'2015-10-05 21:54:54',12,'User zzeceo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(649,'2015-10-05 19:57:02','USER_MODIFY',1,'2015-10-05 21:57:02',12,'User zzeceo modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(650,'2015-10-05 19:57:57','USER_NEW_PASSWORD',1,'2015-10-05 21:57:57',12,'Password change for pcurie','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(651,'2015-10-05 19:57:57','USER_MODIFY',1,'2015-10-05 21:57:57',12,'User pcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(652,'2015-10-05 19:59:42','USER_NEW_PASSWORD',1,'2015-10-05 21:59:42',12,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(653,'2015-10-05 19:59:42','USER_MODIFY',1,'2015-10-05 21:59:42',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(654,'2015-10-05 20:00:21','USER_MODIFY',1,'2015-10-05 22:00:21',12,'User adminx modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(655,'2015-10-05 20:05:36','USER_MODIFY',1,'2015-10-05 22:05:36',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(656,'2015-10-05 20:06:25','USER_MODIFY',1,'2015-10-05 22:06:25',12,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(657,'2015-10-05 20:07:18','USER_MODIFY',1,'2015-10-05 22:07:18',12,'User mcurie modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(658,'2015-10-05 20:07:36','USER_MODIFY',1,'2015-10-05 22:07:36',12,'User aeinstein modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(659,'2015-10-05 20:08:34','USER_MODIFY',1,'2015-10-05 22:08:34',12,'User bbookkeeper modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(660,'2015-10-05 20:47:52','USER_CREATE',1,'2015-10-05 22:47:52',12,'User cc1 created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(661,'2015-10-05 20:47:52','USER_NEW_PASSWORD',1,'2015-10-05 22:47:52',12,'Password change for cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(662,'2015-10-05 20:47:55','USER_LOGOUT',1,'2015-10-05 22:47:55',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(663,'2015-10-05 20:48:08','USER_LOGIN',1,'2015-10-05 22:48:08',11,'(UserLogged,zzeceo) - TZ=1;TZString=Europe/Berlin;Screen=1590x434','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(664,'2015-10-05 20:48:39','USER_CREATE',1,'2015-10-05 22:48:39',11,'User cc2 created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(665,'2015-10-05 20:48:39','USER_NEW_PASSWORD',1,'2015-10-05 22:48:39',11,'Password change for cc2','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(666,'2015-10-05 20:48:59','USER_NEW_PASSWORD',1,'2015-10-05 22:48:59',11,'Password change for cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(667,'2015-10-05 20:48:59','USER_MODIFY',1,'2015-10-05 22:48:59',11,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(668,'2015-10-05 21:06:36','USER_LOGOUT',1,'2015-10-05 23:06:35',11,'(UserLogoff,zzeceo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(669,'2015-10-05 21:06:44','USER_LOGIN_FAILED',1,'2015-10-05 23:06:44',NULL,'Bad value for login or password - login=cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(670,'2015-10-05 21:07:12','USER_LOGIN_FAILED',1,'2015-10-05 23:07:12',NULL,'Bad value for login or password - login=cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(671,'2015-10-05 21:07:19','USER_LOGIN_FAILED',1,'2015-10-05 23:07:19',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(672,'2015-10-05 21:07:27','USER_LOGIN_FAILED',1,'2015-10-05 23:07:27',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(673,'2015-10-05 21:07:32','USER_LOGIN',1,'2015-10-05 23:07:32',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(674,'2015-10-05 21:12:28','USER_NEW_PASSWORD',1,'2015-10-05 23:12:28',12,'Password change for cc1','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(675,'2015-10-05 21:12:28','USER_MODIFY',1,'2015-10-05 23:12:28',12,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(676,'2015-10-05 21:13:00','USER_CREATE',1,'2015-10-05 23:13:00',12,'User aaa created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(677,'2015-10-05 21:13:00','USER_NEW_PASSWORD',1,'2015-10-05 23:13:00',12,'Password change for aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(678,'2015-10-05 21:13:40','USER_DELETE',1,'2015-10-05 23:13:40',12,'User aaa removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(679,'2015-10-05 21:14:47','USER_LOGOUT',1,'2015-10-05 23:14:47',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(680,'2015-10-05 21:14:56','USER_LOGIN',1,'2015-10-05 23:14:56',16,'(UserLogged,cc1) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(681,'2015-10-05 21:15:56','USER_LOGOUT',1,'2015-10-05 23:15:56',16,'(UserLogoff,cc1)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(682,'2015-10-05 21:16:06','USER_LOGIN',1,'2015-10-05 23:16:06',17,'(UserLogged,cc2) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(683,'2015-10-05 21:37:25','USER_LOGOUT',1,'2015-10-05 23:37:25',17,'(UserLogoff,cc2)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(684,'2015-10-05 21:37:31','USER_LOGIN',1,'2015-10-05 23:37:31',16,'(UserLogged,cc1) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(685,'2015-10-05 21:43:53','USER_LOGOUT',1,'2015-10-05 23:43:53',16,'(UserLogoff,cc1)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(686,'2015-10-05 21:44:00','USER_LOGIN',1,'2015-10-05 23:44:00',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(687,'2015-10-05 21:46:17','USER_LOGOUT',1,'2015-10-05 23:46:17',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(688,'2015-10-05 21:46:24','USER_LOGIN',1,'2015-10-05 23:46:24',16,'(UserLogged,cc1) - TZ=1;TZString=Europe/Berlin;Screen=1590x767','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',NULL),(689,'2015-11-04 15:17:06','USER_LOGIN',1,'2015-11-04 16:17:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(690,'2015-11-15 22:04:04','USER_LOGIN',1,'2015-11-15 23:04:04',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(691,'2015-11-15 22:23:45','USER_MODIFY',1,'2015-11-15 23:23:45',12,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(692,'2015-11-15 22:24:22','USER_MODIFY',1,'2015-11-15 23:24:22',12,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(693,'2015-11-15 22:24:53','USER_MODIFY',1,'2015-11-15 23:24:53',12,'User cc2 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(694,'2015-11-15 22:25:17','USER_MODIFY',1,'2015-11-15 23:25:17',12,'User cc1 modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(695,'2015-11-15 22:45:37','USER_LOGOUT',1,'2015-11-15 23:45:37',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(696,'2015-11-18 13:41:02','USER_LOGIN',1,'2015-11-18 14:41:02',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(697,'2015-11-18 14:23:35','USER_LOGIN',1,'2015-11-18 15:23:35',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(698,'2015-11-18 15:15:46','USER_LOGOUT',1,'2015-11-18 16:15:46',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(699,'2015-11-18 15:15:51','USER_LOGIN',1,'2015-11-18 16:15:51',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(700,'2015-11-30 17:52:08','USER_LOGIN',1,'2015-11-30 18:52:08',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(701,'2016-01-10 16:45:43','USER_LOGIN',1,'2016-01-10 17:45:43',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(702,'2016-01-10 16:45:52','USER_LOGOUT',1,'2016-01-10 17:45:52',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(703,'2016-01-10 16:46:06','USER_LOGIN',1,'2016-01-10 17:46:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(704,'2016-01-16 14:53:47','USER_LOGIN',1,'2016-01-16 15:53:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(705,'2016-01-16 15:04:29','USER_LOGOUT',1,'2016-01-16 16:04:29',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(706,'2016-01-16 15:04:40','USER_LOGIN',1,'2016-01-16 16:04:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(707,'2016-01-22 09:33:26','USER_LOGIN',1,'2016-01-22 10:33:26',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(708,'2016-01-22 09:35:19','USER_LOGOUT',1,'2016-01-22 10:35:19',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(709,'2016-01-22 09:35:29','USER_LOGIN',1,'2016-01-22 10:35:29',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(710,'2016-01-22 10:47:34','USER_CREATE',1,'2016-01-22 11:47:34',12,'User aaa created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(711,'2016-01-22 10:47:34','USER_NEW_PASSWORD',1,'2016-01-22 11:47:34',12,'Password change for aaa','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(712,'2016-01-22 12:07:56','USER_LOGIN',1,'2016-01-22 13:07:56',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(713,'2016-01-22 12:36:25','USER_NEW_PASSWORD',1,'2016-01-22 13:36:25',12,'Password change for admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(714,'2016-01-22 12:36:25','USER_MODIFY',1,'2016-01-22 13:36:25',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(715,'2016-01-22 12:56:32','USER_MODIFY',1,'2016-01-22 13:56:32',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(716,'2016-01-22 12:58:05','USER_MODIFY',1,'2016-01-22 13:58:05',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(717,'2016-01-22 13:01:02','USER_MODIFY',1,'2016-01-22 14:01:02',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(718,'2016-01-22 13:01:18','USER_MODIFY',1,'2016-01-22 14:01:18',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(719,'2016-01-22 13:13:42','USER_MODIFY',1,'2016-01-22 14:13:42',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(720,'2016-01-22 13:15:20','USER_DELETE',1,'2016-01-22 14:15:20',12,'User aaa removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(721,'2016-01-22 13:19:21','USER_LOGOUT',1,'2016-01-22 14:19:21',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(722,'2016-01-22 13:19:32','USER_LOGIN',1,'2016-01-22 14:19:32',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(723,'2016-01-22 13:19:51','USER_LOGOUT',1,'2016-01-22 14:19:51',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(724,'2016-01-22 13:20:01','USER_LOGIN',1,'2016-01-22 14:20:01',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(725,'2016-01-22 13:28:22','USER_LOGOUT',1,'2016-01-22 14:28:22',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(726,'2016-01-22 13:28:35','USER_LOGIN',1,'2016-01-22 14:28:35',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(727,'2016-01-22 13:33:54','USER_LOGOUT',1,'2016-01-22 14:33:54',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(728,'2016-01-22 13:34:05','USER_LOGIN',1,'2016-01-22 14:34:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(729,'2016-01-22 13:51:46','USER_MODIFY',1,'2016-01-22 14:51:46',12,'User ccommercy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',NULL),(730,'2016-01-22 16:20:12','USER_LOGIN',1,'2016-01-22 17:20:12',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(731,'2016-01-22 16:20:22','USER_LOGOUT',1,'2016-01-22 17:20:22',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(732,'2016-01-22 16:20:36','USER_LOGIN',1,'2016-01-22 17:20:36',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(733,'2016-01-22 16:27:02','USER_CREATE',1,'2016-01-22 17:27:02',12,'User ldestailleur created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(734,'2016-01-22 16:27:02','USER_NEW_PASSWORD',1,'2016-01-22 17:27:02',12,'Password change for ldestailleur','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(735,'2016-01-22 16:28:34','USER_MODIFY',1,'2016-01-22 17:28:34',12,'User ldestailleur modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(736,'2016-01-22 16:30:01','USER_ENABLEDISABLE',1,'2016-01-22 17:30:01',12,'User cc2 activated','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(737,'2016-01-22 17:11:06','USER_LOGIN',1,'2016-01-22 18:11:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=1600x790','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(738,'2016-01-22 18:00:02','USER_DELETE',1,'2016-01-22 19:00:02',12,'User zzz removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(739,'2016-01-22 18:01:40','USER_DELETE',1,'2016-01-22 19:01:40',12,'User aaab removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(740,'2016-01-22 18:01:52','USER_DELETE',1,'2016-01-22 19:01:52',12,'User zzzg removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36',NULL),(741,'2016-03-13 10:54:59','USER_LOGIN',1,'2016-03-13 14:54:59',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x971','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36',NULL),(742,'2016-07-30 11:13:10','USER_LOGIN',1,'2016-07-30 15:13:10',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(743,'2016-07-30 12:50:23','USER_CREATE',1,'2016-07-30 16:50:23',12,'User eldy created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(744,'2016-07-30 12:50:23','USER_CREATE',1,'2016-07-30 16:50:23',12,'User eldy created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(745,'2016-07-30 12:50:23','USER_NEW_PASSWORD',1,'2016-07-30 16:50:23',12,'Password change for eldy','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(746,'2016-07-30 12:50:38','USER_MODIFY',1,'2016-07-30 16:50:38',12,'User eldy modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(747,'2016-07-30 12:50:54','USER_DELETE',1,'2016-07-30 16:50:54',12,'User eldy removed','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(748,'2016-07-30 12:51:23','USER_NEW_PASSWORD',1,'2016-07-30 16:51:23',12,'Password change for ldestailleur','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(749,'2016-07-30 12:51:23','USER_MODIFY',1,'2016-07-30 16:51:23',12,'User ldestailleur modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(750,'2016-07-30 18:26:58','USER_LOGIN',1,'2016-07-30 22:26:58',18,'(UserLogged,ldestailleur) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(751,'2016-07-30 18:27:40','USER_LOGOUT',1,'2016-07-30 22:27:40',18,'(UserLogoff,ldestailleur)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(752,'2016-07-30 18:27:47','USER_LOGIN',1,'2016-07-30 22:27:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(753,'2016-07-30 19:00:00','USER_LOGOUT',1,'2016-07-30 23:00:00',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(754,'2016-07-30 19:00:04','USER_LOGIN',1,'2016-07-30 23:00:04',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(755,'2016-07-30 19:00:14','USER_LOGOUT',1,'2016-07-30 23:00:14',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(756,'2016-07-30 19:00:19','USER_LOGIN',1,'2016-07-30 23:00:19',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(757,'2016-07-30 19:00:43','USER_LOGOUT',1,'2016-07-30 23:00:43',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(758,'2016-07-30 19:00:48','USER_LOGIN',1,'2016-07-30 23:00:48',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(759,'2016-07-30 19:03:52','USER_LOGOUT',1,'2016-07-30 23:03:52',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(760,'2016-07-30 19:03:57','USER_LOGIN_FAILED',1,'2016-07-30 23:03:57',NULL,'Bad value for login or password - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(761,'2016-07-30 19:03:59','USER_LOGIN',1,'2016-07-30 23:03:59',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(762,'2016-07-30 19:04:13','USER_LOGOUT',1,'2016-07-30 23:04:13',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(763,'2016-07-30 19:04:17','USER_LOGIN',1,'2016-07-30 23:04:17',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(764,'2016-07-30 19:04:26','USER_LOGOUT',1,'2016-07-30 23:04:26',2,'(UserLogoff,demo)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(765,'2016-07-30 19:04:31','USER_LOGIN',1,'2016-07-30 23:04:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(766,'2016-07-30 19:10:50','USER_LOGOUT',1,'2016-07-30 23:10:50',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(767,'2016-07-30 19:10:54','USER_LOGIN',1,'2016-07-30 23:10:54',2,'(UserLogged,demo) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(768,'2016-07-31 10:15:52','USER_LOGIN',1,'2016-07-31 14:15:52',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Lynx/2.8.8pre.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/2.12.23',NULL),(769,'2016-07-31 10:16:27','USER_LOGIN',1,'2016-07-31 14:16:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(770,'2016-07-31 10:32:14','USER_LOGIN',1,'2016-07-31 14:32:14',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Lynx/2.8.8pre.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/2.12.23',NULL),(771,'2016-07-31 10:36:28','USER_LOGIN',1,'2016-07-31 14:36:28',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Links (2.8; Linux 3.19.0-46-generic x86_64; GNU C 4.8.2; text)',NULL),(772,'2016-07-31 10:40:10','USER_LOGIN',1,'2016-07-31 14:40:10',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Links (2.8; Linux 3.19.0-46-generic x86_64; GNU C 4.8.2; text)',NULL),(773,'2016-07-31 10:54:16','USER_LOGIN',1,'2016-07-31 14:54:16',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Lynx/2.8.8pre.4 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/2.12.23',NULL),(774,'2016-07-31 12:52:52','USER_LOGIN',1,'2016-07-31 16:52:52',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x592','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(775,'2016-07-31 13:25:33','USER_LOGOUT',1,'2016-07-31 17:25:33',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(776,'2016-07-31 13:26:32','USER_LOGIN',1,'2016-07-31 17:26:32',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1280x751','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(777,'2016-07-31 14:13:57','USER_LOGOUT',1,'2016-07-31 18:13:57',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(778,'2016-07-31 14:14:04','USER_LOGIN',1,'2016-07-31 18:14:04',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(779,'2016-07-31 16:04:35','USER_LOGIN',1,'2016-07-31 20:04:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(780,'2016-07-31 21:14:14','USER_LOGIN',1,'2016-08-01 01:14:14',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',NULL),(781,'2017-01-29 15:14:05','USER_LOGOUT',1,'2017-01-29 19:14:05',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(782,'2017-01-29 15:34:43','USER_LOGIN',1,'2017-01-29 19:34:43',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(783,'2017-01-29 15:35:04','USER_LOGOUT',1,'2017-01-29 19:35:04',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(784,'2017-01-29 15:35:12','USER_LOGIN',1,'2017-01-29 19:35:12',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(785,'2017-01-29 15:36:43','USER_LOGOUT',1,'2017-01-29 19:36:43',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(786,'2017-01-29 15:41:21','USER_LOGIN',1,'2017-01-29 19:41:21',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(787,'2017-01-29 15:41:41','USER_LOGOUT',1,'2017-01-29 19:41:41',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(788,'2017-01-29 15:42:43','USER_LOGIN',1,'2017-01-29 19:42:43',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(789,'2017-01-29 15:43:18','USER_LOGOUT',1,'2017-01-29 19:43:18',12,'(UserLogoff,admin)','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(790,'2017-01-29 15:46:31','USER_LOGIN',1,'2017-01-29 19:46:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x571','192.168.0.254','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(791,'2017-01-29 16:18:56','USER_LOGIN',1,'2017-01-29 20:18:56',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Berlin;Screen=360x526','192.168.0.254','Mozilla/5.0 (Linux; Android 6.0; LG-H818 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36 - DoliDroid - Android client pour Dolibarr ERP-CRM',NULL),(792,'2017-01-29 17:20:59','USER_LOGIN',1,'2017-01-29 21:20:59',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(793,'2017-01-30 11:19:40','USER_LOGIN',1,'2017-01-30 15:19:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(794,'2017-01-31 16:49:39','USER_LOGIN',1,'2017-01-31 20:49:39',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x520','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(795,'2017-02-01 10:55:23','USER_LOGIN',1,'2017-02-01 14:55:23',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(796,'2017-02-01 13:34:31','USER_LOGIN',1,'2017-02-01 17:34:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(797,'2017-02-01 14:41:26','USER_LOGIN',1,'2017-02-01 18:41:26',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(798,'2017-02-01 23:51:48','USER_LOGIN_FAILED',1,'2017-02-02 03:51:48',NULL,'Bad value for login or password - login=autologin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(799,'2017-02-01 23:52:55','USER_LOGIN',1,'2017-02-02 03:52:55',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(800,'2017-02-01 23:55:45','USER_CREATE',1,'2017-02-02 03:55:45',12,'User aboston created','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(801,'2017-02-01 23:55:45','USER_NEW_PASSWORD',1,'2017-02-02 03:55:45',12,'Password change for aboston','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(802,'2017-02-01 23:56:38','USER_MODIFY',1,'2017-02-02 03:56:38',12,'User aboston modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(803,'2017-02-01 23:56:50','USER_MODIFY',1,'2017-02-02 03:56:50',12,'User aboston modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(804,'2017-02-02 01:14:44','USER_LOGIN',1,'2017-02-02 05:14:44',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',NULL),(805,'2017-02-03 10:27:18','USER_LOGIN',1,'2017-02-03 14:27:18',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(806,'2017-02-04 10:22:34','USER_LOGIN',1,'2017-02-04 14:22:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x489','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(807,'2017-02-06 04:01:31','USER_LOGIN',1,'2017-02-06 08:01:31',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(808,'2017-02-06 10:21:32','USER_LOGIN',1,'2017-02-06 14:21:32',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(809,'2017-02-06 19:09:27','USER_LOGIN',1,'2017-02-06 23:09:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(810,'2017-02-06 23:39:17','USER_LOGIN',1,'2017-02-07 03:39:17',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(811,'2017-02-07 11:36:34','USER_LOGIN',1,'2017-02-07 15:36:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x676','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(812,'2017-02-07 18:51:53','USER_LOGIN',1,'2017-02-07 22:51:53',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(813,'2017-02-07 23:13:40','USER_LOGIN',1,'2017-02-08 03:13:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(814,'2017-02-08 09:29:12','USER_LOGIN',1,'2017-02-08 13:29:12',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(815,'2017-02-08 17:33:12','USER_LOGIN',1,'2017-02-08 21:33:12',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(816,'2017-02-09 17:30:34','USER_LOGIN',1,'2017-02-09 21:30:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(817,'2017-02-10 09:30:02','USER_LOGIN',1,'2017-02-10 13:30:02',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(818,'2017-02-10 16:16:14','USER_LOGIN',1,'2017-02-10 20:16:14',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(819,'2017-02-10 17:28:15','USER_LOGIN',1,'2017-02-10 21:28:15',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(820,'2017-02-11 12:54:03','USER_LOGIN',1,'2017-02-11 16:54:03',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(821,'2017-02-11 17:23:52','USER_LOGIN',1,'2017-02-11 21:23:52',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(822,'2017-02-12 12:44:03','USER_LOGIN',1,'2017-02-12 16:44:03',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(823,'2017-02-12 16:42:13','USER_LOGIN',1,'2017-02-12 20:42:13',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(824,'2017-02-12 19:14:18','USER_LOGIN',1,'2017-02-12 23:14:18',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(825,'2017-02-15 17:17:00','USER_LOGIN',1,'2017-02-15 21:17:00',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(826,'2017-02-15 22:02:40','USER_LOGIN',1,'2017-02-16 02:02:40',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(827,'2017-02-16 22:13:27','USER_LOGIN',1,'2017-02-17 02:13:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x619','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(828,'2017-02-16 23:54:04','USER_LOGIN',1,'2017-02-17 03:54:04',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(829,'2017-02-17 09:14:27','USER_LOGIN',1,'2017-02-17 13:14:27',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(830,'2017-02-17 12:07:05','USER_LOGIN',1,'2017-02-17 16:07:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(831,'2017-02-19 21:22:20','USER_LOGIN',1,'2017-02-20 01:22:20',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(832,'2017-02-20 09:26:47','USER_LOGIN',1,'2017-02-20 13:26:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(833,'2017-02-20 16:39:55','USER_LOGIN',1,'2017-02-20 20:39:55',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(834,'2017-02-20 16:49:00','USER_MODIFY',1,'2017-02-20 20:49:00',12,'Modification utilisateur ccommerson','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(835,'2017-02-20 17:57:15','USER_LOGIN',1,'2017-02-20 21:57:14',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(836,'2017-02-20 19:43:48','USER_LOGIN',1,'2017-02-20 23:43:48',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(837,'2017-02-21 00:04:05','USER_LOGIN',1,'2017-02-21 04:04:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(838,'2017-02-21 10:23:13','USER_LOGIN',1,'2017-02-21 14:23:13',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(839,'2017-02-21 10:30:17','USER_LOGOUT',1,'2017-02-21 14:30:17',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(840,'2017-02-21 10:30:22','USER_LOGIN',1,'2017-02-21 14:30:22',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(841,'2017-02-21 11:44:05','USER_LOGIN',1,'2017-02-21 15:44:05',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',NULL),(842,'2017-05-12 09:02:48','USER_LOGIN',1,'2017-05-12 13:02:48',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36',NULL),(843,'2017-08-27 13:29:16','USER_LOGIN',1,'2017-08-27 17:29:16',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(844,'2017-08-28 09:11:07','USER_LOGIN',1,'2017-08-28 13:11:07',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(845,'2017-08-28 10:08:58','USER_LOGIN',1,'2017-08-28 14:08:58',12,'(UserLogged,admin) - TZ=;TZString=;Screen=x','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(846,'2017-08-28 10:12:46','USER_MODIFY',1,'2017-08-28 14:12:46',12,'User admin modified','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(847,'2017-08-28 10:28:25','USER_LOGIN',1,'2017-08-28 14:28:25',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(848,'2017-08-28 10:28:36','USER_LOGOUT',1,'2017-08-28 14:28:36',12,'(UserLogoff,admin)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(849,'2017-08-28 10:34:50','USER_LOGIN',1,'2017-08-28 14:34:50',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(850,'2017-08-28 11:59:02','USER_LOGIN',1,'2017-08-28 15:59:02',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36',NULL),(851,'2017-08-29 09:57:34','USER_LOGIN',1,'2017-08-29 13:57:34',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(852,'2017-08-29 11:05:51','USER_LOGIN',1,'2017-08-29 15:05:51',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(853,'2017-08-29 14:15:58','USER_LOGIN',1,'2017-08-29 18:15:58',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(854,'2017-08-29 17:49:28','USER_LOGIN',1,'2017-08-29 21:49:28',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(855,'2017-08-30 11:53:25','USER_LOGIN',1,'2017-08-30 15:53:25',18,'(UserLogged,ldestailleur) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(856,'2017-08-30 12:19:31','USER_MODIFY',1,'2017-08-30 16:19:31',18,'Modification utilisateur ldestailleur - UserRemovedFromGroup','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(857,'2017-08-30 12:19:32','USER_MODIFY',1,'2017-08-30 16:19:32',18,'Modification utilisateur ldestailleur - UserRemovedFromGroup','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(858,'2017-08-30 12:19:33','USER_MODIFY',1,'2017-08-30 16:19:33',18,'Modification utilisateur ldestailleur - UserRemovedFromGroup','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(859,'2017-08-30 12:21:42','USER_LOGOUT',1,'2017-08-30 16:21:42',18,'(UserLogoff,ldestailleur)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(860,'2017-08-30 12:21:48','USER_LOGIN',1,'2017-08-30 16:21:48',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(861,'2017-08-30 15:02:06','USER_LOGIN',1,'2017-08-30 19:02:06',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(862,'2017-08-31 09:25:42','USER_LOGIN',1,'2017-08-31 13:25:42',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(863,'2017-09-04 07:51:21','USER_LOGIN',1,'2017-09-04 11:51:21',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x577','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(864,'2017-09-04 09:17:09','USER_LOGIN',1,'2017-09-04 13:17:09',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(865,'2017-09-04 13:40:28','USER_LOGIN',1,'2017-09-04 17:40:28',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(866,'2017-09-06 07:55:30','USER_LOGIN',1,'2017-09-06 11:55:30',18,'(UserLogged,ldestailleur) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(867,'2017-09-06 07:55:33','USER_LOGOUT',1,'2017-09-06 11:55:33',18,'(UserLogoff,ldestailleur)','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(868,'2017-09-06 07:55:38','USER_LOGIN',1,'2017-09-06 11:55:38',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(869,'2017-09-06 16:03:38','USER_LOGIN',1,'2017-09-06 20:03:38',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(870,'2017-09-06 19:43:07','USER_LOGIN',1,'2017-09-06 23:43:07',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x937','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',NULL),(871,'2018-01-19 11:18:08','USER_LOGOUT',1,'2018-01-19 11:18:08',12,'(UserLogoff,admin)','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',NULL),(872,'2018-01-19 11:18:47','USER_LOGIN',1,'2018-01-19 11:18:47',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x965','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',NULL),(873,'2018-01-19 11:21:41','USER_LOGIN',1,'2018-01-19 11:21:41',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x926','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(874,'2018-01-19 11:24:18','USER_NEW_PASSWORD',1,'2018-01-19 11:24:18',12,'Password change for admin','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(875,'2018-01-19 11:24:18','USER_MODIFY',1,'2018-01-19 11:24:18',12,'User admin modified','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(876,'2018-01-19 11:28:45','USER_LOGOUT',1,'2018-01-19 11:28:45',12,'(UserLogoff,admin)','82.240.38.230','Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0',NULL),(877,'2018-03-16 09:54:15','USER_LOGIN_FAILED',1,'2018-03-16 13:54:15',NULL,'Identifiant ou mot de passe incorrect - login=admin','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36',NULL),(878,'2018-03-16 09:54:23','USER_LOGIN',1,'2018-03-16 13:54:23',12,'(UserLogged,admin) - TZ=1;TZString=Europe/Paris;Screen=1920x936','127.0.0.1','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36',NULL); /*!40000 ALTER TABLE `llx_events` ENABLE KEYS */; UNLOCK TABLES; @@ -4258,13 +4401,13 @@ DROP TABLE IF EXISTS `llx_expedition`; CREATE TABLE `llx_expedition` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_customer` varchar(30) DEFAULT NULL, + `ref_customer` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_soc` int(11) NOT NULL, `fk_projet` int(11) DEFAULT NULL, - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(30) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `date_creation` datetime DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `date_valid` datetime DEFAULT NULL, @@ -4273,7 +4416,7 @@ CREATE TABLE `llx_expedition` ( `date_delivery` datetime DEFAULT NULL, `fk_address` int(11) DEFAULT NULL, `fk_shipping_method` int(11) DEFAULT NULL, - `tracking_number` varchar(50) DEFAULT NULL, + `tracking_number` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_statut` smallint(6) DEFAULT '0', `height` float DEFAULT NULL, `height_unit` int(11) DEFAULT NULL, @@ -4282,16 +4425,16 @@ CREATE TABLE `llx_expedition` ( `size` float DEFAULT NULL, `weight_units` int(11) DEFAULT NULL, `weight` float DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `billed` smallint(6) DEFAULT '0', `fk_user_modif` int(11) DEFAULT NULL, - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `idx_expedition_uk_ref` (`ref`,`entity`), KEY `idx_expedition_fk_soc` (`fk_soc`), @@ -4302,7 +4445,7 @@ CREATE TABLE `llx_expedition` ( CONSTRAINT `fk_expedition_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_expedition_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_expedition_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4326,10 +4469,10 @@ CREATE TABLE `llx_expedition_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_expedition_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4351,12 +4494,12 @@ DROP TABLE IF EXISTS `llx_expedition_methode`; CREATE TABLE `llx_expedition_methode` ( `rowid` int(11) NOT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `code` varchar(30) NOT NULL, - `libelle` varchar(50) NOT NULL, - `description` text, + `code` varchar(30) COLLATE utf8_unicode_ci NOT NULL, + `libelle` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, `active` tinyint(4) DEFAULT '0', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4386,7 +4529,7 @@ CREATE TABLE `llx_expeditiondet` ( PRIMARY KEY (`rowid`), KEY `idx_expeditiondet_fk_expedition` (`fk_expedition`), CONSTRAINT `fk_expeditiondet_fk_expedition` FOREIGN KEY (`fk_expedition`) REFERENCES `llx_expedition` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4411,13 +4554,13 @@ CREATE TABLE `llx_expeditiondet_batch` ( `fk_expeditiondet` int(11) NOT NULL, `eatby` date DEFAULT NULL, `sellby` date DEFAULT NULL, - `batch` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `batch` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `qty` double NOT NULL DEFAULT '0', `fk_origin_stock` int(11) NOT NULL, PRIMARY KEY (`rowid`), KEY `idx_fk_expeditiondet` (`fk_expeditiondet`), CONSTRAINT `fk_expeditiondet_batch_fk_expeditiondet` FOREIGN KEY (`fk_expeditiondet`) REFERENCES `llx_expeditiondet` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4440,10 +4583,10 @@ CREATE TABLE `llx_expeditiondet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_expeditiondet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4464,7 +4607,7 @@ DROP TABLE IF EXISTS `llx_expensereport`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_expensereport` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(50) NOT NULL, + `ref` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `ref_number_int` int(11) DEFAULT NULL, `ref_ext` int(11) DEFAULT NULL, @@ -4491,21 +4634,21 @@ CREATE TABLE `llx_expensereport` ( `fk_statut` int(11) NOT NULL, `fk_c_paiement` int(11) DEFAULT NULL, `paid` smallint(6) NOT NULL DEFAULT '0', - `note_public` text, - `note_private` text, - `detail_refuse` varchar(255) DEFAULT NULL, - `detail_cancel` varchar(255) DEFAULT NULL, + `note_public` text COLLATE utf8_unicode_ci, + `note_private` text COLLATE utf8_unicode_ci, + `detail_refuse` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `detail_cancel` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `integration_compta` int(11) DEFAULT NULL, `fk_bank_account` int(11) DEFAULT NULL, - `model_pdf` varchar(50) DEFAULT NULL, + `model_pdf` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `idx_expensereport_uk_ref` (`ref`,`entity`), KEY `idx_expensereport_date_debut` (`date_debut`), @@ -4515,7 +4658,7 @@ CREATE TABLE `llx_expensereport` ( KEY `idx_expensereport_fk_user_valid` (`fk_user_valid`), KEY `idx_expensereport_fk_user_approve` (`fk_user_approve`), KEY `idx_expensereport_fk_refuse` (`fk_user_approve`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4524,7 +4667,7 @@ CREATE TABLE `llx_expensereport` ( LOCK TABLES `llx_expensereport` WRITE; /*!40000 ALTER TABLE `llx_expensereport` DISABLE KEYS */; -INSERT INTO `llx_expensereport` VALUES (1,'ADMIN-ER00002-150101',1,2,NULL,8.33000000,1.67000000,0.00000000,0.00000000,10.00000000,'2015-01-01','2015-01-03','2016-01-22 19:03:37','2016-01-22 19:06:50','2017-02-16 02:12:40',NULL,NULL,'2017-02-15 22:12:40',12,NULL,12,12,12,NULL,NULL,5,NULL,0,'Holidays',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(2,'(PROV2)',1,NULL,NULL,141.67000000,28.33000000,0.00000000,0.00000000,170.00000000,'2015-02-01','2015-02-28','2016-01-22 19:04:44',NULL,NULL,NULL,NULL,'2016-01-22 18:06:21',12,12,NULL,12,NULL,NULL,NULL,0,NULL,0,'Work on projet X','','',NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(3,'(PROV3)',1,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,'2017-02-02','2017-02-02','2017-02-02 03:57:03',NULL,NULL,NULL,NULL,'2017-02-01 23:57:03',19,NULL,NULL,NULL,NULL,NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL); +INSERT INTO `llx_expensereport` VALUES (1,'ADMIN-ER00002-150101',1,2,NULL,8.33000000,1.67000000,0.00000000,0.00000000,10.00000000,'2015-01-01','2015-01-03','2016-01-22 19:03:37','2016-01-22 19:06:50','2017-02-16 02:12:40',NULL,NULL,'2017-02-15 22:12:40',12,NULL,12,12,12,NULL,NULL,5,NULL,0,'Holidays',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(2,'(PROV2)',1,NULL,NULL,141.67000000,28.33000000,0.00000000,0.00000000,170.00000000,'2015-02-01','2015-02-28','2016-01-22 19:04:44','2015-02-28 00:00:00',NULL,NULL,NULL,'2018-03-16 10:00:54',12,12,NULL,12,NULL,NULL,NULL,0,NULL,0,'Work on projet X','','',NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(3,'(PROV3)',1,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,'2017-02-02','2017-02-02','2017-02-02 03:57:03','2017-02-02 00:00:00',NULL,NULL,NULL,'2018-03-16 10:00:54',19,NULL,NULL,NULL,NULL,NULL,NULL,0,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL); /*!40000 ALTER TABLE `llx_expensereport` ENABLE KEYS */; UNLOCK TABLES; @@ -4540,16 +4683,16 @@ CREATE TABLE `llx_expensereport_det` ( `fk_expensereport` int(11) NOT NULL, `fk_c_type_fees` int(11) NOT NULL, `fk_projet` int(11) DEFAULT NULL, - `comments` text NOT NULL, + `comments` text COLLATE utf8_unicode_ci NOT NULL, `product_type` int(11) DEFAULT '-1', `qty` double NOT NULL, `value_unit` double NOT NULL, `remise_percent` double DEFAULT NULL, `tva_tx` double(6,3) DEFAULT NULL, `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) DEFAULT NULL, + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) DEFAULT NULL, + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `total_ht` double(24,8) NOT NULL DEFAULT '0.00000000', `total_tva` double(24,8) NOT NULL DEFAULT '0.00000000', `total_localtax1` double(24,8) DEFAULT '0.00000000', @@ -4559,20 +4702,20 @@ CREATE TABLE `llx_expensereport_det` ( `info_bits` int(11) DEFAULT '0', `special_code` int(11) DEFAULT '0', `rang` int(11) DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', `fk_facture` int(11) DEFAULT '0', `fk_code_ventilation` int(11) DEFAULT '0', - `vat_src_code` varchar(10) DEFAULT '', - `rule_warning_message` text, + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', + `rule_warning_message` text COLLATE utf8_unicode_ci, `fk_c_exp_tax_cat` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4684,12 +4827,12 @@ DROP TABLE IF EXISTS `llx_export_compta`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_export_compta` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(12) NOT NULL, + `ref` varchar(12) COLLATE utf8_unicode_ci NOT NULL, `date_export` datetime NOT NULL, `fk_user` int(11) NOT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4711,13 +4854,13 @@ DROP TABLE IF EXISTS `llx_export_model`; CREATE TABLE `llx_export_model` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_user` int(11) NOT NULL DEFAULT '0', - `label` varchar(50) NOT NULL, - `type` varchar(20) NOT NULL, - `field` text NOT NULL, - `filter` text, + `label` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `field` text COLLATE utf8_unicode_ci NOT NULL, + `filter` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_export_model` (`label`,`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4738,31 +4881,31 @@ DROP TABLE IF EXISTS `llx_extrafields`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `elementtype` varchar(64) NOT NULL DEFAULT 'member', - `name` varchar(64) NOT NULL, + `elementtype` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'member', + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `label` varchar(255) NOT NULL, - `type` varchar(8) DEFAULT NULL, - `size` varchar(8) DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, + `size` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, `pos` int(11) DEFAULT '0', `alwayseditable` int(11) DEFAULT '0', - `param` text, + `param` text COLLATE utf8_unicode_ci, `fieldunique` int(11) DEFAULT '0', `fieldrequired` int(11) DEFAULT '0', - `perms` varchar(255) DEFAULT NULL, + `perms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `list` int(11) DEFAULT '1', `ishidden` int(11) DEFAULT '0', - `fieldcomputed` text, - `fielddefault` varchar(255) DEFAULT NULL, - `langs` varchar(64) DEFAULT NULL, + `fieldcomputed` text COLLATE utf8_unicode_ci, + `fielddefault` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `langs` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `datec` datetime DEFAULT NULL, - `enabled` varchar(255) DEFAULT '1', + `enabled` varchar(255) COLLATE utf8_unicode_ci DEFAULT '1', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_extrafields_name` (`name`,`entity`,`elementtype`) -) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4784,13 +4927,13 @@ DROP TABLE IF EXISTS `llx_facture`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_facture` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `facnumber` varchar(30) NOT NULL, + `facnumber` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `type` smallint(6) NOT NULL DEFAULT '0', - `ref_client` varchar(255) DEFAULT NULL, - `increment` varchar(10) DEFAULT NULL, + `ref_client` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `increment` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_soc` int(11) NOT NULL, `datec` datetime DEFAULT NULL, `datef` date DEFAULT NULL, @@ -4801,8 +4944,8 @@ CREATE TABLE `llx_facture` ( `remise_percent` double DEFAULT '0', `remise_absolue` double DEFAULT '0', `remise` double DEFAULT '0', - `close_code` varchar(16) DEFAULT NULL, - `close_note` varchar(128) DEFAULT NULL, + `close_code` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + `close_note` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `tva` double(24,8) DEFAULT '0.00000000', `localtax1` double(24,8) DEFAULT '0.00000000', `localtax2` double(24,8) DEFAULT '0.00000000', @@ -4816,29 +4959,29 @@ CREATE TABLE `llx_facture` ( `fk_facture_source` int(11) DEFAULT NULL, `fk_projet` int(11) DEFAULT NULL, `fk_account` int(11) DEFAULT NULL, - `fk_currency` varchar(3) DEFAULT NULL, + `fk_currency` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_cond_reglement` int(11) NOT NULL DEFAULT '1', `fk_mode_reglement` int(11) DEFAULT NULL, `date_lim_reglement` date DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `situation_cycle_ref` smallint(6) DEFAULT NULL, `situation_counter` smallint(6) DEFAULT NULL, `situation_final` smallint(6) DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_pointoftax` date DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', `fk_fac_rec_source` int(11) DEFAULT NULL, - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `idx_facture_uk_facnumber` (`facnumber`,`entity`), KEY `idx_facture_fk_soc` (`fk_soc`), @@ -4854,7 +4997,7 @@ CREATE TABLE `llx_facture` ( CONSTRAINT `fk_facture_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_facture_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_facture_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=218 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=218 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4863,7 +5006,7 @@ CREATE TABLE `llx_facture` ( LOCK TABLES `llx_facture` WRITE; /*!40000 ALTER TABLE `llx_facture` DISABLE KEYS */; -INSERT INTO `llx_facture` VALUES (2,'FA1007-0002',1,NULL,NULL,0,NULL,NULL,2,'2010-07-10 18:20:13','2016-07-10',NULL,'2016-07-30 15:13:20',1,10.00000000,NULL,NULL,0,NULL,NULL,0.10000000,0.00000000,0.00000000,0.00000000,46.00000000,46.10000000,2,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2016-07-10',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(3,'FA1107-0006',1,NULL,NULL,0,NULL,NULL,10,'2011-07-18 20:33:35','2016-07-18',NULL,'2016-07-30 15:13:20',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,15.00000000,15.00000000,2,1,NULL,1,NULL,1,NULL,NULL,1,0,'2016-07-18',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(5,'FA1108-0003',1,NULL,NULL,0,NULL,NULL,7,'2011-08-01 03:34:11','2015-08-01',NULL,'2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.63000000,0.00000000,0.00000000,0.00000000,5.00000000,5.63000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,6,'2015-08-01',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(6,'FA1108-0004',1,NULL,NULL,0,NULL,NULL,7,'2011-08-06 20:33:53','2015-08-06',NULL,'2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.98000000,0.00000000,0.00000000,0.00000000,5.00000000,5.98000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,4,'2015-08-06','Cash\nReceived : 6 EUR\nRendu : 0.02 EUR\n\n--------------------------------------',NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(8,'FA1108-0005',1,NULL,NULL,3,NULL,NULL,2,'2011-08-08 02:41:44','2015-08-08',NULL,'2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,10.00000000,10.00000000,2,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-08-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(9,'FA1108-0007',1,NULL,NULL,3,NULL,NULL,10,'2011-08-08 02:55:14','2015-08-08',NULL,'2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,1.96000000,0.00000000,0.00000000,0.00000000,10.00000000,11.96000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-08-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(10,'AV1212-0001',1,NULL,NULL,2,NULL,NULL,10,'2012-12-08 17:45:20','2015-12-08','2015-12-08','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,-0.63000000,0.00000000,0.00000000,0.00000000,-11.00000000,-11.63000000,1,1,NULL,1,3,NULL,NULL,NULL,0,0,'2015-12-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(12,'AV1212-0002',1,NULL,NULL,2,NULL,NULL,10,'2012-12-08 18:20:14','2015-12-08','2015-12-08','2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,-5.00000000,-5.00000000,2,1,NULL,1,3,NULL,NULL,NULL,0,0,'2015-12-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(13,'FA1212-0011',1,NULL,NULL,0,NULL,NULL,7,'2012-12-09 20:04:19','2015-12-09','2016-02-12','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,2.74000000,0.00000000,0.00000000,0.00000000,14.00000000,16.74000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-09',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(32,'FA1212-0021',1,NULL,NULL,0,NULL,NULL,1,'2012-12-11 09:34:23','2015-12-11','2016-03-24','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,90.00000000,0.00000000,0.00000000,0.60000000,520.00000000,610.60000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-11','This is a comment (private)','This is a comment (public)','crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(33,'FA1212-0023',1,NULL,NULL,0,NULL,NULL,1,'2012-12-11 09:34:23','2015-12-11','2017-03-03','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,'abandon',NULL,0.24000000,0.00000000,0.00000000,0.00000000,2.48000000,2.72000000,3,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-11','This is a comment (private)','This is a comment (public)','crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(55,'FA1212-0009',1,NULL,NULL,0,NULL,NULL,1,'2012-12-11 09:35:51','2015-12-11','2015-12-12','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,0.24000000,0.00000000,0.00000000,0.00000000,2.48000000,2.72000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-11','This is a comment (private)','This is a comment (public)','generic_invoice_odt:/home/ldestailleur/git/dolibarr_3.8/documents/doctemplates/invoices/template_invoice.odt',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(148,'FS1301-0001',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:22:48','2016-01-19','2016-01-19','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,0.63000000,0.00000000,0.00000000,0.00000000,5.00000000,5.63000000,1,1,NULL,1,NULL,NULL,NULL,NULL,0,1,'2016-01-19',NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(149,'FA1601-0024',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:30:05','2016-01-19','2017-08-29','2017-08-29 18:39:09',0,0.00000000,NULL,NULL,0,NULL,NULL,1.80000000,0.90000000,0.90000000,0.00000000,20.00000000,23.60000000,1,1,NULL,12,NULL,NULL,NULL,NULL,0,0,'2016-01-19',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,20.00000000,1.80000000,23.60000000,NULL,NULL),(150,'FA6801-0010',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:31:10','2016-01-19','2016-01-19','2016-07-30 15:13:20',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,1,'2016-01-19',NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(151,'FS1301-0002',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:31:58','2016-01-19','2016-01-19','2016-07-30 15:13:20',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,1,'2016-01-19',NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(160,'FA1507-0015',1,NULL,NULL,0,NULL,NULL,12,'2013-03-06 16:47:48','2016-07-18','2014-03-06','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,1.11000000,0.00000000,0.00000000,0.00000000,8.89000000,10.00000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2016-07-18',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(210,'FA1107-0019',1,NULL,NULL,0,NULL,NULL,10,'2013-03-20 14:30:11','2016-07-10','2018-03-20','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,10.00000000,10.00000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2016-07-10',NULL,NULL,'generic_invoice_odt:/home/ldestailleur/git/dolibarr_3.8/documents/doctemplates/invoices/template_invoice.odt',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(211,'FA1303-0020',1,NULL,NULL,0,NULL,NULL,19,'2013-03-22 09:40:10','2016-03-22','2017-03-02','2017-02-06 04:11:17',0,0.00000000,NULL,NULL,0,NULL,NULL,17.64000000,0.00000000,0.00000000,0.40000000,340.00000000,358.04000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,3,'2016-03-22',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(213,'AV1303-0003',1,NULL,NULL,2,NULL,NULL,1,'2014-03-03 19:22:03','2016-03-03','2017-03-03','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,-1000.00000000,-1000.00000000,1,1,NULL,1,32,NULL,NULL,NULL,0,0,'2016-03-03',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(216,'(PROV216)',1,NULL,NULL,0,NULL,NULL,26,'2017-02-12 23:21:27','2017-02-12',NULL,'2017-02-12 19:21:27',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0,12,NULL,NULL,NULL,NULL,NULL,NULL,1,0,'2017-02-12',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,0,0,'',NULL,0,'EUR',1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(217,'(PROV217)',1,NULL,NULL,0,NULL,NULL,1,'2017-08-31 13:26:17','2017-08-31',NULL,'2017-08-31 09:26:17',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,10.00000000,10.00000000,0,12,NULL,NULL,NULL,NULL,NULL,NULL,1,0,'2017-08-31',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,0,0,'',NULL,1,'EUR',1.00000000,10.00000000,0.00000000,10.00000000,NULL,NULL); +INSERT INTO `llx_facture` VALUES (2,'FA1007-0002',1,NULL,NULL,0,NULL,NULL,2,'2010-07-10 18:20:13','2016-07-10',NULL,'2016-07-30 15:13:20',1,10.00000000,NULL,NULL,0,NULL,NULL,0.10000000,0.00000000,0.00000000,0.00000000,46.00000000,46.10000000,2,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2016-07-10',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(3,'FA1107-0006',1,NULL,NULL,0,NULL,NULL,10,'2011-07-18 20:33:35','2016-07-18',NULL,'2016-07-30 15:13:20',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,15.00000000,15.00000000,2,1,NULL,1,NULL,1,NULL,NULL,1,0,'2016-07-18',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(5,'FA1108-0003',1,NULL,NULL,0,NULL,NULL,7,'2011-08-01 03:34:11','2015-08-01',NULL,'2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.63000000,0.00000000,0.00000000,0.00000000,5.00000000,5.63000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,6,'2015-08-01',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(6,'FA1108-0004',1,NULL,NULL,0,NULL,NULL,7,'2011-08-06 20:33:53','2015-08-06',NULL,'2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.98000000,0.00000000,0.00000000,0.00000000,5.00000000,5.98000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,4,'2015-08-06','Cash\nReceived : 6 EUR\nRendu : 0.02 EUR\n\n--------------------------------------',NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(8,'FA1108-0005',1,NULL,NULL,3,NULL,NULL,2,'2011-08-08 02:41:44','2015-08-08',NULL,'2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,10.00000000,10.00000000,2,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-08-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(9,'FA1108-0007',1,NULL,NULL,3,NULL,NULL,10,'2011-08-08 02:55:14','2015-08-08',NULL,'2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,1.96000000,0.00000000,0.00000000,0.00000000,10.00000000,11.96000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-08-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(10,'AV1212-0001',1,NULL,NULL,2,NULL,NULL,10,'2012-12-08 17:45:20','2015-12-08','2015-12-08','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,-0.63000000,0.00000000,0.00000000,0.00000000,-11.00000000,-11.63000000,1,1,NULL,1,3,NULL,NULL,NULL,0,0,'2015-12-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(12,'AV1212-0002',1,NULL,NULL,2,NULL,NULL,10,'2012-12-08 18:20:14','2015-12-08','2015-12-08','2016-07-30 15:12:32',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,-5.00000000,-5.00000000,2,1,NULL,1,3,NULL,NULL,NULL,0,0,'2015-12-08',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(13,'FA1212-0011',1,NULL,NULL,0,NULL,NULL,7,'2012-12-09 20:04:19','2015-12-09','2016-02-12','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,2.74000000,0.00000000,0.00000000,0.00000000,14.00000000,16.74000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-09',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(32,'FA1212-0021',1,NULL,NULL,0,NULL,NULL,1,'2012-12-11 09:34:23','2015-12-11','2016-03-24','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,90.00000000,0.00000000,0.00000000,0.60000000,520.00000000,610.60000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-11','This is a comment (private)','This is a comment (public)','crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(33,'FA1212-0023',1,NULL,NULL,0,NULL,NULL,1,'2012-12-11 09:34:23','2015-12-11','2017-03-03','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,'abandon',NULL,0.24000000,0.00000000,0.00000000,0.00000000,2.48000000,2.72000000,3,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-11','This is a comment (private)','This is a comment (public)','crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(55,'FA1212-0009',1,NULL,NULL,0,NULL,NULL,1,'2012-12-11 09:35:51','2015-12-11','2015-12-12','2016-07-30 15:12:32',0,0.00000000,NULL,NULL,0,NULL,NULL,0.24000000,0.00000000,0.00000000,0.00000000,2.48000000,2.72000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2015-12-11','This is a comment (private)','This is a comment (public)','generic_invoice_odt:/home/ldestailleur/git/dolibarr_3.8/documents/doctemplates/invoices/template_invoice.odt',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(148,'FS1301-0001',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:22:48','2016-01-19','2016-01-19','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,0.63000000,0.00000000,0.00000000,0.00000000,5.00000000,5.63000000,1,1,NULL,1,NULL,NULL,NULL,NULL,0,1,'2016-01-19',NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(149,'FA1601-0024',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:30:05','2016-01-19','2017-08-29','2018-03-16 09:59:31',0,0.00000000,NULL,NULL,0,NULL,NULL,1.80000000,0.90000000,0.90000000,0.00000000,20.00000000,23.60000000,1,1,NULL,12,NULL,NULL,NULL,NULL,0,0,'2016-01-19',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,20.00000000,1.80000000,23.60000000,NULL,'facture/FA1601-0024/FA1601-0024.pdf'),(150,'FA6801-0010',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:31:10','2016-01-19','2016-01-19','2016-07-30 15:13:20',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,1,'2016-01-19',NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(151,'FS1301-0002',1,NULL,NULL,0,NULL,NULL,1,'2013-01-19 18:31:58','2016-01-19','2016-01-19','2016-07-30 15:13:20',1,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,2,1,NULL,1,NULL,NULL,NULL,NULL,0,1,'2016-01-19',NULL,NULL,'',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(160,'FA1507-0015',1,NULL,NULL,0,NULL,NULL,12,'2013-03-06 16:47:48','2016-07-18','2014-03-06','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,1.11000000,0.00000000,0.00000000,0.00000000,8.89000000,10.00000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2016-07-18',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(210,'FA1107-0019',1,NULL,NULL,0,NULL,NULL,10,'2013-03-20 14:30:11','2016-07-10','2018-03-20','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,10.00000000,10.00000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,0,'2016-07-10',NULL,NULL,'generic_invoice_odt:/home/ldestailleur/git/dolibarr_3.8/documents/doctemplates/invoices/template_invoice.odt',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(211,'FA1303-0020',1,NULL,NULL,0,NULL,NULL,19,'2013-03-22 09:40:10','2016-03-22','2017-03-02','2017-02-06 04:11:17',0,0.00000000,NULL,NULL,0,NULL,NULL,17.64000000,0.00000000,0.00000000,0.40000000,340.00000000,358.04000000,1,1,NULL,1,NULL,NULL,NULL,NULL,1,3,'2016-03-22',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(213,'AV1303-0003',1,NULL,NULL,2,NULL,NULL,1,'2014-03-03 19:22:03','2016-03-03','2017-03-03','2016-07-30 15:13:20',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,-1000.00000000,-1000.00000000,1,1,NULL,1,32,NULL,NULL,NULL,0,0,'2016-03-03',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(216,'(PROV216)',1,NULL,NULL,0,NULL,NULL,26,'2017-02-12 23:21:27','2017-02-12',NULL,'2017-02-12 19:21:27',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0,12,NULL,NULL,NULL,NULL,NULL,NULL,1,0,'2017-02-12',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,0,0,'',NULL,0,'EUR',1.00000000,0.00000000,0.00000000,0.00000000,NULL,NULL),(217,'(PROV217)',1,NULL,NULL,0,NULL,NULL,1,'2017-08-31 13:26:17','2017-08-31',NULL,'2017-08-31 09:26:17',0,0.00000000,NULL,NULL,0,NULL,NULL,0.00000000,0.00000000,0.00000000,0.00000000,10.00000000,10.00000000,0,12,NULL,NULL,NULL,NULL,NULL,NULL,1,0,'2017-08-31',NULL,NULL,'crabe',NULL,NULL,NULL,NULL,0,0,'',NULL,1,'EUR',1.00000000,10.00000000,0.00000000,10.00000000,NULL,NULL); /*!40000 ALTER TABLE `llx_facture` ENABLE KEYS */; UNLOCK TABLES; @@ -4878,10 +5021,10 @@ CREATE TABLE `llx_facture_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_facture_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4902,21 +5045,21 @@ DROP TABLE IF EXISTS `llx_facture_fourn`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_facture_fourn` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(255) NOT NULL, - `ref_supplier` varchar(255) DEFAULT NULL, + `ref` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `ref_supplier` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `type` smallint(6) NOT NULL DEFAULT '0', `fk_soc` int(11) NOT NULL, `datec` datetime DEFAULT NULL, `datef` date DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `libelle` varchar(255) DEFAULT NULL, + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `paye` smallint(6) NOT NULL DEFAULT '0', `amount` double(24,8) NOT NULL DEFAULT '0.00000000', `remise` double(24,8) DEFAULT '0.00000000', - `close_code` varchar(16) DEFAULT NULL, - `close_note` varchar(128) DEFAULT NULL, + `close_code` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + `close_note` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `tva` double(24,8) DEFAULT '0.00000000', `localtax1` double(24,8) DEFAULT '0.00000000', `localtax2` double(24,8) DEFAULT '0.00000000', @@ -4934,20 +5077,20 @@ CREATE TABLE `llx_facture_fourn` ( `fk_cond_reglement` int(11) DEFAULT NULL, `fk_mode_reglement` int(11) DEFAULT NULL, `date_lim_reglement` date DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_pointoftax` date DEFAULT NULL, `date_valid` date DEFAULT NULL, PRIMARY KEY (`rowid`), @@ -4962,7 +5105,7 @@ CREATE TABLE `llx_facture_fourn` ( CONSTRAINT `fk_facture_fourn_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_facture_fourn_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_facture_fourn_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -4987,19 +5130,19 @@ CREATE TABLE `llx_facture_fourn_det` ( `fk_facture_fourn` int(11) NOT NULL, `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `ref` varchar(50) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `description` text, + `ref` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `pu_ht` double(24,8) DEFAULT NULL, `pu_ttc` double(24,8) DEFAULT NULL, `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `tva_tx` double(6,3) DEFAULT NULL, - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `total_ht` double(24,8) DEFAULT NULL, `tva` double(24,8) DEFAULT NULL, `total_localtax1` double(24,8) DEFAULT '0.00000000', @@ -5009,13 +5152,13 @@ CREATE TABLE `llx_facture_fourn_det` ( `date_start` datetime DEFAULT NULL, `date_end` datetime DEFAULT NULL, `info_bits` int(11) NOT NULL DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_code_ventilation` int(11) NOT NULL DEFAULT '0', `special_code` int(11) DEFAULT '0', `rang` int(11) DEFAULT '0', `fk_unit` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -5027,7 +5170,7 @@ CREATE TABLE `llx_facture_fourn_det` ( KEY `idx_facture_fourn_det_fk_product` (`fk_product`), CONSTRAINT `fk_facture_fourn_det_fk_facture` FOREIGN KEY (`fk_facture_fourn`) REFERENCES `llx_facture_fourn` (`rowid`), CONSTRAINT `fk_facture_fourn_det_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5051,10 +5194,10 @@ CREATE TABLE `llx_facture_fourn_det_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_facture_fourn_det_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5077,10 +5220,10 @@ CREATE TABLE `llx_facture_fourn_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_facture_fourn_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5101,7 +5244,7 @@ DROP TABLE IF EXISTS `llx_facture_rec`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_facture_rec` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `titre` varchar(100) DEFAULT NULL, + `titre` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `fk_soc` int(11) NOT NULL, `datec` datetime DEFAULT NULL, @@ -5119,11 +5262,11 @@ CREATE TABLE `llx_facture_rec` ( `fk_cond_reglement` int(11) DEFAULT '0', `fk_mode_reglement` int(11) DEFAULT '0', `date_lim_reglement` date DEFAULT NULL, - `note_private` text, - `note_public` text, - `modelpdf` varchar(255) DEFAULT NULL, - `last_gen` varchar(7) DEFAULT NULL, - `unit_frequency` varchar(2) DEFAULT 'd', + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `modelpdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `last_gen` varchar(7) COLLATE utf8_unicode_ci DEFAULT NULL, + `unit_frequency` varchar(2) COLLATE utf8_unicode_ci DEFAULT 'd', `date_when` datetime DEFAULT NULL, `date_last_gen` datetime DEFAULT NULL, `nb_gen_done` int(11) DEFAULT NULL, @@ -5135,14 +5278,14 @@ CREATE TABLE `llx_facture_rec` ( `generate_pdf` int(11) DEFAULT '1', `fk_account` int(11) DEFAULT '0', `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', `fk_user_modif` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `suspended` int(11) DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `idx_facture_rec_uk_titre` (`titre`,`entity`), @@ -5152,7 +5295,7 @@ CREATE TABLE `llx_facture_rec` ( CONSTRAINT `fk_facture_rec_fk_projet` FOREIGN KEY (`fk_projet`) REFERENCES `llx_projet` (`rowid`), CONSTRAINT `fk_facture_rec_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_facture_rec_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5203,14 +5346,14 @@ CREATE TABLE `llx_facturedet` ( `fk_facture` int(11) NOT NULL, `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `description` text, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `tva_tx` double(6,3) DEFAULT NULL, - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `remise` double DEFAULT '0', @@ -5232,14 +5375,14 @@ CREATE TABLE `llx_facturedet` ( `special_code` int(10) unsigned DEFAULT '0', `rang` int(11) DEFAULT '0', `fk_contract_line` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `situation_percent` double DEFAULT NULL, `fk_prev_id` int(11) DEFAULT NULL, `fk_unit` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -5252,7 +5395,7 @@ CREATE TABLE `llx_facturedet` ( KEY `idx_facturedet_fk_code_ventilation` (`fk_code_ventilation`), CONSTRAINT `fk_facturedet_fk_facture` FOREIGN KEY (`fk_facture`) REFERENCES `llx_facture` (`rowid`), CONSTRAINT `fk_facturedet_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=1031 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=1031 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5276,10 +5419,10 @@ CREATE TABLE `llx_facturedet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_facturedet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5304,14 +5447,14 @@ CREATE TABLE `llx_facturedet_rec` ( `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, `product_type` int(11) DEFAULT '0', - `label` varchar(255) DEFAULT NULL, - `description` text, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `tva_tx` double(6,3) DEFAULT NULL, - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `remise` double DEFAULT '0', @@ -5327,9 +5470,9 @@ CREATE TABLE `llx_facturedet_rec` ( `rang` int(11) DEFAULT '0', `fk_contract_line` int(11) DEFAULT NULL, `fk_unit` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -5337,7 +5480,7 @@ CREATE TABLE `llx_facturedet_rec` ( PRIMARY KEY (`rowid`), KEY `fk_facturedet_rec_fk_unit` (`fk_unit`), CONSTRAINT `fk_facturedet_rec_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5388,7 +5531,7 @@ CREATE TABLE `llx_fichinter` ( `fk_soc` int(11) NOT NULL, `fk_projet` int(11) DEFAULT '0', `fk_contrat` int(11) DEFAULT '0', - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, @@ -5402,19 +5545,19 @@ CREATE TABLE `llx_fichinter` ( `dateo` date DEFAULT NULL, `datee` date DEFAULT NULL, `datet` date DEFAULT NULL, - `description` text, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, - `ref_ext` varchar(255) DEFAULT NULL, - `last_main_doc` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_fichinter_ref` (`ref`,`entity`), KEY `idx_fichinter_fk_soc` (`fk_soc`), CONSTRAINT `fk_fichinter_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5438,10 +5581,10 @@ CREATE TABLE `llx_fichinter_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_ficheinter_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5465,13 +5608,13 @@ CREATE TABLE `llx_fichinterdet` ( `fk_fichinter` int(11) DEFAULT NULL, `fk_parent_line` int(11) DEFAULT NULL, `date` datetime DEFAULT NULL, - `description` text, + `description` text COLLATE utf8_unicode_ci, `duree` int(11) DEFAULT NULL, `rang` int(11) DEFAULT '0', PRIMARY KEY (`rowid`), KEY `idx_fichinterdet_fk_fichinter` (`fk_fichinter`), CONSTRAINT `fk_fichinterdet_fk_fichinter` FOREIGN KEY (`fk_fichinter`) REFERENCES `llx_fichinter` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5495,10 +5638,10 @@ CREATE TABLE `llx_fichinterdet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_ficheinterdet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5521,7 +5664,7 @@ CREATE TABLE `llx_holiday` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_user` int(11) NOT NULL, `date_create` datetime NOT NULL, - `description` varchar(255) NOT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `date_debut` date NOT NULL, `date_fin` date NOT NULL, `halfday` int(11) DEFAULT '0', @@ -5533,18 +5676,18 @@ CREATE TABLE `llx_holiday` ( `fk_user_refuse` int(11) DEFAULT NULL, `date_cancel` datetime DEFAULT NULL, `fk_user_cancel` int(11) DEFAULT NULL, - `detail_refuse` varchar(250) DEFAULT NULL, - `note_private` text, - `note_public` text, - `note` text, + `detail_refuse` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `note` text COLLATE utf8_unicode_ci, `fk_user_create` int(11) DEFAULT NULL, `fk_type` int(11) NOT NULL DEFAULT '1', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `entity` int(11) NOT NULL DEFAULT '1', - `ref` varchar(30) DEFAULT NULL, - `ref_ext` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_holiday_fk_user` (`fk_user`), @@ -5554,7 +5697,7 @@ CREATE TABLE `llx_holiday` ( KEY `idx_holiday_date_create` (`date_create`), KEY `idx_holiday_fk_validator` (`fk_validator`), KEY `idx_holiday_entity` (`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5576,12 +5719,12 @@ DROP TABLE IF EXISTS `llx_holiday_config`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_holiday_config` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(128) DEFAULT NULL, - `value` text, + `name` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `value` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `name` (`name`), UNIQUE KEY `idx_holiday_config` (`name`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5606,12 +5749,12 @@ CREATE TABLE `llx_holiday_logs` ( `date_action` datetime NOT NULL, `fk_user_action` int(11) NOT NULL, `fk_user_update` int(11) NOT NULL, - `type_action` varchar(255) NOT NULL, - `prev_solde` varchar(255) NOT NULL, - `new_solde` varchar(255) NOT NULL, + `type_action` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `prev_solde` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `new_solde` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `fk_type` int(11) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=195 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=195 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5636,7 +5779,7 @@ CREATE TABLE `llx_holiday_users` ( `nb_holiday` double NOT NULL DEFAULT '0', `fk_type` int(11) NOT NULL DEFAULT '1', UNIQUE KEY `uk_holiday_users` (`fk_user`,`fk_type`,`nb_holiday`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5659,12 +5802,12 @@ DROP TABLE IF EXISTS `llx_import_model`; CREATE TABLE `llx_import_model` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_user` int(11) NOT NULL DEFAULT '0', - `label` varchar(50) NOT NULL, - `type` varchar(50) DEFAULT NULL, - `field` text NOT NULL, + `label` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `field` text COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_import_model` (`label`,`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5794,13 +5937,13 @@ CREATE TABLE `llx_links` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', `datea` datetime NOT NULL, - `url` varchar(255) NOT NULL, - `label` varchar(255) NOT NULL, - `objecttype` varchar(255) NOT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `objecttype` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `objectid` int(11) NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_links` (`objectid`,`label`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5823,12 +5966,12 @@ DROP TABLE IF EXISTS `llx_livraison`; CREATE TABLE `llx_livraison` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_customer` varchar(30) DEFAULT NULL, + `ref_customer` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_soc` int(11) NOT NULL, - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(30) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `date_creation` datetime DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `date_valid` datetime DEFAULT NULL, @@ -5837,14 +5980,14 @@ CREATE TABLE `llx_livraison` ( `fk_address` int(11) DEFAULT NULL, `fk_statut` smallint(6) DEFAULT '0', `total_ht` double(24,8) DEFAULT '0.00000000', - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, - `last_main_doc` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `idx_livraison_uk_ref` (`ref`,`entity`), KEY `idx_livraison_fk_soc` (`fk_soc`), @@ -5853,7 +5996,7 @@ CREATE TABLE `llx_livraison` ( CONSTRAINT `fk_livraison_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_livraison_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_livraison_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5876,10 +6019,10 @@ CREATE TABLE `llx_livraison_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_livraison_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5903,7 +6046,7 @@ CREATE TABLE `llx_livraisondet` ( `fk_livraison` int(11) DEFAULT NULL, `fk_origin_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `description` text, + `description` text COLLATE utf8_unicode_ci, `qty` double DEFAULT NULL, `subprice` double(24,8) DEFAULT '0.00000000', `total_ht` double(24,8) DEFAULT '0.00000000', @@ -5911,7 +6054,7 @@ CREATE TABLE `llx_livraisondet` ( PRIMARY KEY (`rowid`), KEY `idx_livraisondet_fk_expedition` (`fk_livraison`), CONSTRAINT `fk_livraisondet_fk_livraison` FOREIGN KEY (`fk_livraison`) REFERENCES `llx_livraison` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5934,10 +6077,10 @@ CREATE TABLE `llx_livraisondet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_livraisondet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -5961,27 +6104,27 @@ CREATE TABLE `llx_loan` ( `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `label` varchar(80) NOT NULL, + `label` varchar(80) COLLATE utf8_unicode_ci NOT NULL, `fk_bank` int(11) DEFAULT NULL, `capital` double(24,8) DEFAULT NULL, `datestart` date DEFAULT NULL, `dateend` date DEFAULT NULL, `nbterm` double DEFAULT NULL, `rate` double NOT NULL, - `note_private` text, - `note_public` text, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, `capital_position` double(24,8) DEFAULT NULL, `date_position` date DEFAULT NULL, `paid` smallint(6) NOT NULL DEFAULT '0', - `accountancy_account_capital` varchar(32) DEFAULT NULL, - `accountancy_account_insurance` varchar(32) DEFAULT NULL, - `accountancy_account_interest` varchar(32) DEFAULT NULL, + `accountancy_account_capital` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_account_insurance` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_account_interest` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `active` tinyint(4) NOT NULL DEFAULT '1', `fk_projet` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6044,13 +6187,13 @@ CREATE TABLE `llx_localtax` ( `datep` date DEFAULT NULL, `datev` date DEFAULT NULL, `amount` double NOT NULL DEFAULT '0', - `label` varchar(255) DEFAULT NULL, - `note` text, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) DEFAULT NULL, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6072,18 +6215,18 @@ DROP TABLE IF EXISTS `llx_mailing`; CREATE TABLE `llx_mailing` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `statut` smallint(6) DEFAULT '0', - `titre` varchar(128) DEFAULT NULL, + `titre` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `sujet` varchar(128) DEFAULT NULL, - `body` mediumtext, - `bgcolor` varchar(8) DEFAULT NULL, - `bgimage` varchar(255) DEFAULT NULL, - `cible` varchar(60) DEFAULT NULL, + `sujet` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `body` mediumtext COLLATE utf8_unicode_ci, + `bgcolor` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, + `bgimage` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cible` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, `nbemail` int(11) DEFAULT NULL, - `email_from` varchar(160) DEFAULT NULL, - `email_replyto` varchar(160) DEFAULT NULL, - `email_errorsto` varchar(160) DEFAULT NULL, - `tag` varchar(128) DEFAULT NULL, + `email_from` varchar(160) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_replyto` varchar(160) COLLATE utf8_unicode_ci DEFAULT NULL, + `email_errorsto` varchar(160) COLLATE utf8_unicode_ci DEFAULT NULL, + `tag` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `date_creat` datetime DEFAULT NULL, `date_valid` datetime DEFAULT NULL, `date_appro` datetime DEFAULT NULL, @@ -6091,13 +6234,13 @@ CREATE TABLE `llx_mailing` ( `fk_user_creat` int(11) DEFAULT NULL, `fk_user_valid` int(11) DEFAULT NULL, `fk_user_appro` int(11) DEFAULT NULL, - `joined_file1` varchar(255) DEFAULT NULL, - `joined_file2` varchar(255) DEFAULT NULL, - `joined_file3` varchar(255) DEFAULT NULL, - `joined_file4` varchar(255) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `joined_file1` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `joined_file2` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `joined_file3` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `joined_file4` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6121,21 +6264,21 @@ CREATE TABLE `llx_mailing_cibles` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_mailing` int(11) NOT NULL, `fk_contact` int(11) NOT NULL, - `lastname` varchar(50) DEFAULT NULL, - `firstname` varchar(50) DEFAULT NULL, - `email` varchar(160) NOT NULL, - `other` varchar(255) DEFAULT NULL, - `tag` varchar(128) DEFAULT NULL, + `lastname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `firstname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(160) COLLATE utf8_unicode_ci NOT NULL, + `other` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `tag` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `statut` smallint(6) NOT NULL DEFAULT '0', - `source_url` varchar(255) DEFAULT NULL, + `source_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `source_id` int(11) DEFAULT NULL, - `source_type` varchar(16) DEFAULT NULL, + `source_type` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, `date_envoi` datetime DEFAULT NULL, - `error_text` varchar(255) DEFAULT NULL, + `error_text` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_mailing_cibles` (`fk_mailing`,`email`), KEY `idx_mailing_cibles_email` (`email`) -) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6157,29 +6300,29 @@ DROP TABLE IF EXISTS `llx_menu`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_menu` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `menu_handler` varchar(16) NOT NULL, + `menu_handler` varchar(16) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `module` varchar(64) DEFAULT NULL, - `type` varchar(4) NOT NULL, - `mainmenu` varchar(100) NOT NULL, + `module` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, + `type` varchar(4) COLLATE utf8_unicode_ci NOT NULL, + `mainmenu` varchar(100) COLLATE utf8_unicode_ci NOT NULL, `fk_menu` int(11) NOT NULL, - `fk_leftmenu` varchar(100) DEFAULT NULL, - `fk_mainmenu` varchar(100) DEFAULT NULL, + `fk_leftmenu` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `fk_mainmenu` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `position` int(11) NOT NULL, - `url` varchar(255) NOT NULL, - `target` varchar(100) DEFAULT NULL, - `titre` varchar(255) NOT NULL, - `langs` varchar(100) DEFAULT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `target` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `titre` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `langs` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, `level` smallint(6) DEFAULT NULL, - `leftmenu` varchar(100) DEFAULT NULL, - `perms` text, - `enabled` varchar(255) DEFAULT '1', + `leftmenu` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `perms` text COLLATE utf8_unicode_ci, + `enabled` varchar(255) COLLATE utf8_unicode_ci DEFAULT '1', `usertype` int(11) NOT NULL DEFAULT '0', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`rowid`), UNIQUE KEY `idx_menu_uk_menu` (`menu_handler`,`fk_menu`,`position`,`url`,`entity`), KEY `idx_menu_menuhandler_type` (`menu_handler`,`type`) -) ENGINE=InnoDB AUTO_INCREMENT=166468 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=166493 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6188,7 +6331,7 @@ CREATE TABLE `llx_menu` ( LOCK TABLES `llx_menu` WRITE; /*!40000 ALTER TABLE `llx_menu` DISABLE KEYS */; -INSERT INTO `llx_menu` VALUES (103094,'all',2,'agenda','top','agenda',0,NULL,NULL,100,'/comm/action/index.php','','Agenda','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103095,'all',2,'agenda','left','agenda',103094,NULL,NULL,100,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Actions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103096,'all',2,'agenda','left','agenda',103095,NULL,NULL,101,'/comm/action/card.php?mainmenu=agenda&leftmenu=agenda&action=create','','NewAction','commercial',NULL,NULL,'($user->rights->agenda->myactions->create||$user->rights->agenda->allactions->create)','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103097,'all',2,'agenda','left','agenda',103095,NULL,NULL,102,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Calendar','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103098,'all',2,'agenda','left','agenda',103097,NULL,NULL,103,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103099,'all',2,'agenda','left','agenda',103097,NULL,NULL,104,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103100,'all',2,'agenda','left','agenda',103097,NULL,NULL,105,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103101,'all',2,'agenda','left','agenda',103097,NULL,NULL,106,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103102,'all',2,'agenda','left','agenda',103095,NULL,NULL,112,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda','','List','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103103,'all',2,'agenda','left','agenda',103102,NULL,NULL,113,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103104,'all',2,'agenda','left','agenda',103102,NULL,NULL,114,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103105,'all',2,'agenda','left','agenda',103102,NULL,NULL,115,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=todo','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103106,'all',2,'agenda','left','agenda',103102,NULL,NULL,116,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=done','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103107,'all',2,'agenda','left','agenda',103095,NULL,NULL,120,'/comm/action/rapport/index.php?mainmenu=agenda&leftmenu=agenda','','Reportings','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103108,'all',2,'pos','top','pos',0,NULL,NULL,100,'/pos/backend/listefac.php','','POS','pos@pos',NULL,'1','1','1',2,'2013-03-13 20:33:09'),(103109,'all',2,'pos','left','pos',103108,NULL,NULL,100,'/pos/backend/list.php','','Tickets','pos@pos',NULL,NULL,'$user->rights->pos->backend','$conf->global->POS_USE_TICKETS',0,'2013-03-13 20:33:09'),(103110,'all',2,'pos','left','pos',103109,NULL,NULL,100,'/pos/backend/list.php','','List','main',NULL,NULL,'$user->rights->pos->backend','$conf->global->POS_USE_TICKETS',0,'2013-03-13 20:33:09'),(103111,'all',2,'pos','left','pos',103110,NULL,NULL,100,'/pos/backend/list.php?viewstatut=0','','StatusTicketDraft','pos@pos',NULL,NULL,'$user->rights->pos->backend','$conf->global->POS_USE_TICKETS',0,'2013-03-13 20:33:09'),(103112,'all',2,'pos','left','@pos',103110,NULL,NULL,100,'/pos/backend/list.php?viewstatut=1','','StatusTicketClosed','main',NULL,NULL,'$user->rights->pos->backend','$conf->global->POS_USE_TICKETS',0,'2013-03-13 20:33:09'),(103113,'all',2,'pos','left','@pos',103110,NULL,NULL,100,'/pos/backend/list.php?viewstatut=2','','StatusTicketProcessed','main',NULL,NULL,'$user->rights->pos->backend','$conf->global->POS_USE_TICKETS',0,'2013-03-13 20:33:09'),(103114,'all',2,'pos','left','@pos',103110,NULL,NULL,100,'/pos/backend/list.php?viewtype=1','','StatusTicketReturned','main',NULL,NULL,'$user->rights->pos->backend','$conf->global->POS_USE_TICKETS',0,'2013-03-13 20:33:09'),(103115,'all',2,'pos','left','pos',103108,NULL,NULL,100,'/pos/backend/listefac.php','','Factures','pos@pos',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103116,'all',2,'pos','left','pos',103115,NULL,NULL,100,'/pos/backend/listefac.php','','List','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103117,'all',2,'pos','left','pos',103116,NULL,NULL,100,'/pos/backend/listefac.php?viewstatut=0','','BillStatusDraft','bills',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103118,'all',2,'pos','left','@pos',103116,NULL,NULL,100,'/pos/backend/listefac.php?viewstatut=1','','BillStatusValidated','bills',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103119,'all',2,'pos','left','@pos',103116,NULL,NULL,100,'/pos/backend/listefac.php?viewstatut=2&viewtype=0','','BillStatusPaid','bills',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103120,'all',2,'pos','left','@pos',103116,NULL,NULL,100,'/pos/backend/listefac.php?viewtype=2','','BillStatusReturned','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103121,'all',2,'pos','left','@pos',103108,NULL,NULL,100,'/pos/frontend/index.php','','POS','main',NULL,NULL,'$user->rights->pos->frontend','1',0,'2013-03-13 20:33:09'),(103122,'all',2,'pos','left','@pos',103121,NULL,NULL,100,'/pos/frontend/index.php','','NewTicket','main',NULL,NULL,'$user->rights->pos->frontend','1',0,'2013-03-13 20:33:09'),(103123,'all',2,'pos','left','@pos',103121,NULL,NULL,101,'/pos/backend/closes.php','','CloseandArching','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103124,'all',2,'pos','left','@pos',103108,NULL,NULL,100,'/pos/backend/terminal/cash.php','','Terminal','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103125,'all',2,'pos','left','@pos',103124,NULL,NULL,100,'/pos/backend/terminal/card.php?action=create','','NewCash','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103126,'all',2,'pos','left','@pos',103124,NULL,NULL,101,'/pos/backend/terminal/cash.php','','List','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103127,'all',2,'pos','left','@pos',103123,NULL,NULL,101,'/pos/backend/closes.php?viewstatut=0','','Arqueo','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103128,'all',2,'pos','left','@pos',103123,NULL,NULL,102,'/pos/backend/closes.php?viewstatut=1','','Closes','main',NULL,NULL,'$user->rights->pos->backend','1',0,'2013-03-13 20:33:09'),(103129,'all',2,'pos','left','@pos',103108,NULL,NULL,102,'/pos/backend/transfers.php','','Transfer','main',NULL,NULL,'$user->rights->pos->transfer','1',0,'2013-03-13 20:33:09'),(103130,'all',2,'pos','left','@pos',103108,NULL,NULL,102,'/pos/backend/resultat/index.php','','Rapport','main',NULL,NULL,'$user->rights->pos->stats','1',0,'2013-03-13 20:33:09'),(103131,'all',2,'pos','left','@pos',103130,NULL,NULL,102,'/pos/backend/resultat/casoc.php','','ReportsCustomer','main',NULL,NULL,'$user->rights->pos->stats','1',0,'2013-03-13 20:33:09'),(103132,'all',2,'pos','left','@pos',103130,NULL,NULL,102,'/pos/backend/resultat/causer.php','','ReportsUser','main',NULL,NULL,'$user->rights->pos->stats','1',0,'2013-03-13 20:33:09'),(103133,'all',2,'pos','left','@pos',103130,NULL,NULL,102,'/pos/backend/resultat/sellsjournal.php','','ReportsSells','main',NULL,NULL,'$user->rights->pos->stats','1',0,'2013-03-13 20:33:09'),(103134,'all',2,'opensurvey','top','opensurvey',0,NULL,NULL,200,'/opensurvey/index.php','','Surveys','opensurvey',NULL,NULL,'$user->rights->opensurvey->survey->read','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(103135,'all',2,'opensurvey','left','opensurvey',-1,NULL,'opensurvey',200,'/opensurvey/index.php?mainmenu=opensurvey&leftmenu=opensurvey','','Survey','opensurvey@opensurvey',NULL,'opensurvey','','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(103136,'all',2,'opensurvey','left','opensurvey',-1,'opensurvey','opensurvey',210,'/opensurvey/public/index.php','_blank','NewSurvey','opensurvey@opensurvey',NULL,'opensurvey_new','','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(103137,'all',2,'opensurvey','left','opensurvey',-1,'opensurvey','opensurvey',220,'/opensurvey/list.php','','List','opensurvey@opensurvey',NULL,'opensurvey_list','','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(124179,'all',1,'cashdesk','top','cashdesk',0,NULL,NULL,100,'/cashdesk/index.php?user=__LOGIN__','pointofsale','CashDeskMenu','cashdesk',NULL,NULL,'$user->rights->cashdesk->use','$conf->cashdesk->enabled',0,'2015-11-15 22:38:33'),(124210,'all',1,'margins','left','accountancy',-1,NULL,'accountancy',100,'/margin/index.php','','Margins','margins',NULL,'margins','$user->rights->margins->liretous','$conf->margin->enabled',2,'2015-11-15 22:41:47'),(145086,'all',1,'supplier_proposal','left','commercial',-1,NULL,'commercial',300,'/supplier_proposal/index.php','','SupplierProposalsShort','supplier_proposal',NULL,'supplier_proposalsubmenu','$user->rights->supplier_proposal->lire','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145087,'all',1,'supplier_proposal','left','commercial',-1,'supplier_proposalsubmenu','commercial',301,'/supplier_proposal/card.php?action=create&leftmenu=supplier_proposals','','SupplierProposalNew','supplier_proposal',NULL,NULL,'$user->rights->supplier_proposal->creer','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145088,'all',1,'supplier_proposal','left','commercial',-1,'supplier_proposalsubmenu','commercial',302,'/supplier_proposal/list.php?leftmenu=supplier_proposals','','List','supplier_proposal',NULL,NULL,'$user->rights->supplier_proposal->lire','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145089,'all',1,'supplier_proposal','left','commercial',-1,'supplier_proposalsubmenu','commercial',303,'/comm/propal/stats/index.php?leftmenu=supplier_proposals&mode=supplier','','Statistics','supplier_proposal',NULL,NULL,'$user->rights->supplier_proposal->lire','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145090,'all',1,'resource','left','tools',-1,NULL,'tools',100,'/resource/list.php','','MenuResourceIndex','resource',NULL,'resource','$user->rights->resource->read','1',0,'2016-07-30 11:13:32'),(145091,'all',1,'resource','left','tools',-1,'resource','tools',101,'/resource/add.php','','MenuResourceAdd','resource',NULL,NULL,'$user->rights->resource->read','1',0,'2016-07-30 11:13:32'),(145092,'all',1,'resource','left','tools',-1,'resource','tools',102,'/resource/list.php','','List','resource',NULL,NULL,'$user->rights->resource->read','1',0,'2016-07-30 11:13:32'),(145127,'all',1,'printing','left','home',-1,'admintools','home',300,'/printing/index.php?mainmenu=home&leftmenu=admintools','','MenuDirectPrinting','printing',NULL,NULL,'$user->rights->printing->read','$conf->printing->enabled && $leftmenu==\'admintools\'',0,'2017-01-29 15:12:44'),(161088,'auguria',1,'','top','home',0,NULL,NULL,10,'/index.php?mainmenu=home&leftmenu=','','Home','',-1,'','','1',2,'2017-08-30 15:14:30'),(161089,'auguria',1,'societe|fournisseur','top','companies',0,NULL,NULL,20,'/societe/index.php?mainmenu=companies&leftmenu=','','ThirdParties','companies',-1,'','$user->rights->societe->lire || $user->rights->societe->contact->lire','( ! empty($conf->societe->enabled) && (empty($conf->global->SOCIETE_DISABLE_PROSPECTS) || empty($conf->global->SOCIETE_DISABLE_CUSTOMERS))) || ! empty($conf->fournisseur->enabled)',2,'2017-08-30 15:14:30'),(161090,'auguria',1,'product|service','top','products',0,NULL,NULL,30,'/product/index.php?mainmenu=products&leftmenu=','','Products/Services','products',-1,'','$user->rights->produit->lire||$user->rights->service->lire','$conf->product->enabled || $conf->service->enabled',0,'2017-08-30 15:14:30'),(161092,'auguria',1,'propal|commande|fournisseur|contrat|ficheinter','top','commercial',0,NULL,NULL,40,'/comm/index.php?mainmenu=commercial&leftmenu=','','Commercial','commercial',-1,'','$user->rights->societe->lire || $user->rights->societe->contact->lire','$conf->propal->enabled || $conf->commande->enabled || $conf->supplier_order->enabled || $conf->contrat->enabled || $conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(161093,'auguria',1,'comptabilite|accounting|facture|don|tax|salaries|loan','top','accountancy',0,NULL,NULL,50,'/compta/index.php?mainmenu=accountancy&leftmenu=','','MenuFinancial','compta',-1,'','$user->rights->compta->resultat->lire || $user->rights->accounting->plancompte->lire || $user->rights->facture->lire|| $user->rights->don->lire || $user->rights->tax->charges->lire || $user->rights->salaries->read || $user->rights->loan->read','$conf->comptabilite->enabled || $conf->accounting->enabled || $conf->facture->enabled || $conf->don->enabled || $conf->tax->enabled || $conf->salaries->enabled || $conf->supplier_invoice->enabled || $conf->loan->enabled',2,'2017-08-30 15:14:30'),(161094,'auguria',1,'projet','top','project',0,NULL,NULL,70,'/projet/index.php?mainmenu=project&leftmenu=','','Projects','projects',-1,'','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(161095,'auguria',1,'mailing|export|import|opensurvey|resource','top','tools',0,NULL,NULL,90,'/core/tools.php?mainmenu=tools&leftmenu=','','Tools','other',-1,'','$user->rights->mailing->lire || $user->rights->export->lire || $user->rights->import->run || $user->rights->opensurvey->read || $user->rights->resource->read','$conf->mailing->enabled || $conf->export->enabled || $conf->import->enabled || $conf->opensurvey->enabled || $conf->resource->enabled',2,'2017-08-30 15:14:30'),(161101,'auguria',1,'banque|prelevement','top','bank',0,NULL,NULL,60,'/compta/bank/index.php?mainmenu=bank&leftmenu=bank','','MenuBankCash','banks',-1,'','$user->rights->banque->lire || $user->rights->prelevement->bons->lire','$conf->banque->enabled || $conf->prelevement->enabled',0,'2017-08-30 15:14:30'),(161102,'auguria',1,'hrm|holiday|deplacement|expensereport','top','hrm',0,NULL,NULL,80,'/hrm/index.php?mainmenu=hrm&leftmenu=','','HRM','holiday',-1,'','$user->rights->hrm->employee->read || $user->rights->holiday->write || $user->rights->deplacement->lire || $user->rights->expensereport->lire','$conf->hrm->enabled || $conf->holiday->enabled || $conf->deplacement->enabled || $conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(161177,'auguria',1,'','left','home',161088,NULL,NULL,0,'/index.php','','MyDashboard','',0,'','','1',2,'2017-08-30 15:14:30'),(161187,'auguria',1,'','left','home',161088,NULL,NULL,1,'/admin/index.php?leftmenu=setup','','Setup','admin',0,'setup','','$user->admin',2,'2017-08-30 15:14:30'),(161188,'auguria',1,'','left','home',161187,NULL,NULL,1,'/admin/company.php?leftmenu=setup','','MenuCompanySetup','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161189,'auguria',1,'','left','home',161187,NULL,NULL,4,'/admin/ihm.php?leftmenu=setup','','GUISetup','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161190,'auguria',1,'','left','home',161187,NULL,NULL,2,'/admin/modules.php?leftmenu=setup','','Modules','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161191,'auguria',1,'','left','home',161187,NULL,NULL,6,'/admin/boxes.php?leftmenu=setup','','Boxes','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161192,'auguria',1,'','left','home',161187,NULL,NULL,3,'/admin/menus.php?leftmenu=setup','','Menus','admin',1,'','','$leftmenu==\'setup\'',2,'2017-09-06 08:29:47'),(161193,'auguria',1,'','left','home',161187,NULL,NULL,7,'/admin/delais.php?leftmenu=setup','','Alerts','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161194,'auguria',1,'','left','home',161187,NULL,NULL,10,'/admin/pdf.php?leftmenu=setup','','PDF','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161195,'auguria',1,'','left','home',161187,NULL,NULL,8,'/admin/security_other.php?leftmenu=setup','','Security','admin',1,'','','$leftmenu==\'setup\'',2,'2017-09-06 08:29:36'),(161196,'auguria',1,'','left','home',161187,NULL,NULL,11,'/admin/mails.php?leftmenu=setup','','Emails','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161197,'auguria',1,'','left','home',161187,NULL,NULL,9,'/admin/limits.php?leftmenu=setup','','MenuLimits','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161198,'auguria',1,'','left','home',161187,NULL,NULL,13,'/admin/dict.php?leftmenu=setup','','Dictionary','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161199,'auguria',1,'','left','home',161187,NULL,NULL,14,'/admin/const.php?leftmenu=setup','','OtherSetup','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161200,'auguria',1,'','left','home',161187,NULL,NULL,12,'/admin/sms.php?leftmenu=setup','','SMS','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161201,'auguria',1,'','left','home',161187,NULL,NULL,4,'/admin/translation.php?leftmenu=setup','','Translation','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161288,'auguria',1,'','left','home',161387,NULL,NULL,0,'/admin/system/dolibarr.php?leftmenu=admintools','','InfoDolibarr','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161289,'auguria',1,'','left','home',161288,NULL,NULL,2,'/admin/system/modules.php?leftmenu=admintools','','Modules','admin',2,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161290,'auguria',1,'','left','home',161288,NULL,NULL,3,'/admin/triggers.php?leftmenu=admintools','','Triggers','admin',2,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161291,'auguria',1,'','left','home',161288,NULL,NULL,4,'/admin/system/filecheck.php?leftmenu=admintools','','FileCheck','admin',2,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161292,'auguria',1,'','left','home',161387,NULL,NULL,1,'/admin/system/browser.php?leftmenu=admintools','','InfoBrowser','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161293,'auguria',1,'','left','home',161387,NULL,NULL,2,'/admin/system/os.php?leftmenu=admintools','','InfoOS','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161294,'auguria',1,'','left','home',161387,NULL,NULL,3,'/admin/system/web.php?leftmenu=admintools','','InfoWebServer','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161295,'auguria',1,'','left','home',161387,NULL,NULL,4,'/admin/system/phpinfo.php?leftmenu=admintools','','InfoPHP','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161297,'auguria',1,'','left','home',161387,NULL,NULL,5,'/admin/system/database.php?leftmenu=admintools','','InfoDatabase','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161387,'auguria',1,'','left','home',161088,NULL,NULL,2,'/admin/tools/index.php?leftmenu=admintools','','AdminTools','admin',0,'admintools','','$user->admin',2,'2017-08-30 15:14:30'),(161388,'auguria',1,'','left','home',161387,NULL,NULL,6,'/admin/tools/dolibarr_export.php?leftmenu=admintools','','Backup','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161389,'auguria',1,'','left','home',161387,NULL,NULL,7,'/admin/tools/dolibarr_import.php?leftmenu=admintools','','Restore','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161392,'auguria',1,'','left','home',161387,NULL,NULL,8,'/admin/tools/update.php?leftmenu=admintools','','MenuUpgrade','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161393,'auguria',1,'','left','home',161387,NULL,NULL,9,'/admin/tools/eaccelerator.php?leftmenu=admintools','','EAccelerator','admin',1,'','','$leftmenu==\"admintools\" && function_exists(\"eaccelerator_info\")',2,'2017-08-30 15:14:30'),(161394,'auguria',1,'','left','home',161387,NULL,NULL,10,'/admin/tools/listevents.php?leftmenu=admintools','','Audit','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161395,'auguria',1,'','left','home',161387,NULL,NULL,11,'/admin/tools/listsessions.php?leftmenu=admintools','','Sessions','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161396,'auguria',1,'','left','home',161387,NULL,NULL,12,'/admin/tools/purge.php?leftmenu=admintools','','Purge','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161398,'auguria',1,'','left','home',161387,NULL,NULL,14,'/admin/system/about.php?leftmenu=admintools','','ExternalResources','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161407,'auguria',1,'','left','home',161387,NULL,NULL,15,'/product/admin/product_tools.php?mainmenu=home&leftmenu=admintools','','ProductVatMassChange','products',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161487,'auguria',1,'','left','home',161088,NULL,NULL,4,'/user/home.php?leftmenu=users','','MenuUsersAndGroups','users',0,'users','','1',2,'2017-08-30 15:14:30'),(161488,'auguria',1,'','left','home',161487,NULL,NULL,0,'/user/index.php?leftmenu=users','','Users','users',1,'','$user->rights->user->user->lire || $user->admin','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161489,'auguria',1,'','left','home',161488,NULL,NULL,0,'/user/card.php?leftmenu=users&action=create','','NewUser','users',2,'','($user->rights->user->user->creer || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161490,'auguria',1,'','left','home',161487,NULL,NULL,1,'/user/group/index.php?leftmenu=users','','Groups','users',1,'','(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->read:$user->rights->user->user->lire) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161491,'auguria',1,'','left','home',161490,NULL,NULL,0,'/user/group/card.php?leftmenu=users&action=create','','NewGroup','users',2,'','(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->write:$user->rights->user->user->creer) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161587,'auguria',1,'','left','companies',161089,NULL,NULL,0,'/societe/index.php?leftmenu=thirdparties','','ThirdParty','companies',0,'thirdparties','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161588,'auguria',1,'','left','companies',161587,NULL,NULL,0,'/societe/card.php?action=create','','MenuNewThirdParty','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161589,'auguria',1,'','left','companies',161587,NULL,NULL,0,'/societe/list.php?action=create','','List','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161590,'auguria',1,'','left','companies',161587,NULL,NULL,5,'/societe/list.php?type=f&leftmenu=suppliers','','ListSuppliersShort','suppliers',1,'','$user->rights->societe->lire && $user->rights->fournisseur->lire','$conf->societe->enabled && $conf->fournisseur->enabled',2,'2017-08-30 15:14:30'),(161591,'auguria',1,'','left','companies',161590,NULL,NULL,0,'/societe/card.php?leftmenu=supplier&action=create&type=f','','NewSupplier','suppliers',2,'','$user->rights->societe->creer','$conf->societe->enabled && $conf->fournisseur->enabled',2,'2017-08-30 15:14:30'),(161593,'auguria',1,'','left','companies',161587,NULL,NULL,3,'/societe/list.php?type=p&leftmenu=prospects','','ListProspectsShort','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161594,'auguria',1,'','left','companies',161593,NULL,NULL,0,'/societe/card.php?leftmenu=prospects&action=create&type=p','','MenuNewProspect','companies',2,'','$user->rights->societe->creer','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161596,'auguria',1,'','left','companies',161587,NULL,NULL,4,'/societe/list.php?type=c&leftmenu=customers','','ListCustomersShort','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161597,'auguria',1,'','left','companies',161596,NULL,NULL,0,'/societe/card.php?leftmenu=customers&action=create&type=c','','MenuNewCustomer','companies',2,'','$user->rights->societe->creer','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161687,'auguria',1,'','left','companies',161089,NULL,NULL,1,'/contact/list.php?leftmenu=contacts','','ContactsAddresses','companies',0,'contacts','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161688,'auguria',1,'','left','companies',161687,NULL,NULL,0,'/contact/card.php?leftmenu=contacts&action=create','','NewContactAddress','companies',1,'','$user->rights->societe->creer','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161689,'auguria',1,'','left','companies',161687,NULL,NULL,1,'/contact/list.php?leftmenu=contacts','','List','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161691,'auguria',1,'','left','companies',161689,NULL,NULL,1,'/contact/list.php?leftmenu=contacts&type=p','','ThirdPartyProspects','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161692,'auguria',1,'','left','companies',161689,NULL,NULL,2,'/contact/list.php?leftmenu=contacts&type=c','','ThirdPartyCustomers','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161693,'auguria',1,'','left','companies',161689,NULL,NULL,3,'/contact/list.php?leftmenu=contacts&type=f','','ThirdPartySuppliers','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled && $conf->fournisseur->enabled',2,'2017-08-30 15:14:30'),(161694,'auguria',1,'','left','companies',161689,NULL,NULL,4,'/contact/list.php?leftmenu=contacts&type=o','','Others','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161737,'auguria',1,'','left','companies',161089,NULL,NULL,3,'/categories/index.php?leftmenu=cat&type=1','','SuppliersCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161738,'auguria',1,'','left','companies',161737,NULL,NULL,0,'/categories/card.php?action=create&type=1','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161747,'auguria',1,'','left','companies',161089,NULL,NULL,4,'/categories/index.php?leftmenu=cat&type=2','','CustomersProspectsCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->fournisseur->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161748,'auguria',1,'','left','companies',161747,NULL,NULL,0,'/categories/card.php?action=create&type=2','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->fournisseur->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161757,'auguria',1,'','left','companies',161089,NULL,NULL,3,'/categories/index.php?leftmenu=cat&type=4','','ContactCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161758,'auguria',1,'','left','companies',161757,NULL,NULL,0,'/categories/card.php?action=create&type=4','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(162187,'auguria',1,'','left','commercial',161092,NULL,NULL,4,'/comm/propal/index.php?leftmenu=propals','','Prop','propal',0,'propals','$user->rights->propale->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162188,'auguria',1,'','left','commercial',162187,NULL,NULL,0,'/comm/propal/card.php?action=create&leftmenu=propals','','NewPropal','propal',1,'','$user->rights->propale->creer','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162189,'auguria',1,'','left','commercial',162187,NULL,NULL,1,'/comm/propal/list.php?leftmenu=propals','','List','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162190,'auguria',1,'','left','commercial',162189,NULL,NULL,2,'/comm/propal/list.php?leftmenu=propals&viewstatut=0','','PropalsDraft','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162191,'auguria',1,'','left','commercial',162189,NULL,NULL,3,'/comm/propal/list.php?leftmenu=propals&viewstatut=1','','PropalsOpened','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162192,'auguria',1,'','left','commercial',162189,NULL,NULL,4,'/comm/propal/list.php?leftmenu=propals&viewstatut=2','','PropalStatusSigned','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162193,'auguria',1,'','left','commercial',162189,NULL,NULL,5,'/comm/propal/list.php?leftmenu=propals&viewstatut=3','','PropalStatusNotSigned','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162194,'auguria',1,'','left','commercial',162189,NULL,NULL,6,'/comm/propal/list.php?leftmenu=propals&viewstatut=4','','PropalStatusBilled','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162197,'auguria',1,'','left','commercial',162187,NULL,NULL,4,'/comm/propal/stats/index.php?leftmenu=propals','','Statistics','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162287,'auguria',1,'','left','commercial',161092,NULL,NULL,5,'/commande/index.php?leftmenu=orders','','CustomersOrders','orders',0,'orders','$user->rights->commande->lire','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162288,'auguria',1,'','left','commercial',162287,NULL,NULL,0,'/commande/card.php?action=create&leftmenu=orders','','NewOrder','orders',1,'','$user->rights->commande->creer','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162289,'auguria',1,'','left','commercial',162287,NULL,NULL,1,'/commande/list.php?leftmenu=orders','','List','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162290,'auguria',1,'','left','commercial',162289,NULL,NULL,2,'/commande/list.php?leftmenu=orders&viewstatut=0','','StatusOrderDraftShort','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162291,'auguria',1,'','left','commercial',162289,NULL,NULL,3,'/commande/list.php?leftmenu=orders&viewstatut=1','','StatusOrderValidated','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162292,'auguria',1,'','left','commercial',162289,NULL,NULL,4,'/commande/list.php?leftmenu=orders&viewstatut=2','','StatusOrderOnProcessShort','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162293,'auguria',1,'','left','commercial',162289,NULL,NULL,5,'/commande/list.php?leftmenu=orders&viewstatut=3','','StatusOrderToBill','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162294,'auguria',1,'','left','commercial',162289,NULL,NULL,6,'/commande/list.php?leftmenu=orders&viewstatut=4','','StatusOrderProcessed','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162295,'auguria',1,'','left','commercial',162289,NULL,NULL,7,'/commande/list.php?leftmenu=orders&viewstatut=-1','','StatusOrderCanceledShort','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162296,'auguria',1,'','left','commercial',162287,NULL,NULL,4,'/commande/stats/index.php?leftmenu=orders','','Statistics','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162387,'auguria',1,'','left','commercial',161090,NULL,NULL,6,'/expedition/index.php?leftmenu=sendings','','Shipments','sendings',0,'sendings','$user->rights->expedition->lire','$conf->expedition->enabled',2,'2017-08-30 15:14:30'),(162388,'auguria',1,'','left','commercial',162387,NULL,NULL,0,'/expedition/card.php?action=create2&leftmenu=sendings','','NewSending','sendings',1,'','$user->rights->expedition->creer','$conf->expedition->enabled && $leftmenu==\"sendings\"',2,'2017-08-30 15:14:30'),(162389,'auguria',1,'','left','commercial',162387,NULL,NULL,1,'/expedition/list.php?leftmenu=sendings','','List','sendings',1,'','$user->rights->expedition->lire','$conf->expedition->enabled && $leftmenu==\"sendings\"',2,'2017-08-30 15:14:30'),(162390,'auguria',1,'','left','commercial',162387,NULL,NULL,2,'/expedition/stats/index.php?leftmenu=sendings','','Statistics','sendings',1,'','$user->rights->expedition->lire','$conf->expedition->enabled && $leftmenu==\"sendings\"',2,'2017-08-30 15:14:30'),(162487,'auguria',1,'','left','commercial',161092,NULL,NULL,7,'/contrat/index.php?leftmenu=contracts','','Contracts','contracts',0,'contracts','$user->rights->contrat->lire','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162488,'auguria',1,'','left','commercial',162487,NULL,NULL,0,'/contrat/card.php?&action=create&leftmenu=contracts','','NewContract','contracts',1,'','$user->rights->contrat->creer','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162489,'auguria',1,'','left','commercial',162487,NULL,NULL,1,'/contrat/list.php?leftmenu=contracts','','List','contracts',1,'','$user->rights->contrat->lire','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162490,'auguria',1,'','left','commercial',162487,NULL,NULL,2,'/contrat/services.php?leftmenu=contracts','','MenuServices','contracts',1,'','$user->rights->contrat->lire','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162491,'auguria',1,'','left','commercial',162490,NULL,NULL,0,'/contrat/services.php?leftmenu=contracts&mode=0','','MenuInactiveServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162492,'auguria',1,'','left','commercial',162490,NULL,NULL,1,'/contrat/services.php?leftmenu=contracts&mode=4','','MenuRunningServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162493,'auguria',1,'','left','commercial',162490,NULL,NULL,2,'/contrat/services.php?leftmenu=contracts&mode=4&filter=expired','','MenuExpiredServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162494,'auguria',1,'','left','commercial',162490,NULL,NULL,3,'/contrat/services.php?leftmenu=contracts&mode=5','','MenuClosedServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162587,'auguria',1,'','left','commercial',161092,NULL,NULL,8,'/fichinter/list.php?leftmenu=ficheinter','','Interventions','interventions',0,'ficheinter','$user->rights->ficheinter->lire','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162588,'auguria',1,'','left','commercial',162587,NULL,NULL,0,'/fichinter/card.php?action=create&leftmenu=ficheinter','','NewIntervention','interventions',1,'','$user->rights->ficheinter->creer','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162589,'auguria',1,'','left','commercial',162587,NULL,NULL,1,'/fichinter/list.php?leftmenu=ficheinter','','List','interventions',1,'','$user->rights->ficheinter->lire','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162590,'auguria',1,'','left','commercial',162587,NULL,NULL,2,'/fichinter/stats/index.php?leftmenu=ficheinter','','Statistics','interventions',1,'','$user->rights->ficheinter->lire','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162687,'auguria',1,'','left','accountancy',161093,NULL,NULL,3,'/fourn/facture/list.php?leftmenu=suppliers_bills','','BillsSuppliers','bills',0,'supplier_bills','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162688,'auguria',1,'','left','accountancy',162687,NULL,NULL,0,'/fourn/facture/card.php?action=create&leftmenu=suppliers_bills','','NewBill','bills',1,'','$user->rights->fournisseur->facture->creer','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162689,'auguria',1,'','left','accountancy',162687,NULL,NULL,1,'/fourn/facture/list.php?leftmenu=suppliers_bills','','List','bills',1,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162690,'auguria',1,'','left','accountancy',162687,NULL,NULL,2,'/fourn/facture/paiement.php?leftmenu=suppliers_bills','','Payments','bills',1,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162691,'auguria',1,'','left','accountancy',162687,NULL,NULL,8,'/compta/facture/stats/index.php?leftmenu=customers_bills&mode=supplier','','Statistics','bills',1,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162692,'auguria',1,'','left','accountancy',162690,NULL,NULL,1,'/fourn/facture/rapport.php?leftmenu=suppliers_bills','','Reporting','bills',2,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162787,'auguria',1,'','left','accountancy',161093,NULL,NULL,3,'/compta/facture/list.php?leftmenu=customers_bills','','BillsCustomers','bills',0,'customer_bills','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162788,'auguria',1,'','left','accountancy',162787,NULL,NULL,3,'/compta/facture/card.php?action=create&leftmenu=customers_bills','','NewBill','bills',1,'','$user->rights->facture->creer','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162789,'auguria',1,'','left','accountancy',162787,NULL,NULL,5,'/compta/facture/fiche-rec.php?leftmenu=customers_bills','','ListOfTemplates','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162791,'auguria',1,'','left','accountancy',162787,NULL,NULL,6,'/compta/paiement/list.php?leftmenu=customers_bills','','Payments','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162792,'auguria',1,'','left','accountancy',162787,NULL,NULL,4,'/compta/facture/list.php?leftmenu=customers_bills','','List','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162797,'auguria',1,'','left','accountancy',162791,NULL,NULL,1,'/compta/paiement/rapport.php?leftmenu=customers_bills','','Reportings','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162798,'auguria',1,'','left','accountancy',161101,NULL,NULL,9,'/compta/paiement/cheque/index.php?leftmenu=checks&mainmenu=bank','','MenuChequeDeposits','bills',0,'checks','$user->rights->banque->lire','empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))',2,'2017-08-30 15:14:30'),(162799,'auguria',1,'','left','accountancy',162798,NULL,NULL,0,'/compta/paiement/cheque/card.php?leftmenu=checks&action=new','','NewCheckDeposit','compta',1,'','$user->rights->banque->lire','empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))',2,'2017-08-30 15:14:30'),(162800,'auguria',1,'','left','accountancy',162798,NULL,NULL,1,'/compta/paiement/cheque/list.php?leftmenu=checks','','List','bills',1,'','$user->rights->banque->lire','empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))',2,'2017-08-30 15:14:30'),(162801,'auguria',1,'','left','accountancy',162787,NULL,NULL,8,'/compta/facture/stats/index.php?leftmenu=customers_bills','','Statistics','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162807,'auguria',1,'','left','accountancy',162792,NULL,NULL,1,'/compta/facture/list.php?leftmenu=customers_bills&search_status=0','','BillShortStatusDraft','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162808,'auguria',1,'','left','accountancy',162792,NULL,NULL,2,'/compta/facture/list.php?leftmenu=customers_bills&search_status=1','','BillShortStatusNotPaid','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162809,'auguria',1,'','left','accountancy',162792,NULL,NULL,3,'/compta/facture/list.php?leftmenu=customers_bills&search_status=2','','BillShortStatusPaid','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162810,'auguria',1,'','left','accountancy',162792,NULL,NULL,4,'/compta/facture/list.php?leftmenu=customers_bills&search_status=3','','BillShortStatusCanceled','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162987,'auguria',1,'','left','accountancy',161093,NULL,NULL,3,'/commande/list.php?leftmenu=orders&viewstatut=3','','MenuOrdersToBill','orders',0,'orders','$user->rights->commande->lire','$conf->commande->enabled',0,'2017-08-30 15:14:30'),(163087,'auguria',1,'','left','accountancy',161093,NULL,NULL,4,'/don/index.php?leftmenu=donations&mainmenu=accountancy','','Donations','donations',0,'donations','$user->rights->don->lire','$conf->don->enabled',2,'2017-08-30 15:14:30'),(163088,'auguria',1,'','left','accountancy',163087,NULL,NULL,0,'/don/card.php?leftmenu=donations&mainmenu=accountancy&action=create','','NewDonation','donations',1,'','$user->rights->don->creer','$conf->don->enabled && $leftmenu==\"donations\"',2,'2017-08-30 15:14:30'),(163089,'auguria',1,'','left','accountancy',163087,NULL,NULL,1,'/don/list.php?leftmenu=donations&mainmenu=accountancy','','List','donations',1,'','$user->rights->don->lire','$conf->don->enabled && $leftmenu==\"donations\"',2,'2017-08-30 15:14:30'),(163187,'auguria',1,'','left','accountancy',161102,NULL,NULL,5,'/compta/deplacement/index.php?leftmenu=tripsandexpenses','','TripsAndExpenses','trips',0,'tripsandexpenses','$user->rights->deplacement->lire','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163188,'auguria',1,'','left','accountancy',163187,NULL,NULL,1,'/compta/deplacement/card.php?action=create&leftmenu=tripsandexpenses','','New','trips',1,'','$user->rights->deplacement->creer','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163189,'auguria',1,'','left','accountancy',163187,NULL,NULL,2,'/compta/deplacement/list.php?leftmenu=tripsandexpenses','','List','trips',1,'','$user->rights->deplacement->lire','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163190,'auguria',1,'','left','accountancy',163187,NULL,NULL,2,'/compta/deplacement/stats/index.php?leftmenu=tripsandexpenses','','Statistics','trips',1,'','$user->rights->deplacement->lire','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163287,'auguria',1,'','left','accountancy',161093,NULL,NULL,6,'/compta/charges/index.php?leftmenu=tax&mainmenu=accountancy','','MenuSpecialExpenses','compta',0,'tax','(! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) || (! empty($conf->salaries->enabled) && $user->rights->salaries->read)','$conf->tax->enabled || $conf->salaries->enabled',0,'2017-08-30 15:14:30'),(163297,'auguria',1,'','left','accountancy',163287,NULL,NULL,1,'/compta/salaries/index.php?leftmenu=tax_salary&mainmenu=accountancy','','Salaries','salaries',1,'tax_sal','$user->rights->salaries->payment->read','$conf->salaries->enabled',0,'2017-08-30 15:14:30'),(163298,'auguria',1,'','left','accountancy',163297,NULL,NULL,2,'/compta/salaries/card.php?leftmenu=tax_salary&action=create','','NewPayment','companies',2,'','$user->rights->salaries->payment->write','$conf->salaries->enabled && $leftmenu==\"tax_salary\"',0,'2017-08-30 15:14:30'),(163299,'auguria',1,'','left','accountancy',163297,NULL,NULL,3,'/compta/salaries/index.php?leftmenu=tax_salary','','Payments','companies',2,'','$user->rights->salaries->payment->read','$conf->salaries->enabled && $leftmenu==\"tax_salary\"',0,'2017-08-30 15:14:30'),(163307,'auguria',1,'','left','accountancy',163287,NULL,NULL,1,'/loan/index.php?leftmenu=tax_loan&mainmenu=accountancy','','Loans','loan',1,'tax_loan','$user->rights->loan->read','$conf->loan->enabled',0,'2017-08-30 15:14:30'),(163308,'auguria',1,'','left','accountancy',163307,NULL,NULL,2,'/loan/card.php?leftmenu=tax_loan&action=create','','NewLoan','loan',2,'','$user->rights->loan->write','$conf->loan->enabled && $leftmenu==\"tax_loan\"',0,'2017-08-30 15:14:30'),(163310,'auguria',1,'','left','accountancy',163307,NULL,NULL,4,'/loan/calc.php?leftmenu=tax_loan','','Calculator','companies',2,'','$user->rights->loan->calc','$conf->loan->enabled && $leftmenu==\"tax_loan\" && ! empty($conf->global->LOAN_SHOW_CALCULATOR)',0,'2017-08-30 15:14:30'),(163337,'auguria',1,'','left','accountancy',163287,NULL,NULL,1,'/compta/sociales/index.php?leftmenu=tax_social','','SocialContributions','',1,'tax_social','$user->rights->tax->charges->lire','$conf->tax->enabled',0,'2017-08-30 15:14:30'),(163338,'auguria',1,'','left','accountancy',163337,NULL,NULL,2,'/compta/sociales/card.php?leftmenu=tax_social&action=create','','MenuNewSocialContribution','',2,'','$user->rights->tax->charges->creer','$conf->tax->enabled && $leftmenu==\"tax_social\"',0,'2017-08-30 15:14:30'),(163339,'auguria',1,'','left','accountancy',163337,NULL,NULL,3,'/compta/sociales/payments.php?leftmenu=tax_social&mainmenu=accountancy&mode=sconly','','Payments','',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && $leftmenu==\"tax_social\"',0,'2017-08-30 15:14:30'),(163387,'auguria',1,'','left','accountancy',163287,NULL,NULL,7,'/compta/tva/index.php?leftmenu=tax_vat&mainmenu=accountancy','','VAT','companies',1,'tax_vat','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS)',0,'2017-08-30 15:14:30'),(163388,'auguria',1,'','left','accountancy',163387,NULL,NULL,0,'/compta/tva/card.php?leftmenu=tax_vat&action=create','','New','companies',2,'','$user->rights->tax->charges->creer','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163389,'auguria',1,'','left','accountancy',163387,NULL,NULL,1,'/compta/tva/reglement.php?leftmenu=tax_vat','','List','companies',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163390,'auguria',1,'','left','accountancy',163387,NULL,NULL,2,'/compta/tva/clients.php?leftmenu=tax_vat','','ReportByCustomers','companies',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163391,'auguria',1,'','left','accountancy',163387,NULL,NULL,3,'/compta/tva/quadri_detail.php?leftmenu=tax_vat','','ReportByQuarter','companies',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163487,'auguria',1,'','left','accountancy',161093,NULL,NULL,7,'/accountancy/index.php?leftmenu=accountancy','','MenuAccountancy','accountancy',0,'accounting','! empty($conf->accounting->enabled) || $user->rights->accounting->bind->write || $user->rights->accounting->bind->write || $user->rights->compta->resultat->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163488,'auguria',1,'','left','accountancy',163487,NULL,NULL,2,'/accountancy/customer/index.php?leftmenu=dispatch_customer','','CustomersVentilation','accountancy',1,'dispatch_customer','$user->rights->accounting->bind->write','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163489,'auguria',1,'','left','accountancy',163488,NULL,NULL,3,'/accountancy/customer/list.php','','ToDispatch','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $leftmenu==\"dispatch_customer\"',0,'2017-08-30 15:14:30'),(163490,'auguria',1,'','left','accountancy',163488,NULL,NULL,4,'/accountancy/customer/lines.php','','Dispatched','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $leftmenu==\"dispatch_customer\"',0,'2017-08-30 15:14:30'),(163497,'auguria',1,'','left','accountancy',163487,NULL,NULL,5,'/accountancy/supplier/index.php?leftmenu=dispatch_supplier','','SuppliersVentilation','accountancy',1,'ventil_supplier','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->fournisseur->enabled',0,'2017-08-30 15:14:30'),(163498,'auguria',1,'','left','accountancy',163497,NULL,NULL,6,'/accountancy/supplier/list.php','','ToDispatch','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->fournisseur->enabled && $leftmenu==\"dispatch_supplier\"',0,'2017-08-30 15:14:30'),(163499,'auguria',1,'','left','accountancy',163497,NULL,NULL,7,'/accountancy/supplier/lines.php','','Dispatched','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->fournisseur->enabled && $leftmenu==\"dispatch_supplier\"',0,'2017-08-30 15:14:30'),(163507,'auguria',1,'','left','accountancy',163487,NULL,NULL,5,'/accountancy/expensereport/index.php?leftmenu=dispatch_expensereport','','ExpenseReportsVentilation','accountancy',1,'ventil_expensereport','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(163508,'auguria',1,'','left','accountancy',163507,NULL,NULL,6,'/accountancy/expensereport/list.php','','ToDispatch','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->expensereport->enabled && $leftmenu==\"dispatch_expensereport\"',0,'2017-08-30 15:14:30'),(163509,'auguria',1,'','left','accountancy',163507,NULL,NULL,7,'/accountancy/expensereport/lines.php','','Dispatched','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->expensereport->enabled && $leftmenu==\"dispatch_expensereport\"',0,'2017-08-30 15:14:30'),(163517,'auguria',1,'','left','accountancy',163487,NULL,NULL,15,'/accountancy/bookkeeping/list.php','','Bookkeeping','accountancy',1,'bookkeeping','$user->rights->accounting->mouvements->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163522,'auguria',1,'','left','accountancy',163487,NULL,NULL,16,'/accountancy/bookkeeping/balance.php','','AccountBalance','accountancy',1,'balance','$user->rights->accounting->mouvements->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163527,'auguria',1,'','left','accountancy',163487,NULL,NULL,17,'/accountancy/report/result.php?mainmenu=accountancy&leftmenu=accountancy','','Reportings','main',1,'report','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163528,'auguria',1,'','left','accountancy',163527,NULL,NULL,19,'/compta/resultat/index.php?mainmenu=accountancy&leftmenu=accountancy','','ReportInOut','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163529,'auguria',1,'','left','accountancy',163528,NULL,NULL,18,'/accountancy/report/result.php?mainmenu=accountancy&leftmenu=accountancy','','ByAccounts','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163530,'auguria',1,'','left','accountancy',163528,NULL,NULL,20,'/compta/resultat/clientfourn.php?mainmenu=accountancy&leftmenu=accountancy','','ByCompanies','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163531,'auguria',1,'','left','accountancy',163527,NULL,NULL,21,'/compta/stats/index.php?mainmenu=accountancy&leftmenu=accountancy','','ReportTurnover','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163532,'auguria',1,'','left','accountancy',163531,NULL,NULL,22,'/compta/stats/casoc.php?mainmenu=accountancy&leftmenu=accountancy','','ByCompanies','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163533,'auguria',1,'','left','accountancy',163531,NULL,NULL,23,'/compta/stats/cabyuser.php?mainmenu=accountancy&leftmenu=accountancy','','ByUsers','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163534,'auguria',1,'','left','accountancy',163531,NULL,NULL,24,'/compta/stats/cabyprodserv.php?mainmenu=accountancy&leftmenu=accountancy','','ByProductsAndServices','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163537,'auguria',1,'','left','accountancy',163538,NULL,NULL,80,'/accountancy/admin/fiscalyear.php?mainmenu=accountancy&leftmenu=accountancy_admin','','FiscalPeriod','admin',1,'accountancy_admin_period','','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\" && $conf->global->MAIN_FEATURES_LEVEL > 0',2,'2017-08-30 15:14:30'),(163538,'auguria',1,'','left','accountancy',163487,NULL,NULL,1,'/accountancy/index.php?mainmenu=accountancy&leftmenu=accountancy_admin','','Setup','accountancy',1,'accountancy_admin','$user->rights->accounting->chartofaccount','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163541,'auguria',1,'','left','accountancy',163538,NULL,NULL,10,'/accountancy/admin/journals_list.php?id=35&mainmenu=accountancy&leftmenu=accountancy_admin','','AccountingJournals','accountancy',2,'accountancy_admin_journal','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163542,'auguria',1,'','left','accountancy',163538,NULL,NULL,20,'/accountancy/admin/account.php?mainmenu=accountancy&leftmenu=accountancy_admin','','Pcg_version','accountancy',2,'accountancy_admin_chartmodel','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163543,'auguria',1,'','left','accountancy',163538,NULL,NULL,30,'/accountancy/admin/account.php?mainmenu=accountancy&leftmenu=accountancy_admin','','Chartofaccounts','accountancy',2,'accountancy_admin_chart','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163544,'auguria',1,'','left','accountancy',163538,NULL,NULL,40,'/accountancy/admin/categories_list.php?id=32&mainmenu=accountancy&leftmenu=accountancy_admin','','AccountingCategory','accountancy',2,'accountancy_admin_chart_group','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163545,'auguria',1,'','left','accountancy',163538,NULL,NULL,50,'/accountancy/admin/defaultaccounts.php?mainmenu=accountancy&leftmenu=accountancy_admin','','MenuDefaultAccounts','accountancy',2,'accountancy_admin_default','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163546,'auguria',1,'','left','accountancy',163538,NULL,NULL,60,'/admin/dict.php?id=10&from=accountancy&search_country_id=__MYCOUNTRYID__&mainmenu=accountancy&leftmenu=accountancy_admin','','MenuVatAccounts','accountancy',2,'accountancy_admin_vat','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163547,'auguria',1,'','left','accountancy',163538,NULL,NULL,70,'/admin/dict.php?id=7&from=accountancy&search_country_id=__MYCOUNTRYID__&mainmenu=accountancy&leftmenu=accountancy_admin','','MenuTaxAccounts','accountancy',2,'accountancy_admin_tax','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163548,'auguria',1,'','left','accountancy',163538,NULL,NULL,80,'/admin/dict.php?id=17&from=accountancy&mainmenu=accountancy&leftmenu=accountancy_admin','','MenuExpenseReportAccounts','accountancy',2,'accountancy_admin_expensereport','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $conf->expensereport->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163549,'auguria',1,'','left','accountancy',163538,NULL,NULL,90,'/accountancy/admin/productaccount.php?mainmenu=accountancy&leftmenu=accountancy_admin','','MenuProductsAccounts','accountancy',2,'accountancy_admin_product','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163587,'auguria',1,'','left','accountancy',161101,NULL,NULL,9,'/compta/prelevement/index.php?leftmenu=withdraw&mainmenu=bank','','StandingOrders','withdrawals',0,'withdraw','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled',2,'2017-08-30 15:14:30'),(163589,'auguria',1,'','left','accountancy',163587,NULL,NULL,0,'/compta/prelevement/create.php?leftmenu=withdraw','','NewStandingOrder','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163590,'auguria',1,'','left','accountancy',163587,NULL,NULL,2,'/compta/prelevement/bons.php?leftmenu=withdraw','','WithdrawalsReceipts','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163591,'auguria',1,'','left','accountancy',163587,NULL,NULL,3,'/compta/prelevement/list.php?leftmenu=withdraw','','WithdrawalsLines','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163593,'auguria',1,'','left','accountancy',163587,NULL,NULL,5,'/compta/prelevement/rejets.php?leftmenu=withdraw','','Rejects','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163594,'auguria',1,'','left','accountancy',163587,NULL,NULL,6,'/compta/prelevement/stats.php?leftmenu=withdraw','','Statistics','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163687,'auguria',1,'','left','accountancy',161101,NULL,NULL,1,'/compta/bank/index.php?leftmenu=bank&mainmenu=bank','','MenuBankCash','banks',0,'bank','$user->rights->banque->lire','$conf->banque->enabled',0,'2017-08-30 15:14:30'),(163688,'auguria',1,'','left','accountancy',163687,NULL,NULL,0,'/compta/bank/card.php?action=create&leftmenu=bank','','MenuNewFinancialAccount','banks',1,'','$user->rights->banque->configurer','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163690,'auguria',1,'','left','accountancy',163687,NULL,NULL,2,'/compta/bank/bankentries.php?leftmenu=bank','','ListTransactions','banks',1,'','$user->rights->banque->lire','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163691,'auguria',1,'','left','accountancy',163687,NULL,NULL,3,'/compta/bank/budget.php?leftmenu=bank','','ListTransactionsByCategory','banks',1,'','$user->rights->banque->lire','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163693,'auguria',1,'','left','accountancy',163687,NULL,NULL,5,'/compta/bank/transfer.php?leftmenu=bank','','BankTransfers','banks',1,'','$user->rights->banque->transfer','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163737,'auguria',1,'','left','accountancy',161101,NULL,NULL,4,'/categories/index.php?leftmenu=bank&type=5','','Categories','categories',0,'cat','$user->rights->categorie->lire','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(163738,'auguria',1,'','left','accountancy',163737,NULL,NULL,0,'/categories/card.php?leftmenu=bank&action=create&type=5','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(163787,'auguria',1,'','left','accountancy',161093,NULL,NULL,11,'/compta/resultat/index.php?leftmenu=ca&mainmenu=accountancy','','Reportings','main',0,'ca','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled',0,'2017-08-30 15:14:30'),(163792,'auguria',1,'','left','accountancy',163487,NULL,NULL,1,'','','Journalization','main',1,'','$user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163793,'auguria',1,'','left','accountancy',163792,NULL,NULL,4,'/accountancy/journal/sellsjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=1','','SellsJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163794,'auguria',1,'','left','accountancy',163792,NULL,NULL,1,'/accountancy/journal/bankjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=3','','BankJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163795,'auguria',1,'','left','accountancy',163792,NULL,NULL,2,'/accountancy/journal/expensereportsjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=6','','ExpenseReportJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163796,'auguria',1,'','left','accountancy',163792,NULL,NULL,3,'/accountancy/journal/purchasesjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=2','','PurchasesJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163798,'auguria',1,'','left','accountancy',163787,NULL,NULL,0,'/compta/resultat/index.php?leftmenu=ca','','ReportInOut','main',1,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163799,'auguria',1,'','left','accountancy',163788,NULL,NULL,0,'/compta/resultat/clientfourn.php?leftmenu=ca','','ByCompanies','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163800,'auguria',1,'','left','accountancy',163787,NULL,NULL,1,'/compta/stats/index.php?leftmenu=ca','','ReportTurnover','main',1,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163801,'auguria',1,'','left','accountancy',163790,NULL,NULL,0,'/compta/stats/casoc.php?leftmenu=ca','','ByCompanies','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163802,'auguria',1,'','left','accountancy',163790,NULL,NULL,1,'/compta/stats/cabyuser.php?leftmenu=ca','','ByUsers','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163803,'auguria',1,'','left','accountancy',163790,NULL,NULL,1,'/compta/stats/cabyprodserv.php?leftmenu=ca','','ByProductsAndServices','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163887,'auguria',1,'','left','products',161090,NULL,NULL,0,'/product/index.php?leftmenu=product&type=0','','Products','products',0,'product','$user->rights->produit->lire','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163888,'auguria',1,'','left','products',163887,NULL,NULL,0,'/product/card.php?leftmenu=product&action=create&type=0','','NewProduct','products',1,'','$user->rights->produit->creer','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163889,'auguria',1,'','left','products',163887,NULL,NULL,1,'/product/list.php?leftmenu=product&type=0','','List','products',1,'','$user->rights->produit->lire','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163890,'auguria',1,'','left','products',163887,NULL,NULL,4,'/product/reassort.php?type=0','','Stocks','products',1,'','$user->rights->produit->lire && $user->rights->stock->lire','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163891,'auguria',1,'','left','products',163887,NULL,NULL,7,'/product/stats/card.php?id=all&leftmenu=stats&type=0','','Statistics','main',1,'','$user->rights->produit->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(163892,'auguria',1,'','left','products',163887,NULL,NULL,5,'/product/reassortlot.php?type=0','','StocksByLotSerial','products',1,'','$user->rights->produit->lire && $user->rights->stock->lire','$conf->productbatch->enabled',2,'2017-08-30 15:14:30'),(163893,'auguria',1,'','left','products',163887,NULL,NULL,6,'/product/stock/productlot_list.php','','LotSerial','products',1,'','$user->rights->produit->lire && $user->rights->stock->lire','$conf->productbatch->enabled',2,'2017-08-30 15:14:30'),(163987,'auguria',1,'','left','products',161090,NULL,NULL,1,'/product/index.php?leftmenu=service&type=1','','Services','products',0,'service','$user->rights->service->lire','$conf->service->enabled',2,'2017-08-30 15:14:30'),(163988,'auguria',1,'','left','products',163987,NULL,NULL,0,'/product/card.php?leftmenu=service&action=create&type=1','','NewService','products',1,'','$user->rights->service->creer','$conf->service->enabled',2,'2017-08-30 15:14:30'),(163989,'auguria',1,'','left','products',163987,NULL,NULL,1,'/product/list.php?leftmenu=service&type=1','','List','products',1,'','$user->rights->service->lire','$conf->service->enabled',2,'2017-08-30 15:14:30'),(163990,'auguria',1,'','left','products',163987,NULL,NULL,5,'/product/stats/card.php?id=all&leftmenu=stats&type=1','','Statistics','main',1,'','$user->rights->service->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(164187,'auguria',1,'','left','products',161090,NULL,NULL,3,'/product/stock/index.php?leftmenu=stock','','Stock','stocks',0,'stock','$user->rights->stock->lire','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164188,'auguria',1,'','left','products',164187,NULL,NULL,0,'/product/stock/card.php?action=create','','MenuNewWarehouse','stocks',1,'','$user->rights->stock->creer','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164189,'auguria',1,'','left','products',164187,NULL,NULL,1,'/product/stock/list.php','','List','stocks',1,'','$user->rights->stock->lire','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164191,'auguria',1,'','left','products',164187,NULL,NULL,3,'/product/stock/mouvement.php','','Movements','stocks',1,'','$user->rights->stock->mouvement->lire','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164192,'auguria',1,'','left','products',164187,NULL,NULL,4,'/product/stock/replenish.php','','Replenishments','stocks',1,'','$user->rights->stock->mouvement->creer && $user->rights->fournisseur->lire','$conf->stock->enabled && $conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(164193,'auguria',1,'','left','products',164187,NULL,NULL,5,'/product/stock/massstockmove.php','','MassStockTransferShort','stocks',1,'','$user->rights->stock->mouvement->creer','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164287,'auguria',1,'','left','products',161090,NULL,NULL,4,'/categories/index.php?leftmenu=cat&type=0','','Categories','categories',0,'cat','$user->rights->categorie->lire','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164288,'auguria',1,'','left','products',164287,NULL,NULL,0,'/categories/card.php?action=create&type=0','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164487,'auguria',1,'','left','project',161094,NULL,NULL,3,'/projet/activity/perweek.php?leftmenu=projects','','NewTimeSpent','projects',0,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164687,'auguria',1,'','left','project',161094,NULL,NULL,0,'/projet/index.php?leftmenu=projects','','Projects','projects',0,'projects','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164688,'auguria',1,'','left','project',164687,NULL,NULL,1,'/projet/card.php?leftmenu=projects&action=create','','NewProject','projects',1,'','$user->rights->projet->creer','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164689,'auguria',1,'','left','project',164687,NULL,NULL,2,'/projet/list.php?leftmenu=projects','','List','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164690,'auguria',1,'','left','project',164687,NULL,NULL,3,'/projet/stats/index.php?leftmenu=projects','','Statistics','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164787,'auguria',1,'','left','project',161094,NULL,NULL,0,'/projet/activity/index.php?leftmenu=projects','','Activities','projects',0,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164788,'auguria',1,'','left','project',164787,NULL,NULL,1,'/projet/tasks.php?leftmenu=projects&action=create','','NewTask','projects',1,'','$user->rights->projet->creer','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164789,'auguria',1,'','left','project',164787,NULL,NULL,2,'/projet/tasks/list.php?leftmenu=projects','','List','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164791,'auguria',1,'','left','project',164787,NULL,NULL,4,'/projet/tasks/stats/index.php?leftmenu=projects','','Statistics','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164891,'auguria',1,'','left','project',161094,NULL,NULL,4,'/categories/index.php?leftmenu=cat&type=6','','Categories','categories',0,'cat','$user->rights->categorie->lire','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164892,'auguria',1,'','left','project',164891,NULL,NULL,0,'/categories/card.php?action=create&type=6','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164987,'auguria',1,'','left','tools',161095,NULL,NULL,0,'/comm/mailing/index.php?leftmenu=mailing','','EMailings','mails',0,'mailing','$user->rights->mailing->lire','$conf->mailing->enabled',0,'2017-08-30 15:14:30'),(164988,'auguria',1,'','left','tools',164987,NULL,NULL,0,'/comm/mailing/card.php?leftmenu=mailing&action=create','','NewMailing','mails',1,'','$user->rights->mailing->creer','$conf->mailing->enabled',0,'2017-08-30 15:14:30'),(164989,'auguria',1,'','left','tools',164987,NULL,NULL,1,'/comm/mailing/list.php?leftmenu=mailing','','List','mails',1,'','$user->rights->mailing->lire','$conf->mailing->enabled',0,'2017-08-30 15:14:30'),(165187,'auguria',1,'','left','tools',161095,NULL,NULL,2,'/exports/index.php?leftmenu=export','','FormatedExport','exports',0,'export','$user->rights->export->lire','$conf->export->enabled',2,'2017-08-30 15:14:30'),(165188,'auguria',1,'','left','tools',165187,NULL,NULL,0,'/exports/export.php?leftmenu=export','','NewExport','exports',1,'','$user->rights->export->creer','$conf->export->enabled',2,'2017-08-30 15:14:30'),(165217,'auguria',1,'','left','tools',161095,NULL,NULL,2,'/imports/index.php?leftmenu=import','','FormatedImport','exports',0,'import','$user->rights->import->run','$conf->import->enabled',2,'2017-08-30 15:14:30'),(165218,'auguria',1,'','left','tools',165217,NULL,NULL,0,'/imports/import.php?leftmenu=import','','NewImport','exports',1,'','$user->rights->import->run','$conf->import->enabled',2,'2017-08-30 15:14:30'),(165287,'auguria',1,'','left','members',161100,NULL,NULL,0,'/adherents/index.php?leftmenu=members&mainmenu=members','','Members','members',0,'members','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165288,'auguria',1,'','left','members',165287,NULL,NULL,0,'/adherents/card.php?leftmenu=members&action=create','','NewMember','members',1,'','$user->rights->adherent->creer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165289,'auguria',1,'','left','members',165287,NULL,NULL,1,'/adherents/list.php','','List','members',1,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165290,'auguria',1,'','left','members',165289,NULL,NULL,2,'/adherents/list.php?leftmenu=members&statut=-1','','MenuMembersToValidate','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165291,'auguria',1,'','left','members',165289,NULL,NULL,3,'/adherents/list.php?leftmenu=members&statut=1','','MenuMembersValidated','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165292,'auguria',1,'','left','members',165289,NULL,NULL,4,'/adherents/list.php?leftmenu=members&statut=1&filter=outofdate','','MenuMembersNotUpToDate','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165293,'auguria',1,'','left','members',165289,NULL,NULL,5,'/adherents/list.php?leftmenu=members&statut=1&filter=uptodate','','MenuMembersUpToDate','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165294,'auguria',1,'','left','members',165289,NULL,NULL,6,'/adherents/list.php?leftmenu=members&statut=0','','MenuMembersResiliated','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165295,'auguria',1,'','left','members',165287,NULL,NULL,7,'/adherents/stats/geo.php?leftmenu=members&mode=memberbycountry','','MenuMembersStats','members',1,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165387,'auguria',1,'','left','members',161100,NULL,NULL,1,'/adherents/index.php?leftmenu=members&mainmenu=members','','Subscriptions','compta',0,'','$user->rights->adherent->cotisation->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165388,'auguria',1,'','left','members',165387,NULL,NULL,0,'/adherents/list.php?statut=-1&leftmenu=accountancy&mainmenu=members','','NewSubscription','compta',1,'','$user->rights->adherent->cotisation->creer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165389,'auguria',1,'','left','members',165387,NULL,NULL,1,'/adherents/subscription/list.php?leftmenu=members','','List','compta',1,'','$user->rights->adherent->cotisation->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165390,'auguria',1,'','left','members',165387,NULL,NULL,7,'/adherents/stats/index.php?leftmenu=members','','MenuMembersStats','members',1,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165589,'auguria',1,'','left','members',165287,NULL,NULL,9,'/adherents/htpasswd.php?leftmenu=export','','Filehtpasswd','members',1,'','$user->rights->adherent->export','! empty($conf->global->MEMBER_LINK_TO_HTPASSWDFILE) && $conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165590,'auguria',1,'','left','members',165287,NULL,NULL,10,'/adherents/cartes/carte.php?leftmenu=export','','MembersCards','members',1,'','$user->rights->adherent->export','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165687,'auguria',1,'','left','hrm',161102,NULL,NULL,1,'/user/index.php?leftmenu=hrm&mode=employee','','Employees','hrm',0,'hrm','$user->rights->hrm->employee->read','$conf->hrm->enabled',0,'2017-08-30 15:14:30'),(165688,'auguria',1,'','left','hrm',165687,NULL,NULL,1,'/user/card.php?action=create&employee=1','','NewEmployee','hrm',1,'','$user->rights->hrm->employee->write','$conf->hrm->enabled',0,'2017-08-30 15:14:30'),(165689,'auguria',1,'','left','hrm',165687,NULL,NULL,2,'/user/index.php?$leftmenu=hrm&mode=employee&contextpage=employeelist','','List','hrm',1,'','$user->rights->hrm->employee->read','$conf->hrm->enabled',0,'2017-08-30 15:14:30'),(165787,'auguria',1,'','left','members',161100,NULL,NULL,5,'/adherents/type.php?leftmenu=setup&mainmenu=members','','MembersTypes','members',0,'setup','$user->rights->adherent->configurer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165788,'auguria',1,'','left','members',165787,NULL,NULL,0,'/adherents/type.php?leftmenu=setup&mainmenu=members&action=create','','New','members',1,'','$user->rights->adherent->configurer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165789,'auguria',1,'','left','members',165787,NULL,NULL,1,'/adherents/type.php?leftmenu=setup&mainmenu=members','','List','members',1,'','$user->rights->adherent->configurer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(166087,'auguria',1,'','left','hrm',161102,NULL,NULL,1,'/holiday/list.php?&leftmenu=hrm','','CPTitreMenu','holiday',0,'hrm','$user->rights->holiday->read','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166088,'auguria',1,'','left','hrm',166087,NULL,NULL,1,'/holiday/card.php?&action=request','','MenuAddCP','holiday',1,'','$user->rights->holiday->write','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166089,'auguria',1,'','left','hrm',166087,NULL,NULL,1,'/holiday/list.php?&leftmenu=hrm','','List','holiday',1,'','$user->rights->holiday->read','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166090,'auguria',1,'','left','hrm',166089,NULL,NULL,1,'/holiday/list.php?select_statut=2&leftmenu=hrm','','ListToApprove','trips',2,'','$user->rights->holiday->read','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166091,'auguria',1,'','left','hrm',166087,NULL,NULL,2,'/holiday/define_holiday.php?&action=request','','MenuConfCP','holiday',1,'','$user->rights->holiday->define_holiday','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166092,'auguria',1,'','left','hrm',166087,NULL,NULL,3,'/holiday/view_log.php?&action=request','','MenuLogCP','holiday',1,'','$user->rights->holiday->define_holiday','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166187,'auguria',1,'','left','commercial',161092,NULL,NULL,6,'/fourn/commande/index.php?leftmenu=orders_suppliers','','SuppliersOrders','orders',0,'orders_suppliers','$user->rights->fournisseur->commande->lire','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166188,'auguria',1,'','left','commercial',166187,NULL,NULL,0,'/fourn/commande/card.php?action=create&leftmenu=orders_suppliers','','NewOrder','orders',1,'','$user->rights->fournisseur->commande->creer','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166189,'auguria',1,'','left','commercial',166187,NULL,NULL,1,'/fourn/commande/list.php?leftmenu=orders_suppliers&viewstatut=0','','List','orders',1,'','$user->rights->fournisseur->commande->lire','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166195,'auguria',1,'','left','commercial',166187,NULL,NULL,7,'/commande/stats/index.php?leftmenu=orders_suppliers&mode=supplier','','Statistics','orders',1,'','$user->rights->fournisseur->commande->lire','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166287,'auguria',1,'','left','members',161100,NULL,NULL,3,'/categories/index.php?leftmenu=cat&type=3','','MembersCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->adherent->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(166288,'auguria',1,'','left','members',166287,NULL,NULL,0,'/categories/card.php?action=create&type=3','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->adherent->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(166387,'auguria',1,'','left','hrm',161102,NULL,NULL,5,'/expensereport/index.php?leftmenu=expensereport','','TripsAndExpenses','trips',0,'expensereport','$user->rights->expensereport->lire','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166388,'auguria',1,'','left','hrm',166387,NULL,NULL,1,'/expensereport/card.php?action=create&leftmenu=expensereport','','New','trips',1,'','$user->rights->expensereport->creer','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166389,'auguria',1,'','left','hrm',166387,NULL,NULL,2,'/expensereport/list.php?leftmenu=expensereport','','List','trips',1,'','$user->rights->expensereport->lire','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166390,'auguria',1,'','left','hrm',166389,NULL,NULL,2,'/expensereport/list.php?search_status=2&leftmenu=expensereport','','ListToApprove','trips',2,'','$user->rights->expensereport->approve','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166391,'auguria',1,'','left','hrm',166387,NULL,NULL,2,'/expensereport/stats/index.php?leftmenu=expensereport','','Statistics','trips',1,'','$user->rights->expensereport->lire','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166442,'all',1,'agenda','top','agenda',0,NULL,NULL,15,'/comm/action/index.php','','TMenuAgenda','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166443,'all',1,'agenda','left','agenda',166442,NULL,NULL,100,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Actions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166444,'all',1,'agenda','left','agenda',166443,NULL,NULL,101,'/comm/action/card.php?mainmenu=agenda&leftmenu=agenda&action=create','','NewAction','commercial',NULL,NULL,'($user->rights->agenda->myactions->create||$user->rights->agenda->allactions->create)','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166445,'all',1,'agenda','left','agenda',166443,NULL,NULL,140,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Agenda','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166446,'all',1,'agenda','left','agenda',166445,NULL,NULL,141,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166447,'all',1,'agenda','left','agenda',166445,NULL,NULL,142,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166448,'all',1,'agenda','left','agenda',166445,NULL,NULL,143,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo&filtert=-1','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-01-19 11:17:54'),(166449,'all',1,'agenda','left','agenda',166445,NULL,NULL,144,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done&filtert=-1','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-01-19 11:17:54'),(166450,'all',1,'agenda','left','agenda',166443,NULL,NULL,110,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda','','List','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166451,'all',1,'agenda','left','agenda',166450,NULL,NULL,111,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166452,'all',1,'agenda','left','agenda',166450,NULL,NULL,112,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166453,'all',1,'agenda','left','agenda',166450,NULL,NULL,113,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=todo&filtert=-1','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-01-19 11:17:54'),(166454,'all',1,'agenda','left','agenda',166450,NULL,NULL,114,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=done&filtert=-1','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-01-19 11:17:54'),(166455,'all',1,'agenda','left','agenda',166443,NULL,NULL,160,'/comm/action/rapport/index.php?mainmenu=agenda&leftmenu=agenda','','Reportings','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$conf->agenda->enabled',2,'2018-01-19 11:17:54'),(166456,'all',1,'barcode','left','tools',-1,NULL,'tools',200,'/barcode/printsheet.php?mainmenu=tools&leftmenu=barcodeprint','','BarCodePrintsheet','products',NULL,'barcodeprint','($conf->global->MAIN_USE_ADVANCED_PERMS && $user->rights->barcode->lire_advance) || (! $conf->global->MAIN_USE_ADVANCED_PERMS)','$conf->barcode->enabled',2,'2018-01-19 11:17:54'),(166457,'all',1,'barcode','left','home',-1,'admintools','home',300,'/barcode/codeinit.php?mainmenu=home&leftmenu=admintools','','MassBarcodeInit','products',NULL,NULL,'($conf->global->MAIN_USE_ADVANCED_PERMS && $user->rights->barcode->creer_advance) || (! $conf->global->MAIN_USE_ADVANCED_PERMS)','$conf->barcode->enabled && preg_match(\'/^admintools/\',$leftmenu)',0,'2018-01-19 11:17:54'),(166458,'all',1,'cron','left','home',-1,'admintools','home',200,'/cron/list.php?status=-2&leftmenu=admintools','','CronList','cron',NULL,NULL,'$user->rights->cron->read','$conf->cron->enabled && preg_match(\'/^admintools/\', $leftmenu)',2,'2018-01-19 11:17:54'),(166459,'all',1,'ecm','top','ecm',0,NULL,NULL,100,'/ecm/index.php','','MenuECM','ecm',NULL,NULL,'$user->rights->ecm->read || $user->rights->ecm->upload || $user->rights->ecm->setup','$conf->ecm->enabled',2,'2018-01-19 11:17:54'),(166460,'all',1,'ecm','left','ecm',-1,NULL,'ecm',101,'/ecm/index.php?mainmenu=ecm&leftmenu=ecm','','ECMArea','ecm',NULL,'ecm','$user->rights->ecm->read || $user->rights->ecm->upload','$user->rights->ecm->read || $user->rights->ecm->upload',2,'2018-01-19 11:17:54'),(166461,'all',1,'ecm','left','ecm',-1,'ecm','ecm',102,'/ecm/index.php?action=file_manager&mainmenu=ecm&leftmenu=ecm','','ECMSectionsManual','ecm',NULL,'ecm_manual','$user->rights->ecm->read || $user->rights->ecm->upload','$user->rights->ecm->read || $user->rights->ecm->upload',2,'2018-01-19 11:17:54'),(166462,'all',1,'ecm','left','ecm',-1,'ecm','ecm',103,'/ecm/index_auto.php?action=file_manager&mainmenu=ecm&leftmenu=ecm','','ECMSectionsAuto','ecm',NULL,NULL,'$user->rights->ecm->read || $user->rights->ecm->upload','($user->rights->ecm->read || $user->rights->ecm->upload) && ! empty($conf->global->ECM_AUTO_TREE_ENABLED)',2,'2018-01-19 11:17:54'),(166463,'all',1,'opensurvey','left','tools',-1,NULL,'tools',200,'/opensurvey/index.php?mainmenu=tools&leftmenu=opensurvey','','Survey','opensurvey',NULL,'opensurvey','$user->rights->opensurvey->read','$conf->opensurvey->enabled',0,'2018-01-19 11:17:54'),(166464,'all',1,'opensurvey','left','tools',-1,'opensurvey','tools',210,'/opensurvey/wizard/index.php','','NewSurvey','opensurvey',NULL,'opensurvey_new','$user->rights->opensurvey->write','$conf->opensurvey->enabled',0,'2018-01-19 11:17:54'),(166465,'all',1,'opensurvey','left','tools',-1,'opensurvey','tools',220,'/opensurvey/list.php','','List','opensurvey',NULL,'opensurvey_list','$user->rights->opensurvey->read','$conf->opensurvey->enabled',0,'2018-01-19 11:17:54'),(166466,'all',1,'blockedlog','left','tools',-1,NULL,'tools',200,'/blockedlog/admin/blockedlog_list.php?mainmenu=tools&leftmenu=blockedlogbrowser','','BrowseBlockedLog','blockedlog',NULL,'blockedlogbrowser','$user->rights->blockedlog->read','$conf->blockedlog->enabled',2,'2018-01-19 11:27:15'),(166467,'all',1,'variants','left','products',-1,'product','products',100,'/variants/list.php','','VariantAttributes','products',NULL,'product','1','$conf->product->enabled',0,'2018-01-19 11:28:04'); +INSERT INTO `llx_menu` VALUES (103094,'all',2,'agenda','top','agenda',0,NULL,NULL,100,'/comm/action/index.php','','Agenda','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103095,'all',2,'agenda','left','agenda',103094,NULL,NULL,100,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Actions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103096,'all',2,'agenda','left','agenda',103095,NULL,NULL,101,'/comm/action/card.php?mainmenu=agenda&leftmenu=agenda&action=create','','NewAction','commercial',NULL,NULL,'($user->rights->agenda->myactions->create||$user->rights->agenda->allactions->create)','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103097,'all',2,'agenda','left','agenda',103095,NULL,NULL,102,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Calendar','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103098,'all',2,'agenda','left','agenda',103097,NULL,NULL,103,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103099,'all',2,'agenda','left','agenda',103097,NULL,NULL,104,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103100,'all',2,'agenda','left','agenda',103097,NULL,NULL,105,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103101,'all',2,'agenda','left','agenda',103097,NULL,NULL,106,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103102,'all',2,'agenda','left','agenda',103095,NULL,NULL,112,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda','','List','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103103,'all',2,'agenda','left','agenda',103102,NULL,NULL,113,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103104,'all',2,'agenda','left','agenda',103102,NULL,NULL,114,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103105,'all',2,'agenda','left','agenda',103102,NULL,NULL,115,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=todo','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103106,'all',2,'agenda','left','agenda',103102,NULL,NULL,116,'/comm/action/listactions.php?mainmenu=agenda&leftmenu=agenda&status=done','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2013-03-13 15:29:19'),(103107,'all',2,'agenda','left','agenda',103095,NULL,NULL,120,'/comm/action/rapport/index.php?mainmenu=agenda&leftmenu=agenda','','Reportings','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$conf->agenda->enabled',2,'2013-03-13 15:29:19'),(103134,'all',2,'opensurvey','top','opensurvey',0,NULL,NULL,200,'/opensurvey/index.php','','Surveys','opensurvey',NULL,NULL,'$user->rights->opensurvey->survey->read','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(103135,'all',2,'opensurvey','left','opensurvey',-1,NULL,'opensurvey',200,'/opensurvey/index.php?mainmenu=opensurvey&leftmenu=opensurvey','','Survey','opensurvey@opensurvey',NULL,'opensurvey','','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(103136,'all',2,'opensurvey','left','opensurvey',-1,'opensurvey','opensurvey',210,'/opensurvey/public/index.php','_blank','NewSurvey','opensurvey@opensurvey',NULL,'opensurvey_new','','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(103137,'all',2,'opensurvey','left','opensurvey',-1,'opensurvey','opensurvey',220,'/opensurvey/list.php','','List','opensurvey@opensurvey',NULL,'opensurvey_list','','$conf->opensurvey->enabled',0,'2013-03-13 20:33:42'),(124179,'all',1,'cashdesk','top','cashdesk',0,NULL,NULL,100,'/cashdesk/index.php?user=__LOGIN__','pointofsale','CashDeskMenu','cashdesk',NULL,NULL,'$user->rights->cashdesk->use','$conf->cashdesk->enabled',0,'2015-11-15 22:38:33'),(124210,'all',1,'margins','left','accountancy',-1,NULL,'accountancy',100,'/margin/index.php','','Margins','margins',NULL,'margins','$user->rights->margins->liretous','$conf->margin->enabled',2,'2015-11-15 22:41:47'),(145086,'all',1,'supplier_proposal','left','commercial',-1,NULL,'commercial',300,'/supplier_proposal/index.php','','SupplierProposalsShort','supplier_proposal',NULL,'supplier_proposalsubmenu','$user->rights->supplier_proposal->lire','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145087,'all',1,'supplier_proposal','left','commercial',-1,'supplier_proposalsubmenu','commercial',301,'/supplier_proposal/card.php?action=create&leftmenu=supplier_proposals','','SupplierProposalNew','supplier_proposal',NULL,NULL,'$user->rights->supplier_proposal->creer','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145088,'all',1,'supplier_proposal','left','commercial',-1,'supplier_proposalsubmenu','commercial',302,'/supplier_proposal/list.php?leftmenu=supplier_proposals','','List','supplier_proposal',NULL,NULL,'$user->rights->supplier_proposal->lire','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145089,'all',1,'supplier_proposal','left','commercial',-1,'supplier_proposalsubmenu','commercial',303,'/comm/propal/stats/index.php?leftmenu=supplier_proposals&mode=supplier','','Statistics','supplier_proposal',NULL,NULL,'$user->rights->supplier_proposal->lire','$conf->supplier_proposal->enabled',2,'2016-07-30 11:13:20'),(145090,'all',1,'resource','left','tools',-1,NULL,'tools',100,'/resource/list.php','','MenuResourceIndex','resource',NULL,'resource','$user->rights->resource->read','1',0,'2016-07-30 11:13:32'),(145091,'all',1,'resource','left','tools',-1,'resource','tools',101,'/resource/add.php','','MenuResourceAdd','resource',NULL,NULL,'$user->rights->resource->read','1',0,'2016-07-30 11:13:32'),(145092,'all',1,'resource','left','tools',-1,'resource','tools',102,'/resource/list.php','','List','resource',NULL,NULL,'$user->rights->resource->read','1',0,'2016-07-30 11:13:32'),(145127,'all',1,'printing','left','home',-1,'admintools','home',300,'/printing/index.php?mainmenu=home&leftmenu=admintools','','MenuDirectPrinting','printing',NULL,NULL,'$user->rights->printing->read','$conf->printing->enabled && $leftmenu==\'admintools\'',0,'2017-01-29 15:12:44'),(161088,'auguria',1,'','top','home',0,NULL,NULL,10,'/index.php?mainmenu=home&leftmenu=','','Home','',-1,'','','1',2,'2017-08-30 15:14:30'),(161089,'auguria',1,'societe|fournisseur','top','companies',0,NULL,NULL,20,'/societe/index.php?mainmenu=companies&leftmenu=','','ThirdParties','companies',-1,'','$user->rights->societe->lire || $user->rights->societe->contact->lire','( ! empty($conf->societe->enabled) && (empty($conf->global->SOCIETE_DISABLE_PROSPECTS) || empty($conf->global->SOCIETE_DISABLE_CUSTOMERS))) || ! empty($conf->fournisseur->enabled)',2,'2017-08-30 15:14:30'),(161090,'auguria',1,'product|service','top','products',0,NULL,NULL,30,'/product/index.php?mainmenu=products&leftmenu=','','Products/Services','products',-1,'','$user->rights->produit->lire||$user->rights->service->lire','$conf->product->enabled || $conf->service->enabled',0,'2017-08-30 15:14:30'),(161092,'auguria',1,'propal|commande|fournisseur|contrat|ficheinter','top','commercial',0,NULL,NULL,40,'/comm/index.php?mainmenu=commercial&leftmenu=','','Commercial','commercial',-1,'','$user->rights->societe->lire || $user->rights->societe->contact->lire','$conf->propal->enabled || $conf->commande->enabled || $conf->supplier_order->enabled || $conf->contrat->enabled || $conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(161093,'auguria',1,'comptabilite|accounting|facture|don|tax|salaries|loan','top','accountancy',0,NULL,NULL,50,'/compta/index.php?mainmenu=accountancy&leftmenu=','','MenuFinancial','compta',-1,'','$user->rights->compta->resultat->lire || $user->rights->accounting->plancompte->lire || $user->rights->facture->lire|| $user->rights->don->lire || $user->rights->tax->charges->lire || $user->rights->salaries->read || $user->rights->loan->read','$conf->comptabilite->enabled || $conf->accounting->enabled || $conf->facture->enabled || $conf->don->enabled || $conf->tax->enabled || $conf->salaries->enabled || $conf->supplier_invoice->enabled || $conf->loan->enabled',2,'2017-08-30 15:14:30'),(161094,'auguria',1,'projet','top','project',0,NULL,NULL,70,'/projet/index.php?mainmenu=project&leftmenu=','','Projects','projects',-1,'','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(161095,'auguria',1,'mailing|export|import|opensurvey|resource','top','tools',0,NULL,NULL,90,'/core/tools.php?mainmenu=tools&leftmenu=','','Tools','other',-1,'','$user->rights->mailing->lire || $user->rights->export->lire || $user->rights->import->run || $user->rights->opensurvey->read || $user->rights->resource->read','$conf->mailing->enabled || $conf->export->enabled || $conf->import->enabled || $conf->opensurvey->enabled || $conf->resource->enabled',2,'2017-08-30 15:14:30'),(161101,'auguria',1,'banque|prelevement','top','bank',0,NULL,NULL,60,'/compta/bank/index.php?mainmenu=bank&leftmenu=bank','','MenuBankCash','banks',-1,'','$user->rights->banque->lire || $user->rights->prelevement->bons->lire','$conf->banque->enabled || $conf->prelevement->enabled',0,'2017-08-30 15:14:30'),(161102,'auguria',1,'hrm|holiday|deplacement|expensereport','top','hrm',0,NULL,NULL,80,'/hrm/index.php?mainmenu=hrm&leftmenu=','','HRM','holiday',-1,'','$user->rights->hrm->employee->read || $user->rights->holiday->write || $user->rights->deplacement->lire || $user->rights->expensereport->lire','$conf->hrm->enabled || $conf->holiday->enabled || $conf->deplacement->enabled || $conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(161177,'auguria',1,'','left','home',161088,NULL,NULL,0,'/index.php','','MyDashboard','',0,'','','1',2,'2017-08-30 15:14:30'),(161187,'auguria',1,'','left','home',161088,NULL,NULL,1,'/admin/index.php?leftmenu=setup','','Setup','admin',0,'setup','','$user->admin',2,'2017-08-30 15:14:30'),(161188,'auguria',1,'','left','home',161187,NULL,NULL,1,'/admin/company.php?leftmenu=setup','','MenuCompanySetup','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161189,'auguria',1,'','left','home',161187,NULL,NULL,4,'/admin/ihm.php?leftmenu=setup','','GUISetup','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161190,'auguria',1,'','left','home',161187,NULL,NULL,2,'/admin/modules.php?leftmenu=setup','','Modules','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161191,'auguria',1,'','left','home',161187,NULL,NULL,6,'/admin/boxes.php?leftmenu=setup','','Boxes','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161192,'auguria',1,'','left','home',161187,NULL,NULL,3,'/admin/menus.php?leftmenu=setup','','Menus','admin',1,'','','$leftmenu==\'setup\'',2,'2017-09-06 08:29:47'),(161193,'auguria',1,'','left','home',161187,NULL,NULL,7,'/admin/delais.php?leftmenu=setup','','Alerts','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161194,'auguria',1,'','left','home',161187,NULL,NULL,10,'/admin/pdf.php?leftmenu=setup','','PDF','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161195,'auguria',1,'','left','home',161187,NULL,NULL,8,'/admin/security_other.php?leftmenu=setup','','Security','admin',1,'','','$leftmenu==\'setup\'',2,'2017-09-06 08:29:36'),(161196,'auguria',1,'','left','home',161187,NULL,NULL,11,'/admin/mails.php?leftmenu=setup','','Emails','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161197,'auguria',1,'','left','home',161187,NULL,NULL,9,'/admin/limits.php?leftmenu=setup','','MenuLimits','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161198,'auguria',1,'','left','home',161187,NULL,NULL,13,'/admin/dict.php?leftmenu=setup','','Dictionary','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161199,'auguria',1,'','left','home',161187,NULL,NULL,14,'/admin/const.php?leftmenu=setup','','OtherSetup','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161200,'auguria',1,'','left','home',161187,NULL,NULL,12,'/admin/sms.php?leftmenu=setup','','SMS','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161201,'auguria',1,'','left','home',161187,NULL,NULL,4,'/admin/translation.php?leftmenu=setup','','Translation','admin',1,'','','$leftmenu==\"setup\"',2,'2017-08-30 15:14:30'),(161288,'auguria',1,'','left','home',161387,NULL,NULL,0,'/admin/system/dolibarr.php?leftmenu=admintools','','InfoDolibarr','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161289,'auguria',1,'','left','home',161288,NULL,NULL,2,'/admin/system/modules.php?leftmenu=admintools','','Modules','admin',2,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161290,'auguria',1,'','left','home',161288,NULL,NULL,3,'/admin/triggers.php?leftmenu=admintools','','Triggers','admin',2,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161291,'auguria',1,'','left','home',161288,NULL,NULL,4,'/admin/system/filecheck.php?leftmenu=admintools','','FileCheck','admin',2,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161292,'auguria',1,'','left','home',161387,NULL,NULL,1,'/admin/system/browser.php?leftmenu=admintools','','InfoBrowser','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161293,'auguria',1,'','left','home',161387,NULL,NULL,2,'/admin/system/os.php?leftmenu=admintools','','InfoOS','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161294,'auguria',1,'','left','home',161387,NULL,NULL,3,'/admin/system/web.php?leftmenu=admintools','','InfoWebServer','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161295,'auguria',1,'','left','home',161387,NULL,NULL,4,'/admin/system/phpinfo.php?leftmenu=admintools','','InfoPHP','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161297,'auguria',1,'','left','home',161387,NULL,NULL,5,'/admin/system/database.php?leftmenu=admintools','','InfoDatabase','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161387,'auguria',1,'','left','home',161088,NULL,NULL,2,'/admin/tools/index.php?leftmenu=admintools','','AdminTools','admin',0,'admintools','','$user->admin',2,'2017-08-30 15:14:30'),(161388,'auguria',1,'','left','home',161387,NULL,NULL,6,'/admin/tools/dolibarr_export.php?leftmenu=admintools','','Backup','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161389,'auguria',1,'','left','home',161387,NULL,NULL,7,'/admin/tools/dolibarr_import.php?leftmenu=admintools','','Restore','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161392,'auguria',1,'','left','home',161387,NULL,NULL,8,'/admin/tools/update.php?leftmenu=admintools','','MenuUpgrade','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161393,'auguria',1,'','left','home',161387,NULL,NULL,9,'/admin/tools/eaccelerator.php?leftmenu=admintools','','EAccelerator','admin',1,'','','$leftmenu==\"admintools\" && function_exists(\"eaccelerator_info\")',2,'2017-08-30 15:14:30'),(161394,'auguria',1,'','left','home',161387,NULL,NULL,10,'/admin/tools/listevents.php?leftmenu=admintools','','Audit','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161395,'auguria',1,'','left','home',161387,NULL,NULL,11,'/admin/tools/listsessions.php?leftmenu=admintools','','Sessions','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161396,'auguria',1,'','left','home',161387,NULL,NULL,12,'/admin/tools/purge.php?leftmenu=admintools','','Purge','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161398,'auguria',1,'','left','home',161387,NULL,NULL,14,'/admin/system/about.php?leftmenu=admintools','','ExternalResources','admin',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161407,'auguria',1,'','left','home',161387,NULL,NULL,15,'/product/admin/product_tools.php?mainmenu=home&leftmenu=admintools','','ProductVatMassChange','products',1,'','','$leftmenu==\"admintools\"',2,'2017-08-30 15:14:30'),(161487,'auguria',1,'','left','home',161088,NULL,NULL,4,'/user/home.php?leftmenu=users','','MenuUsersAndGroups','users',0,'users','','1',2,'2017-08-30 15:14:30'),(161488,'auguria',1,'','left','home',161487,NULL,NULL,0,'/user/index.php?leftmenu=users','','Users','users',1,'','$user->rights->user->user->lire || $user->admin','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161489,'auguria',1,'','left','home',161488,NULL,NULL,0,'/user/card.php?leftmenu=users&action=create','','NewUser','users',2,'','($user->rights->user->user->creer || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161490,'auguria',1,'','left','home',161487,NULL,NULL,1,'/user/group/index.php?leftmenu=users','','Groups','users',1,'','(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->read:$user->rights->user->user->lire) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161491,'auguria',1,'','left','home',161490,NULL,NULL,0,'/user/group/card.php?leftmenu=users&action=create','','NewGroup','users',2,'','(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->write:$user->rights->user->user->creer) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)','$leftmenu==\"users\"',2,'2017-08-30 15:14:30'),(161587,'auguria',1,'','left','companies',161089,NULL,NULL,0,'/societe/index.php?leftmenu=thirdparties','','ThirdParty','companies',0,'thirdparties','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161588,'auguria',1,'','left','companies',161587,NULL,NULL,0,'/societe/card.php?action=create','','MenuNewThirdParty','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161589,'auguria',1,'','left','companies',161587,NULL,NULL,0,'/societe/list.php?action=create','','List','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161590,'auguria',1,'','left','companies',161587,NULL,NULL,5,'/societe/list.php?type=f&leftmenu=suppliers','','ListSuppliersShort','suppliers',1,'','$user->rights->societe->lire && $user->rights->fournisseur->lire','$conf->societe->enabled && $conf->fournisseur->enabled',2,'2017-08-30 15:14:30'),(161591,'auguria',1,'','left','companies',161590,NULL,NULL,0,'/societe/card.php?leftmenu=supplier&action=create&type=f','','NewSupplier','suppliers',2,'','$user->rights->societe->creer','$conf->societe->enabled && $conf->fournisseur->enabled',2,'2017-08-30 15:14:30'),(161593,'auguria',1,'','left','companies',161587,NULL,NULL,3,'/societe/list.php?type=p&leftmenu=prospects','','ListProspectsShort','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161594,'auguria',1,'','left','companies',161593,NULL,NULL,0,'/societe/card.php?leftmenu=prospects&action=create&type=p','','MenuNewProspect','companies',2,'','$user->rights->societe->creer','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161596,'auguria',1,'','left','companies',161587,NULL,NULL,4,'/societe/list.php?type=c&leftmenu=customers','','ListCustomersShort','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161597,'auguria',1,'','left','companies',161596,NULL,NULL,0,'/societe/card.php?leftmenu=customers&action=create&type=c','','MenuNewCustomer','companies',2,'','$user->rights->societe->creer','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161687,'auguria',1,'','left','companies',161089,NULL,NULL,1,'/contact/list.php?leftmenu=contacts','','ContactsAddresses','companies',0,'contacts','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161688,'auguria',1,'','left','companies',161687,NULL,NULL,0,'/contact/card.php?leftmenu=contacts&action=create','','NewContactAddress','companies',1,'','$user->rights->societe->creer','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161689,'auguria',1,'','left','companies',161687,NULL,NULL,1,'/contact/list.php?leftmenu=contacts','','List','companies',1,'','$user->rights->societe->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161691,'auguria',1,'','left','companies',161689,NULL,NULL,1,'/contact/list.php?leftmenu=contacts&type=p','','ThirdPartyProspects','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161692,'auguria',1,'','left','companies',161689,NULL,NULL,2,'/contact/list.php?leftmenu=contacts&type=c','','ThirdPartyCustomers','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161693,'auguria',1,'','left','companies',161689,NULL,NULL,3,'/contact/list.php?leftmenu=contacts&type=f','','ThirdPartySuppliers','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled && $conf->fournisseur->enabled',2,'2017-08-30 15:14:30'),(161694,'auguria',1,'','left','companies',161689,NULL,NULL,4,'/contact/list.php?leftmenu=contacts&type=o','','Others','companies',2,'','$user->rights->societe->contact->lire','$conf->societe->enabled',2,'2017-08-30 15:14:30'),(161737,'auguria',1,'','left','companies',161089,NULL,NULL,3,'/categories/index.php?leftmenu=cat&type=1','','SuppliersCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161738,'auguria',1,'','left','companies',161737,NULL,NULL,0,'/categories/card.php?action=create&type=1','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161747,'auguria',1,'','left','companies',161089,NULL,NULL,4,'/categories/index.php?leftmenu=cat&type=2','','CustomersProspectsCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->fournisseur->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161748,'auguria',1,'','left','companies',161747,NULL,NULL,0,'/categories/card.php?action=create&type=2','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->fournisseur->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161757,'auguria',1,'','left','companies',161089,NULL,NULL,3,'/categories/index.php?leftmenu=cat&type=4','','ContactCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(161758,'auguria',1,'','left','companies',161757,NULL,NULL,0,'/categories/card.php?action=create&type=4','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->societe->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(162187,'auguria',1,'','left','commercial',161092,NULL,NULL,4,'/comm/propal/index.php?leftmenu=propals','','Prop','propal',0,'propals','$user->rights->propale->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162188,'auguria',1,'','left','commercial',162187,NULL,NULL,0,'/comm/propal/card.php?action=create&leftmenu=propals','','NewPropal','propal',1,'','$user->rights->propale->creer','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162189,'auguria',1,'','left','commercial',162187,NULL,NULL,1,'/comm/propal/list.php?leftmenu=propals','','List','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162190,'auguria',1,'','left','commercial',162189,NULL,NULL,2,'/comm/propal/list.php?leftmenu=propals&viewstatut=0','','PropalsDraft','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162191,'auguria',1,'','left','commercial',162189,NULL,NULL,3,'/comm/propal/list.php?leftmenu=propals&viewstatut=1','','PropalsOpened','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162192,'auguria',1,'','left','commercial',162189,NULL,NULL,4,'/comm/propal/list.php?leftmenu=propals&viewstatut=2','','PropalStatusSigned','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162193,'auguria',1,'','left','commercial',162189,NULL,NULL,5,'/comm/propal/list.php?leftmenu=propals&viewstatut=3','','PropalStatusNotSigned','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162194,'auguria',1,'','left','commercial',162189,NULL,NULL,6,'/comm/propal/list.php?leftmenu=propals&viewstatut=4','','PropalStatusBilled','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled && $leftmenu==\"propals\"',2,'2017-08-30 15:14:30'),(162197,'auguria',1,'','left','commercial',162187,NULL,NULL,4,'/comm/propal/stats/index.php?leftmenu=propals','','Statistics','propal',1,'','$user->rights->propale->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(162287,'auguria',1,'','left','commercial',161092,NULL,NULL,5,'/commande/index.php?leftmenu=orders','','CustomersOrders','orders',0,'orders','$user->rights->commande->lire','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162288,'auguria',1,'','left','commercial',162287,NULL,NULL,0,'/commande/card.php?action=create&leftmenu=orders','','NewOrder','orders',1,'','$user->rights->commande->creer','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162289,'auguria',1,'','left','commercial',162287,NULL,NULL,1,'/commande/list.php?leftmenu=orders','','List','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162290,'auguria',1,'','left','commercial',162289,NULL,NULL,2,'/commande/list.php?leftmenu=orders&viewstatut=0','','StatusOrderDraftShort','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162291,'auguria',1,'','left','commercial',162289,NULL,NULL,3,'/commande/list.php?leftmenu=orders&viewstatut=1','','StatusOrderValidated','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162292,'auguria',1,'','left','commercial',162289,NULL,NULL,4,'/commande/list.php?leftmenu=orders&viewstatut=2','','StatusOrderOnProcessShort','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162293,'auguria',1,'','left','commercial',162289,NULL,NULL,5,'/commande/list.php?leftmenu=orders&viewstatut=3','','StatusOrderToBill','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162294,'auguria',1,'','left','commercial',162289,NULL,NULL,6,'/commande/list.php?leftmenu=orders&viewstatut=4','','StatusOrderProcessed','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162295,'auguria',1,'','left','commercial',162289,NULL,NULL,7,'/commande/list.php?leftmenu=orders&viewstatut=-1','','StatusOrderCanceledShort','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled && $leftmenu==\"orders\"',2,'2017-08-30 15:14:30'),(162296,'auguria',1,'','left','commercial',162287,NULL,NULL,4,'/commande/stats/index.php?leftmenu=orders','','Statistics','orders',1,'','$user->rights->commande->lire','$conf->commande->enabled',2,'2017-08-30 15:14:30'),(162387,'auguria',1,'','left','commercial',161090,NULL,NULL,6,'/expedition/index.php?leftmenu=sendings','','Shipments','sendings',0,'sendings','$user->rights->expedition->lire','$conf->expedition->enabled',2,'2017-08-30 15:14:30'),(162388,'auguria',1,'','left','commercial',162387,NULL,NULL,0,'/expedition/card.php?action=create2&leftmenu=sendings','','NewSending','sendings',1,'','$user->rights->expedition->creer','$conf->expedition->enabled && $leftmenu==\"sendings\"',2,'2017-08-30 15:14:30'),(162389,'auguria',1,'','left','commercial',162387,NULL,NULL,1,'/expedition/list.php?leftmenu=sendings','','List','sendings',1,'','$user->rights->expedition->lire','$conf->expedition->enabled && $leftmenu==\"sendings\"',2,'2017-08-30 15:14:30'),(162390,'auguria',1,'','left','commercial',162387,NULL,NULL,2,'/expedition/stats/index.php?leftmenu=sendings','','Statistics','sendings',1,'','$user->rights->expedition->lire','$conf->expedition->enabled && $leftmenu==\"sendings\"',2,'2017-08-30 15:14:30'),(162487,'auguria',1,'','left','commercial',161092,NULL,NULL,7,'/contrat/index.php?leftmenu=contracts','','Contracts','contracts',0,'contracts','$user->rights->contrat->lire','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162488,'auguria',1,'','left','commercial',162487,NULL,NULL,0,'/contrat/card.php?&action=create&leftmenu=contracts','','NewContract','contracts',1,'','$user->rights->contrat->creer','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162489,'auguria',1,'','left','commercial',162487,NULL,NULL,1,'/contrat/list.php?leftmenu=contracts','','List','contracts',1,'','$user->rights->contrat->lire','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162490,'auguria',1,'','left','commercial',162487,NULL,NULL,2,'/contrat/services.php?leftmenu=contracts','','MenuServices','contracts',1,'','$user->rights->contrat->lire','$conf->contrat->enabled',2,'2017-08-30 15:14:30'),(162491,'auguria',1,'','left','commercial',162490,NULL,NULL,0,'/contrat/services.php?leftmenu=contracts&mode=0','','MenuInactiveServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162492,'auguria',1,'','left','commercial',162490,NULL,NULL,1,'/contrat/services.php?leftmenu=contracts&mode=4','','MenuRunningServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162493,'auguria',1,'','left','commercial',162490,NULL,NULL,2,'/contrat/services.php?leftmenu=contracts&mode=4&filter=expired','','MenuExpiredServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162494,'auguria',1,'','left','commercial',162490,NULL,NULL,3,'/contrat/services.php?leftmenu=contracts&mode=5','','MenuClosedServices','contracts',2,'','$user->rights->contrat->lire','$conf->contrat->enabled && $leftmenu==\"contracts\"',2,'2017-08-30 15:14:30'),(162587,'auguria',1,'','left','commercial',161092,NULL,NULL,8,'/fichinter/list.php?leftmenu=ficheinter','','Interventions','interventions',0,'ficheinter','$user->rights->ficheinter->lire','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162588,'auguria',1,'','left','commercial',162587,NULL,NULL,0,'/fichinter/card.php?action=create&leftmenu=ficheinter','','NewIntervention','interventions',1,'','$user->rights->ficheinter->creer','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162589,'auguria',1,'','left','commercial',162587,NULL,NULL,1,'/fichinter/list.php?leftmenu=ficheinter','','List','interventions',1,'','$user->rights->ficheinter->lire','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162590,'auguria',1,'','left','commercial',162587,NULL,NULL,2,'/fichinter/stats/index.php?leftmenu=ficheinter','','Statistics','interventions',1,'','$user->rights->ficheinter->lire','$conf->ficheinter->enabled',2,'2017-08-30 15:14:30'),(162687,'auguria',1,'','left','accountancy',161093,NULL,NULL,3,'/fourn/facture/list.php?leftmenu=suppliers_bills','','BillsSuppliers','bills',0,'supplier_bills','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162688,'auguria',1,'','left','accountancy',162687,NULL,NULL,0,'/fourn/facture/card.php?action=create&leftmenu=suppliers_bills','','NewBill','bills',1,'','$user->rights->fournisseur->facture->creer','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162689,'auguria',1,'','left','accountancy',162687,NULL,NULL,1,'/fourn/facture/list.php?leftmenu=suppliers_bills','','List','bills',1,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162690,'auguria',1,'','left','accountancy',162687,NULL,NULL,2,'/fourn/facture/paiement.php?leftmenu=suppliers_bills','','Payments','bills',1,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162691,'auguria',1,'','left','accountancy',162687,NULL,NULL,8,'/compta/facture/stats/index.php?leftmenu=customers_bills&mode=supplier','','Statistics','bills',1,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162692,'auguria',1,'','left','accountancy',162690,NULL,NULL,1,'/fourn/facture/rapport.php?leftmenu=suppliers_bills','','Reporting','bills',2,'','$user->rights->fournisseur->facture->lire','$conf->supplier_invoice->enabled',2,'2017-08-30 15:14:30'),(162787,'auguria',1,'','left','accountancy',161093,NULL,NULL,3,'/compta/facture/list.php?leftmenu=customers_bills','','BillsCustomers','bills',0,'customer_bills','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162788,'auguria',1,'','left','accountancy',162787,NULL,NULL,3,'/compta/facture/card.php?action=create&leftmenu=customers_bills','','NewBill','bills',1,'','$user->rights->facture->creer','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162789,'auguria',1,'','left','accountancy',162787,NULL,NULL,5,'/compta/facture/fiche-rec.php?leftmenu=customers_bills','','ListOfTemplates','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162791,'auguria',1,'','left','accountancy',162787,NULL,NULL,6,'/compta/paiement/list.php?leftmenu=customers_bills','','Payments','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162792,'auguria',1,'','left','accountancy',162787,NULL,NULL,4,'/compta/facture/list.php?leftmenu=customers_bills','','List','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162797,'auguria',1,'','left','accountancy',162791,NULL,NULL,1,'/compta/paiement/rapport.php?leftmenu=customers_bills','','Reportings','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162798,'auguria',1,'','left','accountancy',161101,NULL,NULL,9,'/compta/paiement/cheque/index.php?leftmenu=checks&mainmenu=bank','','MenuChequeDeposits','bills',0,'checks','$user->rights->banque->lire','empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))',2,'2017-08-30 15:14:30'),(162799,'auguria',1,'','left','accountancy',162798,NULL,NULL,0,'/compta/paiement/cheque/card.php?leftmenu=checks&action=new','','NewCheckDeposit','compta',1,'','$user->rights->banque->lire','empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))',2,'2017-08-30 15:14:30'),(162800,'auguria',1,'','left','accountancy',162798,NULL,NULL,1,'/compta/paiement/cheque/list.php?leftmenu=checks','','List','bills',1,'','$user->rights->banque->lire','empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))',2,'2017-08-30 15:14:30'),(162801,'auguria',1,'','left','accountancy',162787,NULL,NULL,8,'/compta/facture/stats/index.php?leftmenu=customers_bills','','Statistics','bills',1,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162807,'auguria',1,'','left','accountancy',162792,NULL,NULL,1,'/compta/facture/list.php?leftmenu=customers_bills&search_status=0','','BillShortStatusDraft','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162808,'auguria',1,'','left','accountancy',162792,NULL,NULL,2,'/compta/facture/list.php?leftmenu=customers_bills&search_status=1','','BillShortStatusNotPaid','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162809,'auguria',1,'','left','accountancy',162792,NULL,NULL,3,'/compta/facture/list.php?leftmenu=customers_bills&search_status=2','','BillShortStatusPaid','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162810,'auguria',1,'','left','accountancy',162792,NULL,NULL,4,'/compta/facture/list.php?leftmenu=customers_bills&search_status=3','','BillShortStatusCanceled','bills',2,'','$user->rights->facture->lire','$conf->facture->enabled',2,'2017-08-30 15:14:30'),(162987,'auguria',1,'','left','accountancy',161093,NULL,NULL,3,'/commande/list.php?leftmenu=orders&viewstatut=3','','MenuOrdersToBill','orders',0,'orders','$user->rights->commande->lire','$conf->commande->enabled',0,'2017-08-30 15:14:30'),(163087,'auguria',1,'','left','accountancy',161093,NULL,NULL,4,'/don/index.php?leftmenu=donations&mainmenu=accountancy','','Donations','donations',0,'donations','$user->rights->don->lire','$conf->don->enabled',2,'2017-08-30 15:14:30'),(163088,'auguria',1,'','left','accountancy',163087,NULL,NULL,0,'/don/card.php?leftmenu=donations&mainmenu=accountancy&action=create','','NewDonation','donations',1,'','$user->rights->don->creer','$conf->don->enabled && $leftmenu==\"donations\"',2,'2017-08-30 15:14:30'),(163089,'auguria',1,'','left','accountancy',163087,NULL,NULL,1,'/don/list.php?leftmenu=donations&mainmenu=accountancy','','List','donations',1,'','$user->rights->don->lire','$conf->don->enabled && $leftmenu==\"donations\"',2,'2017-08-30 15:14:30'),(163187,'auguria',1,'','left','accountancy',161102,NULL,NULL,5,'/compta/deplacement/index.php?leftmenu=tripsandexpenses','','TripsAndExpenses','trips',0,'tripsandexpenses','$user->rights->deplacement->lire','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163188,'auguria',1,'','left','accountancy',163187,NULL,NULL,1,'/compta/deplacement/card.php?action=create&leftmenu=tripsandexpenses','','New','trips',1,'','$user->rights->deplacement->creer','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163189,'auguria',1,'','left','accountancy',163187,NULL,NULL,2,'/compta/deplacement/list.php?leftmenu=tripsandexpenses','','List','trips',1,'','$user->rights->deplacement->lire','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163190,'auguria',1,'','left','accountancy',163187,NULL,NULL,2,'/compta/deplacement/stats/index.php?leftmenu=tripsandexpenses','','Statistics','trips',1,'','$user->rights->deplacement->lire','$conf->deplacement->enabled',0,'2017-08-30 15:14:30'),(163287,'auguria',1,'','left','accountancy',161093,NULL,NULL,6,'/compta/charges/index.php?leftmenu=tax&mainmenu=accountancy','','MenuSpecialExpenses','compta',0,'tax','(! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) || (! empty($conf->salaries->enabled) && $user->rights->salaries->read)','$conf->tax->enabled || $conf->salaries->enabled',0,'2017-08-30 15:14:30'),(163297,'auguria',1,'','left','accountancy',163287,NULL,NULL,1,'/compta/salaries/index.php?leftmenu=tax_salary&mainmenu=accountancy','','Salaries','salaries',1,'tax_sal','$user->rights->salaries->payment->read','$conf->salaries->enabled',0,'2017-08-30 15:14:30'),(163298,'auguria',1,'','left','accountancy',163297,NULL,NULL,2,'/compta/salaries/card.php?leftmenu=tax_salary&action=create','','NewPayment','companies',2,'','$user->rights->salaries->payment->write','$conf->salaries->enabled && $leftmenu==\"tax_salary\"',0,'2017-08-30 15:14:30'),(163299,'auguria',1,'','left','accountancy',163297,NULL,NULL,3,'/compta/salaries/index.php?leftmenu=tax_salary','','Payments','companies',2,'','$user->rights->salaries->payment->read','$conf->salaries->enabled && $leftmenu==\"tax_salary\"',0,'2017-08-30 15:14:30'),(163307,'auguria',1,'','left','accountancy',163287,NULL,NULL,1,'/loan/index.php?leftmenu=tax_loan&mainmenu=accountancy','','Loans','loan',1,'tax_loan','$user->rights->loan->read','$conf->loan->enabled',0,'2017-08-30 15:14:30'),(163308,'auguria',1,'','left','accountancy',163307,NULL,NULL,2,'/loan/card.php?leftmenu=tax_loan&action=create','','NewLoan','loan',2,'','$user->rights->loan->write','$conf->loan->enabled && $leftmenu==\"tax_loan\"',0,'2017-08-30 15:14:30'),(163310,'auguria',1,'','left','accountancy',163307,NULL,NULL,4,'/loan/calc.php?leftmenu=tax_loan','','Calculator','companies',2,'','$user->rights->loan->calc','$conf->loan->enabled && $leftmenu==\"tax_loan\" && ! empty($conf->global->LOAN_SHOW_CALCULATOR)',0,'2017-08-30 15:14:30'),(163337,'auguria',1,'','left','accountancy',163287,NULL,NULL,1,'/compta/sociales/index.php?leftmenu=tax_social','','SocialContributions','',1,'tax_social','$user->rights->tax->charges->lire','$conf->tax->enabled',0,'2017-08-30 15:14:30'),(163338,'auguria',1,'','left','accountancy',163337,NULL,NULL,2,'/compta/sociales/card.php?leftmenu=tax_social&action=create','','MenuNewSocialContribution','',2,'','$user->rights->tax->charges->creer','$conf->tax->enabled && $leftmenu==\"tax_social\"',0,'2017-08-30 15:14:30'),(163339,'auguria',1,'','left','accountancy',163337,NULL,NULL,3,'/compta/sociales/payments.php?leftmenu=tax_social&mainmenu=accountancy&mode=sconly','','Payments','',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && $leftmenu==\"tax_social\"',0,'2017-08-30 15:14:30'),(163387,'auguria',1,'','left','accountancy',163287,NULL,NULL,7,'/compta/tva/index.php?leftmenu=tax_vat&mainmenu=accountancy','','VAT','companies',1,'tax_vat','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS)',0,'2017-08-30 15:14:30'),(163388,'auguria',1,'','left','accountancy',163387,NULL,NULL,0,'/compta/tva/card.php?leftmenu=tax_vat&action=create','','New','companies',2,'','$user->rights->tax->charges->creer','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163389,'auguria',1,'','left','accountancy',163387,NULL,NULL,1,'/compta/tva/reglement.php?leftmenu=tax_vat','','List','companies',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163390,'auguria',1,'','left','accountancy',163387,NULL,NULL,2,'/compta/tva/clients.php?leftmenu=tax_vat','','ReportByCustomers','companies',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163391,'auguria',1,'','left','accountancy',163387,NULL,NULL,3,'/compta/tva/quadri_detail.php?leftmenu=tax_vat','','ReportByQuarter','companies',2,'','$user->rights->tax->charges->lire','$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu==\"tax_vat\"',0,'2017-08-30 15:14:30'),(163487,'auguria',1,'','left','accountancy',161093,NULL,NULL,7,'/accountancy/index.php?leftmenu=accountancy','','MenuAccountancy','accountancy',0,'accounting','! empty($conf->accounting->enabled) || $user->rights->accounting->bind->write || $user->rights->accounting->bind->write || $user->rights->compta->resultat->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163488,'auguria',1,'','left','accountancy',163487,NULL,NULL,2,'/accountancy/customer/index.php?leftmenu=dispatch_customer','','CustomersVentilation','accountancy',1,'dispatch_customer','$user->rights->accounting->bind->write','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163489,'auguria',1,'','left','accountancy',163488,NULL,NULL,3,'/accountancy/customer/list.php','','ToDispatch','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $leftmenu==\"dispatch_customer\"',0,'2017-08-30 15:14:30'),(163490,'auguria',1,'','left','accountancy',163488,NULL,NULL,4,'/accountancy/customer/lines.php','','Dispatched','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $leftmenu==\"dispatch_customer\"',0,'2017-08-30 15:14:30'),(163497,'auguria',1,'','left','accountancy',163487,NULL,NULL,5,'/accountancy/supplier/index.php?leftmenu=dispatch_supplier','','SuppliersVentilation','accountancy',1,'ventil_supplier','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->fournisseur->enabled',0,'2017-08-30 15:14:30'),(163498,'auguria',1,'','left','accountancy',163497,NULL,NULL,6,'/accountancy/supplier/list.php','','ToDispatch','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->fournisseur->enabled && $leftmenu==\"dispatch_supplier\"',0,'2017-08-30 15:14:30'),(163499,'auguria',1,'','left','accountancy',163497,NULL,NULL,7,'/accountancy/supplier/lines.php','','Dispatched','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->fournisseur->enabled && $leftmenu==\"dispatch_supplier\"',0,'2017-08-30 15:14:30'),(163507,'auguria',1,'','left','accountancy',163487,NULL,NULL,5,'/accountancy/expensereport/index.php?leftmenu=dispatch_expensereport','','ExpenseReportsVentilation','accountancy',1,'ventil_expensereport','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(163508,'auguria',1,'','left','accountancy',163507,NULL,NULL,6,'/accountancy/expensereport/list.php','','ToDispatch','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->expensereport->enabled && $leftmenu==\"dispatch_expensereport\"',0,'2017-08-30 15:14:30'),(163509,'auguria',1,'','left','accountancy',163507,NULL,NULL,7,'/accountancy/expensereport/lines.php','','Dispatched','accountancy',2,'','$user->rights->accounting->bind->write','$conf->accounting->enabled && $conf->expensereport->enabled && $leftmenu==\"dispatch_expensereport\"',0,'2017-08-30 15:14:30'),(163517,'auguria',1,'','left','accountancy',163487,NULL,NULL,15,'/accountancy/bookkeeping/list.php','','Bookkeeping','accountancy',1,'bookkeeping','$user->rights->accounting->mouvements->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163522,'auguria',1,'','left','accountancy',163487,NULL,NULL,16,'/accountancy/bookkeeping/balance.php','','AccountBalance','accountancy',1,'balance','$user->rights->accounting->mouvements->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163527,'auguria',1,'','left','accountancy',163487,NULL,NULL,17,'/accountancy/report/result.php?mainmenu=accountancy&leftmenu=accountancy','','Reportings','main',1,'report','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163528,'auguria',1,'','left','accountancy',163527,NULL,NULL,19,'/compta/resultat/index.php?mainmenu=accountancy&leftmenu=accountancy','','ReportInOut','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163529,'auguria',1,'','left','accountancy',163528,NULL,NULL,18,'/accountancy/report/result.php?mainmenu=accountancy&leftmenu=accountancy','','ByAccounts','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163530,'auguria',1,'','left','accountancy',163528,NULL,NULL,20,'/compta/resultat/clientfourn.php?mainmenu=accountancy&leftmenu=accountancy','','ByCompanies','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163531,'auguria',1,'','left','accountancy',163527,NULL,NULL,21,'/compta/stats/index.php?mainmenu=accountancy&leftmenu=accountancy','','ReportTurnover','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163532,'auguria',1,'','left','accountancy',163531,NULL,NULL,22,'/compta/stats/casoc.php?mainmenu=accountancy&leftmenu=accountancy','','ByCompanies','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163533,'auguria',1,'','left','accountancy',163531,NULL,NULL,23,'/compta/stats/cabyuser.php?mainmenu=accountancy&leftmenu=accountancy','','ByUsers','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163534,'auguria',1,'','left','accountancy',163531,NULL,NULL,24,'/compta/stats/cabyprodserv.php?mainmenu=accountancy&leftmenu=accountancy','','ByProductsAndServices','main',3,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled && $leftmenu==\"accountancy\"',0,'2017-08-30 15:14:30'),(163537,'auguria',1,'','left','accountancy',163538,NULL,NULL,80,'/accountancy/admin/fiscalyear.php?mainmenu=accountancy&leftmenu=accountancy_admin','','FiscalPeriod','admin',1,'accountancy_admin_period','','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\" && $conf->global->MAIN_FEATURES_LEVEL > 0',2,'2017-08-30 15:14:30'),(163538,'auguria',1,'','left','accountancy',163487,NULL,NULL,1,'/accountancy/index.php?mainmenu=accountancy&leftmenu=accountancy_admin','','Setup','accountancy',1,'accountancy_admin','$user->rights->accounting->chartofaccount','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163541,'auguria',1,'','left','accountancy',163538,NULL,NULL,10,'/accountancy/admin/journals_list.php?id=35&mainmenu=accountancy&leftmenu=accountancy_admin','','AccountingJournals','accountancy',2,'accountancy_admin_journal','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163542,'auguria',1,'','left','accountancy',163538,NULL,NULL,20,'/accountancy/admin/account.php?mainmenu=accountancy&leftmenu=accountancy_admin','','Pcg_version','accountancy',2,'accountancy_admin_chartmodel','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163543,'auguria',1,'','left','accountancy',163538,NULL,NULL,30,'/accountancy/admin/account.php?mainmenu=accountancy&leftmenu=accountancy_admin','','Chartofaccounts','accountancy',2,'accountancy_admin_chart','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163544,'auguria',1,'','left','accountancy',163538,NULL,NULL,40,'/accountancy/admin/categories_list.php?id=32&mainmenu=accountancy&leftmenu=accountancy_admin','','AccountingCategory','accountancy',2,'accountancy_admin_chart_group','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163545,'auguria',1,'','left','accountancy',163538,NULL,NULL,50,'/accountancy/admin/defaultaccounts.php?mainmenu=accountancy&leftmenu=accountancy_admin','','MenuDefaultAccounts','accountancy',2,'accountancy_admin_default','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163546,'auguria',1,'','left','accountancy',163538,NULL,NULL,60,'/admin/dict.php?id=10&from=accountancy&search_country_id=__MYCOUNTRYID__&mainmenu=accountancy&leftmenu=accountancy_admin','','MenuVatAccounts','accountancy',2,'accountancy_admin_vat','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163547,'auguria',1,'','left','accountancy',163538,NULL,NULL,70,'/admin/dict.php?id=7&from=accountancy&search_country_id=__MYCOUNTRYID__&mainmenu=accountancy&leftmenu=accountancy_admin','','MenuTaxAccounts','accountancy',2,'accountancy_admin_tax','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163548,'auguria',1,'','left','accountancy',163538,NULL,NULL,80,'/admin/dict.php?id=17&from=accountancy&mainmenu=accountancy&leftmenu=accountancy_admin','','MenuExpenseReportAccounts','accountancy',2,'accountancy_admin_expensereport','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $conf->expensereport->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163549,'auguria',1,'','left','accountancy',163538,NULL,NULL,90,'/accountancy/admin/productaccount.php?mainmenu=accountancy&leftmenu=accountancy_admin','','MenuProductsAccounts','accountancy',2,'accountancy_admin_product','$user->rights->accounting->chartofaccount','$conf->accounting->enabled && $leftmenu==\"accountancy_admin\"',0,'2017-08-30 15:14:30'),(163587,'auguria',1,'','left','accountancy',161101,NULL,NULL,9,'/compta/prelevement/index.php?leftmenu=withdraw&mainmenu=bank','','StandingOrders','withdrawals',0,'withdraw','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled',2,'2017-08-30 15:14:30'),(163589,'auguria',1,'','left','accountancy',163587,NULL,NULL,0,'/compta/prelevement/create.php?leftmenu=withdraw','','NewStandingOrder','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163590,'auguria',1,'','left','accountancy',163587,NULL,NULL,2,'/compta/prelevement/bons.php?leftmenu=withdraw','','WithdrawalsReceipts','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163591,'auguria',1,'','left','accountancy',163587,NULL,NULL,3,'/compta/prelevement/list.php?leftmenu=withdraw','','WithdrawalsLines','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163593,'auguria',1,'','left','accountancy',163587,NULL,NULL,5,'/compta/prelevement/rejets.php?leftmenu=withdraw','','Rejects','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163594,'auguria',1,'','left','accountancy',163587,NULL,NULL,6,'/compta/prelevement/stats.php?leftmenu=withdraw','','Statistics','withdrawals',1,'','$user->rights->prelevement->bons->lire','$conf->prelevement->enabled && $leftmenu==\"withdraw\"',2,'2017-08-30 15:14:30'),(163687,'auguria',1,'','left','accountancy',161101,NULL,NULL,1,'/compta/bank/index.php?leftmenu=bank&mainmenu=bank','','MenuBankCash','banks',0,'bank','$user->rights->banque->lire','$conf->banque->enabled',0,'2017-08-30 15:14:30'),(163688,'auguria',1,'','left','accountancy',163687,NULL,NULL,0,'/compta/bank/card.php?action=create&leftmenu=bank','','MenuNewFinancialAccount','banks',1,'','$user->rights->banque->configurer','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163690,'auguria',1,'','left','accountancy',163687,NULL,NULL,2,'/compta/bank/bankentries.php?leftmenu=bank','','ListTransactions','banks',1,'','$user->rights->banque->lire','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163691,'auguria',1,'','left','accountancy',163687,NULL,NULL,3,'/compta/bank/budget.php?leftmenu=bank','','ListTransactionsByCategory','banks',1,'','$user->rights->banque->lire','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163693,'auguria',1,'','left','accountancy',163687,NULL,NULL,5,'/compta/bank/transfer.php?leftmenu=bank','','BankTransfers','banks',1,'','$user->rights->banque->transfer','$conf->banque->enabled && ($leftmenu==\"bank\" || $leftmenu==\"checks\" || $leftmenu==\"withdraw\")',0,'2017-08-30 15:14:30'),(163737,'auguria',1,'','left','accountancy',161101,NULL,NULL,4,'/categories/index.php?leftmenu=bank&type=5','','Categories','categories',0,'cat','$user->rights->categorie->lire','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(163738,'auguria',1,'','left','accountancy',163737,NULL,NULL,0,'/categories/card.php?leftmenu=bank&action=create&type=5','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(163787,'auguria',1,'','left','accountancy',161093,NULL,NULL,11,'/compta/resultat/index.php?leftmenu=ca&mainmenu=accountancy','','Reportings','main',0,'ca','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled',0,'2017-08-30 15:14:30'),(163792,'auguria',1,'','left','accountancy',163487,NULL,NULL,1,'','','Journalization','main',1,'','$user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163793,'auguria',1,'','left','accountancy',163792,NULL,NULL,4,'/accountancy/journal/sellsjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=1','','SellsJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163794,'auguria',1,'','left','accountancy',163792,NULL,NULL,1,'/accountancy/journal/bankjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=3','','BankJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163795,'auguria',1,'','left','accountancy',163792,NULL,NULL,2,'/accountancy/journal/expensereportsjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=6','','ExpenseReportJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163796,'auguria',1,'','left','accountancy',163792,NULL,NULL,3,'/accountancy/journal/purchasesjournal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal=2','','PurchasesJournal','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->accounting->enabled',0,'2017-08-30 15:14:30'),(163798,'auguria',1,'','left','accountancy',163787,NULL,NULL,0,'/compta/resultat/index.php?leftmenu=ca','','ReportInOut','main',1,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163799,'auguria',1,'','left','accountancy',163788,NULL,NULL,0,'/compta/resultat/clientfourn.php?leftmenu=ca','','ByCompanies','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163800,'auguria',1,'','left','accountancy',163787,NULL,NULL,1,'/compta/stats/index.php?leftmenu=ca','','ReportTurnover','main',1,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163801,'auguria',1,'','left','accountancy',163790,NULL,NULL,0,'/compta/stats/casoc.php?leftmenu=ca','','ByCompanies','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163802,'auguria',1,'','left','accountancy',163790,NULL,NULL,1,'/compta/stats/cabyuser.php?leftmenu=ca','','ByUsers','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163803,'auguria',1,'','left','accountancy',163790,NULL,NULL,1,'/compta/stats/cabyprodserv.php?leftmenu=ca','','ByProductsAndServices','main',2,'','$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire','$conf->comptabilite->enabled && $leftmenu==\"ca\"',0,'2017-08-30 15:14:30'),(163887,'auguria',1,'','left','products',161090,NULL,NULL,0,'/product/index.php?leftmenu=product&type=0','','Products','products',0,'product','$user->rights->produit->lire','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163888,'auguria',1,'','left','products',163887,NULL,NULL,0,'/product/card.php?leftmenu=product&action=create&type=0','','NewProduct','products',1,'','$user->rights->produit->creer','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163889,'auguria',1,'','left','products',163887,NULL,NULL,1,'/product/list.php?leftmenu=product&type=0','','List','products',1,'','$user->rights->produit->lire','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163890,'auguria',1,'','left','products',163887,NULL,NULL,4,'/product/reassort.php?type=0','','Stocks','products',1,'','$user->rights->produit->lire && $user->rights->stock->lire','$conf->product->enabled',2,'2017-08-30 15:14:30'),(163891,'auguria',1,'','left','products',163887,NULL,NULL,7,'/product/stats/card.php?id=all&leftmenu=stats&type=0','','Statistics','main',1,'','$user->rights->produit->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(163892,'auguria',1,'','left','products',163887,NULL,NULL,5,'/product/reassortlot.php?type=0','','StocksByLotSerial','products',1,'','$user->rights->produit->lire && $user->rights->stock->lire','$conf->productbatch->enabled',2,'2017-08-30 15:14:30'),(163893,'auguria',1,'','left','products',163887,NULL,NULL,6,'/product/stock/productlot_list.php','','LotSerial','products',1,'','$user->rights->produit->lire && $user->rights->stock->lire','$conf->productbatch->enabled',2,'2017-08-30 15:14:30'),(163987,'auguria',1,'','left','products',161090,NULL,NULL,1,'/product/index.php?leftmenu=service&type=1','','Services','products',0,'service','$user->rights->service->lire','$conf->service->enabled',2,'2017-08-30 15:14:30'),(163988,'auguria',1,'','left','products',163987,NULL,NULL,0,'/product/card.php?leftmenu=service&action=create&type=1','','NewService','products',1,'','$user->rights->service->creer','$conf->service->enabled',2,'2017-08-30 15:14:30'),(163989,'auguria',1,'','left','products',163987,NULL,NULL,1,'/product/list.php?leftmenu=service&type=1','','List','products',1,'','$user->rights->service->lire','$conf->service->enabled',2,'2017-08-30 15:14:30'),(163990,'auguria',1,'','left','products',163987,NULL,NULL,5,'/product/stats/card.php?id=all&leftmenu=stats&type=1','','Statistics','main',1,'','$user->rights->service->lire','$conf->propal->enabled',2,'2017-08-30 15:14:30'),(164187,'auguria',1,'','left','products',161090,NULL,NULL,3,'/product/stock/index.php?leftmenu=stock','','Stock','stocks',0,'stock','$user->rights->stock->lire','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164188,'auguria',1,'','left','products',164187,NULL,NULL,0,'/product/stock/card.php?action=create','','MenuNewWarehouse','stocks',1,'','$user->rights->stock->creer','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164189,'auguria',1,'','left','products',164187,NULL,NULL,1,'/product/stock/list.php','','List','stocks',1,'','$user->rights->stock->lire','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164191,'auguria',1,'','left','products',164187,NULL,NULL,3,'/product/stock/mouvement.php','','Movements','stocks',1,'','$user->rights->stock->mouvement->lire','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164192,'auguria',1,'','left','products',164187,NULL,NULL,4,'/product/stock/replenish.php','','Replenishments','stocks',1,'','$user->rights->stock->mouvement->creer && $user->rights->fournisseur->lire','$conf->stock->enabled && $conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(164193,'auguria',1,'','left','products',164187,NULL,NULL,5,'/product/stock/massstockmove.php','','MassStockTransferShort','stocks',1,'','$user->rights->stock->mouvement->creer','$conf->stock->enabled',2,'2017-08-30 15:14:30'),(164287,'auguria',1,'','left','products',161090,NULL,NULL,4,'/categories/index.php?leftmenu=cat&type=0','','Categories','categories',0,'cat','$user->rights->categorie->lire','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164288,'auguria',1,'','left','products',164287,NULL,NULL,0,'/categories/card.php?action=create&type=0','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164487,'auguria',1,'','left','project',161094,NULL,NULL,3,'/projet/activity/perweek.php?leftmenu=projects','','NewTimeSpent','projects',0,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164687,'auguria',1,'','left','project',161094,NULL,NULL,0,'/projet/index.php?leftmenu=projects','','Projects','projects',0,'projects','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164688,'auguria',1,'','left','project',164687,NULL,NULL,1,'/projet/card.php?leftmenu=projects&action=create','','NewProject','projects',1,'','$user->rights->projet->creer','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164689,'auguria',1,'','left','project',164687,NULL,NULL,2,'/projet/list.php?leftmenu=projects','','List','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164690,'auguria',1,'','left','project',164687,NULL,NULL,3,'/projet/stats/index.php?leftmenu=projects','','Statistics','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled',2,'2017-08-30 15:14:30'),(164787,'auguria',1,'','left','project',161094,NULL,NULL,0,'/projet/activity/index.php?leftmenu=projects','','Activities','projects',0,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164788,'auguria',1,'','left','project',164787,NULL,NULL,1,'/projet/tasks.php?leftmenu=projects&action=create','','NewTask','projects',1,'','$user->rights->projet->creer','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164789,'auguria',1,'','left','project',164787,NULL,NULL,2,'/projet/tasks/list.php?leftmenu=projects','','List','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164791,'auguria',1,'','left','project',164787,NULL,NULL,4,'/projet/tasks/stats/index.php?leftmenu=projects','','Statistics','projects',1,'','$user->rights->projet->lire','$conf->projet->enabled && $conf->global->PROJECT_USE_TASKS',2,'2017-08-30 15:14:30'),(164891,'auguria',1,'','left','project',161094,NULL,NULL,4,'/categories/index.php?leftmenu=cat&type=6','','Categories','categories',0,'cat','$user->rights->categorie->lire','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164892,'auguria',1,'','left','project',164891,NULL,NULL,0,'/categories/card.php?action=create&type=6','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->categorie->enabled',2,'2017-08-30 15:14:30'),(164987,'auguria',1,'','left','tools',161095,NULL,NULL,0,'/comm/mailing/index.php?leftmenu=mailing','','EMailings','mails',0,'mailing','$user->rights->mailing->lire','$conf->mailing->enabled',0,'2017-08-30 15:14:30'),(164988,'auguria',1,'','left','tools',164987,NULL,NULL,0,'/comm/mailing/card.php?leftmenu=mailing&action=create','','NewMailing','mails',1,'','$user->rights->mailing->creer','$conf->mailing->enabled',0,'2017-08-30 15:14:30'),(164989,'auguria',1,'','left','tools',164987,NULL,NULL,1,'/comm/mailing/list.php?leftmenu=mailing','','List','mails',1,'','$user->rights->mailing->lire','$conf->mailing->enabled',0,'2017-08-30 15:14:30'),(165187,'auguria',1,'','left','tools',161095,NULL,NULL,2,'/exports/index.php?leftmenu=export','','FormatedExport','exports',0,'export','$user->rights->export->lire','$conf->export->enabled',2,'2017-08-30 15:14:30'),(165188,'auguria',1,'','left','tools',165187,NULL,NULL,0,'/exports/export.php?leftmenu=export','','NewExport','exports',1,'','$user->rights->export->creer','$conf->export->enabled',2,'2017-08-30 15:14:30'),(165217,'auguria',1,'','left','tools',161095,NULL,NULL,2,'/imports/index.php?leftmenu=import','','FormatedImport','exports',0,'import','$user->rights->import->run','$conf->import->enabled',2,'2017-08-30 15:14:30'),(165218,'auguria',1,'','left','tools',165217,NULL,NULL,0,'/imports/import.php?leftmenu=import','','NewImport','exports',1,'','$user->rights->import->run','$conf->import->enabled',2,'2017-08-30 15:14:30'),(165287,'auguria',1,'','left','members',161100,NULL,NULL,0,'/adherents/index.php?leftmenu=members&mainmenu=members','','Members','members',0,'members','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165288,'auguria',1,'','left','members',165287,NULL,NULL,0,'/adherents/card.php?leftmenu=members&action=create','','NewMember','members',1,'','$user->rights->adherent->creer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165289,'auguria',1,'','left','members',165287,NULL,NULL,1,'/adherents/list.php','','List','members',1,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165290,'auguria',1,'','left','members',165289,NULL,NULL,2,'/adherents/list.php?leftmenu=members&statut=-1','','MenuMembersToValidate','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165291,'auguria',1,'','left','members',165289,NULL,NULL,3,'/adherents/list.php?leftmenu=members&statut=1','','MenuMembersValidated','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165292,'auguria',1,'','left','members',165289,NULL,NULL,4,'/adherents/list.php?leftmenu=members&statut=1&filter=outofdate','','MenuMembersNotUpToDate','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165293,'auguria',1,'','left','members',165289,NULL,NULL,5,'/adherents/list.php?leftmenu=members&statut=1&filter=uptodate','','MenuMembersUpToDate','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165294,'auguria',1,'','left','members',165289,NULL,NULL,6,'/adherents/list.php?leftmenu=members&statut=0','','MenuMembersResiliated','members',2,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165295,'auguria',1,'','left','members',165287,NULL,NULL,7,'/adherents/stats/geo.php?leftmenu=members&mode=memberbycountry','','MenuMembersStats','members',1,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165387,'auguria',1,'','left','members',161100,NULL,NULL,1,'/adherents/index.php?leftmenu=members&mainmenu=members','','Subscriptions','compta',0,'','$user->rights->adherent->cotisation->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165388,'auguria',1,'','left','members',165387,NULL,NULL,0,'/adherents/list.php?statut=-1&leftmenu=accountancy&mainmenu=members','','NewSubscription','compta',1,'','$user->rights->adherent->cotisation->creer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165389,'auguria',1,'','left','members',165387,NULL,NULL,1,'/adherents/subscription/list.php?leftmenu=members','','List','compta',1,'','$user->rights->adherent->cotisation->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165390,'auguria',1,'','left','members',165387,NULL,NULL,7,'/adherents/stats/index.php?leftmenu=members','','MenuMembersStats','members',1,'','$user->rights->adherent->lire','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165589,'auguria',1,'','left','members',165287,NULL,NULL,9,'/adherents/htpasswd.php?leftmenu=export','','Filehtpasswd','members',1,'','$user->rights->adherent->export','! empty($conf->global->MEMBER_LINK_TO_HTPASSWDFILE) && $conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165590,'auguria',1,'','left','members',165287,NULL,NULL,10,'/adherents/cartes/carte.php?leftmenu=export','','MembersCards','members',1,'','$user->rights->adherent->export','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165687,'auguria',1,'','left','hrm',161102,NULL,NULL,1,'/user/index.php?leftmenu=hrm&mode=employee','','Employees','hrm',0,'hrm','$user->rights->hrm->employee->read','$conf->hrm->enabled',0,'2017-08-30 15:14:30'),(165688,'auguria',1,'','left','hrm',165687,NULL,NULL,1,'/user/card.php?action=create&employee=1','','NewEmployee','hrm',1,'','$user->rights->hrm->employee->write','$conf->hrm->enabled',0,'2017-08-30 15:14:30'),(165689,'auguria',1,'','left','hrm',165687,NULL,NULL,2,'/user/index.php?$leftmenu=hrm&mode=employee&contextpage=employeelist','','List','hrm',1,'','$user->rights->hrm->employee->read','$conf->hrm->enabled',0,'2017-08-30 15:14:30'),(165787,'auguria',1,'','left','members',161100,NULL,NULL,5,'/adherents/type.php?leftmenu=setup&mainmenu=members','','MembersTypes','members',0,'setup','$user->rights->adherent->configurer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165788,'auguria',1,'','left','members',165787,NULL,NULL,0,'/adherents/type.php?leftmenu=setup&mainmenu=members&action=create','','New','members',1,'','$user->rights->adherent->configurer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(165789,'auguria',1,'','left','members',165787,NULL,NULL,1,'/adherents/type.php?leftmenu=setup&mainmenu=members','','List','members',1,'','$user->rights->adherent->configurer','$conf->adherent->enabled',2,'2017-08-30 15:14:30'),(166087,'auguria',1,'','left','hrm',161102,NULL,NULL,1,'/holiday/list.php?&leftmenu=hrm','','CPTitreMenu','holiday',0,'hrm','$user->rights->holiday->read','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166088,'auguria',1,'','left','hrm',166087,NULL,NULL,1,'/holiday/card.php?&action=request','','MenuAddCP','holiday',1,'','$user->rights->holiday->write','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166089,'auguria',1,'','left','hrm',166087,NULL,NULL,1,'/holiday/list.php?&leftmenu=hrm','','List','holiday',1,'','$user->rights->holiday->read','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166090,'auguria',1,'','left','hrm',166089,NULL,NULL,1,'/holiday/list.php?select_statut=2&leftmenu=hrm','','ListToApprove','trips',2,'','$user->rights->holiday->read','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166091,'auguria',1,'','left','hrm',166087,NULL,NULL,2,'/holiday/define_holiday.php?&action=request','','MenuConfCP','holiday',1,'','$user->rights->holiday->define_holiday','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166092,'auguria',1,'','left','hrm',166087,NULL,NULL,3,'/holiday/view_log.php?&action=request','','MenuLogCP','holiday',1,'','$user->rights->holiday->define_holiday','$conf->holiday->enabled',0,'2017-08-30 15:14:30'),(166187,'auguria',1,'','left','commercial',161092,NULL,NULL,6,'/fourn/commande/index.php?leftmenu=orders_suppliers','','SuppliersOrders','orders',0,'orders_suppliers','$user->rights->fournisseur->commande->lire','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166188,'auguria',1,'','left','commercial',166187,NULL,NULL,0,'/fourn/commande/card.php?action=create&leftmenu=orders_suppliers','','NewOrder','orders',1,'','$user->rights->fournisseur->commande->creer','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166189,'auguria',1,'','left','commercial',166187,NULL,NULL,1,'/fourn/commande/list.php?leftmenu=orders_suppliers&viewstatut=0','','List','orders',1,'','$user->rights->fournisseur->commande->lire','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166195,'auguria',1,'','left','commercial',166187,NULL,NULL,7,'/commande/stats/index.php?leftmenu=orders_suppliers&mode=supplier','','Statistics','orders',1,'','$user->rights->fournisseur->commande->lire','$conf->supplier_order->enabled',2,'2017-08-30 15:14:30'),(166287,'auguria',1,'','left','members',161100,NULL,NULL,3,'/categories/index.php?leftmenu=cat&type=3','','MembersCategoriesShort','categories',0,'cat','$user->rights->categorie->lire','$conf->adherent->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(166288,'auguria',1,'','left','members',166287,NULL,NULL,0,'/categories/card.php?action=create&type=3','','NewCategory','categories',1,'','$user->rights->categorie->creer','$conf->adherent->enabled && $conf->categorie->enabled',2,'2017-08-30 15:14:30'),(166387,'auguria',1,'','left','hrm',161102,NULL,NULL,5,'/expensereport/index.php?leftmenu=expensereport','','TripsAndExpenses','trips',0,'expensereport','$user->rights->expensereport->lire','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166388,'auguria',1,'','left','hrm',166387,NULL,NULL,1,'/expensereport/card.php?action=create&leftmenu=expensereport','','New','trips',1,'','$user->rights->expensereport->creer','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166389,'auguria',1,'','left','hrm',166387,NULL,NULL,2,'/expensereport/list.php?leftmenu=expensereport','','List','trips',1,'','$user->rights->expensereport->lire','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166390,'auguria',1,'','left','hrm',166389,NULL,NULL,2,'/expensereport/list.php?search_status=2&leftmenu=expensereport','','ListToApprove','trips',2,'','$user->rights->expensereport->approve','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166391,'auguria',1,'','left','hrm',166387,NULL,NULL,2,'/expensereport/stats/index.php?leftmenu=expensereport','','Statistics','trips',1,'','$user->rights->expensereport->lire','$conf->expensereport->enabled',0,'2017-08-30 15:14:30'),(166467,'all',1,'variants','left','products',-1,'product','products',100,'/variants/list.php','','VariantAttributes','products',NULL,'product','1','$conf->product->enabled',0,'2018-01-19 11:28:04'),(166468,'all',1,'agenda','top','agenda',0,NULL,NULL,15,'/comm/action/index.php','','TMenuAgenda','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166469,'all',1,'agenda','left','agenda',166468,NULL,NULL,100,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Actions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166470,'all',1,'agenda','left','agenda',166469,NULL,NULL,101,'/comm/action/card.php?mainmenu=agenda&leftmenu=agenda&action=create','','NewAction','commercial',NULL,NULL,'($user->rights->agenda->myactions->create||$user->rights->agenda->allactions->create)','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166471,'all',1,'agenda','left','agenda',166469,NULL,NULL,140,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda','','Agenda','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166472,'all',1,'agenda','left','agenda',166471,NULL,NULL,141,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166473,'all',1,'agenda','left','agenda',166471,NULL,NULL,142,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166474,'all',1,'agenda','left','agenda',166471,NULL,NULL,143,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=todo&filtert=-1','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-03-16 09:54:05'),(166475,'all',1,'agenda','left','agenda',166471,NULL,NULL,144,'/comm/action/index.php?mainmenu=agenda&leftmenu=agenda&status=done&filtert=-1','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-03-16 09:54:05'),(166476,'all',1,'agenda','left','agenda',166469,NULL,NULL,110,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda','','List','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166477,'all',1,'agenda','left','agenda',166476,NULL,NULL,111,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=todo&filter=mine','','MenuToDoMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166478,'all',1,'agenda','left','agenda',166476,NULL,NULL,112,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=done&filter=mine','','MenuDoneMyActions','agenda',NULL,NULL,'$user->rights->agenda->myactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166479,'all',1,'agenda','left','agenda',166476,NULL,NULL,113,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=todo&filtert=-1','','MenuToDoActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-03-16 09:54:05'),(166480,'all',1,'agenda','left','agenda',166476,NULL,NULL,114,'/comm/action/list.php?mainmenu=agenda&leftmenu=agenda&status=done&filtert=-1','','MenuDoneActions','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$user->rights->agenda->allactions->read',2,'2018-03-16 09:54:05'),(166481,'all',1,'agenda','left','agenda',166469,NULL,NULL,160,'/comm/action/rapport/index.php?mainmenu=agenda&leftmenu=agenda','','Reportings','agenda',NULL,NULL,'$user->rights->agenda->allactions->read','$conf->agenda->enabled',2,'2018-03-16 09:54:05'),(166482,'all',1,'barcode','left','tools',-1,NULL,'tools',200,'/barcode/printsheet.php?mainmenu=tools&leftmenu=barcodeprint','','BarCodePrintsheet','products',NULL,'barcodeprint','($conf->global->MAIN_USE_ADVANCED_PERMS && $user->rights->barcode->lire_advance) || (! $conf->global->MAIN_USE_ADVANCED_PERMS)','$conf->barcode->enabled',2,'2018-03-16 09:54:05'),(166483,'all',1,'barcode','left','home',-1,'admintools','home',300,'/barcode/codeinit.php?mainmenu=home&leftmenu=admintools','','MassBarcodeInit','products',NULL,NULL,'($conf->global->MAIN_USE_ADVANCED_PERMS && $user->rights->barcode->creer_advance) || (! $conf->global->MAIN_USE_ADVANCED_PERMS)','$conf->barcode->enabled && preg_match(\'/^admintools/\',$leftmenu)',0,'2018-03-16 09:54:05'),(166484,'all',1,'cron','left','home',-1,'admintools','home',200,'/cron/list.php?status=-2&leftmenu=admintools','','CronList','cron',NULL,NULL,'$user->rights->cron->read','$conf->cron->enabled && preg_match(\'/^admintools/\', $leftmenu)',2,'2018-03-16 09:54:05'),(166485,'all',1,'ecm','top','ecm',0,NULL,NULL,100,'/ecm/index.php','','MenuECM','ecm',NULL,NULL,'$user->rights->ecm->read || $user->rights->ecm->upload || $user->rights->ecm->setup','$conf->ecm->enabled',2,'2018-03-16 09:54:05'),(166486,'all',1,'ecm','left','ecm',-1,NULL,'ecm',101,'/ecm/index.php?mainmenu=ecm&leftmenu=ecm','','ECMArea','ecm',NULL,'ecm','$user->rights->ecm->read || $user->rights->ecm->upload','$user->rights->ecm->read || $user->rights->ecm->upload',2,'2018-03-16 09:54:05'),(166487,'all',1,'ecm','left','ecm',-1,'ecm','ecm',102,'/ecm/index.php?action=file_manager&mainmenu=ecm&leftmenu=ecm','','ECMSectionsManual','ecm',NULL,'ecm_manual','$user->rights->ecm->read || $user->rights->ecm->upload','$user->rights->ecm->read || $user->rights->ecm->upload',2,'2018-03-16 09:54:05'),(166488,'all',1,'ecm','left','ecm',-1,'ecm','ecm',103,'/ecm/index_auto.php?action=file_manager&mainmenu=ecm&leftmenu=ecm','','ECMSectionsAuto','ecm',NULL,NULL,'$user->rights->ecm->read || $user->rights->ecm->upload','($user->rights->ecm->read || $user->rights->ecm->upload) && ! empty($conf->global->ECM_AUTO_TREE_ENABLED)',2,'2018-03-16 09:54:05'),(166489,'all',1,'opensurvey','left','tools',-1,NULL,'tools',200,'/opensurvey/index.php?mainmenu=tools&leftmenu=opensurvey','','Survey','opensurvey',NULL,'opensurvey','$user->rights->opensurvey->read','$conf->opensurvey->enabled',0,'2018-03-16 09:54:05'),(166490,'all',1,'opensurvey','left','tools',-1,'opensurvey','tools',210,'/opensurvey/wizard/index.php','','NewSurvey','opensurvey',NULL,'opensurvey_new','$user->rights->opensurvey->write','$conf->opensurvey->enabled',0,'2018-03-16 09:54:05'),(166491,'all',1,'opensurvey','left','tools',-1,'opensurvey','tools',220,'/opensurvey/list.php','','List','opensurvey',NULL,'opensurvey_list','$user->rights->opensurvey->read','$conf->opensurvey->enabled',0,'2018-03-16 09:54:05'),(166492,'all',1,'blockedlog','left','tools',-1,NULL,'tools',200,'/blockedlog/admin/blockedlog_list.php?mainmenu=tools&leftmenu=blockedlogbrowser','','BrowseBlockedLog','blockedlog',NULL,'blockedlogbrowser','$user->rights->blockedlog->read','$conf->blockedlog->enabled',2,'2018-03-16 09:57:24'); /*!40000 ALTER TABLE `llx_menu` ENABLE KEYS */; UNLOCK TABLES; @@ -6202,12 +6345,12 @@ DROP TABLE IF EXISTS `llx_multicurrency`; CREATE TABLE `llx_multicurrency` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `date_create` datetime DEFAULT NULL, - `code` varchar(255) DEFAULT NULL, - `name` varchar(255) DEFAULT NULL, + `code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) DEFAULT '1', `fk_user` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6234,7 +6377,7 @@ CREATE TABLE `llx_multicurrency_rate` ( `fk_multicurrency` int(11) NOT NULL, `entity` int(11) DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6262,13 +6405,13 @@ CREATE TABLE `llx_notify` ( `fk_soc` int(11) DEFAULT NULL, `fk_contact` int(11) DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, - `objet_type` varchar(24) NOT NULL, + `objet_type` varchar(24) COLLATE utf8_unicode_ci NOT NULL, `objet_id` int(11) NOT NULL, - `email` varchar(255) DEFAULT NULL, - `type` varchar(16) DEFAULT 'email', - `type_target` varchar(16) DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `type` varchar(16) COLLATE utf8_unicode_ci DEFAULT 'email', + `type_target` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6295,9 +6438,9 @@ CREATE TABLE `llx_notify_def` ( `fk_soc` int(11) DEFAULT NULL, `fk_contact` int(11) DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, - `type` varchar(16) DEFAULT 'email', + `type` varchar(16) COLLATE utf8_unicode_ci DEFAULT 'email', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6347,13 +6490,13 @@ DROP TABLE IF EXISTS `llx_oauth_state`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_oauth_state` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `service` varchar(36) DEFAULT NULL, - `state` varchar(128) DEFAULT NULL, + `service` varchar(36) COLLATE utf8_unicode_ci DEFAULT NULL, + `state` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, `fk_adherent` int(11) DEFAULT NULL, `entity` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6374,13 +6517,13 @@ DROP TABLE IF EXISTS `llx_oauth_token`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_oauth_token` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `service` varchar(36) DEFAULT NULL, - `token` text, + `service` varchar(36) COLLATE utf8_unicode_ci DEFAULT NULL, + `token` text COLLATE utf8_unicode_ci, `fk_user` int(11) DEFAULT NULL, `fk_adherent` int(11) DEFAULT NULL, `entity` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6431,13 +6574,13 @@ DROP TABLE IF EXISTS `llx_opensurvey_comments`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_opensurvey_comments` ( `id_comment` int(10) unsigned NOT NULL AUTO_INCREMENT, - `id_sondage` char(16) NOT NULL, - `comment` text NOT NULL, - `usercomment` text, + `id_sondage` char(16) COLLATE utf8_unicode_ci NOT NULL, + `comment` text COLLATE utf8_unicode_ci NOT NULL, + `usercomment` text COLLATE utf8_unicode_ci, PRIMARY KEY (`id_comment`), KEY `idx_id_comment` (`id_comment`), KEY `idx_id_sondage` (`id_sondage`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6459,11 +6602,11 @@ DROP TABLE IF EXISTS `llx_opensurvey_formquestions`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_opensurvey_formquestions` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `id_sondage` varchar(16) DEFAULT NULL, - `question` text, - `available_answers` text, + `id_sondage` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL, + `question` text COLLATE utf8_unicode_ci, + `available_answers` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6483,24 +6626,24 @@ DROP TABLE IF EXISTS `llx_opensurvey_sondage`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_opensurvey_sondage` ( - `id_sondage` varchar(16) NOT NULL, - `commentaires` text, - `mail_admin` varchar(128) DEFAULT NULL, - `nom_admin` varchar(64) DEFAULT NULL, + `id_sondage` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `commentaires` text COLLATE utf8_unicode_ci, + `mail_admin` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `nom_admin` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_creat` int(11) NOT NULL, - `titre` text NOT NULL, + `titre` text COLLATE utf8_unicode_ci NOT NULL, `date_fin` datetime DEFAULT NULL, `status` int(11) DEFAULT '1', - `format` varchar(2) NOT NULL, + `format` varchar(2) COLLATE utf8_unicode_ci NOT NULL, `mailsonde` tinyint(4) NOT NULL DEFAULT '0', `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `entity` int(11) NOT NULL DEFAULT '1', `allow_comments` tinyint(4) NOT NULL DEFAULT '1', `allow_spy` tinyint(4) NOT NULL DEFAULT '1', - `sujet` text, + `sujet` text COLLATE utf8_unicode_ci, PRIMARY KEY (`id_sondage`), KEY `idx_date_fin` (`date_fin`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6509,7 +6652,7 @@ CREATE TABLE `llx_opensurvey_sondage` ( LOCK TABLES `llx_opensurvey_sondage` WRITE; /*!40000 ALTER TABLE `llx_opensurvey_sondage` DISABLE KEYS */; -INSERT INTO `llx_opensurvey_sondage` VALUES ('m4467s2mtk6khmxc','What is your prefered date for a brunch','myemail@aaa.com','fdfds',0,'Date of next brunch','2013-03-07 00:00:00',1,'D',1,'2017-02-20 16:47:22',1,1,1,',1483473600'),('tim1dye8x5eeetxu','Please vote for the candidate you want to have for our new president this year.',NULL,NULL,12,'Election of new president','2017-02-26 04:00:00',1,'A',0,'2017-02-20 16:53:37',1,1,0,'Alan Candide@foragainst,Alex Candor@foragainst'); +INSERT INTO `llx_opensurvey_sondage` VALUES ('m4467s2mtk6khmxc','What is your prefered date for a brunch','myemail@aaa.com','fdfds',0,'Date of next brunch','2013-03-07 00:00:00',1,'D',1,'2018-03-16 10:00:54',1,1,1,',1483473600'),('tim1dye8x5eeetxu','Please vote for the candidate you want to have for our new president this year.',NULL,NULL,12,'Election of new president','2017-02-26 04:00:00',1,'A',0,'2018-03-16 10:00:54',1,1,0,'Alan Candide@foragainst,Alex Candor@foragainst'); /*!40000 ALTER TABLE `llx_opensurvey_sondage` ENABLE KEYS */; UNLOCK TABLES; @@ -6523,8 +6666,8 @@ DROP TABLE IF EXISTS `llx_opensurvey_user_formanswers`; CREATE TABLE `llx_opensurvey_user_formanswers` ( `fk_user_survey` int(11) NOT NULL, `fk_question` int(11) NOT NULL, - `reponses` text -) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `reponses` text COLLATE utf8_unicode_ci +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6545,9 +6688,9 @@ DROP TABLE IF EXISTS `llx_opensurvey_user_studs`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_opensurvey_user_studs` ( `id_users` int(11) NOT NULL AUTO_INCREMENT, - `nom` varchar(64) NOT NULL, - `id_sondage` varchar(16) NOT NULL, - `reponses` varchar(100) NOT NULL, + `nom` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `id_sondage` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `reponses` varchar(100) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id_users`), KEY `idx_id_users` (`id_users`), KEY `idx_nom` (`nom`), @@ -6555,7 +6698,7 @@ CREATE TABLE `llx_opensurvey_user_studs` ( KEY `idx_opensurvey_user_studs_id_users` (`id_users`), KEY `idx_opensurvey_user_studs_nom` (`nom`), KEY `idx_opensurvey_user_studs_id_sondage` (`id_sondage`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6578,12 +6721,12 @@ DROP TABLE IF EXISTS `llx_overwrite_trans`; CREATE TABLE `llx_overwrite_trans` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `lang` varchar(5) DEFAULT NULL, - `transkey` varchar(128) DEFAULT NULL, - `transvalue` text, + `lang` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, + `transkey` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `transvalue` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_overwrite_trans` (`lang`,`transkey`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6604,15 +6747,15 @@ DROP TABLE IF EXISTS `llx_paiement`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_paiement` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(30) NOT NULL DEFAULT '', + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datep` datetime DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, `fk_paiement` int(11) NOT NULL, - `num_paiement` varchar(50) DEFAULT NULL, - `note` text, + `num_paiement` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) NOT NULL DEFAULT '0', `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, @@ -6620,7 +6763,7 @@ CREATE TABLE `llx_paiement` ( `fk_export_compta` int(11) NOT NULL DEFAULT '0', `multicurrency_amount` double(24,8) DEFAULT '0.00000000', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6629,7 +6772,7 @@ CREATE TABLE `llx_paiement` ( LOCK TABLES `llx_paiement` WRITE; /*!40000 ALTER TABLE `llx_paiement` DISABLE KEYS */; -INSERT INTO `llx_paiement` VALUES (2,'',1,'2011-07-18 20:50:24','2016-07-30 15:13:20','2016-07-08 12:00:00',20.00000000,6,'','',5,1,NULL,0,0,0.00000000),(3,'',1,'2011-07-18 20:50:47','2016-07-30 15:13:20','2016-07-08 12:00:00',10.00000000,4,'','',6,1,NULL,0,0,0.00000000),(5,'',1,'2011-08-01 03:34:11','2016-07-30 15:12:32','2015-08-01 03:34:11',5.63000000,6,'','Payment Invoice FA1108-0003',8,1,NULL,0,0,0.00000000),(6,'',1,'2011-08-06 20:33:54','2016-07-30 15:12:32','2015-08-06 20:33:53',5.98000000,4,'','Payment Invoice FA1108-0004',13,1,NULL,0,0,0.00000000),(8,'',1,'2011-08-08 02:53:40','2016-07-30 15:12:32','2015-08-08 12:00:00',26.10000000,4,'','',14,1,NULL,0,0,0.00000000),(9,'',1,'2011-08-08 02:55:58','2016-07-30 15:12:32','2015-08-08 12:00:00',26.96000000,1,'','',15,1,NULL,0,0,0.00000000),(17,'',1,'2012-12-09 15:28:44','2016-07-30 15:12:32','2015-12-09 12:00:00',2.00000000,4,'','',16,1,NULL,0,0,0.00000000),(18,'',1,'2012-12-09 15:28:53','2016-07-30 15:12:32','2015-12-09 12:00:00',-2.00000000,4,'','',17,1,NULL,0,0,0.00000000),(19,'',1,'2012-12-09 17:35:55','2016-07-30 15:12:32','2015-12-09 12:00:00',-2.00000000,4,'','',18,1,NULL,0,0,0.00000000),(20,'',1,'2012-12-09 17:37:02','2016-07-30 15:12:32','2015-12-09 12:00:00',2.00000000,4,'','',19,1,NULL,0,0,0.00000000),(21,'',1,'2012-12-09 18:35:07','2016-07-30 15:12:32','2015-12-09 12:00:00',-2.00000000,4,'','',20,1,NULL,0,0,0.00000000),(23,'',1,'2012-12-12 18:54:33','2016-07-30 15:12:32','2015-12-12 12:00:00',1.00000000,1,'','',21,1,NULL,0,0,0.00000000),(24,'',1,'2013-03-06 16:48:16','2016-07-30 15:13:20','2016-03-06 00:00:00',20.00000000,4,'','Adhésion/cotisation 2016',22,1,NULL,0,0,0.00000000),(25,'',1,'2013-03-20 14:30:11','2016-07-30 15:13:20','2016-03-20 00:00:00',10.00000000,2,'','Adhésion/cotisation 2011',23,1,NULL,0,0,0.00000000),(26,'',1,'2014-03-02 19:57:58','2016-07-30 15:13:20','2016-07-09 12:00:00',605.00000000,2,'','',24,1,NULL,0,0,0.00000000),(29,'',1,'2014-03-02 20:01:39','2016-07-30 15:13:20','2016-03-19 12:00:00',500.00000000,4,'','',26,1,NULL,0,0,0.00000000),(30,'',1,'2014-03-02 20:02:06','2016-07-30 15:13:20','2016-03-21 12:00:00',400.00000000,2,'','',27,1,NULL,0,0,0.00000000),(32,'',1,'2014-03-03 19:22:32','2016-07-30 15:12:32','2015-10-03 12:00:00',-400.00000000,4,'','',28,1,NULL,0,0,0.00000000),(33,'',1,'2014-03-03 19:23:16','2016-07-30 15:13:20','2016-03-10 12:00:00',-300.00000000,4,'','',29,1,NULL,0,0,0.00000000),(34,'PAY1603-0001',1,'2017-02-06 08:10:24','2017-02-06 04:10:24','2016-03-22 12:00:00',150.00000000,7,'','',33,12,NULL,0,0,150.00000000),(35,'PAY1603-0002',1,'2017-02-06 08:10:50','2017-02-06 04:10:50','2016-03-25 12:00:00',140.00000000,3,'','',34,12,NULL,0,0,140.00000000),(36,'PAY1702-0003',1,'2017-02-21 16:07:43','2017-02-21 12:07:43','2017-02-21 12:00:00',50.00000000,3,'T170201','',37,12,NULL,0,0,50.00000000); +INSERT INTO `llx_paiement` VALUES (2,'',1,'2011-07-18 20:50:24','2016-07-30 15:13:20','2016-07-08 12:00:00',20.00000000,6,'','',5,1,NULL,0,0,0.00000000),(3,'',1,'2011-07-18 20:50:47','2016-07-30 15:13:20','2016-07-08 12:00:00',10.00000000,4,'','',6,1,NULL,0,0,0.00000000),(5,'',1,'2011-08-01 03:34:11','2016-07-30 15:12:32','2015-08-01 03:34:11',5.63000000,6,'','Payment Invoice FA1108-0003',8,1,NULL,0,0,0.00000000),(6,'',1,'2011-08-06 20:33:54','2016-07-30 15:12:32','2015-08-06 20:33:53',5.98000000,4,'','Payment Invoice FA1108-0004',13,1,NULL,0,0,0.00000000),(8,'',1,'2011-08-08 02:53:40','2016-07-30 15:12:32','2015-08-08 12:00:00',26.10000000,4,'','',14,1,NULL,0,0,0.00000000),(9,'',1,'2011-08-08 02:55:58','2016-07-30 15:12:32','2015-08-08 12:00:00',26.96000000,1,'','',15,1,NULL,0,0,0.00000000),(17,'',1,'2012-12-09 15:28:44','2016-07-30 15:12:32','2015-12-09 12:00:00',2.00000000,4,'','',16,1,NULL,0,0,0.00000000),(18,'',1,'2012-12-09 15:28:53','2016-07-30 15:12:32','2015-12-09 12:00:00',-2.00000000,4,'','',17,1,NULL,0,0,0.00000000),(19,'',1,'2012-12-09 17:35:55','2016-07-30 15:12:32','2015-12-09 12:00:00',-2.00000000,4,'','',18,1,NULL,0,0,0.00000000),(20,'',1,'2012-12-09 17:37:02','2016-07-30 15:12:32','2015-12-09 12:00:00',2.00000000,4,'','',19,1,NULL,0,0,0.00000000),(21,'',1,'2012-12-09 18:35:07','2016-07-30 15:12:32','2015-12-09 12:00:00',-2.00000000,4,'','',20,1,NULL,0,0,0.00000000),(23,'',1,'2012-12-12 18:54:33','2016-07-30 15:12:32','2015-12-12 12:00:00',1.00000000,1,'','',21,1,NULL,0,0,0.00000000),(24,'',1,'2013-03-06 16:48:16','2016-07-30 15:13:20','2016-03-06 00:00:00',20.00000000,4,'','Adhésion/cotisation 2016',22,1,NULL,0,0,0.00000000),(25,'',1,'2013-03-20 14:30:11','2016-07-30 15:13:20','2016-03-20 00:00:00',10.00000000,2,'','Adhésion/cotisation 2011',23,1,NULL,0,0,0.00000000),(26,'',1,'2014-03-02 19:57:58','2016-07-30 15:13:20','2016-07-09 12:00:00',605.00000000,2,'','',24,1,NULL,0,0,0.00000000),(29,'',1,'2014-03-02 20:01:39','2016-07-30 15:13:20','2016-03-19 12:00:00',500.00000000,4,'','',26,1,NULL,0,0,0.00000000),(30,'',1,'2014-03-02 20:02:06','2016-07-30 15:13:20','2016-03-21 12:00:00',400.00000000,2,'','',27,1,NULL,0,0,0.00000000),(32,'',1,'2014-03-03 19:22:32','2016-07-30 15:12:32','2015-10-03 12:00:00',-400.00000000,4,'','',28,1,NULL,0,0,0.00000000),(33,'',1,'2014-03-03 19:23:16','2016-07-30 15:13:20','2016-03-10 12:00:00',-300.00000000,4,'','',29,1,NULL,0,0,0.00000000),(34,'PAY1603-0001',1,'2017-02-06 08:10:24','2017-02-06 04:10:24','2016-03-22 12:00:00',150.00000000,7,'','',33,12,NULL,0,0,150.00000000),(35,'PAY1603-0002',1,'2017-02-06 08:10:50','2017-02-06 04:10:50','2016-03-25 12:00:00',140.00000000,3,'','',34,12,NULL,0,0,140.00000000),(36,'PAY1702-0003',1,'2017-02-21 16:07:43','2017-02-21 12:07:43','2017-02-21 12:00:00',50.00000000,3,'T170201','',37,12,NULL,0,0,50.00000000),(38,'PAY1803-0004',1,'2018-03-16 13:59:31','2018-03-16 09:59:31','2018-03-16 12:00:00',10.00000000,7,'','',39,12,NULL,0,0,10.00000000); /*!40000 ALTER TABLE `llx_paiement` ENABLE KEYS */; UNLOCK TABLES; @@ -6652,7 +6795,7 @@ CREATE TABLE `llx_paiement_facture` ( KEY `idx_paiement_facture_fk_paiement` (`fk_paiement`), CONSTRAINT `fk_paiement_facture_fk_facture` FOREIGN KEY (`fk_facture`) REFERENCES `llx_facture` (`rowid`), CONSTRAINT `fk_paiement_facture_fk_paiement` FOREIGN KEY (`fk_paiement`) REFERENCES `llx_paiement` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6661,7 +6804,7 @@ CREATE TABLE `llx_paiement_facture` ( LOCK TABLES `llx_paiement_facture` WRITE; /*!40000 ALTER TABLE `llx_paiement_facture` DISABLE KEYS */; -INSERT INTO `llx_paiement_facture` VALUES (2,2,2,20.00000000,0.00000000),(3,3,2,10.00000000,0.00000000),(5,5,5,5.63000000,0.00000000),(6,6,6,5.98000000,0.00000000),(9,8,2,16.10000000,0.00000000),(10,8,8,10.00000000,0.00000000),(11,9,3,15.00000000,0.00000000),(12,9,9,11.96000000,0.00000000),(24,20,9,1.00000000,0.00000000),(31,26,32,600.00000000,0.00000000),(36,29,32,500.00000000,0.00000000),(37,30,32,400.00000000,0.00000000),(38,34,211,150.00000000,150.00000000),(39,35,211,140.00000000,140.00000000),(40,36,211,50.00000000,50.00000000); +INSERT INTO `llx_paiement_facture` VALUES (2,2,2,20.00000000,0.00000000),(3,3,2,10.00000000,0.00000000),(5,5,5,5.63000000,0.00000000),(6,6,6,5.98000000,0.00000000),(9,8,2,16.10000000,0.00000000),(10,8,8,10.00000000,0.00000000),(11,9,3,15.00000000,0.00000000),(12,9,9,11.96000000,0.00000000),(24,20,9,1.00000000,0.00000000),(31,26,32,600.00000000,0.00000000),(36,29,32,500.00000000,0.00000000),(37,30,32,400.00000000,0.00000000),(38,34,211,150.00000000,150.00000000),(39,35,211,140.00000000,140.00000000),(40,36,211,50.00000000,50.00000000),(42,38,149,10.00000000,10.00000000); /*!40000 ALTER TABLE `llx_paiement_facture` ENABLE KEYS */; UNLOCK TABLES; @@ -6680,13 +6823,13 @@ CREATE TABLE `llx_paiementcharge` ( `datep` datetime DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, `fk_typepaiement` int(11) NOT NULL, - `num_paiement` varchar(50) DEFAULT NULL, - `note` text, + `num_paiement` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) NOT NULL, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6708,7 +6851,7 @@ DROP TABLE IF EXISTS `llx_paiementfourn`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_paiementfourn` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(30) DEFAULT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, @@ -6716,14 +6859,14 @@ CREATE TABLE `llx_paiementfourn` ( `amount` double(24,8) DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_paiement` int(11) NOT NULL, - `num_paiement` varchar(50) DEFAULT NULL, - `note` text, + `num_paiement` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) NOT NULL, `statut` smallint(6) NOT NULL DEFAULT '0', `multicurrency_amount` double(24,8) DEFAULT '0.00000000', - `model_pdf` varchar(255) DEFAULT NULL, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6753,7 +6896,7 @@ CREATE TABLE `llx_paiementfourn_facturefourn` ( UNIQUE KEY `uk_paiementfourn_facturefourn` (`fk_paiementfourn`,`fk_facturefourn`), KEY `idx_paiementfourn_facturefourn_fk_facture` (`fk_facturefourn`), KEY `idx_paiementfourn_facturefourn_fk_paiement` (`fk_paiementfourn`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6781,13 +6924,13 @@ CREATE TABLE `llx_payment_donation` ( `datep` datetime DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, `fk_typepayment` int(11) NOT NULL, - `num_payment` varchar(50) DEFAULT NULL, - `note` text, + `num_payment` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) NOT NULL, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6815,13 +6958,13 @@ CREATE TABLE `llx_payment_expensereport` ( `datep` datetime DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, `fk_typepayment` int(11) NOT NULL, - `num_payment` varchar(50) DEFAULT NULL, - `note` text, + `num_payment` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) NOT NULL, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6851,14 +6994,14 @@ CREATE TABLE `llx_payment_loan` ( `amount_insurance` double(24,8) DEFAULT NULL, `amount_interest` double(24,8) DEFAULT NULL, `fk_typepayment` int(11) NOT NULL, - `num_payment` varchar(50) DEFAULT NULL, - `note_private` text, - `note_public` text, + `num_payment` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, `fk_bank` int(11) NOT NULL, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6887,12 +7030,12 @@ CREATE TABLE `llx_payment_salary` ( `salary` double(24,8) DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, `fk_typepayment` int(11) NOT NULL, - `num_payment` varchar(50) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, + `num_payment` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `datesp` date DEFAULT NULL, `dateep` date DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `note` text, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, @@ -6903,7 +7046,7 @@ CREATE TABLE `llx_payment_salary` ( KEY `idx_payment_salary_datesp` (`datesp`), KEY `idx_payment_salary_dateep` (`dateep`), CONSTRAINT `fk_payment_salary_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -6963,13 +7106,13 @@ DROP TABLE IF EXISTS `llx_prelevement_bons`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_prelevement_bons` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(12) DEFAULT NULL, + `ref` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, `statut` smallint(6) DEFAULT '0', `credite` smallint(6) DEFAULT '0', - `note` text, + `note` text COLLATE utf8_unicode_ci, `date_trans` datetime DEFAULT NULL, `method_trans` smallint(6) DEFAULT NULL, `fk_user_trans` int(11) DEFAULT NULL, @@ -6977,7 +7120,7 @@ CREATE TABLE `llx_prelevement_bons` ( `fk_user_credit` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_prelevement_bons_ref` (`ref`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7004,7 +7147,7 @@ CREATE TABLE `llx_prelevement_facture` ( PRIMARY KEY (`rowid`), KEY `idx_prelevement_facture_fk_prelevement_lignes` (`fk_prelevement_lignes`), CONSTRAINT `fk_prelevement_facture_fk_prelevement_lignes` FOREIGN KEY (`fk_prelevement_lignes`) REFERENCES `llx_prelevement_lignes` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7033,12 +7176,12 @@ CREATE TABLE `llx_prelevement_facture_demande` ( `date_traite` datetime DEFAULT NULL, `fk_prelevement_bons` int(11) DEFAULT NULL, `fk_user_demande` int(11) NOT NULL, - `code_banque` varchar(128) DEFAULT NULL, - `code_guichet` varchar(6) DEFAULT NULL, - `number` varchar(255) DEFAULT NULL, - `cle_rib` varchar(5) DEFAULT NULL, + `code_banque` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_guichet` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cle_rib` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7063,17 +7206,17 @@ CREATE TABLE `llx_prelevement_lignes` ( `fk_prelevement_bons` int(11) DEFAULT NULL, `fk_soc` int(11) NOT NULL, `statut` smallint(6) DEFAULT '0', - `client_nom` varchar(255) DEFAULT NULL, + `client_nom` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, - `code_banque` varchar(128) DEFAULT NULL, - `code_guichet` varchar(6) DEFAULT NULL, - `number` varchar(255) DEFAULT NULL, - `cle_rib` varchar(5) DEFAULT NULL, - `note` text, + `code_banque` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_guichet` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cle_rib` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), KEY `idx_prelevement_lignes_fk_prelevement_bons` (`fk_prelevement_bons`), CONSTRAINT `fk_prelevement_lignes_fk_prelevement_bons` FOREIGN KEY (`fk_prelevement_bons`) REFERENCES `llx_prelevement_bons` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7100,11 +7243,11 @@ CREATE TABLE `llx_prelevement_rejet` ( `motif` int(11) DEFAULT NULL, `date_creation` datetime DEFAULT NULL, `fk_user_creation` int(11) DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, `afacturer` tinyint(4) DEFAULT '0', `fk_facture` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7125,13 +7268,13 @@ DROP TABLE IF EXISTS `llx_printer_receipt`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_printer_receipt` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(128) DEFAULT NULL, + `name` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_type` int(11) DEFAULT NULL, `fk_profile` int(11) DEFAULT NULL, - `parameter` varchar(128) DEFAULT NULL, + `parameter` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7152,11 +7295,11 @@ DROP TABLE IF EXISTS `llx_printer_receipt_template`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_printer_receipt_template` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(128) DEFAULT NULL, - `template` text, + `name` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `template` text COLLATE utf8_unicode_ci, `entity` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7179,15 +7322,15 @@ CREATE TABLE `llx_printing` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, - `printer_name` text NOT NULL, - `printer_location` text NOT NULL, - `printer_id` varchar(255) NOT NULL, + `printer_name` text COLLATE utf8_unicode_ci NOT NULL, + `printer_location` text COLLATE utf8_unicode_ci NOT NULL, + `printer_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `copy` int(11) NOT NULL DEFAULT '1', - `module` varchar(16) NOT NULL, - `driver` varchar(16) NOT NULL, + `module` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `driver` varchar(16) COLLATE utf8_unicode_ci NOT NULL, `userid` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7212,25 +7355,25 @@ CREATE TABLE `llx_product` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `virtual` tinyint(4) NOT NULL DEFAULT '0', `fk_parent` int(11) DEFAULT '0', - `ref` varchar(128) NOT NULL, + `ref` varchar(128) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(128) DEFAULT NULL, - `label` varchar(255) NOT NULL, - `description` text, - `note` text, - `customcode` varchar(32) DEFAULT NULL, + `ref_ext` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `note` text COLLATE utf8_unicode_ci, + `customcode` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_country` int(11) DEFAULT NULL, `price` double(24,8) DEFAULT '0.00000000', `price_ttc` double(24,8) DEFAULT '0.00000000', `price_min` double(24,8) DEFAULT '0.00000000', `price_min_ttc` double(24,8) DEFAULT '0.00000000', - `price_base_type` varchar(3) DEFAULT 'HT', + `price_base_type` varchar(3) COLLATE utf8_unicode_ci DEFAULT 'HT', `tva_tx` double(6,3) DEFAULT NULL, `recuperableonly` int(11) NOT NULL DEFAULT '0', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `fk_user_author` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `tosell` tinyint(4) DEFAULT '1', @@ -7238,16 +7381,16 @@ CREATE TABLE `llx_product` ( `onportal` smallint(6) DEFAULT '0', `tobatch` tinyint(4) NOT NULL DEFAULT '0', `fk_product_type` int(11) DEFAULT '0', - `duration` varchar(6) DEFAULT NULL, + `duration` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, `seuil_stock_alerte` int(11) DEFAULT NULL, - `url` varchar(255) DEFAULT NULL, - `barcode` varchar(255) DEFAULT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `barcode` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_barcode_type` int(11) DEFAULT NULL, - `accountancy_code_sell` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `accountancy_code_sell_intra` varchar(32) DEFAULT NULL, - `accountancy_code_sell_export` varchar(32) DEFAULT NULL, - `accountancy_code_buy` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - `partnumber` varchar(32) DEFAULT NULL, + `accountancy_code_sell` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code_sell_intra` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code_sell_export` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code_buy` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `partnumber` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `weight` float DEFAULT NULL, `weight_units` tinyint(4) DEFAULT NULL, `length` float DEFAULT NULL, @@ -7260,18 +7403,18 @@ CREATE TABLE `llx_product` ( `pmp` double(24,8) NOT NULL DEFAULT '0.00000000', `fifo` double(24,8) DEFAULT NULL, `lifo` double(24,8) DEFAULT NULL, - `canvas` varchar(32) DEFAULT 'default@product', + `canvas` varchar(32) COLLATE utf8_unicode_ci DEFAULT 'default@product', `finished` tinyint(4) DEFAULT NULL, `hidden` tinyint(4) DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `desiredstock` int(11) DEFAULT '0', `fk_price_expression` int(11) DEFAULT NULL, `fk_unit` int(11) DEFAULT NULL, `cost_price` double(24,8) DEFAULT NULL, - `default_vat_code` varchar(10) DEFAULT NULL, + `default_vat_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `price_autogen` smallint(6) DEFAULT '0', - `note_public` text, - `model_pdf` varchar(255) DEFAULT '', + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT '', `width` float DEFAULT NULL, `width_units` tinyint(4) DEFAULT NULL, `height` float DEFAULT NULL, @@ -7290,7 +7433,7 @@ CREATE TABLE `llx_product` ( CONSTRAINT `fk_product_barcode_type` FOREIGN KEY (`fk_barcode_type`) REFERENCES `llx_c_barcode_type` (`rowid`), CONSTRAINT `fk_product_fk_country` FOREIGN KEY (`fk_country`) REFERENCES `llx_c_country` (`rowid`), CONSTRAINT `fk_product_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7320,7 +7463,7 @@ CREATE TABLE `llx_product_association` ( UNIQUE KEY `uk_product_association` (`fk_product_pere`,`fk_product_fils`), KEY `idx_product_association` (`fk_product_fils`), KEY `idx_product_association_fils` (`fk_product_fils`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7454,16 +7597,16 @@ CREATE TABLE `llx_product_batch` ( `fk_product_stock` int(11) NOT NULL, `eatby` datetime DEFAULT NULL, `sellby` datetime DEFAULT NULL, - `batch` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `batch` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `qty` double NOT NULL DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_product_batch` (`fk_product_stock`,`batch`), KEY `idx_fk_product_stock` (`fk_product_stock`), KEY `ix_fk_product_stock` (`fk_product_stock`), KEY `idx_batch` (`batch`), CONSTRAINT `fk_product_batch_fk_product_stock` FOREIGN KEY (`fk_product_stock`) REFERENCES `llx_product_stock` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7494,16 +7637,16 @@ CREATE TABLE `llx_product_customer_price` ( `price_ttc` double(24,8) DEFAULT '0.00000000', `price_min` double(24,8) DEFAULT '0.00000000', `price_min_ttc` double(24,8) DEFAULT '0.00000000', - `price_base_type` varchar(3) DEFAULT 'HT', + `price_base_type` varchar(3) COLLATE utf8_unicode_ci DEFAULT 'HT', `tva_tx` double(6,3) DEFAULT NULL, - `default_vat_code` varchar(10) DEFAULT NULL, + `default_vat_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `recuperableonly` int(11) NOT NULL DEFAULT '0', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `fk_user` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_customer_price_fk_product_fk_soc` (`fk_product`,`fk_soc`), KEY `idx_product_customer_price_fk_user` (`fk_user`), @@ -7514,7 +7657,7 @@ CREATE TABLE `llx_product_customer_price` ( CONSTRAINT `fk_product_customer_price_fk_product` FOREIGN KEY (`fk_product`) REFERENCES `llx_product` (`rowid`), CONSTRAINT `fk_product_customer_price_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_product_customer_price_fk_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7543,18 +7686,18 @@ CREATE TABLE `llx_product_customer_price_log` ( `price_ttc` double(24,8) DEFAULT '0.00000000', `price_min` double(24,8) DEFAULT '0.00000000', `price_min_ttc` double(24,8) DEFAULT '0.00000000', - `price_base_type` varchar(3) DEFAULT 'HT', + `price_base_type` varchar(3) COLLATE utf8_unicode_ci DEFAULT 'HT', `tva_tx` double(6,3) DEFAULT NULL, `recuperableonly` int(11) NOT NULL DEFAULT '0', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `fk_user` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `default_vat_code` varchar(10) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `default_vat_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7577,10 +7720,10 @@ CREATE TABLE `llx_product_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_product_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7592,6 +7735,36 @@ LOCK TABLES `llx_product_extrafields` WRITE; /*!40000 ALTER TABLE `llx_product_extrafields` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_product_factory` +-- + +DROP TABLE IF EXISTS `llx_product_factory`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_product_factory` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `fk_product_father` int(11) NOT NULL DEFAULT '0', + `fk_product_children` int(11) NOT NULL DEFAULT '0', + `pmp` double(24,8) DEFAULT '0.00000000', + `price` double(24,8) DEFAULT '0.00000000', + `qty` double DEFAULT NULL, + PRIMARY KEY (`rowid`), + UNIQUE KEY `uk_product_factory` (`fk_product_father`,`fk_product_children`), + KEY `idx_product_factory_fils` (`fk_product_children`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_product_factory` +-- + +LOCK TABLES `llx_product_factory` WRITE; +/*!40000 ALTER TABLE `llx_product_factory` DISABLE KEYS */; +INSERT INTO `llx_product_factory` VALUES (2,26,25,0.00000000,0.00000000,3),(3,27,26,0.00000000,0.00000000,2); +/*!40000 ALTER TABLE `llx_product_factory` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_product_fournisseur_price` -- @@ -7605,7 +7778,7 @@ CREATE TABLE `llx_product_fournisseur_price` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_product` int(11) DEFAULT NULL, `fk_soc` int(11) DEFAULT NULL, - `ref_fourn` varchar(30) DEFAULT NULL, + `ref_fourn` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_availability` int(11) DEFAULT NULL, `price` double(24,8) DEFAULT '0.00000000', `quantity` double DEFAULT NULL, @@ -7615,24 +7788,24 @@ CREATE TABLE `llx_product_fournisseur_price` ( `charges` double(24,8) DEFAULT '0.00000000', `unitcharges` double(24,8) DEFAULT '0.00000000', `tva_tx` double(6,3) NOT NULL DEFAULT '0.000', - `default_vat_code` varchar(10) DEFAULT NULL, + `default_vat_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `info_bits` int(11) NOT NULL DEFAULT '0', `fk_user` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `fk_supplier_price_expression` int(11) DEFAULT NULL, `fk_price_expression` int(11) DEFAULT NULL, `delivery_time_days` int(11) DEFAULT NULL, - `supplier_reputation` varchar(10) DEFAULT NULL, + `supplier_reputation` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_price_ttc` double(24,8) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_price` double(24,8) DEFAULT NULL, `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', PRIMARY KEY (`rowid`), UNIQUE KEY `uk_product_fournisseur_price_ref` (`ref_fourn`,`fk_soc`,`quantity`,`entity`), KEY `idx_product_fournisseur_price_fk_user` (`fk_user`), @@ -7640,7 +7813,7 @@ CREATE TABLE `llx_product_fournisseur_price` ( KEY `idx_product_fourn_price_fk_soc` (`fk_soc`,`entity`), CONSTRAINT `fk_product_fournisseur_price_fk_product` FOREIGN KEY (`fk_product`) REFERENCES `llx_product` (`rowid`), CONSTRAINT `fk_product_fournisseur_price_fk_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7668,12 +7841,12 @@ CREATE TABLE `llx_product_fournisseur_price_log` ( `quantity` double DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_price` double(24,8) DEFAULT NULL, `multicurrency_price_ttc` double(24,8) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7696,15 +7869,15 @@ DROP TABLE IF EXISTS `llx_product_lang`; CREATE TABLE `llx_product_lang` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_product` int(11) NOT NULL DEFAULT '0', - `lang` varchar(5) NOT NULL DEFAULT '0', - `label` varchar(255) NOT NULL, - `description` text, - `note` text, - `import_key` varchar(14) DEFAULT NULL, + `lang` varchar(5) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `note` text COLLATE utf8_unicode_ci, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_product_lang` (`fk_product`,`lang`), CONSTRAINT `fk_product_lang_fk_product` FOREIGN KEY (`fk_product`) REFERENCES `llx_product` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7728,7 +7901,7 @@ CREATE TABLE `llx_product_lot` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) DEFAULT '1', `fk_product` int(11) NOT NULL, - `batch` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `batch` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `eatby` date DEFAULT NULL, `sellby` date DEFAULT NULL, `datec` datetime DEFAULT NULL, @@ -7738,7 +7911,7 @@ CREATE TABLE `llx_product_lot` ( `import_key` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_product_lot` (`fk_product`,`batch`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7795,21 +7968,21 @@ CREATE TABLE `llx_product_price` ( `price_ttc` double(24,8) DEFAULT NULL, `price_min` double(24,8) DEFAULT NULL, `price_min_ttc` double(24,8) DEFAULT NULL, - `price_base_type` varchar(3) DEFAULT 'HT', + `price_base_type` varchar(3) COLLATE utf8_unicode_ci DEFAULT 'HT', `tva_tx` double(6,3) NOT NULL, - `default_vat_code` varchar(10) DEFAULT NULL, + `default_vat_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `recuperableonly` int(11) NOT NULL DEFAULT '0', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `fk_user_author` int(11) DEFAULT NULL, `tosell` tinyint(4) DEFAULT '1', `price_by_qty` int(11) NOT NULL DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_price_expression` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_price` double(24,8) DEFAULT '0.00000000', `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_price_ttc` double(24,8) DEFAULT NULL, @@ -7818,7 +7991,7 @@ CREATE TABLE `llx_product_price` ( KEY `idx_product_price_fk_product` (`fk_product`), CONSTRAINT `fk_product_price_product` FOREIGN KEY (`fk_product`) REFERENCES `llx_product` (`rowid`), CONSTRAINT `fk_product_price_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7849,12 +8022,12 @@ CREATE TABLE `llx_product_price_by_qty` ( `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `quantity` double DEFAULT NULL, `unitprice` double(24,8) DEFAULT '0.00000000', - `price_base_type` varchar(3) DEFAULT 'HT', + `price_base_type` varchar(3) COLLATE utf8_unicode_ci DEFAULT 'HT', `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_price` double(24,8) DEFAULT NULL, `multicurrency_price_ttc` double(24,8) DEFAULT NULL, @@ -7862,7 +8035,7 @@ CREATE TABLE `llx_product_price_by_qty` ( UNIQUE KEY `uk_product_price_by_qty_level` (`fk_product_price`,`quantity`), KEY `idx_product_price_by_qty_fk_product_price` (`fk_product_price`), CONSTRAINT `fk_product_price_by_qty_fk_product_price` FOREIGN KEY (`fk_product_price`) REFERENCES `llx_product_price` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7889,7 +8062,7 @@ CREATE TABLE `llx_product_pricerules` ( `var_min_percent` float NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `unique_level` (`level`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7914,12 +8087,12 @@ CREATE TABLE `llx_product_stock` ( `fk_product` int(11) NOT NULL, `fk_entrepot` int(11) NOT NULL, `reel` double DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_product_stock` (`fk_product`,`fk_entrepot`), KEY `idx_product_stock_fk_product` (`fk_product`), KEY `idx_product_stock_fk_entrepot` (`fk_entrepot`) -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7945,7 +8118,7 @@ CREATE TABLE `llx_product_subproduct` ( `fk_product_subproduct` int(11) NOT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `fk_product` (`fk_product`,`fk_product_subproduct`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -7999,29 +8172,29 @@ CREATE TABLE `llx_projet` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `dateo` date DEFAULT NULL, `datee` date DEFAULT NULL, - `ref` varchar(50) DEFAULT NULL, + `ref` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `title` varchar(255) NOT NULL, - `description` text, + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, `fk_user_creat` int(11) NOT NULL, `public` int(11) DEFAULT NULL, `fk_statut` smallint(6) NOT NULL DEFAULT '0', `fk_opp_status` int(11) DEFAULT NULL, `opp_percent` double(5,2) DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `budget_amount` double(24,8) DEFAULT NULL, `date_close` datetime DEFAULT NULL, `fk_user_close` int(11) DEFAULT NULL, `opp_amount` double(24,8) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_projet_ref` (`ref`,`entity`), KEY `idx_projet_fk_soc` (`fk_soc`), CONSTRAINT `fk_projet_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8045,11 +8218,11 @@ CREATE TABLE `llx_projet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, - `priority` text, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `priority` mediumtext COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`), KEY `idx_projet_extrafields` (`fk_object`) -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8071,7 +8244,7 @@ DROP TABLE IF EXISTS `llx_projet_task`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_projet_task` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(50) DEFAULT NULL, + `ref` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', `fk_projet` int(11) NOT NULL, `fk_task_parent` int(11) NOT NULL DEFAULT '0', @@ -8080,8 +8253,8 @@ CREATE TABLE `llx_projet_task` ( `dateo` datetime DEFAULT NULL, `datee` datetime DEFAULT NULL, `datev` datetime DEFAULT NULL, - `label` varchar(255) NOT NULL, - `description` text, + `label` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, `duration_effective` double DEFAULT '0', `planned_workload` double DEFAULT '0', `progress` int(11) DEFAULT '0', @@ -8089,11 +8262,11 @@ CREATE TABLE `llx_projet_task` ( `fk_user_creat` int(11) DEFAULT NULL, `fk_user_valid` int(11) DEFAULT NULL, `fk_statut` smallint(6) NOT NULL DEFAULT '0', - `note_private` text, - `note_public` text, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, `rang` int(11) DEFAULT '0', - `model_pdf` varchar(255) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_projet_task_ref` (`ref`,`entity`), KEY `idx_projet_task_fk_projet` (`fk_projet`), @@ -8102,7 +8275,7 @@ CREATE TABLE `llx_projet_task` ( CONSTRAINT `fk_projet_task_fk_projet` FOREIGN KEY (`fk_projet`) REFERENCES `llx_projet` (`rowid`), CONSTRAINT `fk_projet_task_fk_user_creat` FOREIGN KEY (`fk_user_creat`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_projet_task_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8126,10 +8299,10 @@ CREATE TABLE `llx_projet_task_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_projet_task_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8157,17 +8330,17 @@ CREATE TABLE `llx_projet_task_time` ( `task_duration` double DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, `thm` double(24,8) DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, `invoice_id` int(11) DEFAULT NULL, `invoice_line_id` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `datec` date DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`rowid`), KEY `idx_projet_task_time_task` (`fk_task`), KEY `idx_projet_task_time_date` (`task_date`), KEY `idx_projet_task_time_datehour` (`task_datehour`) -) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8176,10 +8349,69 @@ CREATE TABLE `llx_projet_task_time` ( LOCK TABLES `llx_projet_task_time` WRITE; /*!40000 ALTER TABLE `llx_projet_task_time` DISABLE KEYS */; -INSERT INTO `llx_projet_task_time` VALUES (2,4,'2014-12-21','2014-12-21 12:00:00',0,3600,1,NULL,'',NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(3,4,'2014-12-18','2014-12-18 12:00:00',0,3600,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(4,3,'2014-12-21','2014-12-21 12:00:00',0,3600,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(5,3,'2014-12-21','2014-12-21 12:00:00',0,1800,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(6,3,'2014-12-21','2014-12-21 12:00:00',0,3600,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(7,6,'2016-07-25','2016-07-25 00:00:00',0,18000,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(8,6,'2016-07-26','2016-07-25 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(9,6,'2016-07-27','2016-07-25 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(10,6,'2016-07-29','2016-07-25 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(11,6,'2016-07-31','2016-07-25 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(12,7,'2016-07-25','2016-07-25 00:00:00',0,10800,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(13,7,'2016-07-26','2016-07-25 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(14,7,'2016-07-27','2016-07-25 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(15,7,'2017-01-30','2017-01-30 10:00:00',1,660,12,NULL,'',NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'); +INSERT INTO `llx_projet_task_time` VALUES (2,4,'2014-12-21','2014-12-21 12:00:00',0,3600,1,NULL,'',NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(3,4,'2014-12-18','2014-12-18 12:00:00',0,3600,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(4,3,'2014-12-21','2014-12-21 12:00:00',0,3600,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(5,3,'2014-12-21','2014-12-21 12:00:00',0,1800,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(6,3,'2014-12-21','2014-12-21 12:00:00',0,3600,1,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(7,6,'2016-07-25','2016-07-25 00:00:00',0,18000,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(8,6,'2016-07-26','2016-07-26 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'2018-03-16 10:00:54'),(9,6,'2016-07-27','2016-07-27 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'2018-03-16 10:00:54'),(10,6,'2016-07-29','2016-07-29 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'2018-03-16 10:00:54'),(11,6,'2016-07-31','2016-07-31 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'2018-03-16 10:00:54'),(12,7,'2016-07-25','2016-07-25 00:00:00',0,10800,12,NULL,NULL,NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'),(13,7,'2016-07-26','2016-07-26 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'2018-03-16 10:00:54'),(14,7,'2016-07-27','2016-07-27 00:00:00',0,14400,12,NULL,NULL,NULL,NULL,NULL,NULL,'2018-03-16 10:00:54'),(15,7,'2017-01-30','2017-01-30 10:00:00',1,660,12,NULL,'',NULL,NULL,NULL,NULL,'0000-00-00 00:00:00'); /*!40000 ALTER TABLE `llx_projet_task_time` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_projet_taskdet` +-- + +DROP TABLE IF EXISTS `llx_projet_taskdet`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_projet_taskdet` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `fk_task` int(11) NOT NULL DEFAULT '0', + `fk_product` int(11) NOT NULL DEFAULT '0', + `qty_planned` double DEFAULT NULL, + `qty_used` double DEFAULT NULL, + `qty_deleted` double DEFAULT NULL, + `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `pmp` double(24,8) DEFAULT '0.00000000', + `price` double(24,8) DEFAULT '0.00000000', + `fk_statut` int(11) NOT NULL DEFAULT '0', + `note_public` mediumtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`rowid`), + UNIQUE KEY `uk_projet_taskdet` (`fk_task`,`fk_product`), + KEY `idx_projet_taskdet_fk_task` (`fk_task`), + KEY `idx_projet_taskdet_fk_product` (`fk_product`), + CONSTRAINT `fk_projet_taskdet_fk_task` FOREIGN KEY (`fk_task`) REFERENCES `llx_projet_task` (`rowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_projet_taskdet` +-- + +LOCK TABLES `llx_projet_taskdet` WRITE; +/*!40000 ALTER TABLE `llx_projet_taskdet` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_projet_taskdet` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `llx_projet_taskdet_equipement` +-- + +DROP TABLE IF EXISTS `llx_projet_taskdet_equipement`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_projet_taskdet_equipement` ( + `fk_equipement` int(11) NOT NULL DEFAULT '0', + `fk_projet_taskdet` int(11) NOT NULL DEFAULT '0', + UNIQUE KEY `uk_factory_equipement` (`fk_equipement`,`fk_projet_taskdet`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_projet_taskdet_equipement` +-- + +LOCK TABLES `llx_projet_taskdet_equipement` WRITE; +/*!40000 ALTER TABLE `llx_projet_taskdet_equipement` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_projet_taskdet_equipement` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_propal` -- @@ -8192,11 +8424,11 @@ CREATE TABLE `llx_propal` ( `fk_soc` int(11) DEFAULT NULL, `fk_projet` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(255) DEFAULT NULL, - `ref_client` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_client` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `datec` datetime DEFAULT NULL, `datep` date DEFAULT NULL, `fin_validite` datetime DEFAULT NULL, @@ -8217,28 +8449,28 @@ CREATE TABLE `llx_propal` ( `localtax2` double(24,8) DEFAULT '0.00000000', `total` double(24,8) DEFAULT '0.00000000', `fk_account` int(11) DEFAULT NULL, - `fk_currency` varchar(3) DEFAULT NULL, + `fk_currency` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_cond_reglement` int(11) DEFAULT NULL, `fk_mode_reglement` int(11) DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_livraison` date DEFAULT NULL, `fk_shipping_method` int(11) DEFAULT NULL, `fk_availability` int(11) DEFAULT NULL, `fk_delivery_address` int(11) DEFAULT NULL, `fk_input_reason` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_propal_ref` (`ref`,`entity`), KEY `idx_propal_fk_soc` (`fk_soc`), @@ -8253,7 +8485,7 @@ CREATE TABLE `llx_propal` ( CONSTRAINT `fk_propal_fk_user_author` FOREIGN KEY (`fk_user_author`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_propal_fk_user_cloture` FOREIGN KEY (`fk_user_cloture`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_propal_fk_user_valid` FOREIGN KEY (`fk_user_valid`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8277,10 +8509,10 @@ CREATE TABLE `llx_propal_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_propal_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8302,15 +8534,15 @@ DROP TABLE IF EXISTS `llx_propal_merge_pdf_product`; CREATE TABLE `llx_propal_merge_pdf_product` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_product` int(11) NOT NULL, - `file_name` varchar(200) NOT NULL, - `lang` varchar(5) DEFAULT NULL, + `file_name` varchar(200) COLLATE utf8_unicode_ci NOT NULL, + `lang` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `fk_user_mod` int(11) NOT NULL, `datec` datetime NOT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8334,15 +8566,15 @@ CREATE TABLE `llx_propaldet` ( `fk_propal` int(11) DEFAULT NULL, `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `description` text, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `fk_remise_except` int(11) DEFAULT NULL, `tva_tx` double(6,3) DEFAULT '0.000', - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) NOT NULL DEFAULT '0', + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) NOT NULL DEFAULT '0', + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `remise` double DEFAULT '0', @@ -8363,7 +8595,7 @@ CREATE TABLE `llx_propaldet` ( `rang` int(11) DEFAULT '0', `fk_unit` int(11) DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -8374,7 +8606,7 @@ CREATE TABLE `llx_propaldet` ( KEY `fk_propaldet_fk_unit` (`fk_unit`), CONSTRAINT `fk_propaldet_fk_propal` FOREIGN KEY (`fk_propal`) REFERENCES `llx_propal` (`rowid`), CONSTRAINT `fk_propaldet_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8398,10 +8630,10 @@ CREATE TABLE `llx_propaldet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_propaldet_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8423,12 +8655,12 @@ DROP TABLE IF EXISTS `llx_resource`; CREATE TABLE `llx_resource` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `ref` varchar(255) DEFAULT NULL, - `asset_number` varchar(255) DEFAULT NULL, - `description` text, - `fk_code_type_resource` varchar(32) DEFAULT NULL, - `note_public` text, - `note_private` text, + `ref` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `asset_number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, + `fk_code_type_resource` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `note_public` text COLLATE utf8_unicode_ci, + `note_private` text COLLATE utf8_unicode_ci, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, `date_valid` datetime DEFAULT NULL, @@ -8436,15 +8668,15 @@ CREATE TABLE `llx_resource` ( `fk_user_modif` int(11) DEFAULT NULL, `fk_user_valid` int(11) DEFAULT NULL, `fk_statut` smallint(6) NOT NULL DEFAULT '0', - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_country` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_resource_ref` (`ref`,`entity`), KEY `fk_code_type_resource_idx` (`fk_code_type_resource`), KEY `idx_resource_fk_country` (`fk_country`), CONSTRAINT `fk_resource_fk_country` FOREIGN KEY (`fk_country`) REFERENCES `llx_c_country` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8492,15 +8724,15 @@ DROP TABLE IF EXISTS `llx_rights_def`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_rights_def` ( `id` int(11) NOT NULL DEFAULT '0', - `libelle` varchar(255) DEFAULT NULL, - `module` varchar(64) DEFAULT NULL, + `libelle` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `module` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `perms` varchar(50) DEFAULT NULL, - `subperms` varchar(50) DEFAULT NULL, - `type` varchar(1) DEFAULT NULL, + `perms` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `subperms` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `type` varchar(1) COLLATE utf8_unicode_ci DEFAULT NULL, `bydefault` tinyint(4) DEFAULT '0', PRIMARY KEY (`id`,`entity`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8509,7 +8741,7 @@ CREATE TABLE `llx_rights_def` ( LOCK TABLES `llx_rights_def` WRITE; /*!40000 ALTER TABLE `llx_rights_def` DISABLE KEYS */; -INSERT INTO `llx_rights_def` VALUES (11,'Read invoices','facture',1,'lire',NULL,'a',0),(11,'Lire les factures','facture',2,'lire',NULL,'a',1),(12,'Create and update invoices','facture',1,'creer',NULL,'a',0),(12,'Creer/modifier les factures','facture',2,'creer',NULL,'a',0),(13,'Devalidate invoices','facture',1,'invoice_advance','unvalidate','a',0),(13,'Dévalider les factures','facture',2,'invoice_advance','unvalidate','a',0),(14,'Validate invoices','facture',1,'invoice_advance','validate','a',0),(14,'Valider les factures','facture',2,'valider',NULL,'a',0),(15,'Send invoices by email','facture',1,'invoice_advance','send','a',0),(15,'Envoyer les factures par mail','facture',2,'invoice_advance','send','a',0),(16,'Issue payments on invoices','facture',1,'paiement',NULL,'a',0),(16,'Emettre des paiements sur les factures','facture',2,'paiement',NULL,'a',0),(19,'Delete invoices','facture',1,'supprimer',NULL,'a',0),(19,'Supprimer les factures','facture',2,'supprimer',NULL,'a',0),(21,'Lire les propositions commerciales','propale',1,'lire',NULL,'r',1),(21,'Lire les propositions commerciales','propale',2,'lire',NULL,'r',1),(22,'Creer/modifier les propositions commerciales','propale',1,'creer',NULL,'w',0),(22,'Creer/modifier les propositions commerciales','propale',2,'creer',NULL,'w',0),(24,'Valider les propositions commerciales','propale',1,'propal_advance','validate','d',0),(24,'Valider les propositions commerciales','propale',2,'valider',NULL,'d',0),(25,'Envoyer les propositions commerciales aux clients','propale',1,'propal_advance','send','d',0),(25,'Envoyer les propositions commerciales aux clients','propale',2,'propal_advance','send','d',0),(26,'Cloturer les propositions commerciales','propale',1,'cloturer',NULL,'d',0),(26,'Cloturer les propositions commerciales','propale',2,'cloturer',NULL,'d',0),(27,'Supprimer les propositions commerciales','propale',1,'supprimer',NULL,'d',0),(27,'Supprimer les propositions commerciales','propale',2,'supprimer',NULL,'d',0),(28,'Exporter les propositions commerciales et attributs','propale',1,'export',NULL,'r',0),(28,'Exporter les propositions commerciales et attributs','propale',2,'export',NULL,'r',0),(31,'Lire les produits','produit',1,'lire',NULL,'r',1),(31,'Lire les produits','produit',2,'lire',NULL,'r',1),(32,'Creer/modifier les produits','produit',1,'creer',NULL,'w',0),(32,'Creer/modifier les produits','produit',2,'creer',NULL,'w',0),(34,'Supprimer les produits','produit',1,'supprimer',NULL,'d',0),(34,'Supprimer les produits','produit',2,'supprimer',NULL,'d',0),(38,'Exporter les produits','produit',1,'export',NULL,'r',0),(38,'Exporter les produits','produit',2,'export',NULL,'r',0),(41,'Read projects and tasks (shared projects or projects I am contact for). Can also enter time consumed on assigned tasks (timesheet)','projet',1,'lire',NULL,'r',1),(42,'Create/modify projects and tasks (shared projects or projects I am contact for)','projet',1,'creer',NULL,'w',0),(44,'Delete project and tasks (shared projects or projects I am contact for)','projet',1,'supprimer',NULL,'d',0),(45,'Export projects','projet',1,'export',NULL,'d',0),(61,'Lire les fiches d\'intervention','ficheinter',1,'lire',NULL,'r',1),(62,'Creer/modifier les fiches d\'intervention','ficheinter',1,'creer',NULL,'w',0),(64,'Supprimer les fiches d\'intervention','ficheinter',1,'supprimer',NULL,'d',0),(67,'Exporter les fiches interventions','ficheinter',1,'export',NULL,'r',0),(68,'Envoyer les fiches d\'intervention par courriel','ficheinter',1,'ficheinter_advance','send','r',0),(69,'Valider les fiches d\'intervention ','ficheinter',1,'ficheinter_advance','validate','a',0),(70,'Dévalider les fiches d\'intervention','ficheinter',1,'ficheinter_advance','unvalidate','a',0),(71,'Read members\' card','adherent',1,'lire',NULL,'r',0),(72,'Create/modify members (need also user module permissions if member linked to a user)','adherent',1,'creer',NULL,'w',0),(74,'Remove members','adherent',1,'supprimer',NULL,'d',0),(75,'Setup types of membership','adherent',1,'configurer',NULL,'w',0),(76,'Export members','adherent',1,'export',NULL,'r',0),(78,'Read subscriptions','adherent',1,'cotisation','lire','r',0),(79,'Create/modify/remove subscriptions','adherent',1,'cotisation','creer','w',0),(81,'Lire les commandes clients','commande',1,'lire',NULL,'r',0),(82,'Creer/modifier les commandes clients','commande',1,'creer',NULL,'w',0),(84,'Valider les commandes clients','commande',1,'order_advance','validate','d',0),(86,'Envoyer les commandes clients','commande',1,'order_advance','send','d',0),(87,'Cloturer les commandes clients','commande',1,'cloturer',NULL,'d',0),(88,'Annuler les commandes clients','commande',1,'order_advance','annuler','d',0),(89,'Supprimer les commandes clients','commande',1,'supprimer',NULL,'d',0),(91,'Lire les charges','tax',1,'charges','lire','r',0),(91,'Lire les charges','tax',2,'charges','lire','r',1),(92,'Creer/modifier les charges','tax',1,'charges','creer','w',0),(92,'Creer/modifier les charges','tax',2,'charges','creer','w',0),(93,'Supprimer les charges','tax',1,'charges','supprimer','d',0),(93,'Supprimer les charges','tax',2,'charges','supprimer','d',0),(94,'Exporter les charges','tax',1,'charges','export','r',0),(94,'Exporter les charges','tax',2,'charges','export','r',0),(101,'Lire les expeditions','expedition',1,'lire',NULL,'r',1),(102,'Creer modifier les expeditions','expedition',1,'creer',NULL,'w',0),(104,'Valider les expeditions','expedition',1,'shipping_advance','validate','d',0),(105,'Envoyer les expeditions aux clients','expedition',1,'shipping_advance','send','d',0),(106,'Exporter les expeditions','expedition',1,'shipment','export','r',0),(109,'Supprimer les expeditions','expedition',1,'supprimer',NULL,'d',0),(111,'Lire les comptes bancaires','banque',1,'lire',NULL,'r',0),(111,'Lire les comptes bancaires','banque',2,'lire',NULL,'r',1),(112,'Creer/modifier montant/supprimer ecriture bancaire','banque',1,'modifier',NULL,'w',0),(112,'Creer/modifier montant/supprimer ecriture bancaire','banque',2,'modifier',NULL,'w',0),(113,'Configurer les comptes bancaires (creer, gerer categories)','banque',1,'configurer',NULL,'a',0),(113,'Configurer les comptes bancaires (creer, gerer categories)','banque',2,'configurer',NULL,'a',0),(114,'Rapprocher les ecritures bancaires','banque',1,'consolidate',NULL,'w',0),(114,'Rapprocher les ecritures bancaires','banque',2,'consolidate',NULL,'w',0),(115,'Exporter transactions et releves','banque',1,'export',NULL,'r',0),(115,'Exporter transactions et releves','banque',2,'export',NULL,'r',0),(116,'Virements entre comptes','banque',1,'transfer',NULL,'w',0),(116,'Virements entre comptes','banque',2,'transfer',NULL,'w',0),(117,'Gerer les envois de cheques','banque',1,'cheque',NULL,'w',0),(117,'Gerer les envois de cheques','banque',2,'cheque',NULL,'w',0),(121,'Read third parties','societe',1,'lire',NULL,'r',0),(121,'Lire les societes','societe',2,'lire',NULL,'r',1),(122,'Create and update third parties','societe',1,'creer',NULL,'w',0),(122,'Creer modifier les societes','societe',2,'creer',NULL,'w',0),(125,'Delete third parties','societe',1,'supprimer',NULL,'d',0),(125,'Supprimer les societes','societe',2,'supprimer',NULL,'d',0),(126,'Export third parties','societe',1,'export',NULL,'r',0),(126,'Exporter les societes','societe',2,'export',NULL,'r',0),(141,'Read all projects and tasks (also private projects I am not contact for)','projet',1,'all','lire','r',0),(142,'Create/modify all projects and tasks (also private projects I am not contact for)','projet',1,'all','creer','w',0),(144,'Delete all projects and tasks (also private projects I am not contact for)','projet',1,'all','supprimer','d',0),(151,'Read withdrawals','prelevement',1,'bons','lire','r',1),(152,'Create/modify a withdrawals','prelevement',1,'bons','creer','w',0),(153,'Send withdrawals to bank','prelevement',1,'bons','send','a',0),(154,'credit/refuse withdrawals','prelevement',1,'bons','credit','a',0),(161,'Lire les contrats','contrat',1,'lire',NULL,'r',1),(162,'Creer / modifier les contrats','contrat',1,'creer',NULL,'w',0),(163,'Activer un service d\'un contrat','contrat',1,'activer',NULL,'w',0),(164,'Desactiver un service d\'un contrat','contrat',1,'desactiver',NULL,'w',0),(165,'Supprimer un contrat','contrat',1,'supprimer',NULL,'d',0),(167,'Export contracts','contrat',1,'export',NULL,'r',0),(221,'Consulter les mailings','mailing',1,'lire',NULL,'r',1),(221,'Consulter les mailings','mailing',2,'lire',NULL,'r',1),(222,'Creer/modifier les mailings (sujet, destinataires...)','mailing',1,'creer',NULL,'w',0),(222,'Creer/modifier les mailings (sujet, destinataires...)','mailing',2,'creer',NULL,'w',0),(223,'Valider les mailings (permet leur envoi)','mailing',1,'valider',NULL,'w',0),(223,'Valider les mailings (permet leur envoi)','mailing',2,'valider',NULL,'w',0),(229,'Supprimer les mailings','mailing',1,'supprimer',NULL,'d',0),(229,'Supprimer les mailings','mailing',2,'supprimer',NULL,'d',0),(237,'View recipients and info','mailing',1,'mailing_advance','recipient','r',0),(237,'View recipients and info','mailing',2,'mailing_advance','recipient','r',0),(238,'Manually send mailings','mailing',1,'mailing_advance','send','w',0),(238,'Manually send mailings','mailing',2,'mailing_advance','send','w',0),(239,'Delete mailings after validation and/or sent','mailing',1,'mailing_advance','delete','d',0),(239,'Delete mailings after validation and/or sent','mailing',2,'mailing_advance','delete','d',0),(241,'Lire les categories','categorie',1,'lire',NULL,'r',1),(242,'Creer/modifier les categories','categorie',1,'creer',NULL,'w',0),(243,'Supprimer les categories','categorie',1,'supprimer',NULL,'d',0),(251,'Consulter les autres utilisateurs','user',1,'user','lire','r',0),(252,'Consulter les permissions des autres utilisateurs','user',1,'user_advance','readperms','r',0),(253,'Creer/modifier utilisateurs internes et externes','user',1,'user','creer','w',0),(254,'Creer/modifier utilisateurs externes seulement','user',1,'user_advance','write','w',0),(255,'Modifier le mot de passe des autres utilisateurs','user',1,'user','password','w',0),(256,'Supprimer ou desactiver les autres utilisateurs','user',1,'user','supprimer','d',0),(262,'Read all third parties by internal users (otherwise only if commercial contact). Not effective for external users (limited to themselves).','societe',1,'client','voir','r',0),(262,'Consulter tous les tiers par utilisateurs internes (sinon uniquement si contact commercial). Non effectif pour utilisateurs externes (tjs limités à eux-meme).','societe',2,'client','voir','r',1),(281,'Read contacts','societe',1,'contact','lire','r',0),(281,'Lire les contacts','societe',2,'contact','lire','r',1),(282,'Create and update contact','societe',1,'contact','creer','w',0),(282,'Creer modifier les contacts','societe',2,'contact','creer','w',0),(283,'Delete contacts','societe',1,'contact','supprimer','d',0),(283,'Supprimer les contacts','societe',2,'contact','supprimer','d',0),(286,'Export contacts','societe',1,'contact','export','d',0),(286,'Exporter les contacts','societe',2,'contact','export','d',0),(300,'Read barcodes','barcode',1,'lire_advance',NULL,'r',1),(301,'Create/modify barcodes','barcode',1,'creer_advance',NULL,'w',0),(331,'Lire les bookmarks','bookmark',1,'lire',NULL,'r',0),(332,'Creer/modifier les bookmarks','bookmark',1,'creer',NULL,'r',0),(333,'Supprimer les bookmarks','bookmark',1,'supprimer',NULL,'r',0),(341,'Consulter ses propres permissions','user',1,'self_advance','readperms','r',0),(342,'Creer/modifier ses propres infos utilisateur','user',1,'self','creer','w',0),(343,'Modifier son propre mot de passe','user',1,'self','password','w',0),(344,'Modifier ses propres permissions','user',1,'self_advance','writeperms','w',0),(351,'Consulter les groupes','user',1,'group_advance','read','r',0),(352,'Consulter les permissions des groupes','user',1,'group_advance','readperms','r',0),(353,'Creer/modifier les groupes et leurs permissions','user',1,'group_advance','write','w',0),(354,'Supprimer ou desactiver les groupes','user',1,'group_advance','delete','d',0),(358,'Exporter les utilisateurs','user',1,'user','export','r',0),(501,'Read employee contracts/salaries','salaries',1,'read',NULL,'r',0),(502,'Create/modify employee contracts/salaries','salaries',1,'write',NULL,'w',0),(511,'Read payment of salaries','salaries',1,'payment','read','w',0),(512,'Create/modify payment of salaries','salaries',1,'payment','write','w',0),(514,'Delete contracts/salaries','salaries',1,'delete',NULL,'d',0),(517,'Export employee contracts and salaries payments','salaries',1,'export',NULL,'r',0),(520,'Read loans','loan',1,'read',NULL,'r',0),(522,'Create/modify loans','loan',1,'write',NULL,'w',0),(524,'Delete loans','loan',1,'delete',NULL,'d',0),(525,'Access loan calculator','loan',1,'calc',NULL,'r',0),(527,'Export loans','loan',1,'export',NULL,'r',0),(531,'Lire les services','service',1,'lire',NULL,'r',0),(532,'Creer/modifier les services','service',1,'creer',NULL,'w',0),(534,'Supprimer les services','service',1,'supprimer',NULL,'d',0),(538,'Exporter les services','service',1,'export',NULL,'r',0),(701,'Lire les dons','don',1,'lire',NULL,'r',1),(701,'Lire les dons','don',2,'lire',NULL,'r',1),(702,'Creer/modifier les dons','don',1,'creer',NULL,'w',0),(702,'Creer/modifier les dons','don',2,'creer',NULL,'w',0),(703,'Supprimer les dons','don',1,'supprimer',NULL,'d',0),(703,'Supprimer les dons','don',2,'supprimer',NULL,'d',0),(771,'Read expense reports (yours and your subordinates)','expensereport',1,'lire',NULL,'r',1),(772,'Create/modify expense reports','expensereport',1,'creer',NULL,'w',0),(773,'Delete expense reports','expensereport',1,'supprimer',NULL,'d',0),(774,'Read all expense reports','expensereport',1,'readall',NULL,'r',1),(775,'Approve expense reports','expensereport',1,'approve',NULL,'w',0),(776,'Pay expense reports','expensereport',1,'to_paid',NULL,'w',0),(777,'Read expense reports of everybody','expensereport',1,'readall',NULL,'r',1),(778,'Create expense reports for everybody','expensereport',1,'writeall_advance',NULL,'w',0),(779,'Export expense reports','expensereport',1,'export',NULL,'r',0),(1001,'Lire les stocks','stock',1,'lire',NULL,'r',1),(1002,'Creer/Modifier les stocks','stock',1,'creer',NULL,'w',0),(1003,'Supprimer les stocks','stock',1,'supprimer',NULL,'d',0),(1004,'Lire mouvements de stocks','stock',1,'mouvement','lire','r',1),(1005,'Creer/modifier mouvements de stocks','stock',1,'mouvement','creer','w',0),(1101,'Lire les bons de livraison','expedition',1,'livraison','lire','r',1),(1102,'Creer modifier les bons de livraison','expedition',1,'livraison','creer','w',0),(1104,'Valider les bons de livraison','expedition',1,'livraison_advance','validate','d',0),(1109,'Supprimer les bons de livraison','expedition',1,'livraison','supprimer','d',0),(1121,'Read supplier proposals','supplier_proposal',1,'lire',NULL,'w',1),(1122,'Create/modify supplier proposals','supplier_proposal',1,'creer',NULL,'w',0),(1123,'Validate supplier proposals','supplier_proposal',1,'validate_advance',NULL,'w',0),(1124,'Envoyer les demandes fournisseurs','supplier_proposal',1,'send_advance',NULL,'w',0),(1125,'Delete supplier proposals','supplier_proposal',1,'supprimer',NULL,'w',0),(1126,'Close supplier price requests','supplier_proposal',1,'cloturer',NULL,'w',0),(1181,'Consulter les fournisseurs','fournisseur',1,'lire',NULL,'r',0),(1182,'Consulter les commandes fournisseur','fournisseur',1,'commande','lire','r',0),(1183,'Creer une commande fournisseur','fournisseur',1,'commande','creer','w',0),(1184,'Valider une commande fournisseur','fournisseur',1,'supplier_order_advance','validate','w',0),(1185,'Approuver une commande fournisseur','fournisseur',1,'commande','approuver','w',0),(1186,'Commander une commande fournisseur','fournisseur',1,'commande','commander','w',0),(1187,'Receptionner une commande fournisseur','fournisseur',1,'commande','receptionner','d',0),(1188,'Supprimer une commande fournisseur','fournisseur',1,'commande','supprimer','d',0),(1189,'Check/Uncheck a supplier order reception','fournisseur',1,'commande_advance','check','w',0),(1201,'Lire les exports','export',1,'lire',NULL,'r',1),(1202,'Creer/modifier un export','export',1,'creer',NULL,'w',0),(1231,'Consulter les factures fournisseur','fournisseur',1,'facture','lire','r',0),(1232,'Creer une facture fournisseur','fournisseur',1,'facture','creer','w',0),(1233,'Valider une facture fournisseur','fournisseur',1,'supplier_invoice_advance','validate','w',0),(1234,'Supprimer une facture fournisseur','fournisseur',1,'facture','supprimer','d',0),(1235,'Envoyer les factures par mail','fournisseur',1,'supplier_invoice_advance','send','a',0),(1236,'Exporter les factures fournisseurs, attributs et reglements','fournisseur',1,'facture','export','r',0),(1237,'Exporter les commande fournisseurs, attributs','fournisseur',1,'commande','export','r',0),(1251,'Run mass imports of external data (data load)','import',1,'run',NULL,'r',0),(1321,'Export customer invoices, attributes and payments','facture',1,'facture','export','r',0),(1321,'Exporter les factures clients, attributs et reglements','facture',2,'facture','export','r',0),(1322,'Re-open a fully paid invoice','facture',1,'invoice_advance','reopen','r',0),(1421,'Exporter les commandes clients et attributs','commande',1,'commande','export','r',0),(2401,'Read actions/tasks linked to his account','agenda',1,'myactions','read','r',0),(2401,'Read actions/tasks linked to his account','agenda',2,'myactions','read','r',1),(2402,'Create/modify actions/tasks linked to his account','agenda',1,'myactions','create','w',0),(2402,'Create/modify actions/tasks linked to his account','agenda',2,'myactions','create','w',0),(2403,'Delete actions/tasks linked to his account','agenda',1,'myactions','delete','w',0),(2403,'Delete actions/tasks linked to his account','agenda',2,'myactions','delete','w',0),(2411,'Read actions/tasks of others','agenda',1,'allactions','read','r',0),(2411,'Read actions/tasks of others','agenda',2,'allactions','read','r',0),(2412,'Create/modify actions/tasks of others','agenda',1,'allactions','create','w',0),(2412,'Create/modify actions/tasks of others','agenda',2,'allactions','create','w',0),(2413,'Delete actions/tasks of others','agenda',1,'allactions','delete','w',0),(2413,'Delete actions/tasks of others','agenda',2,'allactions','delete','w',0),(2414,'Export actions/tasks of others','agenda',1,'export',NULL,'w',0),(2501,'Consulter/Télécharger les documents','ecm',1,'read',NULL,'r',0),(2503,'Soumettre ou supprimer des documents','ecm',1,'upload',NULL,'w',0),(2515,'Administrer les rubriques de documents','ecm',1,'setup',NULL,'w',0),(3200,'Read archived events and fingerprints','blockedlog',1,'read',NULL,'w',0),(20001,'Read your own holidays','holiday',1,'read',NULL,'w',0),(20001,'Créer / Modifier / Lire ses demandes de congés payés','holiday',2,'write',NULL,'w',1),(20002,'Create/modify your own holidays','holiday',1,'write',NULL,'w',0),(20002,'Lire / Modifier toutes les demandes de congés payés','holiday',2,'lire_tous',NULL,'w',0),(20003,'Delete holidays','holiday',1,'delete',NULL,'w',0),(20003,'Supprimer des demandes de congés payés','holiday',2,'delete',NULL,'w',0),(20004,'Read holidays for everybody','holiday',1,'read_all',NULL,'w',0),(20004,'Définir les congés payés des utilisateurs','holiday',2,'define_holiday',NULL,'w',0),(20005,'Create/modify holidays for everybody','holiday',1,'write_all',NULL,'w',0),(20005,'Voir les logs de modification des congés payés','holiday',2,'view_log',NULL,'w',0),(20006,'Setup holidays of users (setup and update balance)','holiday',1,'define_holiday',NULL,'w',0),(20006,'Accéder au rapport mensuel des congés payés','holiday',2,'month_report',NULL,'w',0),(23001,'Read cron jobs','cron',1,'read',NULL,'w',0),(23002,'Create cron Jobs','cron',1,'create',NULL,'w',0),(23003,'Delete cron Jobs','cron',1,'delete',NULL,'w',0),(23004,'Execute cron Jobs','cron',1,'execute',NULL,'w',0),(50101,'Use point of sale','cashdesk',1,'use',NULL,'a',1),(50401,'Bind products and invoices with accounting accounts','accounting',1,'bind','write','r',0),(50411,'Read operations in General Ledger','accounting',1,'mouvements','lire','r',0),(50412,'Write/Edit operations in General Ledger','accounting',1,'mouvements','creer','w',0),(50420,'Report and export reports (turnover, balance, journals, general ledger)','accounting',1,'comptarapport','lire','r',0),(50430,'Define and close a fiscal year','accounting',1,'fiscalyear',NULL,'r',0),(50440,'Manage chart of accounts, setup of accountancy','accounting',1,'chartofaccount',NULL,'r',0),(55001,'Read surveys','opensurvey',1,'read',NULL,'r',0),(55002,'Create/modify surveys','opensurvey',1,'write',NULL,'w',0),(59001,'Visualiser les marges','margins',1,'liretous',NULL,'r',1),(59002,'Définir les marges','margins',1,'creer',NULL,'w',0),(59003,'Read every user margin','margins',1,'read','all','r',0),(63001,'Read resources','resource',1,'read',NULL,'w',1),(63002,'Create/Modify resources','resource',1,'write',NULL,'w',0),(63003,'Delete resources','resource',1,'delete',NULL,'w',0),(63004,'Link resources','resource',1,'link',NULL,'w',0),(64001,'DirectPrint','printing',1,'read',NULL,'r',0),(101250,'Read surveys','opensurvey',2,'survey','read','r',0),(101251,'Create/modify surveys','opensurvey',2,'survey','write','w',0),(400051,'Use POS','pos',2,'frontend',NULL,'a',1),(400052,'Use Backend','pos',2,'backend',NULL,'a',1),(400053,'Make Transfers','pos',2,'transfer',NULL,'a',1),(400055,'Stats','pos',2,'stats',NULL,'a',1); +INSERT INTO `llx_rights_def` VALUES (11,'Read invoices','facture',1,'lire',NULL,'a',0),(11,'Lire les factures','facture',2,'lire',NULL,'a',1),(12,'Create and update invoices','facture',1,'creer',NULL,'a',0),(12,'Creer/modifier les factures','facture',2,'creer',NULL,'a',0),(13,'Devalidate invoices','facture',1,'invoice_advance','unvalidate','a',0),(13,'Dévalider les factures','facture',2,'invoice_advance','unvalidate','a',0),(14,'Validate invoices','facture',1,'invoice_advance','validate','a',0),(14,'Valider les factures','facture',2,'valider',NULL,'a',0),(15,'Send invoices by email','facture',1,'invoice_advance','send','a',0),(15,'Envoyer les factures par mail','facture',2,'invoice_advance','send','a',0),(16,'Issue payments on invoices','facture',1,'paiement',NULL,'a',0),(16,'Emettre des paiements sur les factures','facture',2,'paiement',NULL,'a',0),(19,'Delete invoices','facture',1,'supprimer',NULL,'a',0),(19,'Supprimer les factures','facture',2,'supprimer',NULL,'a',0),(21,'Lire les propositions commerciales','propale',1,'lire',NULL,'r',1),(21,'Lire les propositions commerciales','propale',2,'lire',NULL,'r',1),(22,'Creer/modifier les propositions commerciales','propale',1,'creer',NULL,'w',0),(22,'Creer/modifier les propositions commerciales','propale',2,'creer',NULL,'w',0),(24,'Valider les propositions commerciales','propale',1,'propal_advance','validate','d',0),(24,'Valider les propositions commerciales','propale',2,'valider',NULL,'d',0),(25,'Envoyer les propositions commerciales aux clients','propale',1,'propal_advance','send','d',0),(25,'Envoyer les propositions commerciales aux clients','propale',2,'propal_advance','send','d',0),(26,'Cloturer les propositions commerciales','propale',1,'cloturer',NULL,'d',0),(26,'Cloturer les propositions commerciales','propale',2,'cloturer',NULL,'d',0),(27,'Supprimer les propositions commerciales','propale',1,'supprimer',NULL,'d',0),(27,'Supprimer les propositions commerciales','propale',2,'supprimer',NULL,'d',0),(28,'Exporter les propositions commerciales et attributs','propale',1,'export',NULL,'r',0),(28,'Exporter les propositions commerciales et attributs','propale',2,'export',NULL,'r',0),(31,'Lire les produits','produit',1,'lire',NULL,'r',1),(31,'Lire les produits','produit',2,'lire',NULL,'r',1),(32,'Creer/modifier les produits','produit',1,'creer',NULL,'w',0),(32,'Creer/modifier les produits','produit',2,'creer',NULL,'w',0),(34,'Supprimer les produits','produit',1,'supprimer',NULL,'d',0),(34,'Supprimer les produits','produit',2,'supprimer',NULL,'d',0),(38,'Exporter les produits','produit',1,'export',NULL,'r',0),(38,'Exporter les produits','produit',2,'export',NULL,'r',0),(41,'Read projects and tasks (shared projects or projects I am contact for). Can also enter time consumed on assigned tasks (timesheet)','projet',1,'lire',NULL,'r',1),(42,'Create/modify projects and tasks (shared projects or projects I am contact for)','projet',1,'creer',NULL,'w',0),(44,'Delete project and tasks (shared projects or projects I am contact for)','projet',1,'supprimer',NULL,'d',0),(45,'Export projects','projet',1,'export',NULL,'d',0),(61,'Lire les fiches d\'intervention','ficheinter',1,'lire',NULL,'r',1),(62,'Creer/modifier les fiches d\'intervention','ficheinter',1,'creer',NULL,'w',0),(64,'Supprimer les fiches d\'intervention','ficheinter',1,'supprimer',NULL,'d',0),(67,'Exporter les fiches interventions','ficheinter',1,'export',NULL,'r',0),(68,'Envoyer les fiches d\'intervention par courriel','ficheinter',1,'ficheinter_advance','send','r',0),(69,'Valider les fiches d\'intervention ','ficheinter',1,'ficheinter_advance','validate','a',0),(70,'Dévalider les fiches d\'intervention','ficheinter',1,'ficheinter_advance','unvalidate','a',0),(71,'Read members\' card','adherent',1,'lire',NULL,'r',0),(72,'Create/modify members (need also user module permissions if member linked to a user)','adherent',1,'creer',NULL,'w',0),(74,'Remove members','adherent',1,'supprimer',NULL,'d',0),(75,'Setup types of membership','adherent',1,'configurer',NULL,'w',0),(76,'Export members','adherent',1,'export',NULL,'r',0),(78,'Read subscriptions','adherent',1,'cotisation','lire','r',0),(79,'Create/modify/remove subscriptions','adherent',1,'cotisation','creer','w',0),(81,'Lire les commandes clients','commande',1,'lire',NULL,'r',0),(82,'Creer/modifier les commandes clients','commande',1,'creer',NULL,'w',0),(84,'Valider les commandes clients','commande',1,'order_advance','validate','d',0),(86,'Envoyer les commandes clients','commande',1,'order_advance','send','d',0),(87,'Cloturer les commandes clients','commande',1,'cloturer',NULL,'d',0),(88,'Annuler les commandes clients','commande',1,'order_advance','annuler','d',0),(89,'Supprimer les commandes clients','commande',1,'supprimer',NULL,'d',0),(91,'Lire les charges','tax',1,'charges','lire','r',0),(91,'Lire les charges','tax',2,'charges','lire','r',1),(92,'Creer/modifier les charges','tax',1,'charges','creer','w',0),(92,'Creer/modifier les charges','tax',2,'charges','creer','w',0),(93,'Supprimer les charges','tax',1,'charges','supprimer','d',0),(93,'Supprimer les charges','tax',2,'charges','supprimer','d',0),(94,'Exporter les charges','tax',1,'charges','export','r',0),(94,'Exporter les charges','tax',2,'charges','export','r',0),(101,'Lire les expeditions','expedition',1,'lire',NULL,'r',1),(102,'Creer modifier les expeditions','expedition',1,'creer',NULL,'w',0),(104,'Valider les expeditions','expedition',1,'shipping_advance','validate','d',0),(105,'Envoyer les expeditions aux clients','expedition',1,'shipping_advance','send','d',0),(106,'Exporter les expeditions','expedition',1,'shipment','export','r',0),(109,'Supprimer les expeditions','expedition',1,'supprimer',NULL,'d',0),(111,'Lire les comptes bancaires','banque',1,'lire',NULL,'r',0),(111,'Lire les comptes bancaires','banque',2,'lire',NULL,'r',1),(112,'Creer/modifier montant/supprimer ecriture bancaire','banque',1,'modifier',NULL,'w',0),(112,'Creer/modifier montant/supprimer ecriture bancaire','banque',2,'modifier',NULL,'w',0),(113,'Configurer les comptes bancaires (creer, gerer categories)','banque',1,'configurer',NULL,'a',0),(113,'Configurer les comptes bancaires (creer, gerer categories)','banque',2,'configurer',NULL,'a',0),(114,'Rapprocher les ecritures bancaires','banque',1,'consolidate',NULL,'w',0),(114,'Rapprocher les ecritures bancaires','banque',2,'consolidate',NULL,'w',0),(115,'Exporter transactions et releves','banque',1,'export',NULL,'r',0),(115,'Exporter transactions et releves','banque',2,'export',NULL,'r',0),(116,'Virements entre comptes','banque',1,'transfer',NULL,'w',0),(116,'Virements entre comptes','banque',2,'transfer',NULL,'w',0),(117,'Gerer les envois de cheques','banque',1,'cheque',NULL,'w',0),(117,'Gerer les envois de cheques','banque',2,'cheque',NULL,'w',0),(121,'Read third parties','societe',1,'lire',NULL,'r',0),(121,'Lire les societes','societe',2,'lire',NULL,'r',1),(122,'Create and update third parties','societe',1,'creer',NULL,'w',0),(122,'Creer modifier les societes','societe',2,'creer',NULL,'w',0),(125,'Delete third parties','societe',1,'supprimer',NULL,'d',0),(125,'Supprimer les societes','societe',2,'supprimer',NULL,'d',0),(126,'Export third parties','societe',1,'export',NULL,'r',0),(126,'Exporter les societes','societe',2,'export',NULL,'r',0),(141,'Read all projects and tasks (also private projects I am not contact for)','projet',1,'all','lire','r',0),(142,'Create/modify all projects and tasks (also private projects I am not contact for)','projet',1,'all','creer','w',0),(144,'Delete all projects and tasks (also private projects I am not contact for)','projet',1,'all','supprimer','d',0),(151,'Read withdrawals','prelevement',1,'bons','lire','r',1),(152,'Create/modify a withdrawals','prelevement',1,'bons','creer','w',0),(153,'Send withdrawals to bank','prelevement',1,'bons','send','a',0),(154,'credit/refuse withdrawals','prelevement',1,'bons','credit','a',0),(161,'Lire les contrats','contrat',1,'lire',NULL,'r',1),(162,'Creer / modifier les contrats','contrat',1,'creer',NULL,'w',0),(163,'Activer un service d\'un contrat','contrat',1,'activer',NULL,'w',0),(164,'Desactiver un service d\'un contrat','contrat',1,'desactiver',NULL,'w',0),(165,'Supprimer un contrat','contrat',1,'supprimer',NULL,'d',0),(167,'Export contracts','contrat',1,'export',NULL,'r',0),(221,'Consulter les mailings','mailing',1,'lire',NULL,'r',1),(221,'Consulter les mailings','mailing',2,'lire',NULL,'r',1),(222,'Creer/modifier les mailings (sujet, destinataires...)','mailing',1,'creer',NULL,'w',0),(222,'Creer/modifier les mailings (sujet, destinataires...)','mailing',2,'creer',NULL,'w',0),(223,'Valider les mailings (permet leur envoi)','mailing',1,'valider',NULL,'w',0),(223,'Valider les mailings (permet leur envoi)','mailing',2,'valider',NULL,'w',0),(229,'Supprimer les mailings','mailing',1,'supprimer',NULL,'d',0),(229,'Supprimer les mailings','mailing',2,'supprimer',NULL,'d',0),(237,'View recipients and info','mailing',1,'mailing_advance','recipient','r',0),(237,'View recipients and info','mailing',2,'mailing_advance','recipient','r',0),(238,'Manually send mailings','mailing',1,'mailing_advance','send','w',0),(238,'Manually send mailings','mailing',2,'mailing_advance','send','w',0),(239,'Delete mailings after validation and/or sent','mailing',1,'mailing_advance','delete','d',0),(239,'Delete mailings after validation and/or sent','mailing',2,'mailing_advance','delete','d',0),(241,'Lire les categories','categorie',1,'lire',NULL,'r',1),(242,'Creer/modifier les categories','categorie',1,'creer',NULL,'w',0),(243,'Supprimer les categories','categorie',1,'supprimer',NULL,'d',0),(251,'Consulter les autres utilisateurs','user',1,'user','lire','r',0),(252,'Consulter les permissions des autres utilisateurs','user',1,'user_advance','readperms','r',0),(253,'Creer/modifier utilisateurs internes et externes','user',1,'user','creer','w',0),(254,'Creer/modifier utilisateurs externes seulement','user',1,'user_advance','write','w',0),(255,'Modifier le mot de passe des autres utilisateurs','user',1,'user','password','w',0),(256,'Supprimer ou desactiver les autres utilisateurs','user',1,'user','supprimer','d',0),(262,'Read all third parties by internal users (otherwise only if commercial contact). Not effective for external users (limited to themselves).','societe',1,'client','voir','r',0),(262,'Consulter tous les tiers par utilisateurs internes (sinon uniquement si contact commercial). Non effectif pour utilisateurs externes (tjs limités à eux-meme).','societe',2,'client','voir','r',1),(281,'Read contacts','societe',1,'contact','lire','r',0),(281,'Lire les contacts','societe',2,'contact','lire','r',1),(282,'Create and update contact','societe',1,'contact','creer','w',0),(282,'Creer modifier les contacts','societe',2,'contact','creer','w',0),(283,'Delete contacts','societe',1,'contact','supprimer','d',0),(283,'Supprimer les contacts','societe',2,'contact','supprimer','d',0),(286,'Export contacts','societe',1,'contact','export','d',0),(286,'Exporter les contacts','societe',2,'contact','export','d',0),(300,'Read barcodes','barcode',1,'lire_advance',NULL,'r',1),(301,'Create/modify barcodes','barcode',1,'creer_advance',NULL,'w',0),(331,'Lire les bookmarks','bookmark',1,'lire',NULL,'r',0),(332,'Creer/modifier les bookmarks','bookmark',1,'creer',NULL,'r',0),(333,'Supprimer les bookmarks','bookmark',1,'supprimer',NULL,'r',0),(341,'Consulter ses propres permissions','user',1,'self_advance','readperms','r',0),(342,'Creer/modifier ses propres infos utilisateur','user',1,'self','creer','w',0),(343,'Modifier son propre mot de passe','user',1,'self','password','w',0),(344,'Modifier ses propres permissions','user',1,'self_advance','writeperms','w',0),(351,'Consulter les groupes','user',1,'group_advance','read','r',0),(352,'Consulter les permissions des groupes','user',1,'group_advance','readperms','r',0),(353,'Creer/modifier les groupes et leurs permissions','user',1,'group_advance','write','w',0),(354,'Supprimer ou desactiver les groupes','user',1,'group_advance','delete','d',0),(358,'Exporter les utilisateurs','user',1,'user','export','r',0),(501,'Read employee contracts/salaries','salaries',1,'read',NULL,'r',0),(502,'Create/modify employee contracts/salaries','salaries',1,'write',NULL,'w',0),(511,'Read payment of salaries','salaries',1,'payment','read','w',0),(512,'Create/modify payment of salaries','salaries',1,'payment','write','w',0),(514,'Delete contracts/salaries','salaries',1,'delete',NULL,'d',0),(517,'Export employee contracts and salaries payments','salaries',1,'export',NULL,'r',0),(520,'Read loans','loan',1,'read',NULL,'r',0),(522,'Create/modify loans','loan',1,'write',NULL,'w',0),(524,'Delete loans','loan',1,'delete',NULL,'d',0),(525,'Access loan calculator','loan',1,'calc',NULL,'r',0),(527,'Export loans','loan',1,'export',NULL,'r',0),(531,'Lire les services','service',1,'lire',NULL,'r',0),(532,'Creer/modifier les services','service',1,'creer',NULL,'w',0),(534,'Supprimer les services','service',1,'supprimer',NULL,'d',0),(538,'Exporter les services','service',1,'export',NULL,'r',0),(701,'Lire les dons','don',1,'lire',NULL,'r',1),(701,'Lire les dons','don',2,'lire',NULL,'r',1),(702,'Creer/modifier les dons','don',1,'creer',NULL,'w',0),(702,'Creer/modifier les dons','don',2,'creer',NULL,'w',0),(703,'Supprimer les dons','don',1,'supprimer',NULL,'d',0),(703,'Supprimer les dons','don',2,'supprimer',NULL,'d',0),(771,'Read expense reports (yours and your subordinates)','expensereport',1,'lire',NULL,'r',1),(772,'Create/modify expense reports','expensereport',1,'creer',NULL,'w',0),(773,'Delete expense reports','expensereport',1,'supprimer',NULL,'d',0),(774,'Read all expense reports','expensereport',1,'readall',NULL,'r',1),(775,'Approve expense reports','expensereport',1,'approve',NULL,'w',0),(776,'Pay expense reports','expensereport',1,'to_paid',NULL,'w',0),(777,'Read expense reports of everybody','expensereport',1,'readall',NULL,'r',1),(778,'Create expense reports for everybody','expensereport',1,'writeall_advance',NULL,'w',0),(779,'Export expense reports','expensereport',1,'export',NULL,'r',0),(1001,'Lire les stocks','stock',1,'lire',NULL,'r',1),(1002,'Creer/Modifier les stocks','stock',1,'creer',NULL,'w',0),(1003,'Supprimer les stocks','stock',1,'supprimer',NULL,'d',0),(1004,'Lire mouvements de stocks','stock',1,'mouvement','lire','r',1),(1005,'Creer/modifier mouvements de stocks','stock',1,'mouvement','creer','w',0),(1101,'Lire les bons de livraison','expedition',1,'livraison','lire','r',1),(1102,'Creer modifier les bons de livraison','expedition',1,'livraison','creer','w',0),(1104,'Valider les bons de livraison','expedition',1,'livraison_advance','validate','d',0),(1109,'Supprimer les bons de livraison','expedition',1,'livraison','supprimer','d',0),(1121,'Read supplier proposals','supplier_proposal',1,'lire',NULL,'w',1),(1122,'Create/modify supplier proposals','supplier_proposal',1,'creer',NULL,'w',0),(1123,'Validate supplier proposals','supplier_proposal',1,'validate_advance',NULL,'w',0),(1124,'Envoyer les demandes fournisseurs','supplier_proposal',1,'send_advance',NULL,'w',0),(1125,'Delete supplier proposals','supplier_proposal',1,'supprimer',NULL,'w',0),(1126,'Close supplier price requests','supplier_proposal',1,'cloturer',NULL,'w',0),(1181,'Consulter les fournisseurs','fournisseur',1,'lire',NULL,'r',0),(1182,'Consulter les commandes fournisseur','fournisseur',1,'commande','lire','r',0),(1183,'Creer une commande fournisseur','fournisseur',1,'commande','creer','w',0),(1184,'Valider une commande fournisseur','fournisseur',1,'supplier_order_advance','validate','w',0),(1185,'Approuver une commande fournisseur','fournisseur',1,'commande','approuver','w',0),(1186,'Commander une commande fournisseur','fournisseur',1,'commande','commander','w',0),(1187,'Receptionner une commande fournisseur','fournisseur',1,'commande','receptionner','d',0),(1188,'Supprimer une commande fournisseur','fournisseur',1,'commande','supprimer','d',0),(1189,'Check/Uncheck a supplier order reception','fournisseur',1,'commande_advance','check','w',0),(1201,'Lire les exports','export',1,'lire',NULL,'r',1),(1202,'Creer/modifier un export','export',1,'creer',NULL,'w',0),(1231,'Consulter les factures fournisseur','fournisseur',1,'facture','lire','r',0),(1232,'Creer une facture fournisseur','fournisseur',1,'facture','creer','w',0),(1233,'Valider une facture fournisseur','fournisseur',1,'supplier_invoice_advance','validate','w',0),(1234,'Supprimer une facture fournisseur','fournisseur',1,'facture','supprimer','d',0),(1235,'Envoyer les factures par mail','fournisseur',1,'supplier_invoice_advance','send','a',0),(1236,'Exporter les factures fournisseurs, attributs et reglements','fournisseur',1,'facture','export','r',0),(1237,'Exporter les commande fournisseurs, attributs','fournisseur',1,'commande','export','r',0),(1251,'Run mass imports of external data (data load)','import',1,'run',NULL,'r',0),(1321,'Export customer invoices, attributes and payments','facture',1,'facture','export','r',0),(1321,'Exporter les factures clients, attributs et reglements','facture',2,'facture','export','r',0),(1322,'Re-open a fully paid invoice','facture',1,'invoice_advance','reopen','r',0),(1421,'Exporter les commandes clients et attributs','commande',1,'commande','export','r',0),(2401,'Read actions/tasks linked to his account','agenda',1,'myactions','read','r',0),(2401,'Read actions/tasks linked to his account','agenda',2,'myactions','read','r',1),(2402,'Create/modify actions/tasks linked to his account','agenda',1,'myactions','create','w',0),(2402,'Create/modify actions/tasks linked to his account','agenda',2,'myactions','create','w',0),(2403,'Delete actions/tasks linked to his account','agenda',1,'myactions','delete','w',0),(2403,'Delete actions/tasks linked to his account','agenda',2,'myactions','delete','w',0),(2411,'Read actions/tasks of others','agenda',1,'allactions','read','r',0),(2411,'Read actions/tasks of others','agenda',2,'allactions','read','r',0),(2412,'Create/modify actions/tasks of others','agenda',1,'allactions','create','w',0),(2412,'Create/modify actions/tasks of others','agenda',2,'allactions','create','w',0),(2413,'Delete actions/tasks of others','agenda',1,'allactions','delete','w',0),(2413,'Delete actions/tasks of others','agenda',2,'allactions','delete','w',0),(2414,'Export actions/tasks of others','agenda',1,'export',NULL,'w',0),(2501,'Consulter/Télécharger les documents','ecm',1,'read',NULL,'r',0),(2503,'Soumettre ou supprimer des documents','ecm',1,'upload',NULL,'w',0),(2515,'Administrer les rubriques de documents','ecm',1,'setup',NULL,'w',0),(3200,'Read archived events and fingerprints','blockedlog',1,'read',NULL,'w',0),(20001,'Read your own holidays','holiday',1,'read',NULL,'w',0),(20001,'Créer / Modifier / Lire ses demandes de congés payés','holiday',2,'write',NULL,'w',1),(20002,'Create/modify your own holidays','holiday',1,'write',NULL,'w',0),(20002,'Lire / Modifier toutes les demandes de congés payés','holiday',2,'lire_tous',NULL,'w',0),(20003,'Delete holidays','holiday',1,'delete',NULL,'w',0),(20003,'Supprimer des demandes de congés payés','holiday',2,'delete',NULL,'w',0),(20004,'Read holidays for everybody','holiday',1,'read_all',NULL,'w',0),(20004,'Définir les congés payés des utilisateurs','holiday',2,'define_holiday',NULL,'w',0),(20005,'Create/modify holidays for everybody','holiday',1,'write_all',NULL,'w',0),(20005,'Voir les logs de modification des congés payés','holiday',2,'view_log',NULL,'w',0),(20006,'Setup holidays of users (setup and update balance)','holiday',1,'define_holiday',NULL,'w',0),(20006,'Accéder au rapport mensuel des congés payés','holiday',2,'month_report',NULL,'w',0),(23001,'Read cron jobs','cron',1,'read',NULL,'w',0),(23002,'Create cron Jobs','cron',1,'create',NULL,'w',0),(23003,'Delete cron Jobs','cron',1,'delete',NULL,'w',0),(23004,'Execute cron Jobs','cron',1,'execute',NULL,'w',0),(50101,'Use point of sale','cashdesk',1,'use',NULL,'a',1),(50401,'Bind products and invoices with accounting accounts','accounting',1,'bind','write','r',0),(50411,'Read operations in General Ledger','accounting',1,'mouvements','lire','r',0),(50412,'Write/Edit operations in General Ledger','accounting',1,'mouvements','creer','w',0),(50420,'Report and export reports (turnover, balance, journals, general ledger)','accounting',1,'comptarapport','lire','r',0),(50430,'Define and close a fiscal year','accounting',1,'fiscalyear',NULL,'r',0),(50440,'Manage chart of accounts, setup of accountancy','accounting',1,'chartofaccount',NULL,'r',0),(55001,'Read surveys','opensurvey',1,'read',NULL,'r',0),(55002,'Create/modify surveys','opensurvey',1,'write',NULL,'w',0),(59001,'Visualiser les marges','margins',1,'liretous',NULL,'r',1),(59002,'Définir les marges','margins',1,'creer',NULL,'w',0),(59003,'Read every user margin','margins',1,'read','all','r',0),(63001,'Read resources','resource',1,'read',NULL,'w',1),(63002,'Create/Modify resources','resource',1,'write',NULL,'w',0),(63003,'Delete resources','resource',1,'delete',NULL,'w',0),(63004,'Link resources','resource',1,'link',NULL,'w',0),(64001,'DirectPrint','printing',1,'read',NULL,'r',0),(101250,'Read surveys','opensurvey',2,'survey','read','r',0),(101251,'Create/modify surveys','opensurvey',2,'survey','write','w',0); /*!40000 ALTER TABLE `llx_rights_def` ENABLE KEYS */; UNLOCK TABLES; @@ -8526,42 +8758,42 @@ CREATE TABLE `llx_societe` ( `parent` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `datec` datetime DEFAULT NULL, - `nom` varchar(128) DEFAULT NULL, + `nom` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(60) DEFAULT NULL, - `code_client` varchar(24) DEFAULT NULL, - `code_fournisseur` varchar(24) DEFAULT NULL, - `code_compta` varchar(24) DEFAULT NULL, - `code_compta_fournisseur` varchar(24) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `zip` varchar(25) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_client` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_fournisseur` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_compta` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_compta_fournisseur` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `zip` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_departement` int(11) DEFAULT '0', `fk_pays` int(11) DEFAULT '0', - `phone` varchar(20) DEFAULT NULL, - `fax` varchar(20) DEFAULT NULL, - `url` varchar(255) DEFAULT NULL, - `email` varchar(128) DEFAULT NULL, - `skype` varchar(255) DEFAULT NULL, + `phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `fax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `skype` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_effectif` int(11) DEFAULT '0', `fk_typent` int(11) DEFAULT '0', `fk_forme_juridique` int(11) DEFAULT '0', - `fk_currency` varchar(3) DEFAULT NULL, - `siren` varchar(128) DEFAULT NULL, - `siret` varchar(128) DEFAULT NULL, - `ape` varchar(128) DEFAULT NULL, - `idprof4` varchar(128) DEFAULT NULL, - `tva_intra` varchar(20) DEFAULT NULL, + `fk_currency` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, + `siren` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `siret` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `ape` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `idprof4` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `tva_intra` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, `capital` double(24,8) DEFAULT NULL, `fk_stcomm` int(11) NOT NULL DEFAULT '0', - `note_private` text, - `note_public` text, - `prefix_comm` varchar(5) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `prefix_comm` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, `client` tinyint(4) DEFAULT '0', `fournisseur` tinyint(4) DEFAULT '0', - `supplier_account` varchar(32) DEFAULT NULL, - `fk_prospectlevel` varchar(12) DEFAULT NULL, + `supplier_account` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `fk_prospectlevel` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, `customer_bad` tinyint(4) DEFAULT '0', `customer_rate` double DEFAULT '0', `supplier_rate` double DEFAULT '0', @@ -8579,24 +8811,24 @@ CREATE TABLE `llx_societe` ( `localtax1_value` double(6,3) DEFAULT NULL, `localtax2_assuj` tinyint(4) DEFAULT '0', `localtax2_value` double(6,3) DEFAULT NULL, - `barcode` varchar(255) DEFAULT NULL, + `barcode` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `price_level` int(11) DEFAULT NULL, - `default_lang` varchar(6) DEFAULT NULL, - `canvas` varchar(32) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `default_lang` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `canvas` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `status` tinyint(4) DEFAULT '1', - `logo` varchar(255) DEFAULT NULL, - `idprof5` varchar(128) DEFAULT NULL, - `idprof6` varchar(128) DEFAULT NULL, + `logo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `idprof5` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `idprof6` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_barcode_type` int(11) DEFAULT '0', - `webservices_url` varchar(255) DEFAULT NULL, - `webservices_key` varchar(128) DEFAULT NULL, - `name_alias` varchar(128) DEFAULT NULL, + `webservices_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `webservices_key` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `name_alias` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_incoterms` int(11) DEFAULT NULL, - `location_incoterms` varchar(255) DEFAULT NULL, - `model_pdf` varchar(255) DEFAULT NULL, + `location_incoterms` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_account` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_societe_prefix_comm` (`prefix_comm`,`entity`), @@ -8606,7 +8838,7 @@ CREATE TABLE `llx_societe` ( KEY `idx_societe_user_creat` (`fk_user_creat`), KEY `idx_societe_user_modif` (`fk_user_modif`), KEY `idx_societe_barcode` (`barcode`) -) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8630,20 +8862,20 @@ CREATE TABLE `llx_societe_address` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `label` varchar(30) DEFAULT NULL, + `label` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_soc` int(11) DEFAULT '0', - `name` varchar(60) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `zip` varchar(10) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, + `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `zip` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_pays` int(11) DEFAULT '0', - `phone` varchar(20) DEFAULT NULL, - `fax` varchar(20) DEFAULT NULL, - `note` text, + `phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `fax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8666,10 +8898,10 @@ CREATE TABLE `llx_societe_commerciaux` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_soc` int(11) DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_societe_commerciaux` (`fk_soc`,`fk_user`) -) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8693,10 +8925,10 @@ CREATE TABLE `llx_societe_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_societe_extrafields` (`fk_object`) -) ENGINE=InnoDB AUTO_INCREMENT=98 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=98 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8722,10 +8954,10 @@ CREATE TABLE `llx_societe_log` ( `fk_soc` int(11) DEFAULT NULL, `fk_statut` int(11) DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, - `author` varchar(30) DEFAULT NULL, - `label` varchar(128) DEFAULT NULL, + `author` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `label` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8752,7 +8984,7 @@ CREATE TABLE `llx_societe_prices` ( `fk_user_author` int(11) DEFAULT NULL, `price_level` tinyint(4) DEFAULT '1', PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8779,9 +9011,9 @@ CREATE TABLE `llx_societe_remise` ( `datec` datetime DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, `remise_client` double(6,3) NOT NULL DEFAULT '0.000', - `note` text, + `note` text COLLATE utf8_unicode_ci, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8813,7 +9045,7 @@ CREATE TABLE `llx_societe_remise_except` ( `fk_facture_line` int(11) DEFAULT NULL, `fk_facture` int(11) DEFAULT NULL, `fk_facture_source` int(11) DEFAULT NULL, - `description` text NOT NULL, + `description` text COLLATE utf8_unicode_ci NOT NULL, `multicurrency_amount_ht` double(24,8) NOT NULL DEFAULT '0.00000000', `multicurrency_amount_tva` double(24,8) NOT NULL DEFAULT '0.00000000', `multicurrency_amount_ttc` double(24,8) NOT NULL DEFAULT '0.00000000', @@ -8828,17 +9060,17 @@ CREATE TABLE `llx_societe_remise_except` ( KEY `idx_societe_remise_except_fk_facture_source` (`fk_facture_source`), KEY `fk_soc_remise_fk_invoice_supplier_line` (`fk_invoice_supplier_line`), KEY `fk_societe_remise_fk_invoice_supplier_source` (`fk_invoice_supplier`), - CONSTRAINT `fk_soc_remise_fk_facture_line` FOREIGN KEY (`fk_facture_line`) REFERENCES `llx_facturedet` (`rowid`), - CONSTRAINT `fk_soc_remise_fk_invoice_supplier_line` FOREIGN KEY (`fk_invoice_supplier_line`) REFERENCES `llx_facture_fourn_det` (`rowid`), - CONSTRAINT `fk_soc_remise_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_societe_remise_fk_facture` FOREIGN KEY (`fk_facture`) REFERENCES `llx_facture` (`rowid`), CONSTRAINT `fk_societe_remise_fk_facture_line` FOREIGN KEY (`fk_facture_line`) REFERENCES `llx_facturedet` (`rowid`), CONSTRAINT `fk_societe_remise_fk_facture_source` FOREIGN KEY (`fk_facture_source`) REFERENCES `llx_facture` (`rowid`), CONSTRAINT `fk_societe_remise_fk_invoice_supplier` FOREIGN KEY (`fk_invoice_supplier`) REFERENCES `llx_facture_fourn` (`rowid`), CONSTRAINT `fk_societe_remise_fk_invoice_supplier_source` FOREIGN KEY (`fk_invoice_supplier`) REFERENCES `llx_facture_fourn` (`rowid`), CONSTRAINT `fk_societe_remise_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), - CONSTRAINT `fk_societe_remise_fk_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; + CONSTRAINT `fk_societe_remise_fk_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`), + CONSTRAINT `fk_soc_remise_fk_facture_line` FOREIGN KEY (`fk_facture_line`) REFERENCES `llx_facturedet` (`rowid`), + CONSTRAINT `fk_soc_remise_fk_invoice_supplier_line` FOREIGN KEY (`fk_invoice_supplier_line`) REFERENCES `llx_facture_fourn_det` (`rowid`), + CONSTRAINT `fk_soc_remise_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8863,24 +9095,24 @@ CREATE TABLE `llx_societe_rib` ( `fk_soc` int(11) NOT NULL, `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `label` varchar(30) DEFAULT NULL, - `bank` varchar(255) DEFAULT NULL, - `code_banque` varchar(128) DEFAULT NULL, - `code_guichet` varchar(6) DEFAULT NULL, - `number` varchar(255) DEFAULT NULL, - `cle_rib` varchar(5) DEFAULT NULL, - `bic` varchar(20) DEFAULT NULL, - `iban_prefix` varchar(34) DEFAULT NULL, - `domiciliation` varchar(255) DEFAULT NULL, - `proprio` varchar(60) DEFAULT NULL, - `owner_address` text, + `label` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `bank` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_banque` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_guichet` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cle_rib` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, + `bic` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `iban_prefix` varchar(34) COLLATE utf8_unicode_ci DEFAULT NULL, + `domiciliation` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `proprio` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, + `owner_address` text COLLATE utf8_unicode_ci, `default_rib` tinyint(4) NOT NULL DEFAULT '0', - `rum` varchar(32) DEFAULT NULL, + `rum` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, `date_rum` date DEFAULT NULL, - `frstrecur` varchar(16) DEFAULT 'FRST', - `import_key` varchar(14) DEFAULT NULL, + `frstrecur` varchar(16) COLLATE utf8_unicode_ci DEFAULT 'FRST', + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8906,41 +9138,41 @@ CREATE TABLE `llx_socpeople` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_soc` int(11) DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `civility` varchar(6) DEFAULT NULL, - `lastname` varchar(50) DEFAULT NULL, - `firstname` varchar(50) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `zip` varchar(25) DEFAULT NULL, - `town` text, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `civility` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `lastname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `firstname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `zip` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` text COLLATE utf8_unicode_ci, `fk_departement` int(11) DEFAULT NULL, `fk_pays` int(11) DEFAULT '0', `birthday` date DEFAULT NULL, - `poste` varchar(80) DEFAULT NULL, - `phone` varchar(30) DEFAULT NULL, - `phone_perso` varchar(30) DEFAULT NULL, - `phone_mobile` varchar(30) DEFAULT NULL, - `fax` varchar(30) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `jabberid` varchar(255) DEFAULT NULL, - `skype` varchar(255) DEFAULT NULL, - `photo` varchar(255) DEFAULT NULL, + `poste` varchar(80) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone_perso` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `phone_mobile` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `fax` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `jabberid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `skype` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `photo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `priv` smallint(6) NOT NULL DEFAULT '0', `no_email` smallint(6) NOT NULL DEFAULT '0', `fk_user_creat` int(11) DEFAULT '0', `fk_user_modif` int(11) DEFAULT NULL, - `note_private` text, - `note_public` text, - `default_lang` varchar(6) DEFAULT NULL, - `canvas` varchar(32) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `default_lang` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `canvas` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, `statut` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (`rowid`), KEY `idx_socpeople_fk_soc` (`fk_soc`), KEY `idx_socpeople_fk_user_creat` (`fk_user_creat`), CONSTRAINT `fk_socpeople_fk_soc` FOREIGN KEY (`fk_soc`) REFERENCES `llx_societe` (`rowid`), CONSTRAINT `fk_socpeople_user_creat_user_rowid` FOREIGN KEY (`fk_user_creat`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8964,10 +9196,10 @@ CREATE TABLE `llx_socpeople_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_socpeople_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -8979,6 +9211,38 @@ LOCK TABLES `llx_socpeople_extrafields` WRITE; /*!40000 ALTER TABLE `llx_socpeople_extrafields` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `llx_stock_lotserial` +-- + +DROP TABLE IF EXISTS `llx_stock_lotserial`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `llx_stock_lotserial` ( + `rowid` int(11) NOT NULL AUTO_INCREMENT, + `entity` int(11) DEFAULT NULL, + `fk_product` int(11) NOT NULL, + `batch` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `eatby` date DEFAULT NULL, + `sellby` date DEFAULT NULL, + `datec` datetime DEFAULT NULL, + `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `fk_user_creat` int(11) DEFAULT NULL, + `fk_user_modif` int(11) DEFAULT NULL, + `import_key` int(11) DEFAULT NULL, + PRIMARY KEY (`rowid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `llx_stock_lotserial` +-- + +LOCK TABLES `llx_stock_lotserial` WRITE; +/*!40000 ALTER TABLE `llx_stock_lotserial` DISABLE KEYS */; +/*!40000 ALTER TABLE `llx_stock_lotserial` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `llx_stock_mouvement` -- @@ -8996,17 +9260,17 @@ CREATE TABLE `llx_stock_mouvement` ( `price` double(24,8) DEFAULT '0.00000000', `type_mouvement` smallint(6) DEFAULT NULL, `fk_user_author` int(11) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_origin` int(11) DEFAULT NULL, - `origintype` varchar(32) DEFAULT NULL, - `inventorycode` varchar(128) DEFAULT NULL, - `batch` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `origintype` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `inventorycode` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `batch` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `eatby` date DEFAULT NULL, `sellby` date DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_stock_mouvement_fk_product` (`fk_product`), KEY `idx_stock_mouvement_fk_entrepot` (`fk_entrepot`) -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9059,10 +9323,10 @@ DROP TABLE IF EXISTS `llx_supplier_proposal`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_supplier_proposal` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `ref` varchar(30) NOT NULL, + `ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `ref_ext` varchar(255) DEFAULT NULL, - `ref_int` varchar(255) DEFAULT NULL, + `ref_ext` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_soc` int(11) DEFAULT NULL, `fk_projet` int(11) DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -9084,25 +9348,25 @@ CREATE TABLE `llx_supplier_proposal` ( `localtax2` double(24,8) DEFAULT '0.00000000', `total` double(24,8) DEFAULT '0.00000000', `fk_account` int(11) DEFAULT NULL, - `fk_currency` varchar(3) DEFAULT NULL, + `fk_currency` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_cond_reglement` int(11) DEFAULT NULL, `fk_mode_reglement` int(11) DEFAULT NULL, - `note_private` text, - `note_public` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note_private` text COLLATE utf8_unicode_ci, + `note_public` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_livraison` date DEFAULT NULL, `fk_shipping_method` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `extraparams` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `extraparams` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_tx` double(24,8) DEFAULT '1.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ttc` double(24,8) DEFAULT '0.00000000', - `last_main_doc` varchar(255) DEFAULT NULL, + `last_main_doc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9126,9 +9390,9 @@ CREATE TABLE `llx_supplier_proposal_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9152,15 +9416,15 @@ CREATE TABLE `llx_supplier_proposaldet` ( `fk_supplier_proposal` int(11) NOT NULL, `fk_parent_line` int(11) DEFAULT NULL, `fk_product` int(11) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, - `description` text, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8_unicode_ci, `fk_remise_except` int(11) DEFAULT NULL, `tva_tx` double(6,3) DEFAULT '0.000', - `vat_src_code` varchar(10) DEFAULT '', + `vat_src_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT '', `localtax1_tx` double(6,3) DEFAULT '0.000', - `localtax1_type` varchar(10) DEFAULT NULL, + `localtax1_type` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `localtax2_tx` double(6,3) DEFAULT '0.000', - `localtax2_type` varchar(10) DEFAULT NULL, + `localtax2_type` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, `qty` double DEFAULT NULL, `remise_percent` double DEFAULT '0', `remise` double DEFAULT '0', @@ -9177,9 +9441,9 @@ CREATE TABLE `llx_supplier_proposaldet` ( `fk_product_fournisseur_price` int(11) DEFAULT NULL, `special_code` int(11) DEFAULT '0', `rang` int(11) DEFAULT '0', - `ref_fourn` varchar(30) DEFAULT NULL, + `ref_fourn` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_multicurrency` int(11) DEFAULT NULL, - `multicurrency_code` varchar(255) DEFAULT NULL, + `multicurrency_code` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `multicurrency_subprice` double(24,8) DEFAULT '0.00000000', `multicurrency_total_ht` double(24,8) DEFAULT '0.00000000', `multicurrency_total_tva` double(24,8) DEFAULT '0.00000000', @@ -9191,7 +9455,7 @@ CREATE TABLE `llx_supplier_proposaldet` ( KEY `fk_supplier_proposaldet_fk_unit` (`fk_unit`), CONSTRAINT `fk_supplier_proposaldet_fk_supplier_proposal` FOREIGN KEY (`fk_supplier_proposal`) REFERENCES `llx_supplier_proposal` (`rowid`), CONSTRAINT `fk_supplier_proposaldet_fk_unit` FOREIGN KEY (`fk_unit`) REFERENCES `llx_c_units` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9215,9 +9479,9 @@ CREATE TABLE `llx_supplier_proposaldet_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9243,17 +9507,17 @@ CREATE TABLE `llx_tva` ( `datep` date DEFAULT NULL, `datev` date DEFAULT NULL, `amount` double(24,8) DEFAULT NULL, - `label` varchar(255) DEFAULT NULL, + `label` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `note` text, + `note` text COLLATE utf8_unicode_ci, `fk_bank` int(11) DEFAULT NULL, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, `fk_typepayment` int(11) DEFAULT NULL, - `num_payment` varchar(50) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `num_payment` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9278,65 +9542,65 @@ CREATE TABLE `llx_user` ( `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_creat` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, - `login` varchar(50) NOT NULL, + `login` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `civility` varchar(6) DEFAULT NULL, - `ref_ext` varchar(50) DEFAULT NULL, - `ref_int` varchar(50) DEFAULT NULL, + `civility` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_ext` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `ref_int` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `employee` smallint(6) DEFAULT '1', `fk_establishment` int(11) DEFAULT '0', - `pass` varchar(128) DEFAULT NULL, - `pass_crypted` varchar(128) DEFAULT NULL, - `pass_temp` varchar(128) DEFAULT NULL, - `api_key` varchar(128) DEFAULT NULL, - `lastname` varchar(50) DEFAULT NULL, - `firstname` varchar(50) DEFAULT NULL, - `job` varchar(128) DEFAULT NULL, - `skype` varchar(255) DEFAULT NULL, - `office_phone` varchar(20) DEFAULT NULL, - `office_fax` varchar(20) DEFAULT NULL, - `user_mobile` varchar(20) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `signature` text, + `pass` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `pass_crypted` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `pass_temp` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `api_key` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `lastname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `firstname` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, + `job` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `skype` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `office_phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `office_fax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `user_mobile` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `signature` text COLLATE utf8_unicode_ci, `admin` smallint(6) DEFAULT '0', - `webcal_login` varchar(25) DEFAULT NULL, + `webcal_login` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, `module_comm` smallint(6) DEFAULT '1', `module_compta` smallint(6) DEFAULT '1', `fk_soc` int(11) DEFAULT NULL, `fk_socpeople` int(11) DEFAULT NULL, `fk_member` int(11) DEFAULT NULL, - `note` text, + `note` text COLLATE utf8_unicode_ci, `datelastlogin` datetime DEFAULT NULL, `datepreviouslogin` datetime DEFAULT NULL, `egroupware_id` int(11) DEFAULT NULL, - `ldap_sid` varchar(255) DEFAULT NULL, + `ldap_sid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `statut` tinyint(4) DEFAULT '1', - `photo` varchar(255) DEFAULT NULL, - `lang` varchar(6) DEFAULT NULL, - `openid` varchar(255) DEFAULT NULL, + `photo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `lang` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `openid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, `thm` double(24,8) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `zip` varchar(25) DEFAULT NULL, - `town` varchar(50) DEFAULT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `zip` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, + `town` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_state` int(11) DEFAULT '0', `fk_country` int(11) DEFAULT '0', - `color` varchar(6) DEFAULT NULL, - `accountancy_code` varchar(32) DEFAULT NULL, - `barcode` varchar(255) DEFAULT NULL, + `color` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `accountancy_code` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `barcode` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_barcode_type` int(11) DEFAULT '0', `nb_holiday` int(11) DEFAULT '0', `salary` double(24,8) DEFAULT NULL, `tjm` double(24,8) DEFAULT NULL, `salaryextra` double(24,8) DEFAULT NULL, `weeklyhours` double(16,8) DEFAULT NULL, - `gender` varchar(10) DEFAULT NULL, - `note_public` text, + `gender` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `note_public` text COLLATE utf8_unicode_ci, `dateemployment` datetime DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, - `model_pdf` varchar(255) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `birth` date DEFAULT NULL, - `pass_encoding` varchar(24) DEFAULT NULL, + `pass_encoding` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL, `default_range` int(11) DEFAULT NULL, `default_c_exp_tax_cat` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`), @@ -9346,7 +9610,7 @@ CREATE TABLE `llx_user` ( UNIQUE KEY `uk_user_api_key` (`api_key`), KEY `idx_user_api_key` (`api_key`), KEY `idx_user_fk_societe` (`fk_soc`) -) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9355,7 +9619,7 @@ CREATE TABLE `llx_user` ( LOCK TABLES `llx_user` WRITE; /*!40000 ALTER TABLE `llx_user` DISABLE KEYS */; -INSERT INTO `llx_user` VALUES (1,'2010-07-08 13:20:11','2017-02-01 15:06:04',NULL,NULL,'aeinstein',0,NULL,NULL,NULL,1,0,NULL,'11c9c772d6471aa24c27274bdd8a223b',NULL,NULL,'Einstein','Albert','','','123456789','','','aeinstein@example.com','',0,'',1,1,NULL,NULL,NULL,'','2015-10-05 08:32:44','2015-10-03 11:43:50',NULL,'',1,'alberteinstein.jpg',NULL,NULL,14,NULL,'','','',NULL,NULL,'aaaaff','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(2,'2010-07-08 13:54:48','2017-02-01 15:06:04',NULL,NULL,'demo',1,NULL,NULL,NULL,1,0,NULL,'fe01ce2a7fbac8fafaed7c982a04e229',NULL,NULL,'Doe','David','','','09123123','','','daviddoe@mycompany.com','',0,'',1,1,NULL,NULL,NULL,'','2016-07-30 23:10:54','2016-07-30 23:04:17',NULL,'',1,'johndoe.png',NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(3,'2010-07-11 16:18:59','2017-02-01 15:06:04',NULL,NULL,'pcurie',1,NULL,NULL,NULL,1,0,NULL,'ab335b4eb4c3c99334f656e5db9584c9',NULL,NULL,'Curie','Pierre','','','','','','pcurie@example.com','',0,'',1,1,NULL,NULL,2,'','2012-12-21 17:38:55',NULL,NULL,'',1,'pierrecurie.jpg',NULL,NULL,14,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(4,'2013-01-23 17:52:27','2017-02-01 15:06:04',NULL,NULL,'bbookkeeper',1,NULL,NULL,NULL,1,0,NULL,'a7d30b58d647fcf59b7163f9592b1dbb',NULL,NULL,'Bookkeeper','Bob','Bookkeeper','','','','','','',0,'',1,1,17,6,NULL,'','2013-02-25 10:18:41','2013-01-23 17:53:20',NULL,'',1,NULL,NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(10,'2015-10-03 11:47:41','2017-02-01 15:06:04',NULL,NULL,'mcurie',1,NULL,NULL,NULL,1,0,NULL,'52cda011808bb282d1d3625ab607a145',NULL,'t3mnkbhs','Curie','Marie','','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,'mariecurie.jpg',NULL,NULL,14,NULL,'','','',NULL,NULL,'ffaaff','',NULL,0,0,NULL,NULL,NULL,NULL,'woman',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(11,'2015-10-05 09:07:52','2017-02-01 15:06:04',NULL,NULL,'zzeceo',1,NULL,NULL,NULL,1,0,NULL,'92af989c4c3a5140fb5d73eb77a52454',NULL,'cq78nf9m','Zeceo','Zack','President','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'','2015-10-05 22:48:08','2015-10-05 21:18:46',NULL,'',1,NULL,NULL,NULL,NULL,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(12,'2015-10-05 09:09:46','2018-01-19 11:24:18',NULL,NULL,'admin',0,NULL,NULL,NULL,1,0,NULL,'f6fdffe48c908deb0f4c3bd36c032e72',NULL,'nd6hgbcr','Adminson','Alice','Admin Technical','','','','','','Alice - 123',1,NULL,1,1,NULL,NULL,NULL,'','2018-01-19 11:21:41','2018-01-19 11:18:47',NULL,'',1,'mariecurie.jpg',NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'woman',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(13,'2015-10-05 21:29:35','2017-02-01 15:06:04',NULL,NULL,'ccommercy',1,NULL,NULL,NULL,1,0,NULL,'179858e041af35e8f4c81d68c55fe9da',NULL,'y451ksdv','Commercy','Charle','Commercial leader','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,NULL,NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(14,'2015-10-05 21:33:33','2017-02-01 15:06:04',NULL,NULL,'sscientol',1,NULL,NULL,NULL,1,0,NULL,'39bee07ac42f31c98e79cdcd5e5fe4c5',NULL,'s2hp8bxd','Scientol','Sam','Scientist leader','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,NULL,NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(16,'2015-10-05 22:47:52','2017-02-20 16:49:00',NULL,NULL,'ccommerson',1,NULL,NULL,NULL,1,0,NULL,'d68005ccf362b82d084551b6291792a3',NULL,'cx9y1dk0','Charle1','Commerson','Sale representative','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'','2015-10-05 23:46:24','2015-10-05 23:37:31',NULL,'',1,NULL,NULL,NULL,13,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(17,'2015-10-05 22:48:39','2017-02-01 15:06:04',NULL,NULL,'cc2',1,NULL,NULL,NULL,1,0,NULL,'a964065211872fb76f876c6c3e952ea3',NULL,'gw8cb7xj','Charle2','Commerson','Sale representative','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'','2015-10-05 23:16:06',NULL,NULL,'',0,NULL,NULL,NULL,13,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(18,'2016-01-22 17:27:02','2017-02-01 15:06:04',NULL,NULL,'ldestailleur',1,NULL,NULL,NULL,1,0,NULL,'1bb7805145a7a5066df9e6d585b8b645',NULL,'87g06wbx','Destailleur','Laurent','Project leader of Dolibarr ERP CRM','','','','','ldestailleur@example.com','
Laurent DESTAILLEUR
\r\n\r\n
\r\n
Project Director
\r\nldestailleur@example.com
\r\n\r\n
 
\r\n\r\n\r\n
',0,NULL,1,1,10,10,NULL,'More information on http://www.destailleur.fr','2017-09-06 11:55:30','2017-08-30 15:53:25',NULL,'',1,'ldestailleur_200x200.jpg',NULL,NULL,NULL,NULL,'','','',NULL,NULL,'007f7f','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(19,'2017-02-02 03:55:44','2017-02-01 23:56:50',NULL,NULL,'aboston',1,NULL,NULL,NULL,1,0,NULL,'a7a77a5aff2d5fc2f75f2f61507c88d4',NULL,NULL,'Boston','Alex','','','','','','aboston@example.com','Alex Boston
\r\nAdmin support service - 555 01 02 03 04',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,NULL,NULL,NULL,12,25.00000000,'','','',NULL,NULL,'ff00ff','',NULL,0,0,NULL,NULL,NULL,32.00000000,NULL,NULL,'2014-11-04 00:00:00',NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO `llx_user` VALUES (1,'2010-07-08 13:20:11','2017-02-01 15:06:04',NULL,NULL,'aeinstein',0,NULL,NULL,NULL,1,0,NULL,'11c9c772d6471aa24c27274bdd8a223b',NULL,NULL,'Einstein','Albert','','','123456789','','','aeinstein@example.com','',0,'',1,1,NULL,NULL,NULL,'','2015-10-05 08:32:44','2015-10-03 11:43:50',NULL,'',1,'alberteinstein.jpg',NULL,NULL,14,NULL,'','','',NULL,NULL,'aaaaff','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(2,'2010-07-08 13:54:48','2017-02-01 15:06:04',NULL,NULL,'demo',1,NULL,NULL,NULL,1,0,NULL,'fe01ce2a7fbac8fafaed7c982a04e229',NULL,NULL,'Doe','David','','','09123123','','','daviddoe@mycompany.com','',0,'',1,1,NULL,NULL,NULL,'','2016-07-30 23:10:54','2016-07-30 23:04:17',NULL,'',1,'johndoe.png',NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(3,'2010-07-11 16:18:59','2017-02-01 15:06:04',NULL,NULL,'pcurie',1,NULL,NULL,NULL,1,0,NULL,'ab335b4eb4c3c99334f656e5db9584c9',NULL,NULL,'Curie','Pierre','','','','','','pcurie@example.com','',0,'',1,1,NULL,NULL,2,'','2012-12-21 17:38:55',NULL,NULL,'',1,'pierrecurie.jpg',NULL,NULL,14,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(4,'2013-01-23 17:52:27','2017-02-01 15:06:04',NULL,NULL,'bbookkeeper',1,NULL,NULL,NULL,1,0,NULL,'a7d30b58d647fcf59b7163f9592b1dbb',NULL,NULL,'Bookkeeper','Bob','Bookkeeper','','','','','','',0,'',1,1,17,6,NULL,'','2013-02-25 10:18:41','2013-01-23 17:53:20',NULL,'',1,NULL,NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(10,'2015-10-03 11:47:41','2017-02-01 15:06:04',NULL,NULL,'mcurie',1,NULL,NULL,NULL,1,0,NULL,'52cda011808bb282d1d3625ab607a145',NULL,'t3mnkbhs','Curie','Marie','','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,'mariecurie.jpg',NULL,NULL,14,NULL,'','','',NULL,NULL,'ffaaff','',NULL,0,0,NULL,NULL,NULL,NULL,'woman',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(11,'2015-10-05 09:07:52','2017-02-01 15:06:04',NULL,NULL,'zzeceo',1,NULL,NULL,NULL,1,0,NULL,'92af989c4c3a5140fb5d73eb77a52454',NULL,'cq78nf9m','Zeceo','Zack','President','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'','2015-10-05 22:48:08','2015-10-05 21:18:46',NULL,'',1,NULL,NULL,NULL,NULL,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(12,'2015-10-05 09:09:46','2018-01-19 11:24:18',NULL,NULL,'admin',0,NULL,NULL,NULL,1,0,NULL,'f6fdffe48c908deb0f4c3bd36c032e72',NULL,'nd6hgbcr','Adminson','Alice','Admin Technical','','','','','','Alice - 123',1,NULL,1,1,NULL,NULL,NULL,'','2018-03-16 13:54:23','2018-01-19 11:21:41',NULL,'',1,'mariecurie.jpg',NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'woman',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(13,'2015-10-05 21:29:35','2017-02-01 15:06:04',NULL,NULL,'ccommercy',1,NULL,NULL,NULL,1,0,NULL,'179858e041af35e8f4c81d68c55fe9da',NULL,'y451ksdv','Commercy','Charle','Commercial leader','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,NULL,NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(14,'2015-10-05 21:33:33','2017-02-01 15:06:04',NULL,NULL,'sscientol',1,NULL,NULL,NULL,1,0,NULL,'39bee07ac42f31c98e79cdcd5e5fe4c5',NULL,'s2hp8bxd','Scientol','Sam','Scientist leader','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,NULL,NULL,NULL,11,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(16,'2015-10-05 22:47:52','2017-02-20 16:49:00',NULL,NULL,'ccommerson',1,NULL,NULL,NULL,1,0,NULL,'d68005ccf362b82d084551b6291792a3',NULL,'cx9y1dk0','Charle1','Commerson','Sale representative','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'','2015-10-05 23:46:24','2015-10-05 23:37:31',NULL,'',1,NULL,NULL,NULL,13,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(17,'2015-10-05 22:48:39','2017-02-01 15:06:04',NULL,NULL,'cc2',1,NULL,NULL,NULL,1,0,NULL,'a964065211872fb76f876c6c3e952ea3',NULL,'gw8cb7xj','Charle2','Commerson','Sale representative','','','','','','',0,NULL,1,1,NULL,NULL,NULL,'','2015-10-05 23:16:06',NULL,NULL,'',0,NULL,NULL,NULL,13,NULL,'','','',NULL,NULL,'','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(18,'2016-01-22 17:27:02','2017-02-01 15:06:04',NULL,NULL,'ldestailleur',1,NULL,NULL,NULL,1,0,NULL,'1bb7805145a7a5066df9e6d585b8b645',NULL,'87g06wbx','Destailleur','Laurent','Project leader of Dolibarr ERP CRM','','','','','ldestailleur@example.com','
Laurent DESTAILLEUR
\r\n\r\n
\r\n
Project Director
\r\nldestailleur@example.com
\r\n\r\n
 
\r\n\r\n\r\n
',0,NULL,1,1,10,10,NULL,'More information on http://www.destailleur.fr','2017-09-06 11:55:30','2017-08-30 15:53:25',NULL,'',1,'ldestailleur_200x200.jpg',NULL,NULL,NULL,NULL,'','','',NULL,NULL,'007f7f','',NULL,0,0,NULL,NULL,NULL,NULL,'man',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),(19,'2017-02-02 03:55:44','2017-02-01 23:56:50',NULL,NULL,'aboston',1,NULL,NULL,NULL,1,0,NULL,'a7a77a5aff2d5fc2f75f2f61507c88d4',NULL,NULL,'Boston','Alex','','','','','','aboston@example.com','Alex Boston
\r\nAdmin support service - 555 01 02 03 04',0,NULL,1,1,NULL,NULL,NULL,'',NULL,NULL,NULL,'',1,NULL,NULL,NULL,12,25.00000000,'','','',NULL,NULL,'ff00ff','',NULL,0,0,NULL,NULL,NULL,32.00000000,NULL,NULL,'2014-11-04 00:00:00',NULL,NULL,NULL,NULL,NULL,NULL); /*!40000 ALTER TABLE `llx_user` ENABLE KEYS */; UNLOCK TABLES; @@ -9372,7 +9636,7 @@ CREATE TABLE `llx_user_alert` ( `fk_contact` int(11) DEFAULT NULL, `fk_user` int(11) DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9394,12 +9658,12 @@ DROP TABLE IF EXISTS `llx_user_clicktodial`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_user_clicktodial` ( `fk_user` int(11) NOT NULL, - `url` varchar(255) DEFAULT NULL, - `login` varchar(32) DEFAULT NULL, - `pass` varchar(64) DEFAULT NULL, - `poste` varchar(20) DEFAULT NULL, + `url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `login` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL, + `pass` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, + `poste` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`fk_user`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9462,10 +9726,10 @@ CREATE TABLE `llx_user_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_user_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9487,10 +9751,10 @@ DROP TABLE IF EXISTS `llx_user_param`; CREATE TABLE `llx_user_param` ( `fk_user` int(11) NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', - `param` varchar(255) NOT NULL, - `value` text NOT NULL, + `param` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `value` text COLLATE utf8_unicode_ci NOT NULL, UNIQUE KEY `uk_user_param` (`fk_user`,`param`,`entity`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9516,19 +9780,19 @@ CREATE TABLE `llx_user_rib` ( `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `label` varchar(30) DEFAULT NULL, - `bank` varchar(255) DEFAULT NULL, - `code_banque` varchar(128) DEFAULT NULL, - `code_guichet` varchar(6) DEFAULT NULL, - `number` varchar(255) DEFAULT NULL, - `cle_rib` varchar(5) DEFAULT NULL, - `bic` varchar(11) DEFAULT NULL, - `iban_prefix` varchar(34) DEFAULT NULL, - `domiciliation` varchar(255) DEFAULT NULL, - `proprio` varchar(60) DEFAULT NULL, - `owner_address` varchar(255) DEFAULT NULL, + `label` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL, + `bank` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_banque` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `code_guichet` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, + `number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `cle_rib` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL, + `bic` varchar(11) COLLATE utf8_unicode_ci DEFAULT NULL, + `iban_prefix` varchar(34) COLLATE utf8_unicode_ci DEFAULT NULL, + `domiciliation` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `proprio` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL, + `owner_address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9556,7 +9820,7 @@ CREATE TABLE `llx_user_rights` ( UNIQUE KEY `uk_user_rights` (`entity`,`fk_user`,`fk_id`), KEY `fk_user_rights_fk_user_user` (`fk_user`), CONSTRAINT `fk_user_rights_fk_user_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=16208 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=16387 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9565,7 +9829,7 @@ CREATE TABLE `llx_user_rights` ( LOCK TABLES `llx_user_rights` WRITE; /*!40000 ALTER TABLE `llx_user_rights` DISABLE KEYS */; -INSERT INTO `llx_user_rights` VALUES (12402,1,1,11),(12380,1,1,12),(12385,1,1,13),(12389,1,1,14),(12393,1,1,15),(12398,1,1,16),(12404,1,1,19),(9726,1,1,21),(9700,1,1,22),(9706,1,1,24),(9711,1,1,25),(9716,1,1,26),(9722,1,1,27),(9728,1,1,28),(9978,1,1,31),(9968,1,1,32),(9974,1,1,34),(1910,1,1,36),(9980,1,1,38),(11573,1,1,41),(11574,1,1,42),(11575,1,1,44),(11576,1,1,45),(7184,1,1,61),(7181,1,1,62),(7183,1,1,64),(7185,1,1,67),(7186,1,1,68),(1678,1,1,71),(1673,1,1,72),(1675,1,1,74),(1679,1,1,75),(1677,1,1,76),(1681,1,1,78),(1682,1,1,79),(12322,1,1,81),(12309,1,1,82),(12312,1,1,84),(12314,1,1,86),(12317,1,1,87),(12320,1,1,88),(12323,1,1,89),(11580,1,1,91),(11581,1,1,92),(11582,1,1,93),(11583,1,1,94),(10097,1,1,95),(10099,1,1,96),(10103,1,1,97),(10104,1,1,98),(7139,1,1,101),(7134,1,1,102),(7136,1,1,104),(7137,1,1,105),(7138,1,1,106),(7140,1,1,109),(10229,1,1,111),(10201,1,1,112),(10207,1,1,113),(10213,1,1,114),(10219,1,1,115),(10225,1,1,116),(10231,1,1,117),(12518,1,1,121),(12508,1,1,122),(12514,1,1,125),(12520,1,1,126),(11577,1,1,141),(11578,1,1,142),(11579,1,1,144),(2307,1,1,151),(2304,1,1,152),(2306,1,1,153),(2308,1,1,154),(10092,1,1,161),(10093,1,1,162),(10094,1,1,163),(10095,1,1,164),(10096,1,1,165),(1585,1,1,170),(12342,1,1,171),(12331,1,1,172),(12335,1,1,173),(12339,1,1,174),(12343,1,1,178),(10000,1,1,221),(9990,1,1,222),(9996,1,1,223),(10002,1,1,229),(10007,1,1,237),(10011,1,1,238),(10015,1,1,239),(1686,1,1,241),(1685,1,1,242),(1687,1,1,243),(12604,1,1,251),(12566,1,1,252),(12569,1,1,253),(12572,1,1,254),(12575,1,1,255),(12579,1,1,256),(1617,1,1,258),(12525,1,1,262),(12544,1,1,281),(12534,1,1,282),(12540,1,1,283),(12546,1,1,286),(12288,1,1,300),(12290,1,1,301),(11591,1,1,302),(1763,1,1,331),(1762,1,1,332),(1764,1,1,333),(12582,1,1,341),(12584,1,1,342),(12586,1,1,343),(12588,1,1,344),(12600,1,1,351),(12593,1,1,352),(12597,1,1,353),(12601,1,1,354),(12605,1,1,358),(12560,1,1,531),(12553,1,1,532),(12557,1,1,534),(1625,1,1,536),(12561,1,1,538),(12358,1,1,700),(12348,1,1,701),(12354,1,1,702),(12360,1,1,703),(1755,1,1,1001),(1754,1,1,1002),(1756,1,1,1003),(1758,1,1,1004),(1759,1,1,1005),(7146,1,1,1101),(7143,1,1,1102),(7145,1,1,1104),(7147,1,1,1109),(12412,1,1,1181),(12458,1,1,1182),(12417,1,1,1183),(12420,1,1,1184),(12423,1,1,1185),(12427,1,1,1186),(12431,1,1,1187),(12437,1,1,1188),(12434,1,1,1189),(1578,1,1,1201),(1579,1,1,1202),(12454,1,1,1231),(12443,1,1,1232),(12446,1,1,1233),(12449,1,1,1234),(12452,1,1,1235),(12455,1,1,1236),(12459,1,1,1237),(1736,1,1,1251),(12409,1,1,1321),(12326,1,1,1421),(8190,1,1,1791),(8187,1,1,1792),(8191,1,1,1793),(12264,1,1,2401),(12260,1,1,2402),(12266,1,1,2403),(12280,1,1,2411),(12276,1,1,2412),(12282,1,1,2413),(12286,1,1,2414),(1618,1,1,2500),(12370,1,1,2501),(12367,1,1,2503),(12371,1,1,2515),(9610,1,1,5001),(9611,1,1,5002),(12490,1,1,20001),(12468,1,1,20002),(12474,1,1,20003),(12480,1,1,20004),(12486,1,1,20005),(12492,1,1,20006),(12302,1,1,23001),(12295,1,1,23002),(12299,1,1,23003),(12303,1,1,23004),(7701,1,1,50101),(4984,1,1,50401),(4983,1,1,50402),(4985,1,1,50403),(4987,1,1,50411),(4988,1,1,50412),(4989,1,1,50415),(12498,1,1,55001),(12499,1,1,55002),(3564,1,1,100700),(3565,1,1,100701),(9596,1,1,101051),(9598,1,1,101052),(9600,1,1,101053),(9604,1,1,101060),(9605,1,1,101061),(7177,1,1,101201),(7178,1,1,101202),(10353,1,1,101250),(10355,1,1,101251),(8980,1,1,101261),(8981,1,1,101262),(7616,1,1,101331),(10030,1,1,101701),(10031,1,1,101702),(3582,1,1,102000),(3583,1,1,102001),(9819,1,1,400051),(9823,1,1,400052),(9827,1,1,400053),(9831,1,1,400055),(132,1,2,11),(133,1,2,12),(134,1,2,13),(135,1,2,14),(136,1,2,16),(137,1,2,19),(138,1,2,21),(139,1,2,22),(140,1,2,24),(141,1,2,25),(142,1,2,26),(143,1,2,27),(10359,1,2,31),(145,1,2,32),(10361,1,2,34),(146,1,2,36),(147,1,2,41),(148,1,2,42),(149,1,2,44),(150,1,2,61),(151,1,2,62),(152,1,2,64),(153,1,2,71),(154,1,2,72),(155,1,2,74),(156,1,2,75),(157,1,2,78),(158,1,2,79),(159,1,2,81),(160,1,2,82),(161,1,2,84),(162,1,2,86),(163,1,2,87),(164,1,2,88),(165,1,2,89),(166,1,2,91),(167,1,2,92),(168,1,2,93),(2475,1,2,95),(2476,1,2,96),(2477,1,2,97),(2478,1,2,98),(169,1,2,101),(170,1,2,102),(171,1,2,104),(172,1,2,109),(173,1,2,111),(174,1,2,112),(175,1,2,113),(176,1,2,114),(177,1,2,116),(178,1,2,117),(179,1,2,121),(180,1,2,122),(181,1,2,125),(182,1,2,141),(183,1,2,142),(184,1,2,144),(2479,1,2,151),(2480,1,2,152),(2481,1,2,153),(2482,1,2,154),(185,1,2,161),(186,1,2,162),(187,1,2,163),(188,1,2,164),(189,1,2,165),(190,1,2,170),(2471,1,2,171),(192,1,2,172),(2472,1,2,173),(193,1,2,221),(194,1,2,222),(195,1,2,229),(196,1,2,241),(197,1,2,242),(198,1,2,243),(199,1,2,251),(201,1,2,262),(202,1,2,281),(203,1,2,282),(204,1,2,283),(205,1,2,331),(15072,1,2,510),(2483,1,2,531),(207,1,2,532),(2484,1,2,534),(208,1,2,536),(2473,1,2,700),(210,1,2,701),(211,1,2,702),(2474,1,2,703),(15064,1,2,771),(15057,1,2,772),(15059,1,2,773),(15061,1,2,774),(15063,1,2,775),(15065,1,2,776),(212,1,2,1001),(213,1,2,1002),(214,1,2,1003),(215,1,2,1004),(216,1,2,1005),(217,1,2,1101),(218,1,2,1102),(219,1,2,1104),(220,1,2,1109),(15073,1,2,1121),(15074,1,2,1122),(15075,1,2,1123),(15076,1,2,1124),(15077,1,2,1125),(15078,1,2,1126),(221,1,2,1181),(222,1,2,1182),(223,1,2,1183),(224,1,2,1184),(225,1,2,1185),(226,1,2,1186),(227,1,2,1187),(228,1,2,1188),(229,1,2,1201),(230,1,2,1202),(231,1,2,1231),(232,1,2,1232),(233,1,2,1233),(234,1,2,1234),(235,1,2,1421),(236,1,2,2401),(237,1,2,2402),(238,1,2,2403),(239,1,2,2411),(240,1,2,2412),(241,1,2,2413),(242,1,2,2500),(2470,1,2,2501),(243,1,2,2515),(10363,1,2,20001),(10364,1,2,20002),(10365,1,2,20003),(10366,1,2,20004),(10367,1,2,20005),(10368,1,2,20006),(15054,1,2,23001),(10362,1,2,50101),(15067,1,2,55001),(15066,1,2,59001),(15068,1,2,63001),(15069,1,2,63002),(15070,1,2,63003),(15071,1,2,63004),(10372,1,2,101250),(1807,1,3,11),(1808,1,3,31),(1809,1,3,36),(1810,1,3,41),(1811,1,3,61),(1812,1,3,71),(1813,1,3,72),(1814,1,3,74),(1815,1,3,75),(1816,1,3,78),(1817,1,3,79),(1818,1,3,91),(1819,1,3,95),(1820,1,3,97),(1821,1,3,111),(1822,1,3,121),(1823,1,3,122),(1824,1,3,125),(1825,1,3,161),(1826,1,3,170),(1827,1,3,171),(1828,1,3,172),(1829,1,3,221),(1830,1,3,222),(1831,1,3,229),(1832,1,3,241),(1833,1,3,242),(1834,1,3,243),(1835,1,3,251),(1836,1,3,255),(1837,1,3,256),(1838,1,3,262),(1839,1,3,281),(1840,1,3,282),(1841,1,3,283),(1842,1,3,331),(1843,1,3,531),(1844,1,3,536),(1845,1,3,700),(1846,1,3,1001),(1847,1,3,1002),(1848,1,3,1003),(1849,1,3,1004),(1850,1,3,1005),(1851,1,3,1181),(1852,1,3,1182),(1853,1,3,1201),(1854,1,3,1202),(1855,1,3,1231),(1856,1,3,2401),(1857,1,3,2402),(1858,1,3,2403),(1859,1,3,2411),(1860,1,3,2412),(1861,1,3,2413),(1862,1,3,2500),(1863,1,3,2515),(8026,1,4,11),(8027,1,4,21),(8028,1,4,31),(8029,1,4,41),(8030,1,4,61),(8031,1,4,71),(8032,1,4,72),(8033,1,4,74),(8034,1,4,75),(8035,1,4,78),(8036,1,4,79),(8037,1,4,81),(8038,1,4,91),(8039,1,4,95),(8040,1,4,97),(8041,1,4,101),(8042,1,4,111),(8043,1,4,121),(8044,1,4,151),(8045,1,4,161),(8046,1,4,171),(8047,1,4,221),(8048,1,4,222),(8049,1,4,229),(8050,1,4,241),(8051,1,4,242),(8052,1,4,243),(8146,1,4,251),(8147,1,4,253),(8053,1,4,262),(8054,1,4,281),(8055,1,4,331),(8056,1,4,341),(8057,1,4,342),(8058,1,4,343),(8059,1,4,344),(8060,1,4,531),(8061,1,4,700),(8062,1,4,1001),(8063,1,4,1002),(8064,1,4,1003),(8065,1,4,1004),(8066,1,4,1005),(8067,1,4,1101),(8068,1,4,1181),(8069,1,4,1182),(8070,1,4,1201),(8071,1,4,1202),(8072,1,4,1231),(8073,1,4,2401),(8074,1,4,2501),(8075,1,4,2503),(8076,1,4,2515),(8077,1,4,20001),(8078,1,4,50101),(8079,1,4,101201),(8080,1,4,101261),(8081,1,4,102000),(8082,1,4,400051),(8083,1,4,400052),(8084,1,4,400053),(8085,1,4,400055),(12608,1,10,11),(12609,1,10,21),(12610,1,10,31),(12611,1,10,41),(12612,1,10,61),(12613,1,10,71),(12614,1,10,72),(12615,1,10,74),(12616,1,10,75),(12617,1,10,78),(12618,1,10,79),(12619,1,10,81),(12620,1,10,91),(12621,1,10,95),(12622,1,10,97),(12623,1,10,101),(12624,1,10,111),(12625,1,10,121),(12626,1,10,151),(12627,1,10,161),(12628,1,10,171),(12629,1,10,221),(12630,1,10,222),(12631,1,10,229),(12632,1,10,241),(12633,1,10,242),(12634,1,10,243),(12635,1,10,262),(12636,1,10,281),(12637,1,10,300),(12638,1,10,331),(12639,1,10,341),(12640,1,10,342),(12641,1,10,343),(12642,1,10,344),(12643,1,10,531),(12644,1,10,700),(12645,1,10,1001),(12646,1,10,1002),(12647,1,10,1003),(12648,1,10,1004),(12649,1,10,1005),(12650,1,10,1101),(12651,1,10,1181),(12652,1,10,1182),(12653,1,10,1201),(12654,1,10,1202),(12655,1,10,1231),(12656,1,10,2401),(12657,1,10,2501),(12658,1,10,2503),(12659,1,10,2515),(12660,1,10,20001),(12661,1,10,20002),(12662,1,10,23001),(12663,1,10,50101),(12664,1,11,11),(12665,1,11,21),(12666,1,11,31),(12667,1,11,41),(12668,1,11,61),(12669,1,11,71),(12670,1,11,72),(12671,1,11,74),(12672,1,11,75),(12673,1,11,78),(12674,1,11,79),(12675,1,11,81),(12676,1,11,91),(12677,1,11,95),(12678,1,11,97),(12679,1,11,101),(12680,1,11,111),(12681,1,11,121),(12682,1,11,151),(12683,1,11,161),(12684,1,11,171),(12685,1,11,221),(12686,1,11,222),(12687,1,11,229),(12688,1,11,241),(12689,1,11,242),(12690,1,11,243),(12691,1,11,262),(12692,1,11,281),(12693,1,11,300),(12694,1,11,331),(12695,1,11,341),(12696,1,11,342),(12697,1,11,343),(12698,1,11,344),(12699,1,11,531),(12700,1,11,700),(12701,1,11,1001),(12702,1,11,1002),(12703,1,11,1003),(12704,1,11,1004),(12705,1,11,1005),(12706,1,11,1101),(12707,1,11,1181),(12708,1,11,1182),(12709,1,11,1201),(12710,1,11,1202),(12711,1,11,1231),(12712,1,11,2401),(12713,1,11,2501),(12714,1,11,2503),(12715,1,11,2515),(12716,1,11,20001),(12717,1,11,20002),(12718,1,11,23001),(12719,1,11,50101),(16076,1,12,11),(16066,1,12,12),(16068,1,12,13),(16070,1,12,14),(16072,1,12,15),(16075,1,12,16),(16078,1,12,19),(14146,1,12,21),(14135,1,12,22),(14137,1,12,24),(14139,1,12,25),(14142,1,12,26),(14145,1,12,27),(14148,1,12,28),(14930,1,12,31),(14926,1,12,32),(14929,1,12,34),(14932,1,12,38),(13816,1,12,41),(13813,1,12,42),(13815,1,12,44),(13817,1,12,45),(14094,1,12,61),(14091,1,12,62),(14093,1,12,64),(14095,1,12,67),(14096,1,12,68),(16203,1,12,71),(16198,1,12,72),(16200,1,12,74),(16204,1,12,75),(16202,1,12,76),(16206,1,12,78),(16207,1,12,79),(16046,1,12,81),(16040,1,12,82),(16041,1,12,84),(16042,1,12,86),(16044,1,12,87),(16045,1,12,88),(16047,1,12,89),(15401,1,12,91),(15397,1,12,92),(15400,1,12,93),(15403,1,12,94),(13990,1,12,95),(12734,1,12,97),(14939,1,12,101),(14935,1,12,102),(14936,1,12,104),(14937,1,12,105),(14938,1,12,106),(14940,1,12,109),(15390,1,12,111),(15377,1,12,112),(15380,1,12,113),(15383,1,12,114),(15386,1,12,115),(15389,1,12,116),(15392,1,12,117),(16135,1,12,121),(16131,1,12,122),(16134,1,12,125),(16137,1,12,126),(13821,1,12,141),(13820,1,12,142),(13822,1,12,144),(13912,1,12,151),(13909,1,12,152),(13911,1,12,153),(13913,1,12,154),(14063,1,12,161),(14056,1,12,162),(14058,1,12,163),(14060,1,12,164),(14062,1,12,165),(14064,1,12,167),(13350,1,12,171),(13345,1,12,172),(13347,1,12,173),(13349,1,12,174),(13351,1,12,178),(13838,1,12,221),(13834,1,12,222),(13837,1,12,223),(13840,1,12,229),(13842,1,12,237),(13844,1,12,238),(13846,1,12,239),(13516,1,12,241),(13515,1,12,242),(13517,1,12,243),(16178,1,12,251),(16159,1,12,252),(16161,1,12,253),(16162,1,12,254),(16164,1,12,255),(16166,1,12,256),(16139,1,12,262),(16148,1,12,281),(16144,1,12,282),(16147,1,12,283),(16150,1,12,286),(16029,1,12,300),(16030,1,12,301),(16194,1,12,331),(16193,1,12,332),(16195,1,12,333),(16167,1,12,341),(16168,1,12,342),(16169,1,12,343),(16170,1,12,344),(16176,1,12,351),(16173,1,12,352),(16175,1,12,353),(16177,1,12,354),(16179,1,12,358),(16188,1,12,501),(16182,1,12,502),(13865,1,12,510),(16184,1,12,511),(16185,1,12,512),(16187,1,12,514),(16189,1,12,517),(15291,1,12,520),(15286,1,12,522),(15288,1,12,524),(15290,1,12,525),(15292,1,12,527),(16156,1,12,531),(16153,1,12,532),(16155,1,12,534),(16157,1,12,538),(13358,1,12,700),(16054,1,12,701),(16053,1,12,702),(16056,1,12,703),(15090,1,12,771),(15081,1,12,772),(15083,1,12,773),(15085,1,12,774),(15087,1,12,775),(15089,1,12,776),(15091,1,12,779),(14917,1,12,1001),(14916,1,12,1002),(14918,1,12,1003),(14920,1,12,1004),(14921,1,12,1005),(14945,1,12,1101),(14943,1,12,1102),(14944,1,12,1104),(14946,1,12,1109),(14762,1,12,1121),(14755,1,12,1122),(14757,1,12,1123),(14759,1,12,1124),(14761,1,12,1125),(14763,1,12,1126),(16082,1,12,1181),(16105,1,12,1182),(16085,1,12,1183),(16086,1,12,1184),(16088,1,12,1185),(16090,1,12,1186),(16092,1,12,1187),(16095,1,12,1188),(16093,1,12,1189),(13827,1,12,1201),(13828,1,12,1202),(16103,1,12,1231),(16098,1,12,1232),(16099,1,12,1233),(16101,1,12,1234),(16102,1,12,1235),(16104,1,12,1236),(16106,1,12,1237),(13829,1,12,1251),(16080,1,12,1321),(16081,1,12,1322),(16048,1,12,1421),(16017,1,12,2401),(16016,1,12,2402),(16019,1,12,2403),(16025,1,12,2411),(16024,1,12,2412),(16027,1,12,2413),(16028,1,12,2414),(16060,1,12,2501),(16059,1,12,2503),(16061,1,12,2515),(16190,1,12,3200),(15435,1,12,5001),(15436,1,12,5002),(16121,1,12,20001),(16111,1,12,20002),(16114,1,12,20003),(16117,1,12,20004),(16120,1,12,20005),(16123,1,12,20006),(16036,1,12,23001),(16033,1,12,23002),(16035,1,12,23003),(16037,1,12,23004),(13712,1,12,50101),(15499,1,12,50401),(15501,1,12,50411),(15502,1,12,50412),(15503,1,12,50420),(15504,1,12,50430),(15498,1,12,50440),(16125,1,12,55001),(16126,1,12,55002),(14128,1,12,59001),(14129,1,12,59002),(14130,1,12,59003),(14818,1,12,63001),(14815,1,12,63002),(14817,1,12,63003),(14819,1,12,63004),(15241,1,12,64001),(16009,1,12,101331),(16010,1,12,101332),(16011,1,12,101333),(15438,1,12,101701),(15439,1,12,101702),(12776,1,13,11),(12777,1,13,21),(12778,1,13,31),(12779,1,13,41),(12780,1,13,61),(12781,1,13,71),(12782,1,13,72),(12783,1,13,74),(12784,1,13,75),(12785,1,13,78),(12786,1,13,79),(12787,1,13,81),(12788,1,13,91),(12789,1,13,95),(12790,1,13,97),(12791,1,13,101),(12792,1,13,111),(12793,1,13,121),(12794,1,13,151),(12795,1,13,161),(12796,1,13,171),(12797,1,13,221),(12798,1,13,222),(12799,1,13,229),(12800,1,13,241),(12801,1,13,242),(12802,1,13,243),(12803,1,13,262),(12804,1,13,281),(12805,1,13,300),(12806,1,13,331),(12807,1,13,341),(12808,1,13,342),(12809,1,13,343),(12810,1,13,344),(12811,1,13,531),(12812,1,13,700),(12813,1,13,1001),(12814,1,13,1002),(12815,1,13,1003),(12816,1,13,1004),(12817,1,13,1005),(12818,1,13,1101),(12819,1,13,1181),(12820,1,13,1182),(12821,1,13,1201),(12822,1,13,1202),(12823,1,13,1231),(12824,1,13,2401),(12825,1,13,2501),(12826,1,13,2503),(12827,1,13,2515),(12828,1,13,20001),(12829,1,13,20002),(12830,1,13,23001),(12831,1,13,50101),(12832,1,14,11),(12833,1,14,21),(12834,1,14,31),(12835,1,14,41),(12836,1,14,61),(12837,1,14,71),(12838,1,14,72),(12839,1,14,74),(12840,1,14,75),(12841,1,14,78),(12842,1,14,79),(12843,1,14,81),(12844,1,14,91),(12845,1,14,95),(12846,1,14,97),(12847,1,14,101),(12848,1,14,111),(12849,1,14,121),(12850,1,14,151),(12851,1,14,161),(12852,1,14,171),(12853,1,14,221),(12854,1,14,222),(12855,1,14,229),(12856,1,14,241),(12857,1,14,242),(12858,1,14,243),(12859,1,14,262),(12860,1,14,281),(12861,1,14,300),(12862,1,14,331),(12863,1,14,341),(12864,1,14,342),(12865,1,14,343),(12866,1,14,344),(12867,1,14,531),(12868,1,14,700),(12869,1,14,1001),(12870,1,14,1002),(12871,1,14,1003),(12872,1,14,1004),(12873,1,14,1005),(12874,1,14,1101),(12875,1,14,1181),(12876,1,14,1182),(12877,1,14,1201),(12878,1,14,1202),(12879,1,14,1231),(12880,1,14,2401),(12881,1,14,2501),(12882,1,14,2503),(12883,1,14,2515),(12884,1,14,20001),(12885,1,14,20002),(12886,1,14,23001),(12887,1,14,50101),(12944,1,16,11),(12945,1,16,21),(12946,1,16,31),(13056,1,16,41),(13057,1,16,42),(13058,1,16,44),(13059,1,16,45),(12948,1,16,61),(12949,1,16,71),(12950,1,16,72),(12951,1,16,74),(12952,1,16,75),(12953,1,16,78),(12954,1,16,79),(12955,1,16,81),(12956,1,16,91),(12957,1,16,95),(12958,1,16,97),(12959,1,16,101),(12960,1,16,111),(12961,1,16,121),(13060,1,16,141),(13061,1,16,142),(13062,1,16,144),(12962,1,16,151),(12963,1,16,161),(12964,1,16,171),(12965,1,16,221),(12966,1,16,222),(12967,1,16,229),(12968,1,16,241),(12969,1,16,242),(12970,1,16,243),(13128,1,16,251),(13064,1,16,262),(12972,1,16,281),(12973,1,16,300),(12974,1,16,331),(12975,1,16,341),(12976,1,16,342),(12977,1,16,343),(12978,1,16,344),(12979,1,16,531),(12980,1,16,700),(12981,1,16,1001),(12982,1,16,1002),(12983,1,16,1003),(12984,1,16,1004),(12985,1,16,1005),(12986,1,16,1101),(12987,1,16,1181),(12988,1,16,1182),(12989,1,16,1201),(12990,1,16,1202),(12991,1,16,1231),(12992,1,16,2401),(12993,1,16,2501),(12994,1,16,2503),(12995,1,16,2515),(12996,1,16,20001),(12997,1,16,20002),(12998,1,16,23001),(12999,1,16,50101),(13000,1,17,11),(13001,1,17,21),(13002,1,17,31),(13065,1,17,41),(13066,1,17,42),(13067,1,17,44),(13068,1,17,45),(13004,1,17,61),(13005,1,17,71),(13006,1,17,72),(13007,1,17,74),(13008,1,17,75),(13009,1,17,78),(13010,1,17,79),(13011,1,17,81),(13012,1,17,91),(13013,1,17,95),(13014,1,17,97),(13015,1,17,101),(13016,1,17,111),(13017,1,17,121),(13069,1,17,141),(13070,1,17,142),(13071,1,17,144),(13018,1,17,151),(13019,1,17,161),(13020,1,17,171),(13021,1,17,221),(13022,1,17,222),(13023,1,17,229),(13024,1,17,241),(13025,1,17,242),(13026,1,17,243),(13028,1,17,281),(13029,1,17,300),(13030,1,17,331),(13031,1,17,341),(13032,1,17,342),(13033,1,17,343),(13034,1,17,344),(13035,1,17,531),(13036,1,17,700),(13037,1,17,1001),(13038,1,17,1002),(13039,1,17,1003),(13040,1,17,1004),(13041,1,17,1005),(13042,1,17,1101),(13043,1,17,1181),(13044,1,17,1182),(13045,1,17,1201),(13046,1,17,1202),(13047,1,17,1231),(13048,1,17,2401),(13049,1,17,2501),(13050,1,17,2503),(13051,1,17,2515),(13052,1,17,20001),(13053,1,17,20002),(13054,1,17,23001),(13055,1,17,50101),(14504,1,18,11),(14505,1,18,21),(14506,1,18,31),(14507,1,18,41),(14508,1,18,61),(14509,1,18,71),(14510,1,18,78),(14511,1,18,81),(14512,1,18,91),(14513,1,18,95),(14514,1,18,101),(14515,1,18,111),(14516,1,18,121),(14517,1,18,151),(14518,1,18,161),(14519,1,18,221),(14520,1,18,241),(14521,1,18,262),(14522,1,18,281),(14523,1,18,300),(14524,1,18,331),(14525,1,18,332),(14526,1,18,333),(14527,1,18,341),(14528,1,18,342),(14529,1,18,343),(14530,1,18,344),(14531,1,18,531),(14532,1,18,701),(14533,1,18,771),(14534,1,18,774),(14535,1,18,1001),(14536,1,18,1004),(14537,1,18,1101),(14538,1,18,1181),(14539,1,18,1182),(14540,1,18,1201),(14541,1,18,1231),(14542,1,18,2401),(14543,1,18,2501),(14544,1,18,2503),(14545,1,18,2515),(14546,1,18,20001),(14547,1,18,20002),(14548,1,18,50101),(14549,1,18,59001),(15242,1,19,21),(15243,1,19,31),(15244,1,19,41),(15245,1,19,61),(15246,1,19,71),(15247,1,19,78),(15248,1,19,81),(15249,1,19,101),(15250,1,19,121),(15251,1,19,151),(15252,1,19,161),(15253,1,19,221),(15254,1,19,241),(15255,1,19,262),(15256,1,19,281),(15257,1,19,300),(15258,1,19,331),(15259,1,19,332),(15260,1,19,341),(15261,1,19,342),(15262,1,19,343),(15263,1,19,344),(15264,1,19,531),(15265,1,19,701),(15266,1,19,771),(15267,1,19,774),(15268,1,19,777),(15269,1,19,1001),(15270,1,19,1004),(15271,1,19,1101),(15272,1,19,1121),(15273,1,19,1181),(15274,1,19,1182),(15275,1,19,1201),(15276,1,19,1231),(15277,1,19,2401),(15278,1,19,2501),(15279,1,19,20001),(15280,1,19,20002),(15281,1,19,50101),(15282,1,19,59001),(15283,1,19,63001); +INSERT INTO `llx_user_rights` VALUES (12402,1,1,11),(12380,1,1,12),(12385,1,1,13),(12389,1,1,14),(12393,1,1,15),(12398,1,1,16),(12404,1,1,19),(9726,1,1,21),(9700,1,1,22),(9706,1,1,24),(9711,1,1,25),(9716,1,1,26),(9722,1,1,27),(9728,1,1,28),(9978,1,1,31),(9968,1,1,32),(9974,1,1,34),(1910,1,1,36),(9980,1,1,38),(11573,1,1,41),(11574,1,1,42),(11575,1,1,44),(11576,1,1,45),(7184,1,1,61),(7181,1,1,62),(7183,1,1,64),(7185,1,1,67),(7186,1,1,68),(1678,1,1,71),(1673,1,1,72),(1675,1,1,74),(1679,1,1,75),(1677,1,1,76),(1681,1,1,78),(1682,1,1,79),(12322,1,1,81),(12309,1,1,82),(12312,1,1,84),(12314,1,1,86),(12317,1,1,87),(12320,1,1,88),(12323,1,1,89),(11580,1,1,91),(11581,1,1,92),(11582,1,1,93),(11583,1,1,94),(10097,1,1,95),(10099,1,1,96),(10103,1,1,97),(10104,1,1,98),(7139,1,1,101),(7134,1,1,102),(7136,1,1,104),(7137,1,1,105),(7138,1,1,106),(7140,1,1,109),(10229,1,1,111),(10201,1,1,112),(10207,1,1,113),(10213,1,1,114),(10219,1,1,115),(10225,1,1,116),(10231,1,1,117),(12518,1,1,121),(12508,1,1,122),(12514,1,1,125),(12520,1,1,126),(11577,1,1,141),(11578,1,1,142),(11579,1,1,144),(2307,1,1,151),(2304,1,1,152),(2306,1,1,153),(2308,1,1,154),(10092,1,1,161),(10093,1,1,162),(10094,1,1,163),(10095,1,1,164),(10096,1,1,165),(1585,1,1,170),(12342,1,1,171),(12331,1,1,172),(12335,1,1,173),(12339,1,1,174),(12343,1,1,178),(10000,1,1,221),(9990,1,1,222),(9996,1,1,223),(10002,1,1,229),(10007,1,1,237),(10011,1,1,238),(10015,1,1,239),(1686,1,1,241),(1685,1,1,242),(1687,1,1,243),(12604,1,1,251),(12566,1,1,252),(12569,1,1,253),(12572,1,1,254),(12575,1,1,255),(12579,1,1,256),(1617,1,1,258),(12525,1,1,262),(12544,1,1,281),(12534,1,1,282),(12540,1,1,283),(12546,1,1,286),(12288,1,1,300),(12290,1,1,301),(11591,1,1,302),(1763,1,1,331),(1762,1,1,332),(1764,1,1,333),(12582,1,1,341),(12584,1,1,342),(12586,1,1,343),(12588,1,1,344),(12600,1,1,351),(12593,1,1,352),(12597,1,1,353),(12601,1,1,354),(12605,1,1,358),(12560,1,1,531),(12553,1,1,532),(12557,1,1,534),(1625,1,1,536),(12561,1,1,538),(12358,1,1,700),(12348,1,1,701),(12354,1,1,702),(12360,1,1,703),(1755,1,1,1001),(1754,1,1,1002),(1756,1,1,1003),(1758,1,1,1004),(1759,1,1,1005),(7146,1,1,1101),(7143,1,1,1102),(7145,1,1,1104),(7147,1,1,1109),(12412,1,1,1181),(12458,1,1,1182),(12417,1,1,1183),(12420,1,1,1184),(12423,1,1,1185),(12427,1,1,1186),(12431,1,1,1187),(12437,1,1,1188),(12434,1,1,1189),(1578,1,1,1201),(1579,1,1,1202),(12454,1,1,1231),(12443,1,1,1232),(12446,1,1,1233),(12449,1,1,1234),(12452,1,1,1235),(12455,1,1,1236),(12459,1,1,1237),(1736,1,1,1251),(12409,1,1,1321),(12326,1,1,1421),(8190,1,1,1791),(8187,1,1,1792),(8191,1,1,1793),(12264,1,1,2401),(12260,1,1,2402),(12266,1,1,2403),(12280,1,1,2411),(12276,1,1,2412),(12282,1,1,2413),(12286,1,1,2414),(1618,1,1,2500),(12370,1,1,2501),(12367,1,1,2503),(12371,1,1,2515),(9610,1,1,5001),(9611,1,1,5002),(12490,1,1,20001),(12468,1,1,20002),(12474,1,1,20003),(12480,1,1,20004),(12486,1,1,20005),(12492,1,1,20006),(12302,1,1,23001),(12295,1,1,23002),(12299,1,1,23003),(12303,1,1,23004),(7701,1,1,50101),(4984,1,1,50401),(4983,1,1,50402),(4985,1,1,50403),(4987,1,1,50411),(4988,1,1,50412),(4989,1,1,50415),(12498,1,1,55001),(12499,1,1,55002),(3564,1,1,100700),(3565,1,1,100701),(9596,1,1,101051),(9598,1,1,101052),(9600,1,1,101053),(9604,1,1,101060),(9605,1,1,101061),(7177,1,1,101201),(7178,1,1,101202),(10353,1,1,101250),(10355,1,1,101251),(8980,1,1,101261),(8981,1,1,101262),(7616,1,1,101331),(10030,1,1,101701),(10031,1,1,101702),(3582,1,1,102000),(3583,1,1,102001),(9819,1,1,400051),(9823,1,1,400052),(9827,1,1,400053),(9831,1,1,400055),(132,1,2,11),(133,1,2,12),(134,1,2,13),(135,1,2,14),(136,1,2,16),(137,1,2,19),(138,1,2,21),(139,1,2,22),(140,1,2,24),(141,1,2,25),(142,1,2,26),(143,1,2,27),(10359,1,2,31),(145,1,2,32),(10361,1,2,34),(146,1,2,36),(147,1,2,41),(148,1,2,42),(149,1,2,44),(150,1,2,61),(151,1,2,62),(152,1,2,64),(153,1,2,71),(154,1,2,72),(155,1,2,74),(156,1,2,75),(157,1,2,78),(158,1,2,79),(159,1,2,81),(160,1,2,82),(161,1,2,84),(162,1,2,86),(163,1,2,87),(164,1,2,88),(165,1,2,89),(166,1,2,91),(167,1,2,92),(168,1,2,93),(2475,1,2,95),(2476,1,2,96),(2477,1,2,97),(2478,1,2,98),(169,1,2,101),(170,1,2,102),(171,1,2,104),(172,1,2,109),(173,1,2,111),(174,1,2,112),(175,1,2,113),(176,1,2,114),(177,1,2,116),(178,1,2,117),(179,1,2,121),(180,1,2,122),(181,1,2,125),(182,1,2,141),(183,1,2,142),(184,1,2,144),(2479,1,2,151),(2480,1,2,152),(2481,1,2,153),(2482,1,2,154),(185,1,2,161),(186,1,2,162),(187,1,2,163),(188,1,2,164),(189,1,2,165),(190,1,2,170),(2471,1,2,171),(192,1,2,172),(2472,1,2,173),(193,1,2,221),(194,1,2,222),(195,1,2,229),(196,1,2,241),(197,1,2,242),(198,1,2,243),(199,1,2,251),(201,1,2,262),(202,1,2,281),(203,1,2,282),(204,1,2,283),(205,1,2,331),(15072,1,2,510),(2483,1,2,531),(207,1,2,532),(2484,1,2,534),(208,1,2,536),(2473,1,2,700),(210,1,2,701),(211,1,2,702),(2474,1,2,703),(15064,1,2,771),(15057,1,2,772),(15059,1,2,773),(15061,1,2,774),(15063,1,2,775),(15065,1,2,776),(212,1,2,1001),(213,1,2,1002),(214,1,2,1003),(215,1,2,1004),(216,1,2,1005),(217,1,2,1101),(218,1,2,1102),(219,1,2,1104),(220,1,2,1109),(15073,1,2,1121),(15074,1,2,1122),(15075,1,2,1123),(15076,1,2,1124),(15077,1,2,1125),(15078,1,2,1126),(221,1,2,1181),(222,1,2,1182),(223,1,2,1183),(224,1,2,1184),(225,1,2,1185),(226,1,2,1186),(227,1,2,1187),(228,1,2,1188),(229,1,2,1201),(230,1,2,1202),(231,1,2,1231),(232,1,2,1232),(233,1,2,1233),(234,1,2,1234),(235,1,2,1421),(236,1,2,2401),(237,1,2,2402),(238,1,2,2403),(239,1,2,2411),(240,1,2,2412),(241,1,2,2413),(242,1,2,2500),(2470,1,2,2501),(243,1,2,2515),(10363,1,2,20001),(10364,1,2,20002),(10365,1,2,20003),(10366,1,2,20004),(10367,1,2,20005),(10368,1,2,20006),(15054,1,2,23001),(10362,1,2,50101),(15067,1,2,55001),(15066,1,2,59001),(15068,1,2,63001),(15069,1,2,63002),(15070,1,2,63003),(15071,1,2,63004),(10372,1,2,101250),(1807,1,3,11),(1808,1,3,31),(1809,1,3,36),(1810,1,3,41),(1811,1,3,61),(1812,1,3,71),(1813,1,3,72),(1814,1,3,74),(1815,1,3,75),(1816,1,3,78),(1817,1,3,79),(1818,1,3,91),(1819,1,3,95),(1820,1,3,97),(1821,1,3,111),(1822,1,3,121),(1823,1,3,122),(1824,1,3,125),(1825,1,3,161),(1826,1,3,170),(1827,1,3,171),(1828,1,3,172),(1829,1,3,221),(1830,1,3,222),(1831,1,3,229),(1832,1,3,241),(1833,1,3,242),(1834,1,3,243),(1835,1,3,251),(1836,1,3,255),(1837,1,3,256),(1838,1,3,262),(1839,1,3,281),(1840,1,3,282),(1841,1,3,283),(1842,1,3,331),(1843,1,3,531),(1844,1,3,536),(1845,1,3,700),(1846,1,3,1001),(1847,1,3,1002),(1848,1,3,1003),(1849,1,3,1004),(1850,1,3,1005),(1851,1,3,1181),(1852,1,3,1182),(1853,1,3,1201),(1854,1,3,1202),(1855,1,3,1231),(1856,1,3,2401),(1857,1,3,2402),(1858,1,3,2403),(1859,1,3,2411),(1860,1,3,2412),(1861,1,3,2413),(1862,1,3,2500),(1863,1,3,2515),(8026,1,4,11),(8027,1,4,21),(8028,1,4,31),(8029,1,4,41),(8030,1,4,61),(8031,1,4,71),(8032,1,4,72),(8033,1,4,74),(8034,1,4,75),(8035,1,4,78),(8036,1,4,79),(8037,1,4,81),(8038,1,4,91),(8039,1,4,95),(8040,1,4,97),(8041,1,4,101),(8042,1,4,111),(8043,1,4,121),(8044,1,4,151),(8045,1,4,161),(8046,1,4,171),(8047,1,4,221),(8048,1,4,222),(8049,1,4,229),(8050,1,4,241),(8051,1,4,242),(8052,1,4,243),(8146,1,4,251),(8147,1,4,253),(8053,1,4,262),(8054,1,4,281),(8055,1,4,331),(8056,1,4,341),(8057,1,4,342),(8058,1,4,343),(8059,1,4,344),(8060,1,4,531),(8061,1,4,700),(8062,1,4,1001),(8063,1,4,1002),(8064,1,4,1003),(8065,1,4,1004),(8066,1,4,1005),(8067,1,4,1101),(8068,1,4,1181),(8069,1,4,1182),(8070,1,4,1201),(8071,1,4,1202),(8072,1,4,1231),(8073,1,4,2401),(8074,1,4,2501),(8075,1,4,2503),(8076,1,4,2515),(8077,1,4,20001),(8078,1,4,50101),(8079,1,4,101201),(8080,1,4,101261),(8081,1,4,102000),(8082,1,4,400051),(8083,1,4,400052),(8084,1,4,400053),(8085,1,4,400055),(12608,1,10,11),(12609,1,10,21),(12610,1,10,31),(12611,1,10,41),(12612,1,10,61),(12613,1,10,71),(12614,1,10,72),(12615,1,10,74),(12616,1,10,75),(12617,1,10,78),(12618,1,10,79),(12619,1,10,81),(12620,1,10,91),(12621,1,10,95),(12622,1,10,97),(12623,1,10,101),(12624,1,10,111),(12625,1,10,121),(12626,1,10,151),(12627,1,10,161),(12628,1,10,171),(12629,1,10,221),(12630,1,10,222),(12631,1,10,229),(12632,1,10,241),(12633,1,10,242),(12634,1,10,243),(12635,1,10,262),(12636,1,10,281),(12637,1,10,300),(12638,1,10,331),(12639,1,10,341),(12640,1,10,342),(12641,1,10,343),(12642,1,10,344),(12643,1,10,531),(12644,1,10,700),(12645,1,10,1001),(12646,1,10,1002),(12647,1,10,1003),(12648,1,10,1004),(12649,1,10,1005),(12650,1,10,1101),(12651,1,10,1181),(12652,1,10,1182),(12653,1,10,1201),(12654,1,10,1202),(12655,1,10,1231),(12656,1,10,2401),(12657,1,10,2501),(12658,1,10,2503),(12659,1,10,2515),(12660,1,10,20001),(12661,1,10,20002),(12662,1,10,23001),(12663,1,10,50101),(12664,1,11,11),(12665,1,11,21),(12666,1,11,31),(12667,1,11,41),(12668,1,11,61),(12669,1,11,71),(12670,1,11,72),(12671,1,11,74),(12672,1,11,75),(12673,1,11,78),(12674,1,11,79),(12675,1,11,81),(12676,1,11,91),(12677,1,11,95),(12678,1,11,97),(12679,1,11,101),(12680,1,11,111),(12681,1,11,121),(12682,1,11,151),(12683,1,11,161),(12684,1,11,171),(12685,1,11,221),(12686,1,11,222),(12687,1,11,229),(12688,1,11,241),(12689,1,11,242),(12690,1,11,243),(12691,1,11,262),(12692,1,11,281),(12693,1,11,300),(12694,1,11,331),(12695,1,11,341),(12696,1,11,342),(12697,1,11,343),(12698,1,11,344),(12699,1,11,531),(12700,1,11,700),(12701,1,11,1001),(12702,1,11,1002),(12703,1,11,1003),(12704,1,11,1004),(12705,1,11,1005),(12706,1,11,1101),(12707,1,11,1181),(12708,1,11,1182),(12709,1,11,1201),(12710,1,11,1202),(12711,1,11,1231),(12712,1,11,2401),(12713,1,11,2501),(12714,1,11,2503),(12715,1,11,2515),(12716,1,11,20001),(12717,1,11,20002),(12718,1,11,23001),(12719,1,11,50101),(16272,1,12,11),(16262,1,12,12),(16264,1,12,13),(16266,1,12,14),(16268,1,12,15),(16271,1,12,16),(16274,1,12,19),(14146,1,12,21),(14135,1,12,22),(14137,1,12,24),(14139,1,12,25),(14142,1,12,26),(14145,1,12,27),(14148,1,12,28),(14930,1,12,31),(14926,1,12,32),(14929,1,12,34),(14932,1,12,38),(13816,1,12,41),(13813,1,12,42),(13815,1,12,44),(13817,1,12,45),(14094,1,12,61),(14091,1,12,62),(14093,1,12,64),(14095,1,12,67),(14096,1,12,68),(16203,1,12,71),(16198,1,12,72),(16200,1,12,74),(16204,1,12,75),(16202,1,12,76),(16206,1,12,78),(16207,1,12,79),(16242,1,12,81),(16236,1,12,82),(16237,1,12,84),(16238,1,12,86),(16240,1,12,87),(16241,1,12,88),(16243,1,12,89),(15401,1,12,91),(15397,1,12,92),(15400,1,12,93),(15403,1,12,94),(13990,1,12,95),(12734,1,12,97),(14939,1,12,101),(14935,1,12,102),(14936,1,12,104),(14937,1,12,105),(14938,1,12,106),(14940,1,12,109),(15390,1,12,111),(15377,1,12,112),(15380,1,12,113),(15383,1,12,114),(15386,1,12,115),(15389,1,12,116),(15392,1,12,117),(16331,1,12,121),(16327,1,12,122),(16330,1,12,125),(16333,1,12,126),(13821,1,12,141),(13820,1,12,142),(13822,1,12,144),(13912,1,12,151),(13909,1,12,152),(13911,1,12,153),(13913,1,12,154),(14063,1,12,161),(14056,1,12,162),(14058,1,12,163),(14060,1,12,164),(14062,1,12,165),(14064,1,12,167),(13350,1,12,171),(13345,1,12,172),(13347,1,12,173),(13349,1,12,174),(13351,1,12,178),(13838,1,12,221),(13834,1,12,222),(13837,1,12,223),(13840,1,12,229),(13842,1,12,237),(13844,1,12,238),(13846,1,12,239),(13516,1,12,241),(13515,1,12,242),(13517,1,12,243),(16374,1,12,251),(16355,1,12,252),(16357,1,12,253),(16358,1,12,254),(16360,1,12,255),(16362,1,12,256),(16335,1,12,262),(16344,1,12,281),(16340,1,12,282),(16343,1,12,283),(16346,1,12,286),(16225,1,12,300),(16226,1,12,301),(16194,1,12,331),(16193,1,12,332),(16195,1,12,333),(16363,1,12,341),(16364,1,12,342),(16365,1,12,343),(16366,1,12,344),(16372,1,12,351),(16369,1,12,352),(16371,1,12,353),(16373,1,12,354),(16375,1,12,358),(16384,1,12,501),(16378,1,12,502),(13865,1,12,510),(16380,1,12,511),(16381,1,12,512),(16383,1,12,514),(16385,1,12,517),(15291,1,12,520),(15286,1,12,522),(15288,1,12,524),(15290,1,12,525),(15292,1,12,527),(16352,1,12,531),(16349,1,12,532),(16351,1,12,534),(16353,1,12,538),(13358,1,12,700),(16250,1,12,701),(16249,1,12,702),(16252,1,12,703),(15090,1,12,771),(15081,1,12,772),(15083,1,12,773),(15085,1,12,774),(15087,1,12,775),(15089,1,12,776),(15091,1,12,779),(14917,1,12,1001),(14916,1,12,1002),(14918,1,12,1003),(14920,1,12,1004),(14921,1,12,1005),(14945,1,12,1101),(14943,1,12,1102),(14944,1,12,1104),(14946,1,12,1109),(14762,1,12,1121),(14755,1,12,1122),(14757,1,12,1123),(14759,1,12,1124),(14761,1,12,1125),(14763,1,12,1126),(16278,1,12,1181),(16301,1,12,1182),(16281,1,12,1183),(16282,1,12,1184),(16284,1,12,1185),(16286,1,12,1186),(16288,1,12,1187),(16291,1,12,1188),(16289,1,12,1189),(13827,1,12,1201),(13828,1,12,1202),(16299,1,12,1231),(16294,1,12,1232),(16295,1,12,1233),(16297,1,12,1234),(16298,1,12,1235),(16300,1,12,1236),(16302,1,12,1237),(13829,1,12,1251),(16276,1,12,1321),(16277,1,12,1322),(16244,1,12,1421),(16213,1,12,2401),(16212,1,12,2402),(16215,1,12,2403),(16221,1,12,2411),(16220,1,12,2412),(16223,1,12,2413),(16224,1,12,2414),(16256,1,12,2501),(16255,1,12,2503),(16257,1,12,2515),(16386,1,12,3200),(15435,1,12,5001),(15436,1,12,5002),(16317,1,12,20001),(16307,1,12,20002),(16310,1,12,20003),(16313,1,12,20004),(16316,1,12,20005),(16319,1,12,20006),(16232,1,12,23001),(16229,1,12,23002),(16231,1,12,23003),(16233,1,12,23004),(13712,1,12,50101),(15499,1,12,50401),(15501,1,12,50411),(15502,1,12,50412),(15503,1,12,50420),(15504,1,12,50430),(15498,1,12,50440),(16321,1,12,55001),(16322,1,12,55002),(14128,1,12,59001),(14129,1,12,59002),(14130,1,12,59003),(14818,1,12,63001),(14815,1,12,63002),(14817,1,12,63003),(14819,1,12,63004),(15241,1,12,64001),(16009,1,12,101331),(16010,1,12,101332),(16011,1,12,101333),(15438,1,12,101701),(15439,1,12,101702),(12776,1,13,11),(12777,1,13,21),(12778,1,13,31),(12779,1,13,41),(12780,1,13,61),(12781,1,13,71),(12782,1,13,72),(12783,1,13,74),(12784,1,13,75),(12785,1,13,78),(12786,1,13,79),(12787,1,13,81),(12788,1,13,91),(12789,1,13,95),(12790,1,13,97),(12791,1,13,101),(12792,1,13,111),(12793,1,13,121),(12794,1,13,151),(12795,1,13,161),(12796,1,13,171),(12797,1,13,221),(12798,1,13,222),(12799,1,13,229),(12800,1,13,241),(12801,1,13,242),(12802,1,13,243),(12803,1,13,262),(12804,1,13,281),(12805,1,13,300),(12806,1,13,331),(12807,1,13,341),(12808,1,13,342),(12809,1,13,343),(12810,1,13,344),(12811,1,13,531),(12812,1,13,700),(12813,1,13,1001),(12814,1,13,1002),(12815,1,13,1003),(12816,1,13,1004),(12817,1,13,1005),(12818,1,13,1101),(12819,1,13,1181),(12820,1,13,1182),(12821,1,13,1201),(12822,1,13,1202),(12823,1,13,1231),(12824,1,13,2401),(12825,1,13,2501),(12826,1,13,2503),(12827,1,13,2515),(12828,1,13,20001),(12829,1,13,20002),(12830,1,13,23001),(12831,1,13,50101),(12832,1,14,11),(12833,1,14,21),(12834,1,14,31),(12835,1,14,41),(12836,1,14,61),(12837,1,14,71),(12838,1,14,72),(12839,1,14,74),(12840,1,14,75),(12841,1,14,78),(12842,1,14,79),(12843,1,14,81),(12844,1,14,91),(12845,1,14,95),(12846,1,14,97),(12847,1,14,101),(12848,1,14,111),(12849,1,14,121),(12850,1,14,151),(12851,1,14,161),(12852,1,14,171),(12853,1,14,221),(12854,1,14,222),(12855,1,14,229),(12856,1,14,241),(12857,1,14,242),(12858,1,14,243),(12859,1,14,262),(12860,1,14,281),(12861,1,14,300),(12862,1,14,331),(12863,1,14,341),(12864,1,14,342),(12865,1,14,343),(12866,1,14,344),(12867,1,14,531),(12868,1,14,700),(12869,1,14,1001),(12870,1,14,1002),(12871,1,14,1003),(12872,1,14,1004),(12873,1,14,1005),(12874,1,14,1101),(12875,1,14,1181),(12876,1,14,1182),(12877,1,14,1201),(12878,1,14,1202),(12879,1,14,1231),(12880,1,14,2401),(12881,1,14,2501),(12882,1,14,2503),(12883,1,14,2515),(12884,1,14,20001),(12885,1,14,20002),(12886,1,14,23001),(12887,1,14,50101),(12944,1,16,11),(12945,1,16,21),(12946,1,16,31),(13056,1,16,41),(13057,1,16,42),(13058,1,16,44),(13059,1,16,45),(12948,1,16,61),(12949,1,16,71),(12950,1,16,72),(12951,1,16,74),(12952,1,16,75),(12953,1,16,78),(12954,1,16,79),(12955,1,16,81),(12956,1,16,91),(12957,1,16,95),(12958,1,16,97),(12959,1,16,101),(12960,1,16,111),(12961,1,16,121),(13060,1,16,141),(13061,1,16,142),(13062,1,16,144),(12962,1,16,151),(12963,1,16,161),(12964,1,16,171),(12965,1,16,221),(12966,1,16,222),(12967,1,16,229),(12968,1,16,241),(12969,1,16,242),(12970,1,16,243),(13128,1,16,251),(13064,1,16,262),(12972,1,16,281),(12973,1,16,300),(12974,1,16,331),(12975,1,16,341),(12976,1,16,342),(12977,1,16,343),(12978,1,16,344),(12979,1,16,531),(12980,1,16,700),(12981,1,16,1001),(12982,1,16,1002),(12983,1,16,1003),(12984,1,16,1004),(12985,1,16,1005),(12986,1,16,1101),(12987,1,16,1181),(12988,1,16,1182),(12989,1,16,1201),(12990,1,16,1202),(12991,1,16,1231),(12992,1,16,2401),(12993,1,16,2501),(12994,1,16,2503),(12995,1,16,2515),(12996,1,16,20001),(12997,1,16,20002),(12998,1,16,23001),(12999,1,16,50101),(13000,1,17,11),(13001,1,17,21),(13002,1,17,31),(13065,1,17,41),(13066,1,17,42),(13067,1,17,44),(13068,1,17,45),(13004,1,17,61),(13005,1,17,71),(13006,1,17,72),(13007,1,17,74),(13008,1,17,75),(13009,1,17,78),(13010,1,17,79),(13011,1,17,81),(13012,1,17,91),(13013,1,17,95),(13014,1,17,97),(13015,1,17,101),(13016,1,17,111),(13017,1,17,121),(13069,1,17,141),(13070,1,17,142),(13071,1,17,144),(13018,1,17,151),(13019,1,17,161),(13020,1,17,171),(13021,1,17,221),(13022,1,17,222),(13023,1,17,229),(13024,1,17,241),(13025,1,17,242),(13026,1,17,243),(13028,1,17,281),(13029,1,17,300),(13030,1,17,331),(13031,1,17,341),(13032,1,17,342),(13033,1,17,343),(13034,1,17,344),(13035,1,17,531),(13036,1,17,700),(13037,1,17,1001),(13038,1,17,1002),(13039,1,17,1003),(13040,1,17,1004),(13041,1,17,1005),(13042,1,17,1101),(13043,1,17,1181),(13044,1,17,1182),(13045,1,17,1201),(13046,1,17,1202),(13047,1,17,1231),(13048,1,17,2401),(13049,1,17,2501),(13050,1,17,2503),(13051,1,17,2515),(13052,1,17,20001),(13053,1,17,20002),(13054,1,17,23001),(13055,1,17,50101),(14504,1,18,11),(14505,1,18,21),(14506,1,18,31),(14507,1,18,41),(14508,1,18,61),(14509,1,18,71),(14510,1,18,78),(14511,1,18,81),(14512,1,18,91),(14513,1,18,95),(14514,1,18,101),(14515,1,18,111),(14516,1,18,121),(14517,1,18,151),(14518,1,18,161),(14519,1,18,221),(14520,1,18,241),(14521,1,18,262),(14522,1,18,281),(14523,1,18,300),(14524,1,18,331),(14525,1,18,332),(14526,1,18,333),(14527,1,18,341),(14528,1,18,342),(14529,1,18,343),(14530,1,18,344),(14531,1,18,531),(14532,1,18,701),(14533,1,18,771),(14534,1,18,774),(14535,1,18,1001),(14536,1,18,1004),(14537,1,18,1101),(14538,1,18,1181),(14539,1,18,1182),(14540,1,18,1201),(14541,1,18,1231),(14542,1,18,2401),(14543,1,18,2501),(14544,1,18,2503),(14545,1,18,2515),(14546,1,18,20001),(14547,1,18,20002),(14548,1,18,50101),(14549,1,18,59001),(15242,1,19,21),(15243,1,19,31),(15244,1,19,41),(15245,1,19,61),(15246,1,19,71),(15247,1,19,78),(15248,1,19,81),(15249,1,19,101),(15250,1,19,121),(15251,1,19,151),(15252,1,19,161),(15253,1,19,221),(15254,1,19,241),(15255,1,19,262),(15256,1,19,281),(15257,1,19,300),(15258,1,19,331),(15259,1,19,332),(15260,1,19,341),(15261,1,19,342),(15262,1,19,343),(15263,1,19,344),(15264,1,19,531),(15265,1,19,701),(15266,1,19,771),(15267,1,19,774),(15268,1,19,777),(15269,1,19,1001),(15270,1,19,1004),(15271,1,19,1101),(15272,1,19,1121),(15273,1,19,1181),(15274,1,19,1182),(15275,1,19,1201),(15276,1,19,1231),(15277,1,19,2401),(15278,1,19,2501),(15279,1,19,20001),(15280,1,19,20002),(15281,1,19,50101),(15282,1,19,59001),(15283,1,19,63001); /*!40000 ALTER TABLE `llx_user_rights` ENABLE KEYS */; UNLOCK TABLES; @@ -9578,15 +9842,15 @@ DROP TABLE IF EXISTS `llx_usergroup`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `llx_usergroup` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, - `nom` varchar(255) NOT NULL, + `nom` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `entity` int(11) NOT NULL DEFAULT '1', `datec` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `note` text, - `model_pdf` varchar(255) DEFAULT NULL, + `note` text COLLATE utf8_unicode_ci, + `model_pdf` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_usergroup_name` (`nom`,`entity`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9610,10 +9874,10 @@ CREATE TABLE `llx_usergroup_extrafields` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_object` int(11) NOT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), KEY `idx_usergroup_extrafields` (`fk_object`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9641,7 +9905,7 @@ CREATE TABLE `llx_usergroup_rights` ( UNIQUE KEY `uk_usergroup_rights` (`entity`,`fk_usergroup`,`fk_id`), KEY `fk_usergroup_rights_fk_usergroup` (`fk_usergroup`), CONSTRAINT `fk_usergroup_rights_fk_usergroup` FOREIGN KEY (`fk_usergroup`) REFERENCES `llx_usergroup` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=200 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=200 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9672,7 +9936,7 @@ CREATE TABLE `llx_usergroup_user` ( KEY `fk_usergroup_user_fk_usergroup` (`fk_usergroup`), CONSTRAINT `fk_usergroup_user_fk_user` FOREIGN KEY (`fk_user`) REFERENCES `llx_user` (`rowid`), CONSTRAINT `fk_usergroup_user_fk_usergroup` FOREIGN KEY (`fk_usergroup`) REFERENCES `llx_usergroup` (`rowid`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9695,20 +9959,20 @@ DROP TABLE IF EXISTS `llx_website`; CREATE TABLE `llx_website` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `entity` int(11) NOT NULL DEFAULT '1', - `ref` varchar(128) DEFAULT NULL, - `description` varchar(255) DEFAULT NULL, + `ref` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `status` int(11) DEFAULT NULL, `fk_default_home` int(11) DEFAULT NULL, - `virtualhost` varchar(255) DEFAULT NULL, + `virtualhost` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `date_creation` datetime DEFAULT NULL, `date_modification` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_create` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, - `import_key` varchar(14) DEFAULT NULL, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_website_ref` (`ref`,`entity`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9827,27 +10091,27 @@ DROP TABLE IF EXISTS `llx_website_page`; CREATE TABLE `llx_website_page` ( `rowid` int(11) NOT NULL AUTO_INCREMENT, `fk_website` int(11) NOT NULL, - `pageurl` varchar(255) DEFAULT NULL, - `title` varchar(255) DEFAULT NULL, - `description` varchar(255) DEFAULT NULL, - `keywords` varchar(255) DEFAULT NULL, - `content` mediumtext, + `pageurl` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `keywords` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `content` mediumtext COLLATE utf8_unicode_ci, `status` int(11) DEFAULT '1', `date_creation` datetime DEFAULT NULL, `date_modification` datetime DEFAULT NULL, `tms` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `fk_user_create` int(11) DEFAULT NULL, `fk_user_modif` int(11) DEFAULT NULL, - `type_container` varchar(16) NOT NULL DEFAULT 'page', - `lang` varchar(6) DEFAULT NULL, + `type_container` varchar(16) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'page', + `lang` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL, `fk_page` int(11) DEFAULT NULL, - `grabbed_from` varchar(255) DEFAULT NULL, - `htmlheader` mediumtext, - `import_key` varchar(14) DEFAULT NULL, + `grabbed_from` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `htmlheader` mediumtext COLLATE utf8_unicode_ci, + `import_key` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`rowid`), UNIQUE KEY `uk_website_page_url` (`fk_website`,`pageurl`), CONSTRAINT `fk_website_page_website` FOREIGN KEY (`fk_website`) REFERENCES `llx_website` (`rowid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -9868,4 +10132,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2018-01-19 11:30:56 +-- Dump completed on 2018-03-16 11:18:15 diff --git a/dev/initdemo/sftpget_and_loaddump.php b/dev/initdemo/sftpget_and_loaddump.php index 64c5ddeb24d..981ff964b8e 100755 --- a/dev/initdemo/sftpget_and_loaddump.php +++ b/dev/initdemo/sftpget_and_loaddump.php @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * or see http://www.gnu.org/ - * + * * Get a distant dump file and load it into a mysql database */ @@ -92,7 +92,7 @@ if ($connection) { if (! @ssh2_auth_password($connection, $login, $password)) { - dol_syslog("Could not authenticate with username ".$login." . and password ".$password,LOG_ERR); + dol_syslog("Could not authenticate with username ".$login." . and password ".preg_replace('/./', '*', $password),LOG_ERR); exit(-5); } else diff --git a/htdocs/accountancy/admin/account.php b/htdocs/accountancy/admin/account.php index 98ecd8e327c..7d73202eb79 100644 --- a/htdocs/accountancy/admin/account.php +++ b/htdocs/accountancy/admin/account.php @@ -1,7 +1,7 @@ * Copyright (C) 2013-2017 Alexandre Spangaro - * Copyright (C) 2016-2017 Laurent Destailleur + * Copyright (C) 2016-2018 Laurent Destailleur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,7 @@ $action = GETPOST('action','aZ09'); $cancel = GETPOST('cancel','alpha'); $id = GETPOST('id', 'int'); $rowid = GETPOST('rowid', 'int'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'accountingaccountlist'; // To manage different context of search $search_account = GETPOST("search_account"); $search_label = GETPOST("search_label"); @@ -74,8 +75,6 @@ $arrayfields=array( $accounting = new AccountingAccount($db); -// Initialize technical object to manage context to save list fields -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'accountingaccountlist'; /* @@ -177,11 +176,10 @@ $sql = "SELECT aa.rowid, aa.fk_pcg_version, aa.pcg_type, aa.pcg_subtype, aa.acco $sql .= " a2.rowid as rowid2, a2.label as label2, a2.account_number as account_number2"; $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_account as aa"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_system as asy ON aa.fk_pcg_version = asy.pcg_version"; -// Dirty hack wainting that foreign key account_parent is an integer to be compared correctly with rowid -if ($db->type == 'pgsql') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as a2 ON a2.rowid = CAST(aa.account_parent AS INTEGER)"; -else $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as a2 ON a2.rowid = CAST(aa.account_parent AS UNSIGNED)"; +if ($db->type == 'pgsql') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as a2 ON a2.rowid = aa.account_parent"; +else $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as a2 ON a2.rowid = aa.account_parent"; $sql .= " WHERE asy.rowid = " . $pcgver; - +//print $sql; if (strlen(trim($search_account))) $sql .= natural_search("aa.account_number", $search_account); if (strlen(trim($search_label))) $sql .= natural_search("aa.label", $search_label); if (strlen(trim($search_accountparent))) $sql .= natural_search("aa.account_parent", $search_accountparent); @@ -257,7 +255,7 @@ if ($resql) else dol_print_error($db); print ""; print ajax_combobox("chartofaccounts"); - print ''; + print ''; print '
'; print '
'; @@ -276,7 +274,7 @@ if ($resql) if (! empty($arrayfields['aa.pcg_type']['checked'])) print ''; if (! empty($arrayfields['aa.pcg_subtype']['checked'])) print ''; if (! empty($arrayfields['aa.active']['checked'])) print ' '; - print ''; + print ''; $searchpicto=$form->showFilterAndCheckAddButtons($massactionbutton?1:0, 'checkforselect', 1); print $searchpicto; print ''; @@ -310,7 +308,7 @@ if ($resql) if (! empty($arrayfields['aa.account_number']['checked'])) { print ""; - print $accountstatic->getNomUrl(1); + print $accountstatic->getNomUrl(1, 0, 0, '', 0, 1); print "\n"; if (! $i) $totalarray['nbfield']++; } diff --git a/htdocs/accountancy/admin/accountmodel.php b/htdocs/accountancy/admin/accountmodel.php index f3924f73a76..9728a9524ea 100644 --- a/htdocs/accountancy/admin/accountmodel.php +++ b/htdocs/accountancy/admin/accountmodel.php @@ -1013,13 +1013,7 @@ if ($id) // Active print ''; if ($canbedisabled) print ''.$actl[$obj->active].''; - else - { - if (in_array($obj->code, array('AC_OTH','AC_OTH_AUTO'))) print $langs->trans("AlwaysActive"); - else if (isset($obj->type) && in_array($obj->type, array('systemauto')) && empty($obj->active)) print $langs->trans("Deprecated"); - else if (isset($obj->type) && in_array($obj->type, array('system')) && ! empty($obj->active) && $obj->code != 'AC_OTH') print $langs->trans("UsedOnlyWithTypeOption"); - else print $langs->trans("AlwaysActive"); - } + else print $langs->trans("AlwaysActive"); print ""; // Modify link diff --git a/htdocs/accountancy/admin/card.php b/htdocs/accountancy/admin/card.php index 82ba00a882c..61166f88d07 100644 --- a/htdocs/accountancy/admin/card.php +++ b/htdocs/accountancy/admin/card.php @@ -188,12 +188,10 @@ if ($action == 'add' && $user->rights->accounting->chartofaccount) } } + /* * View */ -$title = $langs->trans('AccountAccounting') ." - ". $langs->trans('Card'); -$helpurl = ''; -llxheader('', $title, $helpurl); $form = new Form($db); $formaccounting = new FormAccounting($db); @@ -201,6 +199,11 @@ $formaccounting = new FormAccounting($db); $accountsystem = new AccountancySystem($db); $accountsystem->fetch($conf->global->CHARTOFACCOUNTS); +$title = $langs->trans('AccountAccounting') ." - ". $langs->trans('Card'); +$helpurl = ''; +llxheader('', $title, $helpurl); + + // Create mode if ($action == 'create') { print load_fiche_titre($langs->trans('NewAccountingAccount')); @@ -330,7 +333,7 @@ else if ($id > 0 || $ref) { print ''; } else { // View mode - $linkback = '' . $langs->trans("BackToList") . ''; + $linkback = '' . $langs->trans("BackToList") . ''; dol_fiche_head($head, 'card', $langs->trans('AccountAccounting'), -1, 'billr'); diff --git a/htdocs/accountancy/admin/categories.php b/htdocs/accountancy/admin/categories.php index 7ab463e3179..a91c22ffe4c 100644 --- a/htdocs/accountancy/admin/categories.php +++ b/htdocs/accountancy/admin/categories.php @@ -53,6 +53,11 @@ if (empty($user->rights->accounting->chartofaccount)) $accountingcategory = new AccountancyCategory($db); + +/* + * Actions + */ + // si ajout de comptes if (! empty($selectcpt)) { $cpts = array (); @@ -83,6 +88,7 @@ if ($action == 'delete') { /* * View */ + $form = new Form($db); $formaccounting = new FormAccounting($db); @@ -99,13 +105,15 @@ print ''; dol_fiche_head(); print ''; -// Category + +// Select the category print ''; print ''; +// Select the accounts if (! empty($cat_id)) { $return = $accountingcategory->getAccountsWithNoCategory($cat_id); @@ -153,7 +161,7 @@ if ($action == 'display' || $action == 'delete') { print "\n"; if (! empty($cat_id)) { - $return = $accountingcategory->display($cat_id); + $return = $accountingcategory->display($cat_id); // This load ->lines_display if ($return < 0) { setEventMessages(null, $accountingcategory->errors, 'errors'); } diff --git a/htdocs/accountancy/admin/categories_list.php b/htdocs/accountancy/admin/categories_list.php index 9833a1c01b1..fb7561dda94 100644 --- a/htdocs/accountancy/admin/categories_list.php +++ b/htdocs/accountancy/admin/categories_list.php @@ -113,7 +113,7 @@ $tabcond[32]= ! empty($conf->accounting->enabled); // List of help for fields $tabhelp=array(); -$tabhelp[32] = array('code'=>$langs->trans("EnterAnyCode")); +$tabhelp[32] = array('code'=>$langs->trans("EnterAnyCode"), 'category_type'=>$langs->trans("SetToYesIfGroupIsComputationOfOtherGroups"), 'formula'=>$langs->trans("EnterCalculationRuleIfPreviousFieldIsYes")); // List of check for fields (NOT USED YET) $tabfieldcheck=array(); @@ -152,7 +152,7 @@ if (GETPOST('actionadd') || GETPOST('actionmodify')) { if ($value == 'formula' && empty($_POST['formula'])) continue; if ($value == 'range_account' && empty($_POST['range_account'])) continue; - if ($value == 'country') continue; // country_id required but not country + if ($value == 'country' || $value == 'country_id') continue; if (! isset($_POST[$value]) || $_POST[$value]=='') { $ok=0; @@ -175,16 +175,12 @@ if (GETPOST('actionadd') || GETPOST('actionmodify')) $ok=0; setEventMessages($langs->transnoentities('ErrorCodeCantContainZero'), null, 'errors'); } - /*if (!is_numeric($_POST['code'])) // disabled, code may not be in numeric base - { - $ok = 0; - $msg .= $langs->transnoentities('ErrorFieldFormat', $langs->transnoentities('Code')).'
'; - }*/ } - if (isset($_POST["country"]) && ($_POST["country"] <= 0)) + if (! is_numeric(GETPOST('position','alpha'))) { - $ok=0; - setEventMessages($langs->transnoentities("ErrorFieldRequired",$langs->transnoentities("Country")), null, 'errors'); + $langs->load("errors"); + $ok=0; + setEventMessages($langs->transnoentities('ErrorFieldMustBeANumeric', $langs->transnoentities("Position")), null, 'errors'); } // Clean some parameters @@ -193,7 +189,7 @@ if (GETPOST('actionadd') || GETPOST('actionmodify')) if ($_POST["accountancy_code_buy"] <= 0) $_POST["accountancy_code_buy"]=''; // If empty, we force to null // Si verif ok et action add, on ajoute la ligne - if ($ok && GETPOST('actionadd')) + if ($ok && GETPOST('actionadd','alpha')) { if ($tabrowid[$id]) { @@ -214,15 +210,13 @@ if (GETPOST('actionadd') || GETPOST('actionmodify')) // Add new entry $sql = "INSERT INTO ".$tabname[$id]." ("; // List of fields - if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldinsert)) - $sql.= $tabrowid[$id].","; + if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldinsert)) $sql.= $tabrowid[$id].","; $sql.= $tabfieldinsert[$id]; $sql.=",active)"; $sql.= " VALUES("; // List of values - if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldinsert)) - $sql.= $newid.","; + if ($tabrowid[$id] && ! in_array($tabrowid[$id],$listfieldinsert)) $sql.= $newid.","; $i=0; foreach ($listfieldinsert as $f => $value) { @@ -306,7 +300,7 @@ if ($action == 'confirm_delete' && $confirm == 'yes') // delete if ($tabrowid[$id]) { $rowidcol=$tabrowid[$id]; } else { $rowidcol="rowid"; } - $sql = "DELETE from ".$tabname[$id]." WHERE ".$rowidcol."='".$rowid."'"; + $sql = "DELETE from ".$tabname[$id]." WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'"; dol_syslog("delete", LOG_DEBUG); $result = $db->query($sql); @@ -330,10 +324,10 @@ if ($action == $acts[0]) else { $rowidcol="rowid"; } if ($rowid) { - $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol."='".$rowid."'"; + $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'"; } elseif ($code) { - $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE code='".$code."'"; + $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE code = '".$this->db->escape($code)."'"; } $result = $db->query($sql); @@ -350,10 +344,10 @@ if ($action == $acts[1]) else { $rowidcol="rowid"; } if ($rowid) { - $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol."='".$rowid."'"; + $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'"; } elseif ($code) { - $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE code='".$code."'"; + $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE code = '".$this->db->escape($code)."'"; } $result = $db->query($sql); @@ -370,10 +364,10 @@ if ($action == 'activate_favorite') else { $rowidcol="rowid"; } if ($rowid) { - $sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE ".$rowidcol."='".$rowid."'"; + $sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'"; } elseif ($code) { - $sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE code='".$code."'"; + $sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE code = '".$this->db->escape($code)."'"; } $result = $db->query($sql); @@ -390,10 +384,10 @@ if ($action == 'disable_favorite') else { $rowidcol="rowid"; } if ($rowid) { - $sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE ".$rowidcol."='".$rowid."'"; + $sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'"; } elseif ($code) { - $sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE code='".$code."'"; + $sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE code = '".$this->db->escape($code)."'"; } $result = $db->query($sql); @@ -440,7 +434,7 @@ if ($id) { if (preg_match('/ WHERE /',$sql)) $sql.= " AND "; else $sql.=" WHERE "; - $sql.= " c.rowid = ".$search_country_id; + $sql.= " (a.fk_country = ".$search_country_id." OR a.fk_country = 0)"; } if ($sortfield) diff --git a/htdocs/accountancy/admin/export.php b/htdocs/accountancy/admin/export.php index bdf0a7c1826..dbe2a3b0c1c 100644 --- a/htdocs/accountancy/admin/export.php +++ b/htdocs/accountancy/admin/export.php @@ -135,7 +135,7 @@ llxHeader(); $form = new Form($db); -// $linkback = '' . $langs->trans("BackToModuleList") . ''; +// $linkback = '' . $langs->trans("BackToModuleList") . ''; print load_fiche_titre($langs->trans('ConfigAccountingExpert'), $linkback, 'title_setup'); print "\n".''."\n"; + + print ''; + print ''; + print ''; + + print '
' . $langs->trans("AccountingCategory") . ''; $formaccounting->select_accounting_category($cat_id, 'account_category', 1, 0, 0, 1); print ''; print '
'; + print ''."\n"; + + // Name + print ''."\n"; + + // Address + print ''."\n"; + + print ''."\n"; + + print ''."\n"; + + // Country + print ''."\n"; + + print ''."\n"; + + print ''; + print ''."\n"; + + print ''; + print ''."\n"; + + print ''; + print ''."\n"; + + // Web + print ''; + print ''."\n"; + + // Note + print ''; + print ''; + + print '
'.$langs->trans("CompanyInfo").''.$langs->trans("Value").'
'; + print '
'; + print '
'; + print '
'; + print '
'; + //if (empty($country_selected)) $country_selected=substr($langs->defaultlang,-2); // By default, country of localization + print $form->select_country($conf->global->MAIN_INFO_ACCOUNTANT_COUNTRY, 'country_id'); + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1); + print '
'; + $formcompany->select_departement($conf->global->MAIN_INFO_ACCOUNTANT_STATE, $conf->global->MAIN_INFO_ACCOUNTANT_COUNTRY, 'state_id'); + print '
'; + print '
'; + print '
'; + print '
'; + print '
'; + print '
'; + + print '
'; + print ''; + print '     '; + print ''; + print '
'; + print '
'; + + print ''; +} +else +{ + /* + * Show parameters + */ + + // Actions buttons + //print '
'; + //print ''.$langs->trans("Modify").''; + //print '

'; + + print '
'; + print ''; + print ''; + + + print ''; + + + print ''; + + + print ''; + + + print ''; + + + print ''; + + + if (! empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT)) print ''; + + print ''; + + print ''; + + print ''; + + // Web + + print ''; + + print ''; + + print '
'.$langs->trans("CompanyInfo").''.$langs->trans("Value").'
'.$langs->trans("CompanyName").''; + print $conf->global->MAIN_INFO_ACCOUNTANT_NAME; + print '
'.$langs->trans("CompanyAddress").'' . nl2br(empty($conf->global->MAIN_INFO_ACCOUNTANT_ADDRESS)?'':$conf->global->MAIN_INFO_ACCOUNTANT_ADDRESS) . '
'.$langs->trans("CompanyZip").'' . (empty($conf->global->MAIN_INFO_ACCOUNTANT_ZIP)?'':$conf->global->MAIN_INFO_ACCOUNTANT_ZIP) . '
'.$langs->trans("CompanyTown").'' . (empty($conf->global->MAIN_INFO_ACCOUNTANT_TOWN)?'':$conf->global->MAIN_INFO_ACCOUNTANT_TOWN) . '
'.$langs->trans("CompanyCountry").''; + if (! empty($conf->global->MAIN_INFO_ACCOUNTANT_COUNTRY)) + { + $code = getCountry($conf->global->MAIN_INFO_ACCOUNTANT_COUNTRY, 2); + $img=picto_from_langcode($code); + print $img?$img.' ':''; + print getCountry($conf->global->MAIN_INFO_ACCOUNTANT_COUNTRY,1); + } + print '
'.$langs->trans("Region-State").''; + else print '
'.$langs->trans("State").''; + if (! empty($conf->global->MAIN_INFO_ACCOUNTANT_STATE)) print getState($conf->global->MAIN_INFO_ACCOUNTANT_STATE,$conf->global->MAIN_SHOW_STATE_CODE,0,$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT); + else print ' '; + print '
'.$langs->trans("Phone").'' . dol_print_phone($conf->global->MAIN_INFO_ACCOUNTANT_PHONE,$mysoc->country_code) . '
'.$langs->trans("Fax").'' . dol_print_phone($conf->global->MAIN_INFO_ACCOUNTANT_FAX,$mysoc->country_code) . '
'.$langs->trans("Mail").'' . dol_print_email($conf->global->MAIN_INFO_ACCOUNTANT_MAIL,0,0,0,80) . '
'.$langs->trans("Web").'' . dol_print_url($conf->global->MAIN_INFO_ACCOUNTANT_WEB,'_blank',80) . '
'.$langs->trans("Note").'' . (! empty($conf->global->MAIN_INFO_ACCOUNTANT_NOTE) ? nl2br($conf->global->MAIN_INFO_ACCOUNTANT_NOTE) : '') . '
'; + print "
"; + + print ''; + + // Actions buttons + print '
'; + print ''; + print '
'; +} + +llxFooter(); + +$db->close(); diff --git a/htdocs/admin/agenda.php b/htdocs/admin/agenda.php index c1610132a79..2c90c0ed99b 100644 --- a/htdocs/admin/agenda.php +++ b/htdocs/admin/agenda.php @@ -143,7 +143,7 @@ if (preg_match('/del_(.*)/',$action,$reg)) $wikihelp='EN:Module_Agenda_En|FR:Module_Agenda|ES:Módulo_Agenda'; llxHeader('', $langs->trans("AgendaSetup"), $wikihelp); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AgendaSetup"),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/agenda_extrafields.php b/htdocs/admin/agenda_extrafields.php index ec7701a1a13..4c61500494d 100644 --- a/htdocs/admin/agenda_extrafields.php +++ b/htdocs/admin/agenda_extrafields.php @@ -70,7 +70,7 @@ $textobject=$langs->transnoentitiesnoconv("Agenda"); $wikihelp='EN:Module_Agenda_En|FR:Module_Agenda|ES:Módulo_Agenda'; llxHeader('', $langs->trans("AgendaSetup"), $wikihelp); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AgendaSetup"),$linkback,'title_setup'); $head=agenda_prepare_head(); diff --git a/htdocs/admin/agenda_extsites.php b/htdocs/admin/agenda_extsites.php index 24319a34a28..4c1b49d75e6 100644 --- a/htdocs/admin/agenda_extsites.php +++ b/htdocs/admin/agenda_extsites.php @@ -48,9 +48,11 @@ $MAXAGENDA=$conf->global->AGENDA_EXT_NB; // List of aviable colors $colorlist=array('BECEDD','DDBECE','BFDDBE','F598B4','F68654','CBF654','A4A4A5'); + /* * Actions */ + if ($actionsave) { $db->begin(); @@ -128,7 +130,7 @@ $arrayofcss=array(); $wikihelp='EN:Module_Agenda_En|FR:Module_Agenda|ES:Módulo_Agenda'; llxHeader('',$langs->trans("AgendaSetup"),$wikihelp,'',0,0,$arrayofjs,$arrayofcss); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AgendaSetup"),$linkback,'title_setup'); print ''; diff --git a/htdocs/admin/agenda_other.php b/htdocs/admin/agenda_other.php index ab53bfb937f..54414e094d3 100644 --- a/htdocs/admin/agenda_other.php +++ b/htdocs/admin/agenda_other.php @@ -178,7 +178,7 @@ $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); $wikihelp='EN:Module_Agenda_En|FR:Module_Agenda|ES:Módulo_Agenda'; llxHeader('', $langs->trans("AgendaSetup"),$wikihelp); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AgendaSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/agenda_reminder.php b/htdocs/admin/agenda_reminder.php index 635f3c0cdd5..3e946dd8e60 100644 --- a/htdocs/admin/agenda_reminder.php +++ b/htdocs/admin/agenda_reminder.php @@ -171,7 +171,7 @@ $formactions=new FormActions($db); $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AgendaSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/agenda_xcal.php b/htdocs/admin/agenda_xcal.php index 93f0e909ab9..ac19eb24c03 100644 --- a/htdocs/admin/agenda_xcal.php +++ b/htdocs/admin/agenda_xcal.php @@ -74,7 +74,7 @@ if (! isset($conf->global->MAIN_AGENDA_EXPORT_PAST_DELAY)) $conf->global->MAIN_A $wikihelp='EN:Module_Agenda_En|FR:Module_Agenda|ES:Módulo_Agenda'; llxHeader('', $langs->trans("AgendaSetup"), $wikihelp); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AgendaSetup"),$linkback,'title_setup'); @@ -173,11 +173,12 @@ $message.='
'; $message.='
'; print $message; -$message=$langs->trans("AgendaUrlOptions1",$user->login,$user->login).'
'; +$message =$langs->trans("AgendaUrlOptions1",$user->login,$user->login).'
'; $message.=$langs->trans("AgendaUrlOptions3",$user->login,$user->login).'
'; $message.=$langs->trans("AgendaUrlOptionsNotAdmin",$user->login,$user->login).'
'; $message.=$langs->trans("AgendaUrlOptions4",$user->login,$user->login).'
'; -$message.=$langs->trans("AgendaUrlOptionsProject",$user->login,$user->login); +$message.=$langs->trans("AgendaUrlOptionsProject",$user->login,$user->login).'
'; +$message.=$langs->trans("AgendaUrlOptionsNotAutoEvent",'systemauto','systemauto').'
'; print info_admin($message); diff --git a/htdocs/admin/bank.php b/htdocs/admin/bank.php index bf3b2e9ea92..236a494c060 100644 --- a/htdocs/admin/bank.php +++ b/htdocs/admin/bank.php @@ -171,7 +171,7 @@ $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); llxHeader("", $langs->trans("BankSetupModule")); -$linkback = '' . $langs->trans("BackToModuleList") . ''; +$linkback = '' . $langs->trans("BackToModuleList") . ''; print load_fiche_titre($langs->trans("BankSetupModule"), $linkback, 'title_setup'); diff --git a/htdocs/admin/bank_extrafields.php b/htdocs/admin/bank_extrafields.php index ccdde0eecb6..aba65f63887 100644 --- a/htdocs/admin/bank_extrafields.php +++ b/htdocs/admin/bank_extrafields.php @@ -65,7 +65,7 @@ $textobject = $langs->transnoentitiesnoconv("Bank"); llxHeader('',$langs->trans("BankSetupModule"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BankSetupModule"),$linkback,'title_setup'); diff --git a/htdocs/admin/barcode.php b/htdocs/admin/barcode.php index ba649fe9223..313a852e384 100644 --- a/htdocs/admin/barcode.php +++ b/htdocs/admin/barcode.php @@ -101,7 +101,7 @@ $formbarcode = new FormBarCode($db); $help_url='EN:Module_Barcode|FR:Module_Codes_Barre|ES:Módulo Código de barra'; llxHeader('',$langs->trans("BarcodeSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BarcodeSetup"),$linkback,'title_setup'); // Detect bar codes modules diff --git a/htdocs/admin/chequereceipts.php b/htdocs/admin/chequereceipts.php index 299d18d3b4f..86b38283649 100644 --- a/htdocs/admin/chequereceipts.php +++ b/htdocs/admin/chequereceipts.php @@ -102,7 +102,7 @@ llxHeader("",$langs->trans("BankSetupModule")); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BankSetupModule"),$linkback,'title_setup'); $head = bank_admin_prepare_head(null); diff --git a/htdocs/admin/clicktodial.php b/htdocs/admin/clicktodial.php index df3c54f758c..e9224ba4688 100644 --- a/htdocs/admin/clicktodial.php +++ b/htdocs/admin/clicktodial.php @@ -62,7 +62,7 @@ $user->fetch_clicktodial(); $wikihelp='EN:Module_ClickToDial_En|FR:Module_ClickToDial|ES:Módulo_ClickTodial_Es'; llxHeader('',$langs->trans("ClickToDialSetup"),$wikihelp); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ClickToDialSetup"),$linkback,'title_setup'); print $langs->trans("ClickToDialDesc")."
\n"; diff --git a/htdocs/admin/commande.php b/htdocs/admin/commande.php index 09e904e3a5d..5db56c82aa2 100644 --- a/htdocs/admin/commande.php +++ b/htdocs/admin/commande.php @@ -253,7 +253,7 @@ $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); llxHeader("",$langs->trans("OrdersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("OrdersSetup"),$linkback,'title_setup'); $head = order_admin_prepare_head(); diff --git a/htdocs/admin/company.php b/htdocs/admin/company.php index b2b7efc3a1c..59fa9eeae2f 100644 --- a/htdocs/admin/company.php +++ b/htdocs/admin/company.php @@ -5,6 +5,7 @@ * Copyright (C) 2010-2014 Juanjo Menent * Copyright (C) 2011-2017 Philippe Grand * Copyright (C) 2015 Alexandre Spangaro + * Copyright (C) 2017 Rui Strecht * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; $action=GETPOST('action','aZ09'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'admincompany'; // To manage different context of search $langs->load("admin"); $langs->load("companies"); @@ -46,8 +48,7 @@ if (! $user->admin) accessforbidden(); $error=0; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('admincompany','globaladmin'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('admincompany','globaladmin')); /* * Actions @@ -78,7 +79,8 @@ if ( ($action == 'update' && ! GETPOST("cancel",'alpha')) dolibarr_set_const($db, "MAIN_INFO_SOCIETE_TOWN", GETPOST("town",'nohtml'),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_INFO_SOCIETE_ZIP", GETPOST("zipcode",'alpha'),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_INFO_SOCIETE_STATE", GETPOST("state_id",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_MONNAIE", GETPOST("currency",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_SOCIETE_REGION", GETPOST("region_code",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_MONNAIE", GETPOST("currency",'aZ09'),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_INFO_SOCIETE_TEL", GETPOST("tel",'alpha'),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_INFO_SOCIETE_FAX", GETPOST("fax",'alpha'),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_INFO_SOCIETE_MAIL", GETPOST("mail",'alpha'),'chaine',0,'',$conf->entity); @@ -154,26 +156,26 @@ if ( ($action == 'update' && ! GETPOST("cancel",'alpha')) } } - dolibarr_set_const($db, "MAIN_INFO_SOCIETE_MANAGERS", GETPOST("MAIN_INFO_SOCIETE_MANAGERS",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_CAPITAL", GETPOST("capital",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_SOCIETE_FORME_JURIDIQUE", GETPOST("forme_juridique_code",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_SIREN", GETPOST("siren",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_SIRET", GETPOST("siret",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_APE", GETPOST("ape",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_RCS", GETPOST("rcs",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_PROFID5", GETPOST("MAIN_INFO_PROFID5",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_PROFID6", GETPOST("MAIN_INFO_PROFID6",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_SOCIETE_MANAGERS", GETPOST("MAIN_INFO_SOCIETE_MANAGERS",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_CAPITAL", GETPOST("capital",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_SOCIETE_FORME_JURIDIQUE", GETPOST("forme_juridique_code",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_SIREN", GETPOST("siren",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_SIRET", GETPOST("siret",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_APE", GETPOST("ape",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_RCS", GETPOST("rcs",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_PROFID5", GETPOST("MAIN_INFO_PROFID5",'nohtml'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_PROFID6", GETPOST("MAIN_INFO_PROFID6",'nohtml'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "MAIN_INFO_TVAINTRA", GETPOST("tva",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_TVAINTRA", GETPOST("tva",'nohtml'),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_INFO_SOCIETE_OBJECT", GETPOST("object",'nohtml'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "SOCIETE_FISCAL_MONTH_START", GETPOST("SOCIETE_FISCAL_MONTH_START",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "SOCIETE_FISCAL_MONTH_START", GETPOST("SOCIETE_FISCAL_MONTH_START",'int'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "FACTURE_TVAOPTION", GETPOST("optiontva",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "FACTURE_TVAOPTION", GETPOST("optiontva",'aZ09'),'chaine',0,'',$conf->entity); // Local taxes - dolibarr_set_const($db, "FACTURE_LOCAL_TAX1_OPTION", GETPOST("optionlocaltax1",'alpha'),'chaine',0,'',$conf->entity); - dolibarr_set_const($db, "FACTURE_LOCAL_TAX2_OPTION", GETPOST("optionlocaltax2",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "FACTURE_LOCAL_TAX1_OPTION", GETPOST("optionlocaltax1",'aZ09'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "FACTURE_LOCAL_TAX2_OPTION", GETPOST("optionlocaltax2",'aZ09'),'chaine',0,'',$conf->entity); if($_POST["optionlocaltax1"]=="localtax1on") { @@ -183,9 +185,9 @@ if ( ($action == 'update' && ! GETPOST("cancel",'alpha')) } else { - dolibarr_set_const($db, "MAIN_INFO_VALUE_LOCALTAX1", GETPOST('lt1','alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_VALUE_LOCALTAX1", GETPOST('lt1','aZ09'),'chaine',0,'',$conf->entity); } - dolibarr_set_const($db,"MAIN_INFO_LOCALTAX_CALC1", GETPOST("clt1",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db,"MAIN_INFO_LOCALTAX_CALC1", GETPOST("clt1",'aZ09'),'chaine',0,'',$conf->entity); } if($_POST["optionlocaltax2"]=="localtax2on") { @@ -195,9 +197,9 @@ if ( ($action == 'update' && ! GETPOST("cancel",'alpha')) } else { - dolibarr_set_const($db, "MAIN_INFO_VALUE_LOCALTAX2", GETPOST('lt2','alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_INFO_VALUE_LOCALTAX2", GETPOST('lt2','aZ09'),'chaine',0,'',$conf->entity); } - dolibarr_set_const($db,"MAIN_INFO_LOCALTAX_CALC2", GETPOST("clt2",'alpha'),'chaine',0,'',$conf->entity); + dolibarr_set_const($db,"MAIN_INFO_LOCALTAX_CALC2", GETPOST("clt2",'aZ09'),'chaine',0,'',$conf->entity); } if ($action != 'updateedit' && ! $error) @@ -293,6 +295,10 @@ $countrynotdefined=''.$langs->trans("ErrorSetACountryFirst") print load_fiche_titre($langs->trans("CompanyFoundation"),'','title_setup'); +$head = company_admin_prepare_head(); + +dol_fiche_head($head, 'company', $langs->trans("Company"), -1, 'company'); + print $langs->trans("CompanyFundationDesc")."
\n"; print "
\n"; @@ -313,10 +319,9 @@ if ($action == 'edit' || $action == 'updateedit') print ''; print ''; print ''; - $var=true; print ''; - print ''."\n"; + print ''."\n"; // Name @@ -340,18 +345,18 @@ if ($action == 'edit' || $action == 'updateedit') print ''."\n"; print ''."\n"; print ''."\n"; @@ -410,8 +415,7 @@ if ($action == 'edit' || $action == 'updateedit') // IDs of the company (country-specific) print '
'.$langs->trans("CompanyInfo").''.$langs->trans("Value").'
'.$langs->trans("CompanyInfo").''.$langs->trans("Value").'
'; //if (empty($country_selected)) $country_selected=substr($langs->defaultlang,-2); // By default, country of localization - print $form->select_country($mysoc->country_id,'country_id'); + print $form->select_country($mysoc->country_id, 'country_id'); if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1); print '
'; - $formcompany->select_departement($conf->global->MAIN_INFO_SOCIETE_STATE,$mysoc->country_code,'state_id'); + $formcompany->select_departement($conf->global->MAIN_INFO_SOCIETE_STATE, $mysoc->country_code, 'state_id'); print '
'; - print $form->selectCurrency($conf->currency,"currency"); + print $form->selectCurrency($conf->currency, "currency"); print '
'; - print ''; - $var=true; + print ''; $langs->load("companies"); @@ -564,7 +568,7 @@ if ($action == 'edit' || $action == 'updateedit') print '
'; print '
'.$langs->trans("CompanyIds").''.$langs->trans("Value").'
'.$langs->trans("CompanyIds").''.$langs->trans("Value").'
'; print ''; - print ''; + print ''; print ''; print "\n"; @@ -597,7 +601,7 @@ if ($action == 'edit' || $action == 'updateedit') print '
'; print '
'.$langs->trans("VATManagement").''.$langs->trans("Description").''.$langs->trans("VATManagement").''.$langs->trans("Description").' 
'; print ''; - print ''; + print ''; print ''; print "\n"; @@ -697,11 +701,12 @@ else //print ''.$langs->trans("Modify").''; //print '
'; + print '
'; print '
'.$langs->transcountry("LocalTax1Management",$mysoc->country_code).''.$langs->trans("Description").''.$langs->transcountry("LocalTax1Management",$mysoc->country_code).''.$langs->trans("Description").' 
'; print ''; - print ''; @@ -727,8 +732,9 @@ else print ''; - print ''; @@ -791,7 +797,7 @@ else print ''; print '
'.$langs->trans("CompanyInfo").''.$langs->trans("Value").'
'.$langs->trans("CompanyName").''; + print '
'.$langs->trans("CompanyName").''; if (! empty($conf->global->MAIN_INFO_SOCIETE_NOM)) print $conf->global->MAIN_INFO_SOCIETE_NOM; else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyName")).''; print '
'.$langs->trans("State").''; - if (! empty($conf->global->MAIN_INFO_SOCIETE_STATE)) print getState($conf->global->MAIN_INFO_SOCIETE_STATE); + if (! empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT)) print '
'.$langs->trans("Region-State").''; + else print '
'.$langs->trans("State").''; + if (! empty($conf->global->MAIN_INFO_SOCIETE_STATE)) print getState($conf->global->MAIN_INFO_SOCIETE_STATE,$conf->global->MAIN_SHOW_STATE_CODE,0,$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT); else print ' '; print '
'.$langs->trans("Note").'' . (! empty($conf->global->MAIN_INFO_SOCIETE_NOTE) ? nl2br($conf->global->MAIN_INFO_SOCIETE_NOTE) : '') . '
'; - + print ""; print '
'; @@ -799,8 +805,10 @@ else // IDs of the company (country-specific) print ''; print ''; + + print '
'; print ''; - print ''; + print ''; // Managing Director(s) @@ -956,12 +964,16 @@ else print ''; print '
'.$langs->trans("CompanyIds").''.$langs->trans("Value").'
'.$langs->trans("CompanyIds").''.$langs->trans("Value").'
'.$langs->trans("CompanyObject").'' . (! empty($conf->global->MAIN_INFO_SOCIETE_OBJECT) ? nl2br($conf->global->MAIN_INFO_SOCIETE_OBJECT) : '') . '
'; + print "
"; + print ''; /* * fiscal year beginning */ print '
'; + + print '
'; print ''; print ''; print ''; @@ -973,11 +985,13 @@ else print dol_print_date(dol_mktime(12,0,0,$monthstart,1,2000,1),'%B','gm') . ''; print "
'.$langs->trans("FiscalYearInformation").''.$langs->trans("Value").'
"; + print "
"; /* * tax options */ print '
'; + print '
'; print ''; print ''; print ''; @@ -1005,7 +1019,7 @@ else print "\n"; print "
'.$langs->trans("VATManagement").''.$langs->trans("Description").'
"; - + print "
"; /* * Local Taxes @@ -1014,6 +1028,7 @@ else { // Local Tax 1 print '
'; + print '
'; print ''; print ''; print ''; @@ -1061,11 +1076,13 @@ else print "\n"; print "
'.$langs->transcountry("LocalTax1Management",$mysoc->country_code).''.$langs->trans("Description").'
"; + print "
"; } if ($mysoc->useLocalTax(2)) // True if we found at least on vat with a setup adding a localtax 1 { // Local Tax 2 print '
'; + print '
'; print ''; print ''; print ''; @@ -1113,6 +1130,7 @@ else print "\n"; print "
'.$langs->transcountry("LocalTax2Management",$mysoc->country_code).''.$langs->trans("Description").'
"; + print "
"; } @@ -1120,8 +1138,6 @@ else print '
'; print ''; print '
'; - - print '
'; } diff --git a/htdocs/admin/compta.php b/htdocs/admin/compta.php index 69f265f1d2e..3f765d3e18c 100644 --- a/htdocs/admin/compta.php +++ b/htdocs/admin/compta.php @@ -103,7 +103,7 @@ llxHeader(); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('ComptaSetup'),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/confexped.php b/htdocs/admin/confexped.php index 0059f337752..cd771cdb0f7 100644 --- a/htdocs/admin/confexped.php +++ b/htdocs/admin/confexped.php @@ -89,7 +89,7 @@ $form=new Form($db); llxHeader("",$langs->trans("SendingsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print '
'; $head = expedition_admin_prepare_head(); diff --git a/htdocs/admin/contract.php b/htdocs/admin/contract.php index b83373b70c0..83c3634c35c 100644 --- a/htdocs/admin/contract.php +++ b/htdocs/admin/contract.php @@ -187,7 +187,7 @@ llxHeader(); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ContractsSetup"),$linkback,'title_setup'); $head=contract_admin_prepare_head(); diff --git a/htdocs/admin/dav.php b/htdocs/admin/dav.php new file mode 100644 index 00000000000..5b532a3822f --- /dev/null +++ b/htdocs/admin/dav.php @@ -0,0 +1,163 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/admin/dav.php + * \ingroup dav + * \brief Page to setup DAV server + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/dav/dav.lib.php'; + + +if (!$user->admin) + accessforbidden(); + +$langs->load("admin"); +$langs->load("other"); +$langs->load("agenda"); + +$def = array(); +$actionsave=GETPOST('save','alpha'); + +// Sauvegardes parametres +if ($actionsave) +{ + $i=0; + + $db->begin(); + + $i+=dolibarr_set_const($db,'XXX',trim(GETPOST('XXX','alpha')),'chaine',0,'',$conf->entity); + + if ($i >= 4) + { + $db->commit(); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + } + else + { + $db->rollback(); + setEventMessages($langs->trans("SaveFailed"), null, 'errors'); + } +} + + + +/** + * View + */ + + +llxHeader('', $langs->trans("DAVSetup"), $wikihelp); + +$linkback=''.$langs->trans("BackToModuleList").''; +print load_fiche_titre($langs->trans("DAVSetup"),$linkback,'title_setup'); + + +print '
'; +print ''; + +$head=dav_admin_prepare_head(); + +dol_fiche_head($head, 'webdav', '', -1, 'action'); + +print $langs->trans("WebDAVSetupDesc")."
\n"; +print "
\n"; + +/* +print ''; + +print ''; +print ""; +print ""; +//print ""; +print ""; +print ""; + +print ''; +print '"; +print ''; +print ""; +print ""; + +print '
".$langs->trans("Parameter")."".$langs->trans("Value")."".$langs->trans("Examples")." 
'.$langs->trans("PasswordTogetVCalExport")."'; +if (! empty($conf->use_javascript_ajax)) + print ' '.img_picto($langs->trans('Generate'), 'refresh', 'id="generate_token" class="linkobject"'); +print ' 
'; +*/ + +dol_fiche_end(); + +/*print '
'; +print "trans("Save")."\">"; +print "
"; +*/ +print "
\n"; + + +clearstatcache(); + +//if ($mesg) print "
$mesg
"; +print "
"; + + +// Define $urlwithroot +$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); +$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file +//$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current + + +// Show message +$message=''; +$url=''.$urlwithroot.'/dav/fileserver.php'; +$message.=img_picto('','object_globe.png').' '.$langs->trans("WebDavServer",'WebDAV',$url); +$message.='
'; +print $message; + +/*$message =$langs->trans("AgendaUrlOptions1",$user->login,$user->login).'
'; +$message.=$langs->trans("AgendaUrlOptions3",$user->login,$user->login).'
'; +$message.=$langs->trans("AgendaUrlOptionsNotAdmin",$user->login,$user->login).'
'; +$message.=$langs->trans("AgendaUrlOptions4",$user->login,$user->login).'
'; +$message.=$langs->trans("AgendaUrlOptionsProject",$user->login,$user->login).'
'; +$message.=$langs->trans("AgendaUrlOptionsNotAutoEvent",'systemauto','systemauto').'
'; + +print info_admin($message); +*/ + +/* +if (! empty($conf->use_javascript_ajax)) +{ + print "\n".''; +} +*/ + +llxFooter(); +$db->close(); diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index fbb0b5f38c5..d8d2382a59f 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2015 Laurent Destailleur + * Copyright (C) 2004-2018 Laurent Destailleur * Copyright (C) 2004 Benoit Mortier * Copyright (C) 2005-2017 Regis Houssin * Copyright (C) 2010-2016 Juanjo Menent @@ -41,14 +41,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT . '/core/class/html.formaccounting.class.php'; -$langs->load("errors"); -$langs->load("admin"); -$langs->load("main"); -$langs->load("companies"); -$langs->load("resource"); -$langs->load("holiday"); -$langs->load("accountancy"); -$langs->load("hrm"); +$langs->loadLangs(array("errors","admin","main","companies","resource","holiday","accountancy","hrm","orders","contracts","projects","propal","bills","interventions")); $action=GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; $confirm=GETPOST('confirm','alpha'); @@ -60,6 +53,7 @@ $code=GETPOST('code','alpha'); $allowed=$user->admin; if ($id == 7 && ! empty($user->rights->accounting->chartofaccount)) $allowed=1; // Tax page allowed to manager of chart account if ($id == 10 && ! empty($user->rights->accounting->chartofaccount)) $allowed=1; // Vat page allowed to manager of chart account +if ($id == 17 && ! empty($user->rights->accounting->chartofaccount)) $allowed=1; // Dictionary with type of expense report and accounting account allowed to manager of chart account if (! $allowed) accessforbidden(); $acts[0] = "activate"; @@ -383,8 +377,8 @@ $tabrowid[8] = "id"; $tabrowid[9] = "code_iso"; $tabrowid[10]= ""; $tabrowid[11]= "rowid"; -$tabrowid[12]= "rowid"; -$tabrowid[13]= "id"; +$tabrowid[12]= ""; +$tabrowid[13]= ""; $tabrowid[14]= ""; $tabrowid[15]= ""; $tabrowid[16]= "code"; @@ -543,33 +537,27 @@ $elementList = array(); $sourceList=array(); if ($id == 11) { - $langs->load("orders"); - $langs->load("contracts"); - $langs->load("projects"); - $langs->load("propal"); - $langs->load("bills"); - $langs->load("interventions"); $elementList = array( '' => '', 'societe' => $langs->trans('ThirdParty'), // 'proposal' => $langs->trans('Proposal'), // 'order' => $langs->trans('Order'), // 'invoice' => $langs->trans('Bill'), - 'invoice_supplier' => $langs->trans('SupplierBill'), + 'supplier_proposal' => $langs->trans('SupplierProposal'), 'order_supplier' => $langs->trans('SupplierOrder'), + 'invoice_supplier' => $langs->trans('SupplierBill'), // 'intervention' => $langs->trans('InterventionCard'), // 'contract' => $langs->trans('Contract'), 'project' => $langs->trans('Project'), 'project_task' => $langs->trans('Task'), 'agenda' => $langs->trans('Agenda'), + 'resource' => $langs->trans('Resource'), // old deprecated - 'contrat' => $langs->trans('Contract'), 'propal' => $langs->trans('Proposal'), 'commande' => $langs->trans('Order'), 'facture' => $langs->trans('Bill'), - 'resource' => $langs->trans('Resource'), -// 'facture_fourn' => $langs->trans('SupplierBill'), - 'fichinter' => $langs->trans('InterventionCard') + 'fichinter' => $langs->trans('InterventionCard'), + 'contrat' => $langs->trans('Contract') ); if (! empty($conf->global->MAIN_SUPPORT_SHARED_CONTACT_BETWEEN_THIRDPARTIES)) $elementList["societe"] = $langs->trans('ThirdParty'); diff --git a/htdocs/admin/ecm.php b/htdocs/admin/ecm.php index 7b7357ee9eb..dd9e748de02 100644 --- a/htdocs/admin/ecm.php +++ b/htdocs/admin/ecm.php @@ -69,7 +69,7 @@ if (preg_match('/del_(.*)/',$action,$reg)) $help_url=''; llxHeader('',$langs->trans("ECMSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ECMSetup"),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/events.php b/htdocs/admin/events.php index 7a8c71c0130..b2d9bd97980 100644 --- a/htdocs/admin/events.php +++ b/htdocs/admin/events.php @@ -73,7 +73,7 @@ if ($action == "save") $wikihelp='EN:Setup_Security|FR:Paramétrage_Sécurité|ES:Configuración_Seguridad'; llxHeader('',$langs->trans("Audit"),$wikihelp); -//$linkback=''.$langs->trans("BackToModuleList").''; +//$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SecuritySetup"),'','title_setup'); print $langs->trans("LogEventDesc")."
\n"; diff --git a/htdocs/admin/expedition.php b/htdocs/admin/expedition.php index d623de33f31..442a85c9c6a 100644 --- a/htdocs/admin/expedition.php +++ b/htdocs/admin/expedition.php @@ -194,7 +194,7 @@ $form=new Form($db); llxHeader("",$langs->trans("SendingsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print '
'; $head = expedition_admin_prepare_head(); diff --git a/htdocs/admin/expedition_extrafields.php b/htdocs/admin/expedition_extrafields.php index 09e0e8b9a92..a12ed1f9b34 100644 --- a/htdocs/admin/expedition_extrafields.php +++ b/htdocs/admin/expedition_extrafields.php @@ -72,7 +72,7 @@ $textobject=$langs->transnoentitiesnoconv("Sendings"); llxHeader('',$langs->trans("SendingsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/expeditiondet_extrafields.php b/htdocs/admin/expeditiondet_extrafields.php index ff388d8a69a..073451eba6e 100644 --- a/htdocs/admin/expeditiondet_extrafields.php +++ b/htdocs/admin/expeditiondet_extrafields.php @@ -72,7 +72,7 @@ $textobject=$langs->transnoentitiesnoconv("Sendings"); llxHeader('',$langs->trans("SendingsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/expensereport.php b/htdocs/admin/expensereport.php index 86fac86f532..28ced963f87 100644 --- a/htdocs/admin/expensereport.php +++ b/htdocs/admin/expensereport.php @@ -195,11 +195,11 @@ else if ($action == 'setoptions') $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); -llxHeader(); +llxHeader('',$langs->trans("ExpenseReportsSetup")); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ExpenseReportsSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/expensereport_extrafields.php b/htdocs/admin/expensereport_extrafields.php index 8635c336a2b..a83c9b04194 100644 --- a/htdocs/admin/expensereport_extrafields.php +++ b/htdocs/admin/expensereport_extrafields.php @@ -67,9 +67,9 @@ require DOL_DOCUMENT_ROOT.'/core/actions_extrafields.inc.php'; $textobject=$langs->transnoentitiesnoconv("expensereports"); -llxHeader('',$langs->trans("expensereportsSetup")); +llxHeader('',$langs->trans("ExpenseReportsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ExpenseReportsSetup"),$linkback,'title_setup'); $head = expensereport_admin_prepare_head(); diff --git a/htdocs/admin/expensereport_ik.php b/htdocs/admin/expensereport_ik.php index 9e653dce906..adf18433caa 100644 --- a/htdocs/admin/expensereport_ik.php +++ b/htdocs/admin/expensereport_ik.php @@ -58,12 +58,12 @@ if ($action == 'updateik') $result = $expIk->fetch($id); if ($result < 0) dol_print_error('', $expIk->error, $expIk->errors); } - + $expIk->setValues($_POST); $result = $expIk->create($user); - + if ($result > 0) setEventMessages('SetupSaved', null, 'mesgs'); - + header('Location: '.$_SERVER['PHP_SELF']); exit; } @@ -74,11 +74,11 @@ elseif ($action == 'delete') // TODO add confirm { $result = $expIk->fetch($id); if ($result < 0) dol_print_error('', $expIk->error, $expIk->errors); - + $expIk->delete($user); } - - + + header('Location: '.$_SERVER['PHP_SELF']); exit; } @@ -89,11 +89,11 @@ $rangesbycateg = ExpenseReportIk::getAllRanges(); * View */ -llxHeader(); +llxHeader('',$langs->trans("ExpenseReportsSetup")); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ExpenseReportsIkSetup"),$linkback,'title_setup'); $head=expensereport_admin_prepare_head(); @@ -125,23 +125,23 @@ foreach ($rangesbycateg as $fk_c_exp_tax_cat => $Tab) echo ''.$langs->trans('expenseReportTotalForFive').''; echo ' '; echo ''; - + if ($Tab['active'] == 0) continue; - + $tranche=1; $var = true; foreach ($Tab['ranges'] as $k => $range) { if (isset($Tab['ranges'][$k+1])) $label = $langs->trans('expenseReportRangeFromTo', $range->range_ik, ($Tab['ranges'][$k+1]->range_ik-1)); else $label = $langs->trans('expenseReportRangeMoreThan', $range->range_ik); - + if ($range->range_active == 0) $label = $form->textwithpicto($label, $langs->trans('expenseReportRangeDisabled'), 1, 'help', '', 0, 3); - + echo ''; - + // Label echo '['.$langs->trans('RangeNum', $tranche++).'] - '.$label.''; - + // Offset echo ''; if ($action == 'edit' && $range->ik->id == $id && $range->rowid == $fk_range && $range->fk_c_exp_tax_cat == $fk_c_exp_tax_cat) echo ''; @@ -152,10 +152,10 @@ foreach ($rangesbycateg as $fk_c_exp_tax_cat => $Tab) if ($action == 'edit' && $range->ik->id == $id && $range->rowid == $fk_range && $range->fk_c_exp_tax_cat == $fk_c_exp_tax_cat) echo ''; else echo ($range->ik->id > 0 ? $range->ik->coef : $langs->trans('expenseReportCoefUndefined')); echo ''; - + // Total for one echo ''.$langs->trans('expenseReportPrintExample', price($range->ik->offset + 5 * $range->ik->coef)).''; - + // Action echo ''; if ($range->range_active == 1) @@ -173,7 +173,7 @@ foreach ($rangesbycateg as $fk_c_exp_tax_cat => $Tab) } } echo ''; - + echo ''; $var=!$var; } diff --git a/htdocs/admin/expensereport_rules.php b/htdocs/admin/expensereport_rules.php index 695bdf09a12..bf615676f81 100644 --- a/htdocs/admin/expensereport_rules.php +++ b/htdocs/admin/expensereport_rules.php @@ -57,17 +57,17 @@ $amount = GETPOST('amount'); $restrictive = GETPOST('restrictive'); $object = new ExpenseReportRule($db); -if (!empty($id)) +if (!empty($id)) { $result = $object->fetch($id); if ($result < 0) dol_print_error('', $object->error, $object->errors); } - + // TODO do action if ($action == 'save') { $error = 0; - + // check parameters if (empty($apply_to)) { $error++; @@ -93,11 +93,11 @@ if ($action == 'save') $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ExpenseReportLimitAmount")), null, 'errors'); } - + if (empty($error)) { $object->setValues($_POST); - + if($apply_to=='U'){ $object->fk_user=$fk_user; $object->fk_usergroup=0; @@ -114,13 +114,13 @@ if ($action == 'save') $object->dates = $dates; $object->datee = $datee; - + $object->entity = $conf->entity; $res = $object->create($user); if ($res > 0) setEventMessages($langs->trans('ExpenseReportRuleSave'), null); else dol_print_error($object->db); - + header('Location: '.$_SERVER['PHP_SELF']); exit; } @@ -129,7 +129,7 @@ elseif ($action == 'delete') { // TODO add confirm $res = $object->delete($user); - + if ($res < 0) dol_print_error($object->db); header('Location: '.$_SERVER['PHP_SELF']); @@ -145,11 +145,11 @@ $tab_rules_type = array('EX_DAY' => $langs->trans('Day'), 'EX_MON' => $langs->tr * View */ -llxHeader(); +llxHeader('',$langs->trans("ExpenseReportsSetup")); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ExpenseReportsRulesSetup"),$linkback,'title_setup'); $head=expensereport_admin_prepare_head(); @@ -194,7 +194,7 @@ if ($action != 'edit') echo ''; echo ''; - echo ''; + echo ''; } @@ -224,7 +224,7 @@ $var=true; foreach ($rules as $rule) { echo ''; - + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -240,8 +240,8 @@ foreach ($rules as $rule) elseif ($rule->fk_user > 0) echo $tab_apply['U'].' ('.$rule->getUserName().')'; } echo ''; - - + + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -250,7 +250,7 @@ foreach ($rules as $rule) else { if ($rule->fk_c_type_fees == -1) echo $langs->trans('AllExpenseReport'); - else + else { $key = getDictvalue(MAIN_DB_PREFIX.'c_type_fees', 'code', $rule->fk_c_type_fees, false, 'id'); if ($key != $langs->trans($key)) echo $langs->trans($key); @@ -258,9 +258,9 @@ foreach ($rules as $rule) } } echo ''; - - - + + + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -271,8 +271,8 @@ foreach ($rules as $rule) echo $tab_rules_type[$rule->code_expense_rules_type]; } echo ''; - - + + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -283,8 +283,8 @@ foreach ($rules as $rule) echo dol_print_date($rule->dates, 'day'); } echo ''; - - + + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -295,8 +295,8 @@ foreach ($rules as $rule) echo dol_print_date($rule->datee, 'day'); } echo ''; - - + + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -307,8 +307,8 @@ foreach ($rules as $rule) echo price($rule->amount, 0, $langs, 1, -1, -1, $conf->currency); } echo ''; - - + + echo ''; if ($action == 'edit' && $object->id == $rule->id) { @@ -319,8 +319,8 @@ foreach ($rules as $rule) echo yn($rule->restrictive, 1, 1); } echo ''; - - + + echo ''; if ($object->id != $rule->id) { @@ -333,7 +333,7 @@ foreach ($rules as $rule) echo ''.$langs->trans('Cancel').''; } echo ''; - + echo ''; $var=!$var; } @@ -355,9 +355,9 @@ echo ''; dol_fiche_end(); diff --git a/htdocs/admin/external_rss.php b/htdocs/admin/external_rss.php index 2e23f9419a4..4a184aa95a0 100644 --- a/htdocs/admin/external_rss.php +++ b/htdocs/admin/external_rss.php @@ -191,7 +191,7 @@ if ($_POST["delete"]) llxHeader('',$langs->trans("ExternalRSSSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ExternalRSSSetup"), $linkback, 'title_setup'); print '
'; diff --git a/htdocs/admin/facture.php b/htdocs/admin/facture.php index 0db1ffc72db..8ce73fd441f 100644 --- a/htdocs/admin/facture.php +++ b/htdocs/admin/facture.php @@ -279,7 +279,7 @@ llxHeader("",$langs->trans("BillsSetup"),'EN:Invoice_Configuration|FR:Configurat $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup'); $head = invoice_admin_prepare_head(); diff --git a/htdocs/admin/fckeditor.php b/htdocs/admin/fckeditor.php index 9f1d3913630..70b79acb931 100644 --- a/htdocs/admin/fckeditor.php +++ b/htdocs/admin/fckeditor.php @@ -135,7 +135,7 @@ if (GETPOST('save','alpha')) llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("AdvancedEditor"),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/fichinter.php b/htdocs/admin/fichinter.php index 0ada37d2ffb..46c9d5fec9e 100644 --- a/htdocs/admin/fichinter.php +++ b/htdocs/admin/fichinter.php @@ -263,7 +263,7 @@ llxHeader(); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("InterventionsSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/geoipmaxmind.php b/htdocs/admin/geoipmaxmind.php index 5ae1a04e2ea..bf588ca90ae 100644 --- a/htdocs/admin/geoipmaxmind.php +++ b/htdocs/admin/geoipmaxmind.php @@ -75,7 +75,7 @@ $form=new Form($db); llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("GeoIPMaxmindSetup"),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/ihm.php b/htdocs/admin/ihm.php index 03f3d364cca..23cc0600705 100644 --- a/htdocs/admin/ihm.php +++ b/htdocs/admin/ihm.php @@ -46,12 +46,10 @@ $langs->load("agenda"); if (! $user->admin) accessforbidden(); $action = GETPOST('action','aZ09'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'adminihm'; // To manage different context of search if (! defined("MAIN_MOTD")) define("MAIN_MOTD",""); -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('adminihm','globaladmin'); -$hookmanager->initHooks($contextpage); /* diff --git a/htdocs/admin/ldap.php b/htdocs/admin/ldap.php index f9b863e487e..5655c0c2714 100644 --- a/htdocs/admin/ldap.php +++ b/htdocs/admin/ldap.php @@ -89,7 +89,7 @@ if (empty($reshook)) llxHeader('',$langs->trans("LDAPSetup"),'EN:Module_LDAP_En|FR:Module_LDAP|ES:Módulo_LDAP'); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("LDAPSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/ldap_contacts.php b/htdocs/admin/ldap_contacts.php index f183c29b5ef..07910185956 100644 --- a/htdocs/admin/ldap_contacts.php +++ b/htdocs/admin/ldap_contacts.php @@ -95,7 +95,7 @@ if ($action == 'setvalue' && $user->admin) $form=new Form($db); llxHeader('',$langs->trans("LDAPSetup"),'EN:Module_LDAP_En|FR:Module_LDAP|ES:Módulo_LDAP'); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("LDAPSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/ldap_groups.php b/htdocs/admin/ldap_groups.php index d431a7aaf30..5a4e36b7792 100644 --- a/htdocs/admin/ldap_groups.php +++ b/htdocs/admin/ldap_groups.php @@ -84,7 +84,7 @@ if ($action == 'setvalue' && $user->admin) */ llxHeader('',$langs->trans("LDAPSetup"),'EN:Module_LDAP_En|FR:Module_LDAP|ES:Módulo_LDAP'); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("LDAPSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/ldap_members.php b/htdocs/admin/ldap_members.php index 0dccb9967fe..a6385c9690c 100644 --- a/htdocs/admin/ldap_members.php +++ b/htdocs/admin/ldap_members.php @@ -112,7 +112,7 @@ if ($action == 'setvalue' && $user->admin) $form=new Form($db); llxHeader('',$langs->trans("LDAPSetup"),'EN:Module_LDAP_En|FR:Module_LDAP|ES:Módulo_LDAP'); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("LDAPSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/ldap_members_types.php b/htdocs/admin/ldap_members_types.php index 47286dc98da..2c08ec9fae8 100644 --- a/htdocs/admin/ldap_members_types.php +++ b/htdocs/admin/ldap_members_types.php @@ -83,7 +83,7 @@ if ($action == 'setvalue' && $user->admin) */ llxHeader('',$langs->trans("LDAPSetup"),'EN:Module_LDAP_En|FR:Module_LDAP|ES:Módulo_LDAP'); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("LDAPSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/ldap_users.php b/htdocs/admin/ldap_users.php index 8be03267783..a2e40abfed2 100644 --- a/htdocs/admin/ldap_users.php +++ b/htdocs/admin/ldap_users.php @@ -101,7 +101,7 @@ if ($action == 'setvalue' && $user->admin) $form=new Form($db); llxHeader('',$langs->trans("LDAPSetup"),'EN:Module_LDAP_En|FR:Module_LDAP|ES:Módulo_LDAP'); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("LDAPSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/livraison.php b/htdocs/admin/livraison.php index bff8d75069a..4c18069b415 100644 --- a/htdocs/admin/livraison.php +++ b/htdocs/admin/livraison.php @@ -184,7 +184,7 @@ llxHeader("",""); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print '
'; $head = expedition_admin_prepare_head(); diff --git a/htdocs/admin/livraison_extrafields.php b/htdocs/admin/livraison_extrafields.php index 5c9a17edc7a..5e9a6824899 100644 --- a/htdocs/admin/livraison_extrafields.php +++ b/htdocs/admin/livraison_extrafields.php @@ -72,7 +72,7 @@ $textobject=$langs->transnoentitiesnoconv("Receivings"); llxHeader('',$langs->trans("SendingsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/livraisondet_extrafields.php b/htdocs/admin/livraisondet_extrafields.php index 47e5b2eb975..f563100a326 100644 --- a/htdocs/admin/livraisondet_extrafields.php +++ b/htdocs/admin/livraisondet_extrafields.php @@ -72,7 +72,7 @@ $textobject=$langs->transnoentitiesnoconv("Receivings"); llxHeader('',$langs->trans("SendingsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SendingsSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/loan.php b/htdocs/admin/loan.php index 1a9ef385bf7..1b5fdafcbb2 100644 --- a/htdocs/admin/loan.php +++ b/htdocs/admin/loan.php @@ -79,7 +79,7 @@ llxHeader(); $form = new Form($db); if (! empty($conf->accounting->enabled)) $formaccounting = new FormAccounting($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('ConfigLoan'),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/mailing.php b/htdocs/admin/mailing.php index 972b7ee73a6..7abb16a0729 100644 --- a/htdocs/admin/mailing.php +++ b/htdocs/admin/mailing.php @@ -80,7 +80,7 @@ if ($action == 'setvalue') llxHeader('',$langs->trans("MailingSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("MailingSetup"),$linkback,'title_setup'); if (! empty($conf->use_javascript_ajax)) diff --git a/htdocs/admin/mailman.php b/htdocs/admin/mailman.php index 3048f519a86..89416a72e36 100644 --- a/htdocs/admin/mailman.php +++ b/htdocs/admin/mailman.php @@ -151,7 +151,7 @@ $help_url=''; llxHeader('',$langs->trans("MailmanSpipSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("MailmanSpipSetup"),$linkback,'title_setup'); $head = mailmanspip_admin_prepare_head(); diff --git a/htdocs/admin/mails.php b/htdocs/admin/mails.php index 4d96f80689e..28d243a3b13 100644 --- a/htdocs/admin/mails.php +++ b/htdocs/admin/mails.php @@ -79,6 +79,11 @@ if ($action == 'update' && empty($_POST["cancel"])) dolibarr_set_const($db, "MAIN_MAIL_SMTPS_PW", GETPOST("MAIN_MAIL_SMTPS_PW"), 'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_MAIL_EMAIL_TLS", GETPOST("MAIN_MAIL_EMAIL_TLS"),'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_MAIL_EMAIL_STARTTLS", GETPOST("MAIN_MAIL_EMAIL_STARTTLS"),'chaine',0,'',$conf->entity); + + dolibarr_set_const($db, "MAIN_MAIL_EMAIL_DKIM_ENABLED", GETPOST("MAIN_MAIL_EMAIL_DKIM_ENABLED"),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_MAIL_EMAIL_DKIM_DOMAIN", GETPOST("MAIN_MAIL_EMAIL_DKIM_DOMAIN"),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_MAIL_EMAIL_DKIM_SELECTOR", GETPOST("MAIN_MAIL_EMAIL_DKIM_SELECTOR"),'chaine',0,'',$conf->entity); + dolibarr_set_const($db, "MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY", GETPOST("MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY"),'chaine',0,'',$conf->entity); // Content parameters dolibarr_set_const($db, "MAIN_MAIL_EMAIL_FROM", GETPOST("MAIN_MAIL_EMAIL_FROM"), 'chaine',0,'',$conf->entity); dolibarr_set_const($db, "MAIN_MAIL_ERRORS_TO", GETPOST("MAIN_MAIL_ERRORS_TO"), 'chaine',0,'',$conf->entity); @@ -147,75 +152,100 @@ if ($action == 'edit') { if (jQuery("#MAIN_MAIL_SENDMODE").val()==\'mail\') { + console.log("I choose php mail mode"); jQuery(".drag").hide(); jQuery("#MAIN_MAIL_EMAIL_TLS").val(0); jQuery("#MAIN_MAIL_EMAIL_TLS").prop("disabled", true); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").val(0); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_ENABLED").val(0); + jQuery("#MAIN_MAIL_EMAIL_DKIM_ENABLED").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_DOMAIN").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_SELECTOR").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").prop("disabled", true); + jQuery(".dkim").hide(); '; if ($linuxlike) { print ' - jQuery("#MAIN_MAIL_SMTP_SERVER").hide(); - jQuery("#MAIN_MAIL_SMTP_PORT").hide(); - jQuery("#smtp_server_mess").show(); - jQuery("#smtp_port_mess").show(); - '; + jQuery("#MAIN_MAIL_SMTP_SERVER").hide(); + jQuery("#MAIN_MAIL_SMTP_PORT").hide(); + jQuery("#smtp_server_mess").show(); + jQuery("#smtp_port_mess").show();'; } else { - print ' - jQuery("#MAIN_MAIL_SMTP_SERVER").prop("disabled", true); - jQuery("#MAIN_MAIL_SMTP_PORT").prop("disabled", true); - jQuery("#smtp_server_mess").hide(); - jQuery("#smtp_port_mess").hide(); - '; - } - print ' + print ' + jQuery("#MAIN_MAIL_SMTP_SERVER").prop("disabled", true); + jQuery("#MAIN_MAIL_SMTP_PORT").prop("disabled", true); + jQuery("#smtp_server_mess").hide(); + jQuery("#smtp_port_mess").hide();'; + } + print ' } if (jQuery("#MAIN_MAIL_SENDMODE").val()==\'smtps\') { + console.log("I choose smtps mode"); jQuery(".drag").show(); jQuery("#MAIN_MAIL_EMAIL_TLS").val('.$conf->global->MAIN_MAIL_EMAIL_TLS.'); jQuery("#MAIN_MAIL_EMAIL_TLS").removeAttr("disabled"); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").val('.$conf->global->MAIN_MAIL_EMAIL_STARTTLS.'); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").removeAttr("disabled"); + jQuery("#MAIN_MAIL_EMAIL_DKIM_ENABLED").val(0); + jQuery("#MAIN_MAIL_EMAIL_DKIM_ENABLED").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_DOMAIN").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_SELECTOR").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").prop("disabled", true); + jQuery("#MAIN_MAIL_EMAIL_DKIM_DOMAIN").hide(); + jQuery("#MAIN_MAIL_EMAIL_DKIM_SELECTOR").hide(); + jQuery("#MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").hide(); jQuery("#MAIN_MAIL_SMTP_SERVER").removeAttr("disabled"); jQuery("#MAIN_MAIL_SMTP_PORT").removeAttr("disabled"); jQuery("#MAIN_MAIL_SMTP_SERVER").show(); jQuery("#MAIN_MAIL_SMTP_PORT").show(); jQuery("#smtp_server_mess").hide(); jQuery("#smtp_port_mess").hide(); + jQuery(".dkim").hide(); } if (jQuery("#MAIN_MAIL_SENDMODE").val()==\'swiftmailer\') { + console.log("I choose swiftmailer mode"); jQuery(".drag").show(); jQuery("#MAIN_MAIL_EMAIL_TLS").val('.$conf->global->MAIN_MAIL_EMAIL_TLS.'); jQuery("#MAIN_MAIL_EMAIL_TLS").removeAttr("disabled"); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").val('.$conf->global->MAIN_MAIL_EMAIL_STARTTLS.'); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").removeAttr("disabled"); + jQuery("#MAIN_MAIL_EMAIL_DKIM_ENABLED").val('.$conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED.'); + jQuery("#MAIN_MAIL_EMAIL_DKIM_ENABLED").removeAttr("disabled"); + jQuery("#MAIN_MAIL_EMAIL_DKIM_DOMAIN").removeAttr("disabled"); + jQuery("#MAIN_MAIL_EMAIL_DKIM_SELECTOR").removeAttr("disabled"); + jQuery("#MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").removeAttr("disabled"); + jQuery("#MAIN_MAIL_EMAIL_DKIM_DOMAIN").show(); + jQuery("#MAIN_MAIL_EMAIL_DKIM_SELECTOR").show(); + jQuery("#MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").show(); jQuery("#MAIN_MAIL_SMTP_SERVER").removeAttr("disabled"); jQuery("#MAIN_MAIL_SMTP_PORT").removeAttr("disabled"); jQuery("#MAIN_MAIL_SMTP_SERVER").show(); jQuery("#MAIN_MAIL_SMTP_PORT").show(); jQuery("#smtp_server_mess").hide(); jQuery("#smtp_port_mess").hide(); + jQuery(".dkim").show(); } } initfields(); jQuery("#MAIN_MAIL_SENDMODE").change(function() { initfields(); }); - jQuery("#MAIN_MAIL_EMAIL_TLS").change(function() { + jQuery("#MAIN_MAIL_EMAIL_TLS").change(function() { if (jQuery("#MAIN_MAIL_EMAIL_STARTTLS").val() == 1) jQuery("#MAIN_MAIL_EMAIL_STARTTLS").val(0); }); jQuery("#MAIN_MAIL_EMAIL_STARTTLS").change(function() { if (jQuery("#MAIN_MAIL_EMAIL_TLS").val() == 1) jQuery("#MAIN_MAIL_EMAIL_TLS").val(0); - }); + }); })'; - print ''."\n"; + print ''."\n"; } print ''; @@ -269,13 +299,12 @@ if ($action == 'edit') print ''; // Host server - print ''; if (! $conf->use_javascript_ajax && $linuxlike && $conf->global->MAIN_MAIL_SENDMODE == 'mail') { print $langs->trans("MAIN_MAIL_SMTP_SERVER_NotAvailableOnLinuxLike"); print ''; - print $langs->trans("SeeLocalSendMailSetup"); + print ''.$langs->trans("SeeLocalSendMailSetup").''; } else { @@ -289,7 +318,7 @@ if ($action == 'edit') { print ''; print ''; - print ''.$langs->trans("SeeLocalSendMailSetup").''; + print ''.$langs->trans("SeeLocalSendMailSetup").''; } else { @@ -308,7 +337,7 @@ if ($action == 'edit') { print $langs->trans("MAIN_MAIL_SMTP_PORT_NotAvailableOnLinuxLike"); print ''; - print $langs->trans("SeeLocalSendMailSetup"); + print ''.$langs->trans("SeeLocalSendMailSetup").''; } else { @@ -322,7 +351,7 @@ if ($action == 'edit') { print ''; print ''; - print ''.$langs->trans("SeeLocalSendMailSetup").''; + print ''.$langs->trans("SeeLocalSendMailSetup").''; } else { @@ -374,9 +403,9 @@ if ($action == 'edit') print ''; } - // TLS + // TLS - print ''.$langs->trans("MAIN_MAIL_EMAIL_TLS").''; + print ''.$langs->trans("MAIN_MAIL_EMAIL_TLS").''; if (! empty($conf->use_javascript_ajax) || (isset($conf->global->MAIN_MAIL_SENDMODE) && in_array($conf->global->MAIN_MAIL_SENDMODE, array('smtps', 'swiftmailer')))) { if (function_exists('openssl_open')) @@ -389,7 +418,6 @@ if ($action == 'edit') print ''; // STARTTLS - print ''.$langs->trans("MAIN_MAIL_EMAIL_STARTTLS").''; if (! empty($conf->use_javascript_ajax) || (isset($conf->global->MAIN_MAIL_SENDMODE) && in_array($conf->global->MAIN_MAIL_SENDMODE, array('smtps', 'swiftmailer')))) { @@ -402,8 +430,35 @@ if ($action == 'edit') else print yn(0).' ('.$langs->trans("NotSupported").')'; print ''; - // Separator + // DKIM + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_ENABLED").''; + if (! empty($conf->use_javascript_ajax) || (isset($conf->global->MAIN_MAIL_SENDMODE) && in_array($conf->global->MAIN_MAIL_SENDMODE, array('swiftmailer')))) + { + if (function_exists('openssl_open')) + { + print $form->selectyesno('MAIN_MAIL_EMAIL_DKIM_ENABLED',(! empty($conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED)?$conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED:0),1); + } + else print yn(0).' ('.$langs->trans("YourPHPDoesNotHaveSSLSupport").')'; + } + else print yn(0).' ('.$langs->trans("NotSupported").')'; + print ''; + // DKIM Domain + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_DOMAIN").''; + print ''; + + // DKIM Selector + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_SELECTOR").''; + print ''; + + // DKIM PRIVATE KEY + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").''; + print ''; + print ''; + + // Separator print ' '; // From @@ -505,7 +560,7 @@ else if ($linuxlike && (isset($conf->global->MAIN_MAIL_SENDMODE) && $conf->global->MAIN_MAIL_SENDMODE == 'mail')) { - print ''.$langs->trans("MAIN_MAIL_SMTP_SERVER_NotAvailableOnLinuxLike").''.$langs->trans("SeeLocalSendMailSetup").''; + print ''.$langs->trans("MAIN_MAIL_SMTP_SERVER_NotAvailableOnLinuxLike").''.$langs->trans("SeeLocalSendMailSetup").''; } else { @@ -516,7 +571,7 @@ else if ($linuxlike && (isset($conf->global->MAIN_MAIL_SENDMODE) && $conf->global->MAIN_MAIL_SENDMODE == 'mail')) { - print ''.$langs->trans("MAIN_MAIL_SMTP_PORT_NotAvailableOnLinuxLike").''.$langs->trans("SeeLocalSendMailSetup").''; + print ''.$langs->trans("MAIN_MAIL_SMTP_PORT_NotAvailableOnLinuxLike").''.$langs->trans("SeeLocalSendMailSetup").''; } else { @@ -548,7 +603,7 @@ else } else print yn(0).' ('.$langs->trans("YourPHPDoesNotHaveSSLSupport").')'; } - else print yn(0).' ('.$langs->trans("NotSupported").')'; + else print ''.yn(0).' ('.$langs->trans("NotSupported").')'; print ''; // STARTTLS @@ -562,10 +617,43 @@ else } else print yn(0).' ('.$langs->trans("YourPHPDoesNotHaveSSLSupport").')'; } - else print yn(0).' ('.$langs->trans("NotSupported").')'; + else print ''.yn(0).' ('.$langs->trans("NotSupported").')'; print ''; - // Separator + + if ($conf->global->MAIN_MAIL_SENDMODE == 'swiftmailer') + { + // DKIM + + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_ENABLED").''; + if (isset($conf->global->MAIN_MAIL_SENDMODE) && in_array($conf->global->MAIN_MAIL_SENDMODE, array('swiftmailer'))) + { + if (function_exists('openssl_open')) + { + print yn($conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED); + } + else print yn(0).' ('.$langs->trans("YourPHPDoesNotHaveSSLSupport").')'; + } + else print yn(0).' ('.$langs->trans("NotSupported").')'; + print ''; + + // Domain + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_DOMAIN").''; + print '' . $conf->global->MAIN_MAIL_EMAIL_DKIM_DOMAIN; + print ''; + + // Selector + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_SELECTOR").''; + print '' . $conf->global->MAIN_MAIL_EMAIL_DKIM_SELECTOR; + print ''; + + // PRIVATE KEY + print ''.$langs->trans("MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY").''; + print '' . $conf->global->MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY; + print ''; + } + + // Separator print ' '; @@ -664,7 +752,7 @@ else { if (function_exists('fsockopen') && $port && $server) { - print ''.$langs->trans("DoTestServerAvailability").''; + print ''.$langs->trans("DoTestServerAvailability").''; } } else @@ -672,11 +760,11 @@ else print ''.$langs->trans("DoTestServerAvailability").''; } - print ''.$langs->trans("DoTestSend").''; + print ''.$langs->trans("DoTestSend").''; if (! empty($conf->fckeditor->enabled)) { - print ''.$langs->trans("DoTestSendHTML").''; + print ''.$langs->trans("DoTestSendHTML").''; } print ''; @@ -753,7 +841,7 @@ else $formmail->fromalsorobot=1; $formmail->fromtype=(GETPOST('fromtype')?GETPOST('fromtype'):(!empty($conf->global->MAIN_MAIL_DEFAULT_FROMTYPE)?$conf->global->MAIN_MAIL_DEFAULT_FROMTYPE:'user')); $formmail->withfromreadonly=1; - $formmail->withsubstit=0; + $formmail->withsubstit=1; $formmail->withfrom=1; $formmail->witherrorsto=1; $formmail->withto=(! empty($_POST['sendto'])?$_POST['sendto']:($user->email?$user->email:1)); diff --git a/htdocs/admin/mails_templates.php b/htdocs/admin/mails_templates.php index 77f969181f4..89f3700c4f7 100644 --- a/htdocs/admin/mails_templates.php +++ b/htdocs/admin/mails_templates.php @@ -55,8 +55,7 @@ $search_lang=GETPOST('search_lang','alpha'); $search_fk_user=GETPOST('search_fk_user','intcomma'); $search_topic=GETPOST('search_topic','alpha'); -$allowed=1; -if (! $allowed) accessforbidden(); +if (! empty($user->socid)) accessforbidden(); $acts[0] = "activate"; $acts[1] = "disable"; @@ -91,17 +90,17 @@ $tabsqlsort[25]="label ASC, lang ASC, position ASC"; // Nom des champs en resultat de select pour affichage du dictionnaire $tabfield=array(); -$tabfield[25]= "label,type_template,lang,fk_user,private,position,topic,joinfiles,content"; +$tabfield[25]= "label,lang,type_template,fk_user,private,position,topic,joinfiles,content"; if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfield[25].=',content_lines'; // Nom des champs d'edition pour modification d'un enregistrement $tabfieldvalue=array(); -$tabfieldvalue[25]= "label,type_template,fk_user,lang,private,position,topic,joinfiles,content"; +$tabfieldvalue[25]= "label,lang,type_template,fk_user,private,position,topic,joinfiles,content"; if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfieldvalue[25].=',content_lines'; // Nom des champs dans la table pour insertion d'un enregistrement $tabfieldinsert=array(); -$tabfieldinsert[25]= "label,type_template,fk_user,lang,private,position,topic,joinfiles,content"; +$tabfieldinsert[25]= "label,lang,type_template,fk_user,private,position,topic,joinfiles,content"; if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) $tabfieldinsert[25].=',content_lines'; $tabfieldinsert[25].=',entity'; // Must be at end because not into other arrays @@ -168,6 +167,7 @@ if ($conf->fournisseur->enabled) $elementList['invoice_supplier_send']=$la if ($conf->societe->enabled) $elementList['thirdparty']=$langs->trans('MailToThirdparty'); if ($conf->adherent->enabled) $elementList['member']=$langs->trans('MailToMember'); if ($conf->contrat->enabled) $elementList['contract']=$langs->trans('MailToSendContract'); +if ($conf->projet->enabled) $elementList['project']=$langs->trans('MailToProject'); $elementList['user']=$langs->trans('MailToUser'); $elementList['all'] =$langs->trans('VisibleEverywhere'); $elementList['none']=$langs->trans('VisibleNowhere'); @@ -233,8 +233,7 @@ if (empty($reshook)) $ok=0; $fieldnamekey=$listfield[$f]; // We take translate key of field - if ($fieldnamekey == 'libelle' || ($fieldnamekey == 'label')) $fieldnamekey='Label'; - if ($fieldnamekey == 'libelle_facture') $fieldnamekey = 'LabelOnDocuments'; + if ($fieldnamekey == 'libelle' || ($fieldnamekey == 'label')) $fieldnamekey='Code'; if ($fieldnamekey == 'code') $fieldnamekey = 'Code'; if ($fieldnamekey == 'note') $fieldnamekey = 'Note'; if ($fieldnamekey == 'type_template') $fieldnamekey = 'TypeOfTemplate'; @@ -478,7 +477,7 @@ foreach ($fieldlist as $field => $value) if ($fieldlist[$field]=='lang') { $valuetoshow=(empty($conf->global->MAIN_MULTILANGS) ? ' ' : $langs->trans("Language")); } if ($fieldlist[$field]=='type') { $valuetoshow=$langs->trans("Type"); } if ($fieldlist[$field]=='code') { $valuetoshow=$langs->trans("Code"); } - if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Label"); } + if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Code"); } if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } if ($fieldlist[$field]=='private') { $align='center'; } if ($fieldlist[$field]=='position') { $align='center'; } @@ -522,7 +521,7 @@ $parameters = array( 'fieldlist' => $fieldlist, 'tabname' => $tabname[$id] ); -$reshook = $hookmanager->executeHooks('createDictionaryFieldlist', $parameters, $obj, $tmpaction); // Note that $action and $object may have been modified by some hooks +$reshook = $hookmanager->executeHooks('createEmailTemplateFieldlist', $parameters, $obj, $tmpaction); // Note that $action and $object may have been modified by some hooks $error = $hookmanager->error; $errors = $hookmanager->errors; @@ -646,7 +645,7 @@ if ($resql) elseif ($value == 'lang') { print ''; - print $formadmin->select_language($search_lang, 'search_lang', 0, null, 1); + print $formadmin->select_language($search_lang, 'search_lang', 0, null, 1, 0, 0, 'maxwidth150'); print ''; } elseif ($value == 'fk_user') @@ -661,7 +660,7 @@ if ($resql) elseif ($value == 'topic') print ''; elseif ($value == 'type_template') { - print ''.$form->selectarray('search_type_template', $elementList, $search_type_template, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth100onsmartphone').''; + print ''.$form->selectarray('search_type_template', $elementList, $search_type_template, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth200 maxwidth100onsmartphone').''; } elseif (! in_array($value, array('content', 'content_lines'))) print ''; } @@ -695,7 +694,7 @@ if ($resql) if ($fieldlist[$field]=='fk_user') { $valuetoshow=$langs->trans("Owner"); } if ($fieldlist[$field]=='lang') { $valuetoshow=$langs->trans("Language"); } if ($fieldlist[$field]=='type') { $valuetoshow=$langs->trans("Type"); } - if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Label"); } + if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Code"); } if ($fieldlist[$field]=='type_template') { $valuetoshow=$langs->trans("TypeOfTemplate"); } if ($fieldlist[$field]=='private') { $align='center'; } if ($fieldlist[$field]=='position') { $align='center'; } @@ -733,7 +732,7 @@ if ($resql) { $tmpaction='edit'; $parameters=array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); - $reshook=$hookmanager->executeHooks('editDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('editEmailTemplateFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks $error=$hookmanager->error; $errors=$hookmanager->errors; // Show fields @@ -777,6 +776,7 @@ if ($resql) } if ($tmpfieldlist == 'content') { + print $form->textwithpicto($langs->trans("Content"), $tabhelp[$id][$tmpfieldlist], 1, 'help', '', 0, 2, $tmpfieldlist) . '
'; $okforextended = true; if (empty($conf->global->FCKEDITOR_ENABLE_MAIL)) $okforextended = false; $doleditor = new DolEditor($tmpfieldlist.'-'.$rowid, (! empty($obj->{$tmpfieldlist}) ? $obj->{$tmpfieldlist} : ''), '', 140, 'dolibarr_mailings', 'In', 0, false, $okforextended, ROWS_6, '90%'); @@ -792,7 +792,7 @@ if ($resql) { $tmpaction = 'view'; $parameters=array('var'=>$var, 'fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); - $reshook=$hookmanager->executeHooks('viewDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('viewEmailTemplateFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks $error=$hookmanager->error; $errors=$hookmanager->errors; @@ -867,7 +867,7 @@ if ($resql) print ""; // Modify link / Delete link - print ''; + print ''; if ($canbemodified) print ''.img_edit().''; if ($iserasable) { @@ -990,7 +990,7 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='') { $selectedlang = $langs->defaultlang; if ($context == 'edit') $selectedlang = $obj->{$fieldlist[$field]}; - print $formadmin->select_language($selectedlang, 'langcode', 0, null, 1); + print $formadmin->select_language($selectedlang, 'langcode', 0, null, 1, 0, 0, 'maxwidth150'); } else { @@ -1016,7 +1016,7 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='') } else { - print $form->selectarray('type_template', $elementList, (! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:''), 1); + print $form->selectarray('type_template', $elementList, (! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:''), 1, 0, 0, '', 0, 0, 0, '', 'maxwidth200'); } print ''; } diff --git a/htdocs/admin/modulehelp.php b/htdocs/admin/modulehelp.php index bc48feb076f..dd90774a05b 100644 --- a/htdocs/admin/modulehelp.php +++ b/htdocs/admin/modulehelp.php @@ -163,8 +163,6 @@ foreach ($modulesdir as $dir) $modules[$i] = $objMod; $filename[$i]= $modName; - $special = $objMod->special; - // Gives the possibility to the module, to provide his own family info and position of this family if (is_array($objMod->familyinfo) && !empty($objMod->familyinfo)) { if (!is_array($familyinfo)) $familyinfo=array(); @@ -180,13 +178,11 @@ foreach ($modulesdir as $dir) $moduleposition = 800; } - if ($special == 1) $familykey='interface'; - $orders[$i] = $familyinfo[$familykey]['position']."_".$familykey."_".$moduleposition."_".$j; // Sort by family, then by module position then number $dirmod[$i] = $dir; //print $i.'-'.$dirmod[$i].'
'; // Set categ[$i] - $specialstring = isset($specialtostring[$special])?$specialtostring[$special]:'unknown'; + $specialstring = 'unknown'; if ($objMod->version == 'development' || $objMod->version == 'experimental') $specialstring='expdev'; if (isset($categ[$specialstring])) $categ[$specialstring]++; // Array of all different modules categories else $categ[$specialstring]=1; @@ -240,7 +236,6 @@ foreach($orders as $tmpkey => $tmpvalue) $i++; } $value = $orders[$key]; -$special = $objMod->special; $tab=explode('_',$value); $familyposition=$tab[0]; $familykey=$tab[1]; $module_position=$tab[2]; $numero=$tab[3]; diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index ee17e647863..22925f09a3e 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -79,9 +79,9 @@ $familyinfo=array( $param=''; if ($search_keyword) $param.='&search_keyword='.urlencode($search_keyword); -if ($search_status > -1) $param.='&search_status='.urlencode($search_status); -if ($search_nature > -1) $param.='&search_nature='.urlencode($search_nature); -if ($search_version > -1) $param.='&search_version='.urlencode($search_version); +if ($search_status && $search_status != '-1') $param.='&search_status='.urlencode($search_status); +if ($search_nature && $search_nature != '-1') $param.='&search_nature='.urlencode($search_nature); +if ($search_version && $search_version != '-1') $param.='&search_version='.urlencode($search_version); $dirins=DOL_DOCUMENT_ROOT.'/custom'; $urldolibarrmodules='https://www.dolistore.com/'; @@ -358,8 +358,6 @@ foreach ($modulesdir as $dir) $filename[$i]= $modName; $modules[$modName] = $objMod; - $special = $objMod->special; - // Gives the possibility to the module, to provide his own family info and position of this family if (is_array($objMod->familyinfo) && !empty($objMod->familyinfo)) { $familyinfo = array_merge($familyinfo, $objMod->familyinfo); @@ -374,8 +372,6 @@ foreach ($modulesdir as $dir) $moduleposition = 800; } - if ($special == 1) $familykey='interface'; - // Add list of warnings to show into arrayofwarnings and arrayofwarningsext if (! empty($objMod->warnings_activation)) { @@ -390,7 +386,7 @@ foreach ($modulesdir as $dir) $dirmod[$i] = $dir; //print $i.'-'.$dirmod[$i].'
'; // Set categ[$i] - $specialstring = isset($specialtostring[$special])?$specialtostring[$special]:'unknown'; + $specialstring = 'unknown'; if ($objMod->version == 'development' || $objMod->version == 'experimental') $specialstring='expdev'; if (isset($categ[$specialstring])) $categ[$specialstring]++; // Array of all different modules categories else $categ[$specialstring]=1; @@ -466,7 +462,7 @@ if ($mode == 'common') { dol_set_focus('#search_keyword'); - print ''; + print ''; if ($optioncss != '') print ''; print ''; print ''; @@ -531,12 +527,9 @@ if ($mode == 'common') $objMod = $modules[$modName]; $dirofmodule = $dirmod[$key]; - $special = $objMod->special; - - //print $objMod->name." - ".$key." - ".$objMod->special.' - '.$objMod->version."
"; + //print $objMod->name." - ".$key." - ".$objMod->version."
"; //if (($mode != (isset($specialtostring[$special])?$specialtostring[$special]:'unknown') && $mode != 'expdev') - if (($special >= 4 && $mode != 'expdev') - || ($mode == 'expdev' && $objMod->version != 'development' && $objMod->version != 'experimental')) continue; // Discard if not for current tab + if ($mode == 'expdev' && $objMod->version != 'development' && $objMod->version != 'experimental') continue; // Discard if not for current tab if (! $objMod->getName()) { @@ -741,12 +734,12 @@ if ($mode == 'common') if (preg_match('/^([^@]+)@([^@]+)$/i',$urlpage,$regs)) { $urltouse=dol_buildpath('/'.$regs[2].'/admin/'.$regs[1],1); - print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; + print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; } else { $urltouse=$urlpage; - print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; + print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; } } } @@ -754,11 +747,11 @@ if ($mode == 'common') } else if (preg_match('/^([^@]+)@([^@]+)$/i',$objMod->config_page_url,$regs)) { - print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; + print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; } else { - print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; + print ''.img_picto($langs->trans("Setup"),"setup",'style="padding-right: 6px"').''; } } else diff --git a/htdocs/admin/multicurrency.php b/htdocs/admin/multicurrency.php index 52ac09074f3..25feceda0d1 100644 --- a/htdocs/admin/multicurrency.php +++ b/htdocs/admin/multicurrency.php @@ -179,7 +179,7 @@ $page_name = "MultiCurrencySetup"; llxHeader('', $langs->trans($page_name)); // Subheader -$linkback = '' . $langs->trans("BackToModuleList") . ''; +$linkback = '' . $langs->trans("BackToModuleList") . ''; print_fiche_titre($langs->trans($page_name), $linkback); // Configuration header diff --git a/htdocs/admin/notification.php b/htdocs/admin/notification.php index 3a342052c6d..5f57ac99efc 100644 --- a/htdocs/admin/notification.php +++ b/htdocs/admin/notification.php @@ -115,7 +115,7 @@ $notify = new Notify($db); llxHeader('',$langs->trans("NotificationSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("NotificationSetup"),$linkback,'title_setup'); print $langs->trans("NotificationsDesc").'
'; diff --git a/htdocs/admin/oauth.php b/htdocs/admin/oauth.php index 0733779a91a..99ade75fdfe 100644 --- a/htdocs/admin/oauth.php +++ b/htdocs/admin/oauth.php @@ -77,7 +77,7 @@ llxHeader(); $form = new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('ConfigOAuth'),$linkback,'title_setup'); print ''; @@ -95,6 +95,7 @@ print ''; $i=0; +// $list is defined into oauth.lib.php foreach ($list as $key) { $supported=0; diff --git a/htdocs/admin/oauthlogintokens.php b/htdocs/admin/oauthlogintokens.php index d2a7232a4fe..97b774edf80 100644 --- a/htdocs/admin/oauthlogintokens.php +++ b/htdocs/admin/oauthlogintokens.php @@ -113,7 +113,7 @@ $form = new Form($db); llxHeader('',$langs->trans("PrintingSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('ConfigOAuth'),$linkback,'title_setup'); $head=oauthadmin_prepare_head($mode); @@ -141,13 +141,27 @@ if ($mode == 'setup' && $user->admin) $urltodelete=$urlwithroot.'/core/modules/oauth/github_oauthcallback.php?action=delete&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php'); $urltocheckperms='https://github.com/settings/applications/'; } - if ($key[0] == 'OAUTH_GOOGLE_NAME') + elseif ($key[0] == 'OAUTH_GOOGLE_NAME') { $OAUTH_SERVICENAME='Google'; $urltorenew=$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?state=userinfo_email,userinfo_profile,cloud_print&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php'); $urltodelete=$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?action=delete&backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php'); $urltocheckperms='https://security.google.com/settings/security/permissions'; } + elseif ($key[0] == 'OAUTH_STRIPE_TEST_NAME') + { + $OAUTH_SERVICENAME='StripeTest'; + $urltorenew=$urlwithroot.'/core/modules/oauth/stripetest_oauthcallback.php?backtourl='.urlencode(DOL_URL_ROOT.'/admin/oauthlogintokens.php'); + $urltodelete=''; + $urltocheckperms=''; + } + else + { + $urltorenew=''; + $urltodelete=''; + $urltocheckperms=''; + } + // Show value of token $tokenobj=null; @@ -204,7 +218,6 @@ if ($mode == 'setup' && $user->admin) print '
'."\n"; - $var=false; print ''; print ''; print ''; @@ -222,7 +235,6 @@ if ($mode == 'setup' && $user->admin) print ''; print ''."\n"; - $var = ! $var; print ''; print ''; //var_dump($key); @@ -237,19 +249,21 @@ if ($mode == 'setup' && $user->admin) if (is_object($tokenobj)) { //test on $storage->hasAccessToken($OAUTH_SERVICENAME) ? - print ''.$langs->trans('DeleteAccess').'

'; + print ''.$langs->trans('DeleteAccess').'
'; } // Request remote token - print ''.$langs->trans('RequestAccess').'

'; + if ($urltorenew) + { + print ''.$langs->trans('RequestAccess').'
'; + } // Check remote access if ($urltocheckperms) { - print $langs->trans("ToCheckDeleteTokenOnProvider", $OAUTH_SERVICENAME).': '.$urltocheckperms.''; + print '
'.$langs->trans("ToCheckDeleteTokenOnProvider", $OAUTH_SERVICENAME).': '.$urltocheckperms.''; } print ''; print '
'; - $var = ! $var; print ''; print ''; //var_dump($key); @@ -272,7 +286,6 @@ if ($mode == 'setup' && $user->admin) if (is_object($tokenobj)) { // Token refresh - $var = ! $var; print ''; print ''; //var_dump($key); @@ -283,7 +296,6 @@ if ($mode == 'setup' && $user->admin) print ''; // Token expired - $var = ! $var; print ''; print ''; //var_dump($key); @@ -294,7 +306,6 @@ if ($mode == 'setup' && $user->admin) print ''; // Token expired at - $var = ! $var; print ''; print ''; //var_dump($key); @@ -354,7 +365,6 @@ if ($mode == 'userconf' && $user->admin) print $langs->trans('PrintUserConfDesc'.$driver)."

\n"; print '
'.$langs->trans($key[0]).'
'; - $var=true; print ''; print ''; print ''; diff --git a/htdocs/admin/order_extrafields.php b/htdocs/admin/order_extrafields.php index c2ecb2a5bc0..0019f7ae8c3 100644 --- a/htdocs/admin/order_extrafields.php +++ b/htdocs/admin/order_extrafields.php @@ -69,7 +69,7 @@ $textobject=$langs->transnoentitiesnoconv("Orders"); llxHeader('',$langs->trans("OrdersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("OrdersSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/orderdet_extrafields.php b/htdocs/admin/orderdet_extrafields.php index db86f7ba2fe..37c83f5dfe0 100644 --- a/htdocs/admin/orderdet_extrafields.php +++ b/htdocs/admin/orderdet_extrafields.php @@ -70,7 +70,7 @@ $textobject=$langs->transnoentitiesnoconv("Orders"); llxHeader('',$langs->trans("OrdersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("OrdersSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/payment.php b/htdocs/admin/payment.php index 3246caeb411..339b4fef566 100644 --- a/htdocs/admin/payment.php +++ b/htdocs/admin/payment.php @@ -100,7 +100,7 @@ llxHeader("",$langs->trans("BillsSetup"),'EN:Invoice_Configuration|FR:Configurat $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup'); $head = invoice_admin_prepare_head(); @@ -257,17 +257,6 @@ print $form->selectyesno("FACTURE_PAYMENTS_ON_DIFFERENT_THIRDPARTIES_BILLS",$con print '\n"; -// Add js auto fill amount on paiement form -/* always on now -$var=! $var; -print '\n"; -*/ - print '
'.$langs->trans("User").''.$langs->trans("PrintModule").''; print "
'; -print $langs->trans("JSOnPaimentBill"); -print ''; -print $form->selectyesno("INVOICE_AUTO_FILLJS",$conf->global->INVOICE_AUTO_FILLJS,1); -print ''; -print "
'; print '
'; diff --git a/htdocs/admin/pdf.php b/htdocs/admin/pdf.php index 8028c81cf04..2f1ae179948 100644 --- a/htdocs/admin/pdf.php +++ b/htdocs/admin/pdf.php @@ -611,18 +611,6 @@ else // Show print ''.$langs->trans("Value").''."\n"; print "\n"; - if (! empty($dolibarr_pdf_force_fpdf)) - { - - print ''."\n"; - print 'dolibarr_pdf_force_fpdf'."\n"; - print ''; - print $dolibarr_pdf_force_fpdf; - print ''; - print ''; - } - - print ''."\n"; print ''.$langs->trans("LibraryToBuildPDF").''."\n"; print ''; @@ -663,11 +651,6 @@ else // Show print "\n"; print ''; - if (! empty($dolibarr_pdf_force_fpdf)) - { - print info_admin($langs->trans("WarningUsingFPDF")).'
'; - } - print '
'; print ''.$langs->trans("Modify").''; print '
'; diff --git a/htdocs/admin/prelevement.php b/htdocs/admin/prelevement.php index aa2effb32e2..602b80c3855 100644 --- a/htdocs/admin/prelevement.php +++ b/htdocs/admin/prelevement.php @@ -187,7 +187,7 @@ $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); llxHeader('',$langs->trans("WithdrawalsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("WithdrawalsSetup"),$linkback,'title_setup'); print '
'; diff --git a/htdocs/admin/propal.php b/htdocs/admin/propal.php index 8d09e81e142..c533ea94795 100644 --- a/htdocs/admin/propal.php +++ b/htdocs/admin/propal.php @@ -234,7 +234,7 @@ llxHeader('',$langs->trans("PropalSetup")); //if ($mesg) print $mesg; -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("PropalSetup"),$linkback,'title_setup'); $head = propal_admin_prepare_head(); @@ -631,7 +631,7 @@ print "\n"; print " ".$langs->trans("Name")."\n"; print " ".$langs->trans("Value")."\n"; print "\n"; -print "\n ".$langs->trans("PathDirectory")."\n ".$conf->propal->dir_output."\n\n"; +print "\n ".$langs->trans("PathDirectory")."\n ".$conf->propal->multidir_output[$conf->entity]."\n\n"; print "\n
"; diff --git a/htdocs/admin/receiptprinter.php b/htdocs/admin/receiptprinter.php index 1bac9d80bee..0de8987e277 100644 --- a/htdocs/admin/receiptprinter.php +++ b/htdocs/admin/receiptprinter.php @@ -212,7 +212,7 @@ $form = new Form($db); llxHeader('',$langs->trans("ReceiptPrinterSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ReceiptPrinterSetup"),$linkback,'title_setup'); $head = receiptprinteradmin_prepare_head($mode); diff --git a/htdocs/admin/resource.php b/htdocs/admin/resource.php index 68cc1172bdd..6560d194ca7 100644 --- a/htdocs/admin/resource.php +++ b/htdocs/admin/resource.php @@ -65,7 +65,7 @@ llxHeader('',$langs->trans('ResourceSetup')); $form = new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('ResourceSetup'),$linkback,'title_setup'); $head=resource_admin_prepare_head(); diff --git a/htdocs/admin/resource_extrafields.php b/htdocs/admin/resource_extrafields.php index 3d08364a1ed..e77ebc19902 100644 --- a/htdocs/admin/resource_extrafields.php +++ b/htdocs/admin/resource_extrafields.php @@ -68,7 +68,7 @@ $textobject=$langs->transnoentitiesnoconv("ResourceSingular"); llxHeader('',$langs->trans("ResourceSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ResourceSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/salaries.php b/htdocs/admin/salaries.php index f5a3624939d..7d3c0eaad77 100644 --- a/htdocs/admin/salaries.php +++ b/htdocs/admin/salaries.php @@ -77,7 +77,7 @@ llxHeader('',$langs->trans('SalariesSetup')); $form = new Form($db); if (! empty($conf->accounting->enabled)) $formaccounting = New FormAccounting($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('SalariesSetup'),$linkback,'title_setup'); print ''; diff --git a/htdocs/admin/spip.php b/htdocs/admin/spip.php index 12fc15823bd..1452ea572d0 100644 --- a/htdocs/admin/spip.php +++ b/htdocs/admin/spip.php @@ -111,7 +111,7 @@ $help_url=''; llxHeader('',$langs->trans("MailmanSpipSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("MailmanSpipSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/stock.php b/htdocs/admin/stock.php index a2f0a4b5346..e7c77f5955f 100644 --- a/htdocs/admin/stock.php +++ b/htdocs/admin/stock.php @@ -136,7 +136,7 @@ if($action) llxHeader('',$langs->trans("StockSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("StockSetup"),$linkback,'title_setup'); $form=new Form($db); diff --git a/htdocs/admin/supplier_invoice.php b/htdocs/admin/supplier_invoice.php index a0b8d9e2e4f..1532ca0a89d 100644 --- a/htdocs/admin/supplier_invoice.php +++ b/htdocs/admin/supplier_invoice.php @@ -199,7 +199,7 @@ llxHeader("",""); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SuppliersSetup"),$linkback,'title_setup'); print "
"; diff --git a/htdocs/admin/supplier_order.php b/htdocs/admin/supplier_order.php index a9dfe95a2c3..b86b674e9ad 100644 --- a/htdocs/admin/supplier_order.php +++ b/htdocs/admin/supplier_order.php @@ -228,7 +228,7 @@ $dirmodels=array_merge(array('/'),(array) $conf->modules_parts['models']); llxHeader("",""); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SuppliersSetup"),$linkback,'title_setup'); print "
"; diff --git a/htdocs/admin/supplier_payment.php b/htdocs/admin/supplier_payment.php index c83b60cab63..86cd8d02207 100644 --- a/htdocs/admin/supplier_payment.php +++ b/htdocs/admin/supplier_payment.php @@ -159,7 +159,7 @@ llxHeader("",$langs->trans("SupplierPaymentSetup"),'EN:Supplier_Payment_Configur $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SupplierPaymentSetup"),$linkback,'title_setup'); print "
"; diff --git a/htdocs/admin/supplier_proposal.php b/htdocs/admin/supplier_proposal.php index 862b90e179f..0c389f6b4c2 100644 --- a/htdocs/admin/supplier_proposal.php +++ b/htdocs/admin/supplier_proposal.php @@ -216,7 +216,7 @@ $form=new Form($db); //if ($mesg) print $mesg; -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SupplierProposalSetup"),$linkback,'title_setup'); $head = supplier_proposal_admin_prepare_head(); diff --git a/htdocs/admin/supplierinvoice_extrafields.php b/htdocs/admin/supplierinvoice_extrafields.php index ad04e1f5256..8ed25fbd036 100644 --- a/htdocs/admin/supplierinvoice_extrafields.php +++ b/htdocs/admin/supplierinvoice_extrafields.php @@ -73,7 +73,7 @@ $textobject=$langs->transnoentitiesnoconv("BillsSuppliers"); llxHeader('',$langs->trans("SuppliersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SuppliersSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/supplierinvoicedet_extrafields.php b/htdocs/admin/supplierinvoicedet_extrafields.php index 469a00732dd..8fd202076d1 100644 --- a/htdocs/admin/supplierinvoicedet_extrafields.php +++ b/htdocs/admin/supplierinvoicedet_extrafields.php @@ -73,7 +73,7 @@ $textobject=$langs->transnoentitiesnoconv("BillsSuppliers"); llxHeader('',$langs->trans("SuppliersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SuppliersSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/supplierorder_extrafields.php b/htdocs/admin/supplierorder_extrafields.php index 55ca530e27f..a95650c7524 100644 --- a/htdocs/admin/supplierorder_extrafields.php +++ b/htdocs/admin/supplierorder_extrafields.php @@ -69,7 +69,7 @@ $textobject=$langs->transnoentitiesnoconv("SuppliersOrders"); llxHeader('',$langs->trans("SuppliersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SuppliersSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/supplierorderdet_extrafields.php b/htdocs/admin/supplierorderdet_extrafields.php index 55491385027..a6f2a75952a 100644 --- a/htdocs/admin/supplierorderdet_extrafields.php +++ b/htdocs/admin/supplierorderdet_extrafields.php @@ -70,7 +70,7 @@ $textobject=$langs->transnoentitiesnoconv("SuppliersOrders"); llxHeader('',$langs->trans("SuppliersSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SuppliersSetup"),$linkback,'title_setup'); print "
\n"; diff --git a/htdocs/admin/syslog.php b/htdocs/admin/syslog.php index 75de985728c..e434f669f45 100644 --- a/htdocs/admin/syslog.php +++ b/htdocs/admin/syslog.php @@ -146,6 +146,16 @@ if ($action == 'setlevel') dol_syslog("admin/syslog: level ".$level); if (! $res > 0) $error++; + + if (! $error) + { + $file_saves = GETPOST("file_saves"); + $res = dolibarr_set_const($db,"SYSLOG_FILE_SAVES",$file_saves,'chaine',0,'',0); + dol_syslog("admin/syslog: file saves ".$file_saves); + + if (! $res > 0) $error++; + } + if (! $error) { setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); @@ -164,7 +174,7 @@ llxHeader(); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SyslogSetup"),$linkback,'title_setup'); print '
'; @@ -284,6 +294,13 @@ print ''; print ''; print ''; + +if(! empty($conf->loghandlers['mod_syslog_file']) && ! empty($conf->cron->enabled)) { + print ''.$langs->trans("SyslogFileNumberOfSaves").''; + print ''; + print ' ('.$langs->trans('ConfigureCleaningCronjobToSetFrequencyOfSaves').')'; +} + print ''; print "\n"; diff --git a/htdocs/admin/system/dbtable.php b/htdocs/admin/system/dbtable.php index 19fa3fc1abd..d6e05ed2e72 100644 --- a/htdocs/admin/system/dbtable.php +++ b/htdocs/admin/system/dbtable.php @@ -75,7 +75,7 @@ else } } - if ($base == 1) + if ($base == 1) // mysql { $link=array(); $cons = explode(";", $row[14]); @@ -97,11 +97,19 @@ else // var_dump($link); - print ''; - print ''; - print ''; + print '
'.$langs->trans("Fields").''.$langs->trans("Type").''.$langs->trans("Index").''.$langs->trans("FieldsLinked").'
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; - $sql = "DESCRIBE ".$table; + //$sql = "DESCRIBE ".$table; + $sql = "SHOW FULL COLUMNS IN ".$db->escape($table); + $resql = $db->query($sql); if ($resql) { @@ -111,12 +119,18 @@ else { $row = $db->fetch_row($resql); print ''; - print ""; - print ""; - print ""; + print ""; + print ""; + print ""; + print ""; + print ""; + print ""; + print ""; + print ""; - + + print ''; print ''; $i++; } diff --git a/htdocs/admin/system/dolibarr.php b/htdocs/admin/system/dolibarr.php index c36db521f70..c0a1c610fe6 100644 --- a/htdocs/admin/system/dolibarr.php +++ b/htdocs/admin/system/dolibarr.php @@ -116,7 +116,7 @@ if (function_exists('curl_init')) } else { - print $langs->trans("LastStableVersion").' : ' .$langs->trans("Check").''; + print $langs->trans("LastStableVersion").' : ' .$langs->trans("Check").''; } } @@ -220,7 +220,6 @@ print ''; + if (! $conf->use_javascript_ajax) + { + print '"; + } + else + { + print '"; + } + print ''; - print '
'; - print load_fiche_titre($langs->trans("SummaryOfVatExigibilityUsedByDefault"),'',''); - //print ' ('.$langs->trans("CanBeChangedWhenMakingInvoice").')'; + print '
'.$langs->trans("Fields").''.$langs->trans("Type").''.$langs->trans("Index").''.$langs->trans("FieldsLinked").'
$row[0]$row[1]$row[3]".$row[0]."".$row[1]."".$row[3]."".(empty($row[4])?'':$row[4])."".(empty($row[5])?'':$row[5])."".(empty($row[6])?'':$row[6])."".(empty($row[7])?'':$row[7])."".(isset($link[$row[0]][0])?$link[$row[0]][0]:'')."."; print (isset($link[$row[0]][1])?$link[$row[0]][1]:'')."
  => price(1234.56)'.price(1234.56) $txt =$langs->trans("OSTZ").' (variable system TZ): '.(! empty($_ENV["TZ"])?$_ENV["TZ"]:$langs->trans("NotDefined")).'
'."\n"; $txt.=$langs->trans("PHPTZ").' (php.ini date.timezone): '.(ini_get("date.timezone")?ini_get("date.timezone"):$langs->trans("NotDefined")).''."
\n"; // date.timezone must be in valued defined in http://fr3.php.net/manual/en/timezones.europe.php $txt.=$langs->trans("Dolibarr constant MAIN_SERVER_TZ").': '.(empty($conf->global->MAIN_SERVER_TZ)?$langs->trans("NotDefined"):$conf->global->MAIN_SERVER_TZ); -//$txt.=$langs->trans("YouCanEditPHPTZ"); // deprecated print '
'.$langs->trans("CurrentTimeZone").''; // Timezone server PHP $a=getServerTimeZoneInt('now'); $b=getServerTimeZoneInt('winter'); @@ -325,13 +324,14 @@ $configfileparameters=array( '?dolibarr_font_DOL_DEFAULT_TTF_BOLD' => 'dolibarr_font_DOL_DEFAULT_TTF_BOLD', 'separator4' => '', 'dolibarr_main_prod' => 'Production mode (Hide all error messages)', + 'dolibarr_main_restrict_os_commands' => 'Restrict CLI commands for backups', + 'dolibarr_main_restrict_ip' => 'Restrict access to some IPs only', '?dolibarr_mailing_limit_sendbyweb' => 'Limit nb of email sent by page', '?dolibarr_mailing_limit_sendbycli' => 'Limit nb of email sent by cli', - '?dolibarr_strict_mode' => 'Strict mode is on/off', - '?dolibarr_pdf_force_fpdf' => 'Force fpdf usage to generate PDF' + '?dolibarr_strict_mode' => 'Strict mode is on/off', + '?dolibarr_nocsrfcheck' => 'Disable CSRF security checks' ); -$var=true; print '
'; print ''; print ''; @@ -441,8 +441,8 @@ if ($resql) $obj = $db->fetch_object($resql); print ''; - print ''."\n"; - print ''."\n"; + print ''."\n"; + print ''."\n"; if (empty($conf->multicompany->enabled) || !$user->entity) print ''."\n"; // If superadmin or multicompany disabled print "\n"; diff --git a/htdocs/admin/system/filecheck.php b/htdocs/admin/system/filecheck.php index 60c20dfafe7..6fd623bf1bb 100644 --- a/htdocs/admin/system/filecheck.php +++ b/htdocs/admin/system/filecheck.php @@ -3,6 +3,7 @@ * Copyright (C) 2007 Rodolphe Quiedeville * Copyright (C) 2007-2012 Regis Houssin * Copyright (C) 2015 Frederic France + * Copyright (C) 2017 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +40,8 @@ $error=0; * View */ +@set_time_limit(300); + llxHeader(); print load_fiche_titre($langs->trans("FileCheckDolibarr"),'','title_setup'); @@ -140,7 +143,7 @@ if (GETPOST('target') == 'remote') if (! $xmlarray['curl_error_no'] && $xmlarray['http_code'] != '404') { $xmlfile = $xmlarray['content']; - //print "eee".$xmlfile."eee"; + //print "xmlfilestart".$xmlfile."xmlfileend"; $xml = simplexml_load_string($xmlfile); } else diff --git a/htdocs/admin/taxes.php b/htdocs/admin/taxes.php index a8f98f02c3b..0374e3c8e42 100644 --- a/htdocs/admin/taxes.php +++ b/htdocs/admin/taxes.php @@ -3,7 +3,7 @@ * Copyright (C) 2004-2008 Laurent Destailleur * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2011-2013 Juanjo Menent - * Copyright (C) 2015 Alexandre Spangaro + * Copyright (C) 2015-2018 Alexandre Spangaro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,15 +30,15 @@ require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT . '/core/class/html.formaccounting.class.php'; $langs->load('admin'); +$langs->load('objects'); +$langs->load("companies"); +$langs->load("products"); if (!$user->admin) accessforbidden(); $action = GETPOST('action','alpha'); -// Other parameters ACCOUNTING_* -$list = array ( - 'ACCOUNTING_VAT_PAY_ACCOUNT' -); + /* * Actions @@ -59,175 +59,206 @@ $list = array ( $tax_mode = empty($conf->global->TAX_MODE)?0:$conf->global->TAX_MODE; if ($action == 'update') { - $error = 0; + $error = 0; // Tax mode $tax_mode = GETPOST('tax_mode','alpha'); - $db->begin(); + $db->begin(); - $res = dolibarr_set_const($db, 'TAX_MODE', $tax_mode,'chaine',0,'',$conf->entity); - if (! $res > 0) $error++; + $res = dolibarr_set_const($db, 'TAX_MODE', $tax_mode,'chaine',0,'',$conf->entity); + if (! $res > 0) $error++; - switch ($tax_mode) - { - case 0: - $value = 'payment'; - break; - case 1: - $value = 'invoice'; - break; - } + switch ($tax_mode) + { + case 0: + $valuesellproduct = 'invoice'; + $valuebuyproduct = 'invoice'; + $valuesellservice = 'payment'; + $valuebuyservice = 'payment'; + break; + case 1: + $valuesellproduct = 'invoice'; + $valuebuyproduct = 'invoice'; + $valuesellservice = 'invoice'; + $valuebuyservice = 'invoice'; + break; + case 2: + $valuesellproduct = 'payment'; + $valuebuyproduct = 'payment'; + $valuesellservice = 'payment'; + $valuebuyservice = 'payment'; + break; + } - $res = dolibarr_set_const($db, 'TAX_MODE_SELL_PRODUCT', 'invoice','chaine',0,'',$conf->entity); - if (! $res > 0) $error++; - $res = dolibarr_set_const($db, 'TAX_MODE_BUY_PRODUCT', 'invoice','chaine',0,'',$conf->entity); - if (! $res > 0) $error++; - $res = dolibarr_set_const($db, 'TAX_MODE_SELL_SERVICE', $value,'chaine',0,'',$conf->entity); - if (! $res > 0) $error++; - $res = dolibarr_set_const($db, 'TAX_MODE_BUY_SERVICE', $value,'chaine',0,'',$conf->entity); - if (! $res > 0) $error++; + $res = dolibarr_set_const($db, 'TAX_MODE_SELL_PRODUCT', $valuesellproduct,'chaine',0,'',$conf->entity); + if (! $res > 0) $error++; + $res = dolibarr_set_const($db, 'TAX_MODE_BUY_PRODUCT', $valuebuyproduct,'chaine',0,'',$conf->entity); + if (! $res > 0) $error++; + $res = dolibarr_set_const($db, 'TAX_MODE_SELL_SERVICE', $valuesellservice, 'chaine',0,'',$conf->entity); + if (! $res > 0) $error++; + $res = dolibarr_set_const($db, 'TAX_MODE_BUY_SERVICE', $valuebuyservice, 'chaine',0,'',$conf->entity); + if (! $res > 0) $error++; - // Others options - foreach ($list as $constname) { - $constvalue = GETPOST($constname, 'alpha'); + dolibarr_set_const($db, "MAIN_INFO_TVAINTRA", GETPOST("tva",'alpha'),'chaine',0,'',$conf->entity); - if (!dolibarr_set_const($db, $constname, $constvalue, 'chaine', 0, '', $conf->entity)) { - $error++; - } - } + dolibarr_set_const($db, "MAIN_INFO_VAT_RETURN", GETPOST("MAIN_INFO_VAT_RETURN",'alpha'),'chaine',0,'',$conf->entity); - if (! $error) { - $db->commit(); - setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); - } else { - $db->rollback(); - setEventMessages($langs->trans("Error"), null, 'errors'); - } + if (! $error) { + $db->commit(); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + } else { + $db->rollback(); + setEventMessages($langs->trans("Error"), null, 'errors'); + } } + /* * View */ -llxHeader(); +llxHeader('', $langs->trans("TaxSetup")); + $form=new Form($db); if (! empty($conf->accounting->enabled)) $formaccounting = new FormAccounting($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans('TaxSetup'),$linkback,'title_setup'); -dol_fiche_head(); +//dol_fiche_head(null, '', '', -1); if (empty($mysoc->tva_assuj)) { - print $langs->trans("YourCompanyDoesNotUseVAT").'
'; + print $langs->trans("YourCompanyDoesNotUseVAT").'
'; } else { - print '
'.$obj->name.''.$obj->value.''.$obj->name.''.dol_escape_htmltag($obj->value).''.$obj->entity.'
'; + print ''; + print ''; + print ''; - // Cas des parametres TAX_MODE_SELL/BUY_SERVICE/PRODUCT - print ''; - print ''; - print ''; + print '
'; + print ''; - print ''; - print ''; - print "\n"; - print ''; - print '\n"; - print ''; - print '\n"; + print ''; - print "
'.$langs->trans("CompanyIds").''.$langs->trans("Value").'
'.$langs->trans('OptionVatMode').''.$langs->trans('Description').'
'.$langs->trans('OptionVATDefault').''.nl2br($langs->trans('OptionVatDefaultDesc')); - print "
'.$langs->trans('OptionVATDebitOption').''.nl2br($langs->trans('OptionVatDebitOptionDesc'))."
'; + print ''; + print '
\n"; + print '
'; + print $langs->trans("NotAvailableWhenAjaxDisabled"); + print "'; + $listval=array( + '0'=>$langs->trans(""), + '1'=>$langs->trans("Monthly"), + '2'=>$langs->trans("Quarterly"), + '3'=>$langs->trans("Annual"), + ); + print $form->selectarray("MAIN_INFO_VAT_RETURN", $listval, $conf->global->MAIN_INFO_VAT_RETURN); + print "
'; - print ''; - print ''; + print '
'; - // Products - print ''; - print ''; - print ''; + print '
 '.$langs->trans("Buy").''.$langs->trans("Sell").'
'.$langs->trans("Product").''; - print $langs->trans("OnDelivery"); - print ' ('.$langs->trans("SupposedToBeInvoiceDate").')'; - print ''; - print $langs->trans("OnDelivery"); - print ' ('.$langs->trans("SupposedToBeInvoiceDate").')'; - print '
'; - // Services - print ''; - print ''; - print ''; + // Cas des parametres TAX_MODE_SELL/BUY_SERVICE/PRODUCT + print ''; + print ''; + print "\n"; + // Standard + print ''; + print '\n"; + // On debit for services + print ''; + print '\n"; + // On payment for both products and services + if ($conf->global->MAIN_FEATURES_LEVEL >= 1) + { + print ''; + print '\n"; + } + print "
'.$langs->trans("Services").''; - if ($tax_mode == 0) - { - print $langs->trans("OnPayment"); - print ' ('.$langs->trans("SupposedToBePaymentDate").')'; - } - if ($tax_mode == 1) - { - print $langs->trans("OnInvoice"); - print ' ('.$langs->trans("InvoiceDateUsed").')'; - } - print ''; - if ($tax_mode == 0) - { - print $langs->trans("OnPayment"); - print ' ('.$langs->trans("SupposedToBePaymentDate").')'; - } - if ($tax_mode == 1) - { - print $langs->trans("OnInvoice"); - print ' ('.$langs->trans("InvoiceDateUsed").')'; - } - print '
'.$langs->trans('OptionVatMode').''.$langs->trans('Description').'
'.$langs->trans('OptionVATDefault').''.nl2br($langs->trans('OptionVatDefaultDesc')); + print "
'.$langs->trans('OptionVATDebitOption').''.nl2br($langs->trans('OptionVatDebitOptionDesc'))."
'.$langs->trans('OptionPaymentForProductAndServices').''.nl2br($langs->trans('OptionPaymentForProductAndServicesDesc'))."
\n"; - print ''; + print '
'; + print load_fiche_titre('', '', '', 0, 0, '', '-> '.$langs->trans("SummaryOfVatExigibilityUsedByDefault")); + //print ' ('.$langs->trans("CanBeChangedWhenMakingInvoice").')'; + + + print ''; + print ''; + + // Products + print ''; + print ''; + print ''; + + // Services + print ''; + print ''; + print ''; + + print '
 '.$langs->trans("Buy").''.$langs->trans("Sell").'
'.$langs->trans("Product").''; + if ($conf->global->TAX_MODE_BUY_PRODUCT == 'payment') + { + print $langs->trans("OnPayment"); + print ' ('.$langs->trans("SupposedToBePaymentDate").')'; + } + else + { + print $langs->trans("OnDelivery"); + print ' ('.$langs->trans("SupposedToBeInvoiceDate").')'; + } + print ''; + if ($conf->global->TAX_MODE_SELL_PRODUCT == 'payment') + { + print $langs->trans("OnPayment"); + print ' ('.$langs->trans("SupposedToBePaymentDate").')'; + } + else + { + print $langs->trans("OnDelivery"); + print ' ('.$langs->trans("SupposedToBeInvoiceDate").')'; + } + print '
'.$langs->trans("Services").''; + if ($conf->global->TAX_MODE_BUY_SERVICE == 'payment') + { + print $langs->trans("OnPayment"); + print ' ('.$langs->trans("SupposedToBePaymentDate").')'; + } + else + { + print $langs->trans("OnInvoice"); + print ' ('.$langs->trans("InvoiceDateUsed").')'; + } + print ''; + if ($conf->global->TAX_MODE_SELL_SERVICE == 'payment') + { + print $langs->trans("OnPayment"); + print ' ('.$langs->trans("SupposedToBePaymentDate").')'; + } + else + { + print $langs->trans("OnInvoice"); + print ' ('.$langs->trans("InvoiceDateUsed").')'; + } + print '
'; } print "
\n"; -/* - * Others params - */ -print ''; -print ''; -print ''; -print "\n"; - -foreach ($list as $key) -{ - - - print ''; - - // Param - $label = $langs->trans($key); - print ''; - - // Value - print ''; -} - -print '
' . $langs->trans('OtherOptions') . '
'; - if (! empty($conf->accounting->enabled)) - { - print $formaccounting->select_account($conf->global->$key, $key, 1, '', 1, 1); - } - else - { - print ''; - } - print '
'; - -dol_fiche_end(); print '
'; print ''; @@ -235,5 +266,15 @@ print '
'; print ''; + + + +if (! empty($conf->accounting->enabled)) +{ + $langs->load("accountancy"); + print '

'.$langs->trans("AccountingAccountForSalesTaxAreDefinedInto", $langs->transnoentitiesnoconv("MenuAccountancy"), $langs->transnoentitiesnoconv("Setup")).''; +} + + llxFooter(); $db->close(); diff --git a/htdocs/admin/ticketsup.php b/htdocs/admin/ticketsup.php new file mode 100644 index 00000000000..4f6839d1e59 --- /dev/null +++ b/htdocs/admin/ticketsup.php @@ -0,0 +1,654 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file admin/ticketsup.php + * \ingroup ticketsup + * \brief This file is a module setup page + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php"; +require_once DOL_DOCUMENT_ROOT."/ticketsup/class/ticketsup.class.php"; +require_once DOL_DOCUMENT_ROOT."/core/lib/ticketsup.lib.php"; + +// Translations +$langs->load("ticketsup"); + +// Access control +if (!$user->admin) { + accessforbidden(); +} + +// Parameters +$value = GETPOST('value', 'alpha'); +$action = GETPOST('action', 'alpha'); +$label = GETPOST('label', 'alpha'); +$scandir = GETPOST('scandir', 'alpha'); +$type = 'ticketsup'; + +if ($action == 'updateMask') { + $maskconstticket = GETPOST('maskconstticketsup', 'alpha'); + $maskticket = GETPOST('maskticketsup', 'alpha'); + + if ($maskconstticket) { + $res = dolibarr_set_const($db, $maskconstticket, $maskticket, 'chaine', 0, '', $conf->entity); + } + + if (!$res > 0) { + $error++; + } + + if (!$error) { + setEventMessage($langs->trans("SetupSaved")); + } else { + setEventMessage($langs->trans("Error"), 'errors'); + } +} elseif ($action == 'setmod') { + // TODO Verifier si module numerotation choisi peut etre active + // par appel methode canBeActivated + + dolibarr_set_const($db, "TICKETSUP_ADDON", $value, 'chaine', 0, '', $conf->entity); +} elseif ($action == 'setvar') { + include_once DOL_DOCUMENT_ROOT . "/core/lib/files.lib.php"; + + $notification_email = GETPOST('TICKETS_NOTIFICATION_EMAIL_FROM', 'alpha'); + if (!empty($notification_email)) { + $res = dolibarr_set_const($db, 'TICKETS_NOTIFICATION_EMAIL_FROM', $notification_email, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_NOTIFICATION_EMAIL_FROM', '000000', 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + // altairis : differentiate notification email FROM and TO + $notification_email_to = GETPOST('TICKETS_NOTIFICATION_EMAIL_TO', 'alpha'); + if (!empty($notification_email_to)) { + $res = dolibarr_set_const($db, 'TICKETS_NOTIFICATION_EMAIL_TO', $notification_email_to, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_NOTIFICATION_EMAIL_TO', '000000', 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $mail_new_ticket = GETPOST('TICKETS_MESSAGE_MAIL_NEW', 'alpha'); + if (!empty($mail_new_ticket)) { + $res = dolibarr_set_const($db, 'TICKETS_MESSAGE_MAIL_NEW', $mail_new_ticket, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_MESSAGE_MAIL_NEW', $langs->trans('TicketMessageMailNewText'), 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $mail_intro = GETPOST('TICKETS_MESSAGE_MAIL_INTRO', 'alpha'); + if (!empty($mail_intro)) { + $res = dolibarr_set_const($db, 'TICKETS_MESSAGE_MAIL_INTRO', $mail_intro, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'), 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $mail_signature = GETPOST('TICKETS_MESSAGE_MAIL_SIGNATURE', 'alpha'); + if (!empty($mail_signature)) { + $res = dolibarr_set_const($db, 'TICKETS_MESSAGE_MAIL_SIGNATURE', $mail_signature, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_MESSAGE_MAIL_SIGNATURE', $langs->trans('TicketMessageMailSignatureText'), 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $url_interface = GETPOST('TICKETS_URL_PUBLIC_INTERFACE', 'alpha'); + if (!empty($mail_signature)) { + $res = dolibarr_set_const($db, 'TICKETS_URL_PUBLIC_INTERFACE', $url_interface, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_URL_PUBLIC_INTERFACE', '', 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $topic_interface = GETPOST('TICKETS_PUBLIC_INTERFACE_TOPIC', 'alpha'); + if (!empty($mail_signature)) { + $res = dolibarr_set_const($db, 'TICKETS_PUBLIC_INTERFACE_TOPIC', $topic_interface, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_PUBLIC_INTERFACE_TOPIC', '', 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $text_home = GETPOST('TICKETS_PUBLIC_TEXT_HOME', 'alpha'); + if (!empty($mail_signature)) { + $res = dolibarr_set_const($db, 'TICKETS_PUBLIC_TEXT_HOME', $text_home, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_PUBLIC_TEXT_HOME', $langs->trans('TicketPublicInterfaceTextHome'), 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } + + $text_help = GETPOST('TICKETS_PUBLIC_TEXT_HELP_MESSAGE', 'alpha'); + if (!empty($text_help)) { + $res = dolibarr_set_const($db, 'TICKETS_PUBLIC_TEXT_HELP_MESSAGE', $text_help, 'chaine', 0, '', $conf->entity); + } else { + $res = dolibarr_set_const($db, 'TICKETS_PUBLIC_TEXT_HELP_MESSAGE', $langs->trans('TicketPublicPleaseBeAccuratelyDescribe'), 'chaine', 0, '', $conf->entity); + } + if (!$res > 0) { + $error++; + } +} + +if ($action == 'setvarother') { + $param_enable_public_interface = GETPOST('TICKETS_ENABLE_PUBLIC_INTERFACE', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_ENABLE_PUBLIC_INTERFACE', $param_enable_public_interface, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_must_exists = GETPOST('TICKETS_EMAIL_MUST_EXISTS', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_EMAIL_MUST_EXISTS', $param_must_exists, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_extrafields_public = GETPOST('TICKETS_EXTRAFIELDS_PUBLIC', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_EXTRAFIELDS_PUBLIC', $param_extrafields_public, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_disable_email = GETPOST('TICKETS_DISABLE_ALL_MAILS', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_DISABLE_ALL_MAILS', $param_disable_email, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_activate_log_by_email = GETPOST('TICKETS_ACTIVATE_LOG_BY_EMAIL', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_ACTIVATE_LOG_BY_EMAIL', $param_activate_log_by_email, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_show_module_logo = GETPOST('TICKETS_SHOW_MODULE_LOGO', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_SHOW_MODULE_LOGO', $param_show_module_logo, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_notification_also_main_addressemail = GETPOST('TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS', $param_notification_also_main_addressemail, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_limit_view = GETPOST('TICKETS_LIMIT_VIEW_ASSIGNED_ONLY', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_LIMIT_VIEW_ASSIGNED_ONLY', $param_limit_view, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } + + $param_auto_assign = GETPOST('TICKETS_AUTO_ASSIGN_USER_CREATE', 'alpha'); + $res = dolibarr_set_const($db, 'TICKETS_AUTO_ASSIGN_USER_CREATE', $param_auto_assign, 'chaine', 0, '', $conf->entity); + if (!$res > 0) { + $error++; + } +} + + + +/* + * View + */ + +$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + +$form = new Form($db); + +$help_url = "FR:Module_Ticket"; +$page_name = "TicketsupSetup"; +llxHeader('', $langs->trans($page_name), $help_url); + +// Subheader +$linkback = '' . $langs->trans("BackToModuleList") . ''; + +print load_fiche_titre($langs->trans($page_name), $linkback, 'title_setup'); + +// Configuration header +$head = ticketsupAdminPrepareHead(); + +dol_fiche_head($head, 'settings', $langs->trans("Module56000Name"), -1, "ticketsup"); + +print $langs->trans("TicketsupSetupDictionaries") . ' : ' . dol_buildpath('/admin/dict.php', 2) . '
'; + +print $langs->trans("TicketsupPublicAccess") . ' : ' . dol_buildpath('/ticketsup/public/index.php', 2) . ''; + +dol_fiche_end(); + + +/* + * Projects Numbering model + */ + +print_titre($langs->trans("TicketSupNumberingModules")); + +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print "\n"; + +clearstatcache(); + +foreach ($dirmodels as $reldir) { + $dir = dol_buildpath($reldir . "core/modules/ticketsup/"); + + if (is_dir($dir)) { + $handle = opendir($dir); + if (is_resource($handle)) { + $var = true; + + while (($file = readdir($handle)) !== false) { + if (preg_match('/^(mod_.*)\.php$/i', $file, $reg)) { + $file = $reg[1]; + $classname = substr($file, 4); + + include_once $dir . $file . '.php'; + + $module = new $file; + + // Show modules according to features level + if ($module->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2) { + continue; + } + + if ($module->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) { + continue; + } + + if ($module->isEnabled()) { + $var = !$var; + print ''; + + // Show example of numbering model + print '' . "\n"; + + print ''; + + $ticket = new Ticketsup($db); + $ticket->initAsSpecimen(); + + // Info + $htmltooltip = ''; + $htmltooltip .= '' . $langs->trans("Version") . ': ' . $module->getVersion() . '
'; + $nextval = $module->getNextValue($mysoc, $ticket); + if ("$nextval" != $langs->trans("NotAvailable")) { // Keep " on nextval + $htmltooltip .= '' . $langs->trans("NextValue") . ': '; + if ($nextval) { + $htmltooltip .= $nextval . '
'; + } else { + $htmltooltip .= $langs->trans($module->error) . '
'; + } + } + + print ''; + + print ''; + } + } + } + closedir($handle); + } + } +} + +print '
' . $langs->trans("Name") . '' . $langs->trans("Description") . '' . $langs->trans("Example") . '' . $langs->trans("Activated") . '' . $langs->trans("ShortInfo") . '
' . $module->name . "\n"; + print $module->info(); + print ''; + $tmp = $module->getExample(); + if (preg_match('/^Error/', $tmp)) { + print '
' . $langs->trans($tmp) . '
'; + } elseif ($tmp == 'NotConfigured') { + print $langs->trans($tmp); + } else { + print $tmp; + } + + print '
'; + if ($conf->global->TICKETSUP_ADDON == 'mod_' . $classname) { + print img_picto($langs->trans("Activated"), 'switch_on'); + } else { + print '' . img_picto($langs->trans("Disabled"), 'switch_off') . ''; + } + print ''; + print $form->textwithpicto('', $htmltooltip, 1, 0); + print '

'; + +if (!$conf->use_javascript_ajax) { + print '
'; + print ''; + print ''; +} +print_titre($langs->trans("TicketParamPublicInterface")); + +print ''; + +// Activate public interface +print ''; +print ''; +print ''; +print ''; + +// Check if email exists +print ''; +print ''; +print ''; +print ''; + +// Show logo for module +print ''; +print ''; +print ''; +print ''; + +// Show logo for company +print ''; +print ''; +print ''; +print ''; + +// Display extrafields into public interface +print ''; +print ''; +print ''; +print ''; + +print '
' . $langs->trans("TicketsActivatePublicInterface") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_ENABLE_PUBLIC_INTERFACE'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_ENABLE_PUBLIC_INTERFACE", $arrval, $conf->global->TICKETS_ENABLE_PUBLIC_INTERFACE); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsActivatePublicInterfaceHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsEmailMustExist") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_EMAIL_MUST_EXISTS'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_EMAIL_MUST_EXISTS", $arrval, $conf->global->TICKETS_EMAIL_MUST_EXISTS); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsEmailMustExistHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsShowModuleLogo") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_SHOW_MODULE_LOGO'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_SHOW_MODULE_LOGO", $arrval, $conf->global->TICKETS_SHOW_MODULE_LOGO); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsShowModuleLogoHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsShowCompanyLogo") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_SHOW_COMPANY_LOGO'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_SHOW_COMPANY_LOGO", $arrval, $conf->global->TICKETS_SHOW_COMPANY_LOGO); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsShowCompanyLogoHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsShowExtrafieldsIntoPublicArea") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_EXTRAFIELDS_PUBLIC'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_EXTRAFIELDS_PUBLIC", $arrval, $conf->global->TICKETS_EXTRAFIELDS_PUBLIC); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsShowExtrafieldsIntoPublicAreaHelp"), 1, 'help'); +print '

'; + +print_titre($langs->trans("TicketParams")); +print ''; + +// Activate email notifications +print ''; +print ''; +print ''; +print ''; + +// Activate log by email +print ''; +print ''; +print ''; +print ''; + +// Also send to main email address +print ''; +print ''; +print ''; +print ''; + +// Limiter la vue des tickets à ceux assignés à l'utilisateur +print ''; +print ''; +print ''; +print ''; + +if (!$conf->use_javascript_ajax) { + print ''; + print ''; +} + +// Auto assign ticket at user who created it +print ''; +print ''; +print ''; +print ''; + +print '
' . $langs->trans("TicketsDisableEmail") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_DISABLE_ALL_MAILS'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_DISABLE_ALL_MAILS", $arrval, $conf->global->TICKETS_DISABLE_ALL_MAILS); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsDisableEmailHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsLogEnableEmail") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_ACTIVATE_LOG_BY_EMAIL'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_ACTIVATE_LOG_BY_EMAIL", $arrval, $conf->global->TICKETS_ACTIVATE_LOG_BY_EMAIL); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsLogEnableEmailHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsEmailAlsoSendToMainAddress") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS", $arrval, $conf->global->TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsEmailAlsoSendToMainAddressHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsLimitViewAssignedOnly") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_LIMIT_VIEW_ASSIGNED_ONLY'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_LIMIT_VIEW_ASSIGNED_ONLY", $arrval, $conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsLimitViewAssignedOnlyHelp"), 1, 'help'); +print '
' . $langs->trans("TicketsAutoAssignTicket") . ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKETS_AUTO_ASSIGN_USER_CREATE'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKETS_AUTO_ASSIGN_USER_CREATE", $arrval, $conf->global->TICKETS_AUTO_ASSIGN_USER_CREATE); +} +print ''; +print $form->textwithpicto('', $langs->trans("TicketsAutoAssignTicketHelp"), 1, 'help'); +print '

'; + +if (!$conf->use_javascript_ajax) { + print '
'; +} + +// Admin var of module +print_titre($langs->trans("TicketParamMail")); + +print ''; + +print ''; +print ''; +print ''; + +print ''; +print ''; +print "\n"; + +if (empty($conf->global->FCKEDITOR_ENABLE_MAIL)) { + print ''; + print ''; + print "\n"; +} + +// Email d'envoi des notifications +print ''; +print ''; +print ''; +print ''; + +// Email de réception des notifications +print ''; +print ''; +print ''; +print ''; + +// Texte de création d'un ticket +$mail_mesg_new = $conf->global->TICKETS_MESSAGE_MAIL_NEW ? $conf->global->TICKETS_MESSAGE_MAIL_NEW : $langs->trans('TicketNewEmailBody'); +print ''; +print ''; + +// Texte d'introduction +$mail_intro = $conf->global->TICKETS_MESSAGE_MAIL_INTRO ? $conf->global->TICKETS_MESSAGE_MAIL_INTRO : $langs->trans('TicketMessageMailIntroText'); +print ''; +print ''; + +// Texte de signature +$mail_signature = $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE ? $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE : $langs->trans('TicketMessageMailSignatureText'); +print ''; +print ''; + +print ''; +print ''; +print "\n"; + +// Url public interface +$url_interface = $conf->global->TICKETS_URL_PUBLIC_INTERFACE; +print ''; +print ''; +print ''; + +// Interface topic +$url_interface = $conf->global->TICKETS_PUBLIC_INTERFACE_TOPIC; +print ''; +print ''; +print ''; + +// Texte d'accueil homepage +$public_text_home = $conf->global->TICKETS_PUBLIC_TEXT_HOME ? $conf->global->TICKETS_PUBLIC_TEXT_HOME : $langs->trans('TicketPublicInterfaceTextHome'); +print ''; +print ''; + +// Texte d'aide à la saisie du message +$public_text_help_message = $conf->global->TICKETS_PUBLIC_TEXT_HELP_MESSAGE ? $conf->global->TICKETS_PUBLIC_TEXT_HELP_MESSAGE : $langs->trans('TicketPublicPleaseBeAccuratelyDescribe'); +print ''; +print ''; + +print ''; +print ''; + +print '
' . $langs->trans("Email") . '
' . $langs->trans("TicketSupCkEditorEmailNotActivated") . '
' . $langs->trans("TicketEmailNotificationFrom") . ''; +print ''; +print $form->textwithpicto('', $langs->trans("TicketEmailNotificationFromHelp"), 1, 'help'); +print '
' . $langs->trans("TicketEmailNotificationTo") . ''; +print ''; +print $form->textwithpicto('', $langs->trans("TicketEmailNotificationToHelp"), 1, 'help'); +print '
' . $langs->trans("TicketNewEmailBodyLabel") . ''; +print ''; +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +$doleditor = new DolEditor('TICKETS_MESSAGE_MAIL_NEW', $mail_mesg_new, '100%', 120, 'dolibarr_mailings', '', false, true, $conf->global->FCKEDITOR_ENABLE_MAIL, ROWS_2, 70); +$doleditor->Create(); +print ''; +print $form->textwithpicto('', $langs->trans("TicketNewEmailBodyHelp"), 1, 'help'); +print '
' . $langs->trans("TicketMessageMailIntroLabelAdmin") . ''; +print ''; +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +$doleditor = new DolEditor('TICKETS_MESSAGE_MAIL_INTRO', $mail_intro, '100%', 120, 'dolibarr_mailings', '', false, true, $conf->global->FCKEDITOR_ENABLE_MAIL, ROWS_2, 70); +$doleditor->Create(); +print ''; +print $form->textwithpicto('', $langs->trans("TicketMessageMailIntroHelpAdmin"), 1, 'help'); +print '
' . $langs->trans("TicketMessageMailSignatureLabelAdmin") . ''; +print ''; +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +$doleditor = new DolEditor('TICKETS_MESSAGE_MAIL_SIGNATURE', $mail_signature, '100%', 120, 'dolibarr_mailings', '', false, true, $conf->global->FCKEDITOR_ENABLE_MAIL, ROWS_2, 70); +$doleditor->Create(); +print ''; +print $form->textwithpicto('', $langs->trans("TicketMessageMailSignatureHelpAdmin"), 1, 'help'); +print '
' . $langs->trans("PublicInterface") . '
' . $langs->trans("TicketUrlPublicInterfaceLabelAdmin") . ''; +print ''; +print ''; +print $form->textwithpicto('', $langs->trans("TicketUrlPublicInterfaceHelpAdmin"), 1, 'help'); +print '
' . $langs->trans("TicketPublicInterfaceTopicLabelAdmin") . ''; +print ''; +print ''; +print $form->textwithpicto('', $langs->trans("TicketPublicInterfaceTopicHelp"), 1, 'help'); +print '
' . $langs->trans("TicketPublicInterfaceTextHomeLabelAdmin") . ''; +print ''; +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +$doleditor = new DolEditor('TICKETS_PUBLIC_TEXT_HOME', $public_text_home, '100%', 180, 'dolibarr_notes', '', false, true, $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_2, 70); +$doleditor->Create(); +print ''; +print $form->textwithpicto('', $langs->trans("TicketPublicInterfaceTextHomeHelpAdmin"), 1, 'help'); +print '
' . $langs->trans("TicketPublicInterfaceTextHelpMessageLabelAdmin") . ''; +print ''; +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; +$doleditor = new DolEditor('TICKETS_PUBLIC_TEXT_HELP_MESSAGE', $public_text_help_message, '100%', 180, 'dolibarr_notes', '', false, true, $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_2, 70); +$doleditor->Create(); +print ''; +print $form->textwithpicto('', $langs->trans("TicketPublicInterfaceTextHelpMessageHelpAdmin"), 1, 'help'); +print '

'; +print ''; + +llxFooter(); + +$db->close(); diff --git a/htdocs/admin/ticketsup_extrafields.php b/htdocs/admin/ticketsup_extrafields.php new file mode 100644 index 00000000000..5709472e858 --- /dev/null +++ b/htdocs/admin/ticketsup_extrafields.php @@ -0,0 +1,114 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/admin/ticketsup_extrafields.php + * \ingroup ticketsup + * \brief Page to setup extra fields of ticket + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT."/core/lib/ticketsup.lib.php"; +require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; + +$langs->load("ticketsup"); +$langs->load("admin"); + +$extrafields = new ExtraFields($db); +$form = new Form($db); + +// List of supported format +$tmptype2label = ExtraFields::$type2label; +$type2label = array(''); +foreach ($tmptype2label as $key => $val) { + $type2label[$key] = $langs->trans($val); +} + +$action = GETPOST('action', 'alpha'); +$attrname = GETPOST('attrname', 'alpha'); +$elementtype = 'ticketsup'; //Must be the $table_element of the class that manage extrafield + +if (!$user->admin) { + accessforbidden(); +} + + +/* + * Actions + */ + +include DOL_DOCUMENT_ROOT . '/core/actions_extrafields.inc.php'; + + + +/* + * View + */ + +$textobject = $langs->transnoentitiesnoconv("TicketSup"); + +$help_url = "FR:Module_Ticket"; +$page_name = "TicketsupSetup"; +llxHeader('', $langs->trans($page_name), $help_url); + +$linkback = '' . $langs->trans("BackToModuleList") . ''; +print load_fiche_titre($langs->trans("TicketsupSetup"), $linkback, 'title_setup'); + +$head = ticketsupAdminPrepareHead(); + +dol_fiche_head($head, 'attributes', $langs->trans("Module56000Name"), -1, "ticketsup"); + +require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php'; + +dol_fiche_end(); + +// Buttons +if ($action != 'create' && $action != 'edit') { + print '"; +} + +/* ************************************************************************** */ +/* */ +/* Creation d'un champ optionnel */ +/* */ +/* ************************************************************************** */ + +if ($action == 'create') { + print "
"; + print_titre($langs->trans('NewAttribute')); + + include DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_add.tpl.php'; +} + +/* ************************************************************************** */ +/* */ +/* Edition d'un champ optionnel */ +/* */ +/* ************************************************************************** */ +if ($action == 'edit' && !empty($attrname)) { + print "
"; + print_titre($langs->trans("FieldEdition", $attrname)); + + include DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_edit.tpl.php'; +} + +llxFooter(); + +$db->close(); diff --git a/htdocs/admin/tools/purge.php b/htdocs/admin/tools/purge.php index bc328e951d4..f415ed1692c 100644 --- a/htdocs/admin/tools/purge.php +++ b/htdocs/admin/tools/purge.php @@ -86,7 +86,7 @@ if (! empty($conf->syslog->enabled)) $filelogparam=$filelog; if ($user->admin && preg_match('/^dolibarr.*\.log$/', basename($filelog))) { - $filelogparam =''.$filelog.''; } diff --git a/htdocs/admin/translation.php b/htdocs/admin/translation.php index 1999aab006c..74494ee09ea 100644 --- a/htdocs/admin/translation.php +++ b/htdocs/admin/translation.php @@ -266,7 +266,7 @@ if ($mode == 'overwrite') print '
'; print img_info().' '.$langs->trans("SomeTranslationAreUncomplete"); $urlwikitranslatordoc='https://wiki.dolibarr.org/index.php/Translator_documentation'; - print ' ('.$langs->trans("SeeAlso").': '.$langs->trans("Here").')
'; + print ' ('.$langs->trans("SeeAlso", ''.$langs->trans("Here").'').')
'; print $langs->trans("TranslationOverwriteDesc",$langs->transnoentitiesnoconv("Language"),$langs->transnoentitiesnoconv("Key"),$langs->transnoentitiesnoconv("NewTranslationStringToShow"))."\n"; print ' ('.$langs->trans("TranslationOverwriteDesc2").').'."
\n"; print '
'; @@ -552,7 +552,9 @@ if ($mode == 'searchkey') if (! empty($conf->global->MAIN_FEATURES_LEVEL)) { $transifexlangfile='$'; // $ means 'All' - $transifexurl = 'https://www.transifex.com/dolibarr-association/dolibarr/translate/#'.$langcode.'/'.$transifexlangfile.'?key='.$key; + //$transifexurl = 'https://www.transifex.com/dolibarr-association/dolibarr/translate/#'.$langcode.'/'.$transifexlangfile.'?key='.$key; + $transifexurl = 'https://www.transifex.com/dolibarr-association/dolibarr/translate/#'.$langcode.'/'.$transifexlangfile.'?q=key%3A'.$key; + print '   '.img_picto('FixOnTransifex', 'object_globe').''; } } diff --git a/htdocs/admin/usergroup.php b/htdocs/admin/usergroup.php index 8d098ee6923..5a1cf94e59f 100644 --- a/htdocs/admin/usergroup.php +++ b/htdocs/admin/usergroup.php @@ -117,7 +117,7 @@ elseif (preg_match('/del_(.*)/',$action,$reg)) $help_url='EN:Module_Users|FR:Module_Utilisateurs|ES:Módulo_Usuarios'; llxHeader('',$langs->trans("UsersSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("UsersSetup"),$linkback,'title_setup'); diff --git a/htdocs/admin/website.php b/htdocs/admin/website.php index 54a91d6ee92..e3854c4eb11 100644 --- a/htdocs/admin/website.php +++ b/htdocs/admin/website.php @@ -64,7 +64,7 @@ $pageprev = $page - 1; $pagenext = $page + 1; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('admin')); +$hookmanager->initHooks(array('website')); // Name of SQL tables of dictionaries $tabname=array(); @@ -514,15 +514,7 @@ if ($id) } } - $tmpaction = 'create'; - $parameters=array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); - $reshook=$hookmanager->executeHooks('createDictionaryFieldlist',$parameters, $obj, $tmpaction); // Note that $action and $object may have been modified by some hooks - $error=$hookmanager->error; $errors=$hookmanager->errors; - - if (empty($reshook)) - { - fieldListWebsites($fieldlist,$obj,$tabname[$id],'add'); - } + fieldListWebsites($fieldlist,$obj,$tabname[$id],'add'); print ''; if ($action != 'edit') @@ -602,7 +594,7 @@ if ($id) { $tmpaction='edit'; $parameters=array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); - $reshook=$hookmanager->executeHooks('editDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('editWebsiteFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks $error=$hookmanager->error; $errors=$hookmanager->errors; if (empty($reshook)) fieldListWebsites($fieldlist,$obj,$tabname[$id],'edit'); @@ -614,7 +606,7 @@ if ($id) { $tmpaction = 'view'; $parameters=array('var'=>$var, 'fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]); - $reshook=$hookmanager->executeHooks('viewDictionaryFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('viewWebsiteFieldlist',$parameters,$obj, $tmpaction); // Note that $action and $object may have been modified by some hooks $error=$hookmanager->error; $errors=$hookmanager->errors; diff --git a/htdocs/admin/workflow.php b/htdocs/admin/workflow.php index 511188e55df..79dacc3276e 100644 --- a/htdocs/admin/workflow.php +++ b/htdocs/admin/workflow.php @@ -59,7 +59,7 @@ if (preg_match('/del(.*)/',$action,$reg)) llxHeader('',$langs->trans("WorkflowSetup"),''); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("WorkflowSetup"),$linkback,'title_setup'); print $langs->trans("WorkflowDesc").'
'; diff --git a/htdocs/api/admin/explorer.php b/htdocs/api/admin/explorer.php index 0aecfcd5d7e..d075da3f344 100644 --- a/htdocs/api/admin/explorer.php +++ b/htdocs/api/admin/explorer.php @@ -166,7 +166,7 @@ $listofapis=Routes::toArray(); // TODO api for "status" is lost here llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ApiSetup"),$linkback,'title_setup'); // Define $urlwithroot diff --git a/htdocs/api/admin/index.php b/htdocs/api/admin/index.php index d912c2e40df..7ebff2c5da6 100644 --- a/htdocs/api/admin/index.php +++ b/htdocs/api/admin/index.php @@ -1,9 +1,9 @@ +/* Copyright (C) 2004 Rodolphe Quiedeville * Copyright (C) 2005-2016 Laurent Destailleur * Copyright (C) 2011 Juanjo Menent - * Copyright (C) 2012 Regis Houssin - * Copyright (C) 2015 Jean-François Ferry + * Copyright (C) 2012-2018 Regis Houssin + * Copyright (C) 2015 Jean-François Ferry * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ if ($action == 'setproductionmode') { $status = GETPOST('status','alpha'); - if (dolibarr_set_const($db, 'API_PRODUCTION_MODE', $status, 'chaine', 0, '', $conf->entity) > 0) + if (dolibarr_set_const($db, 'API_PRODUCTION_MODE', $status, 'chaine', 0, '', 0) > 0) { $error=0; @@ -86,7 +86,7 @@ dol_mkdir(DOL_DATA_ROOT.'/api/temp'); // May have been deleted by a purge llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ApiSetup"),$linkback,'title_setup'); print $langs->trans("ApiDesc")."
\n"; diff --git a/htdocs/api/class/api.class.php b/htdocs/api/class/api.class.php index df87efcd8c3..92391a14dc0 100644 --- a/htdocs/api/class/api.class.php +++ b/htdocs/api/class/api.class.php @@ -140,7 +140,6 @@ class DolibarrApi unset($object->class_element_line); unset($object->picto); - unset($object->facturee); // Replace with billed unset($object->fieldsforcombobox); unset($object->comments); diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index d5b30d67d11..07f124741fe 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -306,7 +306,7 @@ class Documents extends DolibarrApi throw new RestException(404, 'Proposal not found'); } - $upload_dir = $conf->propal->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'propal'); + $upload_dir = $conf->propal->multidir_output[$object->entity] . "/" . get_exdir(0, 0, 0, 1, $object, 'propal'); } else if ($modulepart == 'commande' || $modulepart == 'order') { diff --git a/htdocs/api/class/api_setup.class.php b/htdocs/api/class/api_setup.class.php index 27b909c2dad..22c763100d4 100644 --- a/htdocs/api/class/api_setup.class.php +++ b/htdocs/api/class/api_setup.class.php @@ -66,7 +66,8 @@ class Setup extends DolibarrApi $sql = "SELECT id, code, type, libelle as label, module"; $sql.= " FROM ".MAIN_DB_PREFIX."c_paiement as t"; - $sql.= " WHERE t.active = ".$active; + $sql.= " WHERE t.entity IN (".getEntity('c_paiement').")"; + $sql.= " AND t.active = ".$active; // Add sql filters if ($sqlfilters) { @@ -538,7 +539,8 @@ class Setup extends DolibarrApi $sql = "SELECT rowid as id, code, sortorder, libelle as label, libelle_facture as descr, type_cdr, nbjour, decalage, module"; $sql.= " FROM ".MAIN_DB_PREFIX."c_payment_term as t"; - $sql.= " WHERE t.active = ".$active; + $sql.= " WHERE t.entity IN (".getEntity('c_payment_term').")"; + $sql.= " AND t.active = ".$active; // Add sql filters if ($sqlfilters) { @@ -638,7 +640,7 @@ class Setup extends DolibarrApi if (! $xmlarray['curl_error_no'] && $xmlarray['http_code'] != '404') { $xmlfile = $xmlarray['content']; - //print "eee".$xmlfile."eee"; + //print "xmlfilestart".$xmlfile."endxmlfile"; $xml = simplexml_load_string($xmlfile); } else diff --git a/htdocs/assets/admin/assets_extrafields.php b/htdocs/assets/admin/assets_extrafields.php new file mode 100644 index 00000000000..85c596b6e37 --- /dev/null +++ b/htdocs/assets/admin/assets_extrafields.php @@ -0,0 +1,113 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/assets/admin/assets_extrafields.php + * \ingroup assets + * \brief Page to setup extra fields of assets + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; + +$langs->loadLangs(array("assets","admin","companies")); + +$extrafields = new ExtraFields($db); +$form = new Form($db); + +// List of supported format +$tmptype2label=ExtraFields::$type2label; +$type2label=array(''); +foreach ($tmptype2label as $key => $val) $type2label[$key]=$langs->transnoentitiesnoconv($val); + +$action=GETPOST('action', 'alpha'); +$attrname=GETPOST('attrname', 'alpha'); +$elementtype='don'; //Must be the $table_element of the class that manage extrafield + +if (!$user->admin) accessforbidden(); + + +/* + * Actions + */ + +require DOL_DOCUMENT_ROOT.'/core/actions_extrafields.inc.php'; + + + +/* + * View + */ + +$textobject=$langs->transnoentitiesnoconv("Assets"); + +llxHeader('',$langs->trans("AssetsSetup")); + +$linkback=''.$langs->trans("BackToModuleList").''; +print load_fiche_titre($langs->trans("AssetsSetup"),$linkback,'title_setup'); + + +$head = AssetsAdminPrepareHead(); + +dol_fiche_head($head, 'attributes', $langs->trans("Assets"), 0, 'generic'); + +require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php'; + +dol_fiche_end(); + + +// Buttons +if ($action != 'create' && $action != 'edit') +{ + print '
'; + print ''; + print "
"; +} + + +/* ************************************************************************** */ +/* */ +/* Create optional field */ +/* */ +/* ************************************************************************** */ + +if ($action == 'create') +{ + print "
"; + print load_fiche_titre($langs->trans('NewAttribute')); + + require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_add.tpl.php'; +} + +/* ************************************************************************** */ +/* */ +/* Edit optional field */ +/* */ +/* ************************************************************************** */ +if ($action == 'edit' && ! empty($attrname)) +{ + print "
"; + print load_fiche_titre($langs->trans("FieldEdition", $attrname)); + + require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_edit.tpl.php'; +} + +llxFooter(); + +$db->close(); diff --git a/htdocs/assets/admin/assets_type_extrafields.php b/htdocs/assets/admin/assets_type_extrafields.php new file mode 100644 index 00000000000..8d136ed1278 --- /dev/null +++ b/htdocs/assets/admin/assets_type_extrafields.php @@ -0,0 +1,113 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see http://www.gnu.org/ + */ + +/** +/** + * \file htdocs/assets/admin/assets_type_extrafields.php + * \ingroup assets + * \brief Page to setup extra fields type of assets + */ +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; + +$langs->load("assets"); +$langs->load("admin"); + +$extrafields = new ExtraFields($db); +$form = new Form($db); + +// List of supported format +$tmptype2label=ExtraFields::$type2label; +$type2label=array(''); +foreach ($tmptype2label as $key => $val) $type2label[$key]=$langs->transnoentitiesnoconv($val); + +$action=GETPOST('action', 'alpha'); +$attrname=GETPOST('attrname', 'alpha'); +$elementtype='adherent_type'; //Must be the $table_element of the class that manage extrafield + +if (!$user->admin) accessforbidden(); + + +/* + * Actions + */ + +require DOL_DOCUMENT_ROOT.'/core/actions_extrafields.inc.php'; + + + +/* + * View + */ + +$textobject=$langs->transnoentitiesnoconv("AssetsTypes"); + +llxHeader('',$langs->trans("AssetsSetup")); + +$linkback=''.$langs->trans("BackToModuleList").''; +print load_fiche_titre($langs->trans("AssetsSetup"),$linkback,'title_setup'); + + +$head = AssetsAdminPrepareHead(); + +dol_fiche_head($head, 'attributes_type', $langs->trans("Assets"), 0, 'generic'); + +require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php'; + +dol_fiche_end(); + +// Buttons +if ($action != 'create' && $action != 'edit') +{ + print '
'; + print ''; + print "
"; +} + + +/* ************************************************************************** */ +/* */ +/* Creation of an optional field */ +/* */ +/* ************************************************************************** */ + +if ($action == 'create') +{ + print "
"; + print load_fiche_titre($langs->trans('NewAttribute')); + + require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_add.tpl.php'; +} + +/* ************************************************************************** */ +/* */ +/* Edition of an optional field */ +/* */ +/* ************************************************************************** */ +if ($action == 'edit' && ! empty($attrname)) +{ + print "
"; + print load_fiche_titre($langs->trans("FieldEdition", $attrname)); + + require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_edit.tpl.php'; +} + +llxFooter(); + +$db->close(); diff --git a/htdocs/assets/admin/setup.php b/htdocs/assets/admin/setup.php new file mode 100644 index 00000000000..19de7b3b752 --- /dev/null +++ b/htdocs/assets/admin/setup.php @@ -0,0 +1,118 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/assets/admin/setup.php + * \ingroup assets + * \brief Assets setup page. + */ + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php"; + +global $langs, $user; + +// Translations +$langs->loadLangs(array("admin", "assets")); + +// Access control +if (! $user->admin) accessforbidden(); + +// Parameters +$action = GETPOST('action', 'alpha'); +$backtopage = GETPOST('backtopage', 'alpha'); + +$arrayofparameters=array('FIXEDASSETS_MYPARAM1'=>array('css'=>'minwidth200'), 'FIXEDASSETS_MYPARAM2'=>array('css'=>'minwidth500')); + + +/* + * Actions + */ + +include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; + + +/* + * View + */ + +llxHeader('',$langs->trans("AssetsSetup")); + +$linkback=''.$langs->trans("BackToModuleList").''; +print load_fiche_titre($langs->trans("AssetsSetup"),$linkback,'title_setup'); + + +$head = AssetsAdminPrepareHead(); + +dol_fiche_head($head, 'settings', $langs->trans("Assets"), 0, 'generic'); + +// Setup page goes here +echo $langs->trans("AssetsSetupPage"); + + +if ($action == 'edit') +{ + print '
'; + print ''; + print ''; + + print ''; + print ''; + + foreach($arrayofparameters as $key => $val) + { + print ''; + } + + print '
'.$langs->trans("Parameter").''.$langs->trans("Value").'
'; + print $form->textwithpicto($langs->trans($key),$langs->trans($key.'Tooltip')); + print '
'; + + print '
'; + print ''; + print '
'; + + print '
'; + print '
'; +} +else +{ + print ''; + print ''; + + foreach($arrayofparameters as $key => $val) + { + print ''; + } + + print '
'.$langs->trans("Parameter").''.$langs->trans("Value").'
'; + print $form->textwithpicto($langs->trans($key),$langs->trans($key.'Tooltip')); + print '' . $conf->global->$key . '
'; + + print '
'; + print ''.$langs->trans("Modify").''; + print '
'; +} + + +// Page end +dol_fiche_end(); + +llxFooter(); +$db->close(); diff --git a/htdocs/assets/card.php b/htdocs/assets/card.php new file mode 100644 index 00000000000..4424ca0cf93 --- /dev/null +++ b/htdocs/assets/card.php @@ -0,0 +1,372 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file card.php + * \ingroup assets + * \brief Page to create/edit/view assets + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/assets.class.php'; +include_once(DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'); +include_once(DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'); + +// Load traductions files requiredby by page +$langs->loadLangs(array("assets")); + +// Get parameters +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'alpha'); +$cancel = GETPOST('cancel', 'aZ09'); +$backtopage = GETPOST('backtopage', 'alpha'); + +// Initialize technical objects +$object=new Assets($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction=$conf->assets->dir_output . '/temp/massgeneration/'.$user->id; +$hookmanager->initHooks(array('assetscard')); // Note that conf->hooks_modules contains array +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('assets'); +$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); + +// Initialize array of search criterias +$search_all=trim(GETPOST("search_all",'alpha')); +$search=array(); +foreach($object->fields as $key => $val) +{ + if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); +} + +if (empty($action) && empty($id) && empty($ref)) $action='view'; + +// Security check - Protection if external user +//if ($user->societe_id > 0) access_forbidden(); +//if ($user->societe_id > 0) $socid = $user->societe_id; +//$result = restrictedArea($user, 'assets', $id); + +// fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + +// Load object +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals + + + +/* + * Actions + * + * Put here all code to do according to value of "action" parameter + */ + +$parameters=array(); +$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +if (empty($reshook)) +{ + $error=0; + + $permissiontoadd = $user->rights->assets->create; + $permissiontodelete = $user->rights->assets->delete; + $backurlforlist = dol_buildpath('/assets/list.php',1); + + // Actions cancel, add, update or delete + include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; + + // Actions when printing a doc from card + include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php'; + + // Actions to send emails + $trigger_name='MYOBJECT_SENTBYMAIL'; + $autocopy='MAIN_MAIL_AUTOCOPY_MYOBJECT_TO'; + $trackid='assets'.$object->id; + include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php'; +} + + + + +/* + * View + * + * Put here all code to build page + */ + +$form=new Form($db); +$formfile=new FormFile($db); + +$title=$langs->trans("Assets").' - '.$langs->trans("Card"); +$help_url=''; +llxHeader('',$title,$help_url); + +// Example : Adding jquery code +print ''; + + +// Part to create +if ($action == 'create') +{ + print load_fiche_titre($langs->trans("NewAsset")); + + print '
'; + print ''; + print ''; + print ''; + + dol_fiche_head(array(), ''); + + print ''."\n"; + + // Common attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_add.tpl.php'; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php'; + + print '
'."\n"; + + dol_fiche_end(); + + print '
'; + print ''; + print '  '; + print ''; // Cancel for create does not post form if we don't know the backtopage + print '
'; + + print '
'; +} + +// Part to edit record +if (($id || $ref) && $action == 'edit') +{ + print load_fiche_titre($langs->trans("Assets")); + + print '
'; + print ''; + print ''; + print ''; + print ''; + + dol_fiche_head(); + + print ''."\n"; + + // Common attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_edit.tpl.php'; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_edit.tpl.php'; + + print '
'; + + dol_fiche_end(); + + print '
'; + print '   '; + print '
'; + + print '
'; +} + +// Part to show record +if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) +{ + $res = $object->fetch_optionals($object->id, $extralabels); + + $head = AssetsPrepareHead($object); + dol_fiche_head($head, 'card', $langs->trans("Asset"), -1, 'generic'); + + $formconfirm = ''; + + // Confirmation to delete + if ($action == 'delete') + { + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('DeleteAssets'), $langs->trans('ConfirmDeleteAssets'), 'confirm_delete', '', 0, 1); + } + + // Confirmation of action xxxx + if ($action == 'xxx') + { + $formquestion=array(); + /* + $formquestion = array( + // 'text' => $langs->trans("ConfirmClone"), + // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1), + // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1), + // array('type' => 'other', 'name' => 'idwarehouse', 'label' => $langs->trans("SelectWarehouseForStockDecrease"), 'value' => $formproduct->selectWarehouses(GETPOST('idwarehouse')?GETPOST('idwarehouse'):'ifone', 'idwarehouse', '', 1))); + }*/ + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('XXX'), $text, 'confirm_xxx', $formquestion, 0, 1, 220); + } + + if (! $formconfirm) { + $parameters = array('lineid' => $lineid); + $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if (empty($reshook)) $formconfirm.=$hookmanager->resPrint; + elseif ($reshook > 0) $formconfirm=$hookmanager->resPrint; + } + + // Print form confirm + print $formconfirm; + + + // Object card + // ------------------------------------------------------------ + $linkback = '' . $langs->trans("BackToList") . ''; + + $morehtmlref='
'; + /* + // Ref bis + $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->assets->creer, 'string', '', 0, 1); + $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->assets->creer, 'string', '', null, null, '', 1); + // Thirdparty + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $soc->getNomUrl(1); + */ + $morehtmlref.='
'; + + dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); + + print '
'; + print '
'; + print '
'; + print ''."\n"; + + // Common attributes + //$keyforbreak='fieldkeytoswithonsecondcolumn'; + include DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_view.tpl.php'; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php'; + + print '
'; + print '
'; + print '
'; + print ''; + + print '

'; + + dol_fiche_end(); + + + // Buttons for actions + if ($action != 'presend' && $action != 'editline') { + print '
'."\n"; + $parameters=array(); + $reshook=$hookmanager->executeHooks('addMoreActionsButtons',$parameters,$object,$action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + + if (empty($reshook)) + { + // Send + print '' . $langs->trans('SendMail') . ''."\n"; + + if ($user->rights->assets->write) + { + print ''.$langs->trans("Modify").''."\n"; + } + else + { + print ''.$langs->trans('Modify').''."\n"; + } + + if ($user->rights->assets->delete) + { + print ''.$langs->trans('Delete').''."\n"; + } + else + { + print ''.$langs->trans('Delete').''."\n"; + } + } + print '
'."\n"; + } + + + // Select mail models is same action as presend + if (GETPOST('modelselected')) { + $action = 'presend'; + } + + if ($action != 'presend') + { + print '
'; + print ''; // ancre + + // Documents + /*$objref = dol_sanitizeFileName($object->ref); + $relativepath = $comref . '/' . $comref . '.pdf'; + $filedir = $conf->assets->dir_output . '/' . $objref; + $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id; + $genallowed = $user->rights->assets->read; // If you can read, you can build the PDF to read content + $delallowed = $user->rights->assets->create; // If you can create/edit, you can remove a file on card + print $formfile->showdocuments('assets', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang); + */ + + // Show links to link elements + $linktoelem = $form->showLinkToObjectBlock($object, null, array('assets')); + $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); + + + print '
'; + + $MAXEVENT = 10; + + $morehtmlright = ''; + $morehtmlright.= $langs->trans("SeeAll"); + $morehtmlright.= ''; + + // List of actions on element + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php'; + $formactions = new FormActions($db); + $somethingshown = $formactions->showactions($object, 'assets', $socid, 1, '', $MAXEVENT, '', $morehtmlright); + + print '
'; + } + + //Select mail models is same action as presend + /* + if (GETPOST('modelselected')) $action = 'presend'; + + // Presend form + $modelmail='inventory'; + $defaulttopic='InformationMessage'; + $diroutput = $conf->product->dir_output.'/inventory'; + $trackid = 'stockinv'.$object->id; + + include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php'; + */ +} + + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/assets/class/asset.class.php b/htdocs/assets/class/asset.class.php new file mode 100644 index 00000000000..efacac10822 --- /dev/null +++ b/htdocs/assets/class/asset.class.php @@ -0,0 +1,463 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file assets/class/assets.class.php + * \ingroup assets + * \brief This file is a CRUD class file for assets (Create/Read/Update/Delete) + */ + +require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + +/** + * Class for Assets + */ +class Assets extends CommonObject +{ + /** + * @var string ID to identify managed object + */ + public $element = 'assets'; + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'assets'; + /** + * @var int Does assets support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + */ + public $ismultientitymanaged = 0; + /** + * @var int Does assets support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 1; + /** + * @var string String with name of icon for assets. Must be the part after the 'object_' into object_assets.png + */ + public $picto = 'assets'; + + + /** + * 'type' if the field format. + * 'label' the translation key. + * 'enabled' is a condition when the field must be managed. + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'position' is the sort order of field. + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). + * 'help' is a string visible as a tooltip on field + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'default' is a default value for creation (can still be replaced by the global setup of default values) + * 'showoncombobox' if field must be shown into the label of combobox + */ + + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-1, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",), + 'ref' => array('type'=>'varchar(10)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object",), + 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>-1, 'enabled'=>1, 'position'=>20, 'notnull'=>1, 'index'=>1,), + 'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text",), + 'amount' => array('type'=>'double(24,8)', 'label'=>'Amount', 'visible'=>1, 'enabled'=>1, 'position'=>40, 'notnull'=>-1, 'isameasure'=>'1', 'help'=>"Help text",), + 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToThirparty",), + 'description' => array('type'=>'text', 'label'=>'Description', 'visible'=>-1, 'enabled'=>1, 'position'=>90, 'notnull'=>-1,), + 'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'visible'=>-1, 'enabled'=>1, 'position'=>91, 'notnull'=>-1,), + 'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'visible'=>-1, 'enabled'=>1, 'position'=>92, 'notnull'=>-1,), + 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>-2, 'enabled'=>1, 'position'=>500, 'notnull'=>1,), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-2, 'enabled'=>1, 'position'=>501, 'notnull'=>1,), + 'fk_user_creat' => array('type'=>'integer', 'label'=>'UserAuthor', 'visible'=>-2, 'enabled'=>1, 'position'=>510, 'notnull'=>1,), + 'fk_user_modif' => array('type'=>'integer', 'label'=>'UserModif', 'visible'=>-2, 'enabled'=>1, 'position'=>511, 'notnull'=>-1,), + 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'visible'=>-2, 'enabled'=>1, 'position'=>1000, 'notnull'=>-1,), + 'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Draft', '1'=>'Active', '-1'=>'Cancel')), + ); + public $rowid; + public $ref; + public $entity; + public $label; + public $amount; + public $fk_soc; + public $description; + public $note_public; + public $note_private; + public $date_creation; + public $tms; + public $fk_user_creat; + public $fk_user_modif; + public $import_key; + public $status; + + // If this object has a subtable with lines + + /** + * @var int Name of subtable line + */ + //public $table_element_line = 'assetsdet'; + /** + * @var int Field with ID of parent key if this field has a parent + */ + //public $fk_element = 'fk_assets'; + /** + * @var int Name of subtable class that manage subtable lines + */ + //public $class_element_line = 'Assetsline'; + /** + * @var array Array of child tables (child tables to delete before deleting a record) + */ + //protected $childtables=array('assetsdet'); + /** + * @var AssetsLine[] Array of subtable lines + */ + //public $lines = array(); + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf; + + $this->db = $db; + + if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID)) $this->fields['rowid']['visible']=0; + if (empty($conf->multicompany->enabled)) $this->fields['entity']['enabled']=0; + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create(User $user, $notrigger = false) + { + return $this->createCommon($user, $notrigger); + } + + /** + * Clone and object into another one + * + * @param User $user User that creates + * @param int $fromid Id of object to clone + * @return mixed New object created, <0 if KO + */ + public function createFromClone(User $user, $fromid) + { + global $hookmanager, $langs; + $error = 0; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $object = new self($this->db); + + $this->db->begin(); + + // Load source object + $object->fetchCommon($fromid); + // Reset some properties + unset($object->id); + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + $object->ref = "copy_of_".$object->ref; + $object->title = $langs->trans("CopyOf")." ".$object->title; + + // Create clone + $object->context['createfromclone'] = 'createfromclone'; + $result = $object->createCommon($user); + if ($result < 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + // End + if (!$error) { + $this->db->commit(); + return $object; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetch($id, $ref = null) + { + $result = $this->fetchCommon($id, $ref); + if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines(); + return $result; + } + + /** + * Load object lines in memory from the database + * + * @return int <0 if KO, 0 if not found, >0 if OK + */ + /*public function fetchLines() + { + $this->lines=array(); + + // Load lines with object AssetsLine + + return count($this->lines)?1:0; + }*/ + + /** + * Update object into database + * + * @param User $user User that modifies + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update(User $user, $notrigger = false) + { + return $this->updateCommon($user, $notrigger); + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete(User $user, $notrigger = false) + { + return $this->deleteCommon($user, $notrigger); + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) + { + global $db, $conf, $langs; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; + + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("Asset") . ''; + $label.= '
'; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = dol_buildpath('/assets/card.php',1).'?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowAssets"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = ''; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + return $result; + } + + /** + * Retourne le libelle du status d'un user (actif, inactif) + * + * @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto + * @return string Label of status + */ + function getLibStatut($mode=0) + { + return $this->LibStatut($this->status,$mode); + } + + /** + * Return the status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + static function LibStatut($status,$mode=0) + { + global $langs; + + if ($mode == 0) + { + $prefix=''; + if ($status == 1) return $langs->trans('Enabled'); + if ($status == 0) return $langs->trans('Disabled'); + } + if ($mode == 1) + { + if ($status == 1) return $langs->trans('Enabled'); + if ($status == 0) return $langs->trans('Disabled'); + } + if ($mode == 2) + { + if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled'); + if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5').' '.$langs->trans('Disabled'); + } + if ($mode == 3) + { + if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4'); + if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5'); + } + if ($mode == 4) + { + if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled'); + if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5').' '.$langs->trans('Disabled'); + } + if ($mode == 5) + { + if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4'); + if ($status == 0) return $langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut5'); + } + if ($mode == 6) + { + if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4'); + if ($status == 0) return $langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut5'); + } + } + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + function info($id) + { + $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; + $sql.= ' fk_user_creat, fk_user_modif'; + $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; + $sql.= ' WHERE t.rowid = '.$id; + $result=$this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_valid) + { + $vuser = new User($this->db); + $vuser->fetch($obj->fk_user_valid); + $this->user_validation = $vuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_validation = $this->db->jdate($obj->datev); + } + + $this->db->free($result); + + } + else + { + dol_print_error($this->db); + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->initAsSpecimenCommon(); + } + + + /** + * Action executed by scheduler + * CAN BE A CRON TASK + * + * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) + */ + public function doScheduledJob() + { + global $conf, $langs; + + $this->output = ''; + $this->error=''; + + dol_syslog(__METHOD__, LOG_DEBUG); + + return 0; + } +} \ No newline at end of file diff --git a/htdocs/assets/class/asset_type.class.php b/htdocs/assets/class/asset_type.class.php new file mode 100644 index 00000000000..9889ceeb5d1 --- /dev/null +++ b/htdocs/assets/class/asset_type.class.php @@ -0,0 +1,421 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/assets/class/asset_type.class.php + * \ingroup assets + * \brief File of class to manage asset types + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; + + +/** + * Class to manage asset type + */ +class AssetType extends CommonObject +{ + public $table_element = 'asset_type'; + public $element = 'asset_type'; + public $picto = 'group'; + public $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + + /** @var string Label */ + public $label; + /** @var string Accountancy code asset */ + public $accountancy_code_asset; + /** @var string Accountancy code depreciation asset */ + public $accountancy_code_depreciation_asset; + /** @var string Accountancy code depreciation expense */ + public $accountancy_code_depreciation_expense; + /** @var string Public note */ + public $note; + /** @var array Array of asset */ + public $asset=array(); + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct($db) + { + $this->db = $db; + } + + + /** + * Fonction qui permet de creer le type d'immobilisation + * + * @param User $user User making creation + * @param int $notrigger 1=do not execute triggers, 0 otherwise + * @return int >0 if OK, < 0 if KO + */ + function create($user,$notrigger=0) + { + global $conf; + + $error=0; + + $this->label=trim($this->label); + + $this->db->begin(); + + $sql = "INSERT INTO ".MAIN_DB_PREFIX."asset_type ("; + $sql.= "label"; + $sql.= ", entity"; + $sql.= ") VALUES ("; + $sql.= "'".$this->db->escape($this->label)."'"; + $sql.= ", ".$conf->entity; + $sql.= ")"; + + dol_syslog("Asset_type::create", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."asset_type"); + + $result = $this->update($user,1); + if ($result < 0) + { + $this->db->rollback(); + return -3; + } + + if (! $notrigger) + { + // Call trigger + $result=$this->call_trigger('ASSET_TYPE_CREATE',$user); + if ($result < 0) { $error++; } + // End call triggers + } + + if (! $error) + { + $this->db->commit(); + return $this->id; + } + else + { + dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR); + $this->db->rollback(); + return -2; + } + } + else + { + $this->error=$this->db->lasterror(); + $this->db->rollback(); + return -1; + } + } + + /** + * Met a jour en base donnees du type + * + * @param User $user Object user making change + * @param int $notrigger 1=do not execute triggers, 0 otherwise + * @return int >0 if OK, < 0 if KO + */ + function update($user,$notrigger=0) + { + global $conf, $hookmanager; + + $error=0; + + $this->label=trim($this->label); + + $this->db->begin(); + + $sql = "UPDATE ".MAIN_DB_PREFIX."asset_type "; + $sql.= "SET "; + $sql.= "label = '".$this->db->escape($this->label) ."',"; + $sql.= "accountancy_code_asset = '".$this->db->escape($this->accountancy_code_asset)."',"; + $sql.= "accountancy_code_depreciation_asset = '".$this->db->escape($this->accountancy_code_depreciation_asset)."',"; + $sql.= "accountancy_code_depreciation_expense = '".$this->db->escape($this->accountancy_code_depreciation_expense)."'"; + $sql.= " WHERE rowid =".$this->id; + + $result = $this->db->query($sql); + if ($result) + { + $action='update'; + + // Actions on extra fields (by external module or standard code) + $hookmanager->initHooks(array('assettypedao')); + $parameters=array('assettype'=>$this->id); + $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if (empty($reshook)) + { + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used + { + $result=$this->insertExtraFields(); + if ($result < 0) + { + $error++; + } + } + } + else if ($reshook < 0) $error++; + + if (! $error && ! $notrigger) + { + // Call trigger + $result=$this->call_trigger('ASSET_TYPE_MODIFY',$user); + if ($result < 0) { $error++; } + // End call triggers + } + + if (! $error) + { + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + dol_syslog(get_class($this)."::update ".$this->error, LOG_ERR); + return -$error; + } + } + else + { + $this->error=$this->db->lasterror(); + $this->db->rollback(); + return -1; + } + } + + /** + * Fonction qui permet de supprimer le status de l'adherent + * + * @return int >0 if OK, 0 if not found, < 0 if KO + */ + function delete() + { + global $user; + + $error = 0; + + $sql = "DELETE FROM ".MAIN_DB_PREFIX."asset_type"; + $sql.= " WHERE rowid = ".$this->id; + + $resql=$this->db->query($sql); + if ($resql) + { + // Call trigger + $result=$this->call_trigger('ASSET_TYPE_DELETE',$user); + if ($result < 0) { $error++; $this->db->rollback(); return -2; } + // End call triggers + + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + $this->error=$this->db->lasterror(); + return -1; + } + } + + /** + * Fonction qui permet de recuperer le status de l'immobilisation + * + * @param int $rowid Id of member type to load + * @return int <0 if KO, >0 if OK + */ + function fetch($rowid) + { + $sql = "SELECT d.rowid, d.label as label, d.accountancy_code_asset, d.accountancy_code_depreciation_asset, d.accountancy_code_depreciation_expense, d.note"; + $sql .= " FROM ".MAIN_DB_PREFIX."asset_type as d"; + $sql .= " WHERE d.rowid = ".(int) $rowid; + + dol_syslog("Asset_type::fetch", LOG_DEBUG); + + $resql=$this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + $this->ref = $obj->rowid; + $this->label = $obj->label; + $this->accountancy_code_asset = $obj->accountancy_code_asset; + $this->accountancy_code_depreciation_asset = $obj->accountancy_code_depreciation_asset; + $this->accountancy_code_depreciation_expense = $obj->accountancy_code_depreciation_expense; + $this->note = $obj->note; + } + + return 1; + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + } + + /** + * Return list of asset's type + * + * @return array List of types of members + */ + function liste_array() + { + global $conf,$langs; + + $assettypes = array(); + + $sql = "SELECT rowid, label as label"; + $sql.= " FROM ".MAIN_DB_PREFIX."asset_type"; + $sql.= " WHERE entity IN (".getEntity('asset_type').")"; + + $resql=$this->db->query($sql); + if ($resql) + { + $nump = $this->db->num_rows($resql); + + if ($nump) + { + $i = 0; + while ($i < $nump) + { + $obj = $this->db->fetch_object($resql); + + $assettypes[$obj->rowid] = $langs->trans($obj->label); + $i++; + } + } + } + else + { + print $this->db->error(); + } + return $assettypes; + } + + /** + * Return array of Asset objects for asset type this->id (or all if this->id not defined) + * + * @param string $excludefilter Filter to exclude + * @param int $mode 0=Return array of asset instance + * 1=Return array of asset instance without extra data + * 2=Return array of asset id only + * @return mixed Array of asset or -1 on error + */ + function listAssetForAssetType($excludefilter='', $mode=0) + { + global $conf, $user; + + $ret=array(); + + $sql = "SELECT a.rowid"; + $sql.= " FROM ".MAIN_DB_PREFIX."asset as a"; + $sql.= " WHERE a.entity IN (".getEntity('asset').")"; + $sql.= " AND a.fk_asset_type = ".$this->id; + if (! empty($excludefilter)) $sql.=' AND ('.$excludefilter.')'; + + dol_syslog(get_class($this)."::listAssetsForGroup", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + while ($obj = $this->db->fetch_object($resql)) + { + if (! array_key_exists($obj->rowid, $ret)) + { + if ($mode < 2) + { + $assetstatic=new Asset($this->db); + if ($mode == 1) { + $assetstatic->fetch($obj->rowid,'','','',false, false); + } else { + $assetstatic->fetch($obj->rowid); + } + $ret[$obj->rowid]=$assetstatic; + } + else $ret[$obj->rowid]=$obj->rowid; + } + } + + $this->db->free($resql); + + $this->asset=$ret; + + return $ret; + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + } + + /** + * Return clicable name (with picto eventually) + * + * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto + * @param int $maxlen length max label + * @param int $notooltip 1=Disable tooltip + * @return string String with URL + */ + function getNomUrl($withpicto=0, $maxlen=0, $notooltip=0) + { + global $langs; + + $result=''; + $label=$langs->trans("ShowTypeCard",$this->label); + + $linkstart = ''; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= ($maxlen?dol_trunc($this->label,$maxlen):$this->label); + $result .= $linkend; + + return $result; + } + + /** + * Initialise an instance with random values. + * Used to build previews or test instances. + * id must be 0 if object instance is a specimen. + * + * @return void + */ + function initAsSpecimen() + { + global $conf, $user, $langs; + + // Initialize parameters + $this->id = 0; + $this->ref = 'ATSPEC'; + $this->specimen=1; + + $this->label='ASSET TYPE SPECIMEN'; + $this->note='This is a note'; + + // Assets of this asset type is just me + $this->asset=array( + $user->id => $user + ); + } + +} diff --git a/htdocs/assets/document.php b/htdocs/assets/document.php new file mode 100644 index 00000000000..490fb8f36a4 --- /dev/null +++ b/htdocs/assets/document.php @@ -0,0 +1,153 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file document.php + * \ingroup assets + * \brief Tab for documents linked to Assets + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/assets.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; + +// Load traductions files requiredby by page +$langs->loadLangs(array("assets","companies")); + + +$action=GETPOST('action','aZ09'); +$confirm=GETPOST('confirm'); +$id=(GETPOST('socid','int') ? GETPOST('socid','int') : GETPOST('id','int')); +$ref = GETPOST('ref', 'alpha'); + +// Security check - Protection if external user +//if ($user->societe_id > 0) access_forbidden(); +//if ($user->societe_id > 0) $socid = $user->societe_id; +//$result = restrictedArea($user, 'assets', $id); + +// Get parameters +$sortfield = GETPOST("sortfield",'alpha'); +$sortorder = GETPOST("sortorder",'alpha'); +$page = GETPOST("page",'int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +$offset = $conf->liste_limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (! $sortorder) $sortorder="ASC"; +if (! $sortfield) $sortfield="name"; + +// Initialize technical objects +$object=new Assets($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction=$conf->assets->dir_output . '/temp/massgeneration/'.$user->id; +$hookmanager->initHooks(array('assetsdocument')); // Note that conf->hooks_modules contains array +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('assets'); + +// Load object +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals + +//if ($id > 0 || ! empty($ref)) $upload_dir = $conf->sellyoursaas->multidir_output[$object->entity] . "/packages/" . dol_sanitizeFileName($object->id); +if ($id > 0 || ! empty($ref)) $upload_dir = $conf->sellyoursaas->multidir_output[$object->entity] . "/packages/" . dol_sanitizeFileName($object->ref); + + + +/* + * Actions + */ + +include_once DOL_DOCUMENT_ROOT . '/core/actions_linkedfiles.inc.php'; + + +/* + * View + */ + +$form = new Form($db); + +$title=$langs->trans("Assets").' - '.$langs->trans("Files"); +$help_url=''; +//$help_url='EN:Module_Third_Parties|FR:Module_Tiers|ES:Empresas'; +llxHeader('', $title, $help_url); + +if ($object->id) +{ + /* + * Show tabs + */ + if (! empty($conf->notification->enabled)) $langs->load("mails"); + $head = AssetsPrepareHead($object); + + dol_fiche_head($head, 'document', $langs->trans("Asset"), -1, 'generic'); + + + // Construit liste des fichiers + $filearray=dol_dir_list($upload_dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1); + $totalsize=0; + foreach($filearray as $key => $file) + { + $totalsize+=$file['size']; + } + + // Object card + // ------------------------------------------------------------ + $linkback = '' . $langs->trans("BackToList") . ''; + + dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); + + print '
'; + + print '
'; + print ''; + + // Number of files + print ''; + + // Total size + print ''; + + print '
'.$langs->trans("NbOfAttachedFiles").''.count($filearray).'
'.$langs->trans("TotalSizeOfAttachedFiles").''.$totalsize.' '.$langs->trans("bytes").'
'; + + print '
'; + + dol_fiche_end(); + + $modulepart = 'assets'; + //$permission = $user->rights->assets->create; + $permission = 1; + //$permtoedit = $user->rights->assets->create; + $permtoedit = 1; + $param = '&id=' . $object->id; + + //$relativepathwithnofile='assets/' . dol_sanitizeFileName($object->id).'/'; + $relativepathwithnofile='assets/' . dol_sanitizeFileName($object->ref).'/'; + + include_once DOL_DOCUMENT_ROOT . '/core/tpl/document_actions_post_headers.tpl.php'; +} +else +{ + accessforbidden('',0,0); +} + + +llxFooter(); +$db->close(); diff --git a/htdocs/assets/info.php b/htdocs/assets/info.php new file mode 100644 index 00000000000..9017a9e6845 --- /dev/null +++ b/htdocs/assets/info.php @@ -0,0 +1,82 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file info.php + * \ingroup assets + * \brief Page to show an asset information + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/assets.class.php'; + +$langs->loadLangs(array("assets")); + +$id = GETPOST('id','int'); +$ref=GETPOST('ref','alpha'); +$action=GETPOST('action','alpha'); + +// Security check +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'assets', $id, ''); + +$object = new Assets($db); +$object->fetch($id); + +/* + * Actions + */ + +/* + * View + */ +$title = $langs->trans('Asset') . " - " . $langs->trans('Info'); +$helpurl = ""; +llxHeader('', $title, $helpurl); + +$form = new Form($db); + +$object->info($id); + +$head = AssetsPrepareHead($object); + +dol_fiche_head($head, 'info', $langs->trans("Asset"), -1, 'generic'); + +$linkback = ''.$langs->trans("BackToList").''; + +$morehtmlref='
'; +$morehtmlref.='
'; + +dol_banner_tab($object, 'rowid', $linkback, 1, 'rowid', 'ref', $morehtmlref); + +print '
'; +print '
'; + +print '
'; + +print '
'; +dol_print_object_info($object); +print '
'; + +print '
'; + +dol_fiche_end(); + +llxFooter(); +$db->close(); diff --git a/htdocs/assets/list.php b/htdocs/assets/list.php new file mode 100644 index 00000000000..c8ac1f869f1 --- /dev/null +++ b/htdocs/assets/list.php @@ -0,0 +1,540 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file list.php + * \ingroup assets + * \brief List page for assets + */ + +//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); +//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1'); +//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); +//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); +//if (! defined('NOSCANGETFORINJECTION')) define('NOSCANGETFORINJECTION','1'); // Do not check anti CSRF attack test +//if (! defined('NOSCANPOSTFORINJECTION')) define('NOSCANPOSTFORINJECTION','1'); // Do not check anti CSRF attack test +//if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK','1'); // Do not check anti CSRF attack test done when option MAIN_SECURITY_CSRF_WITH_TOKEN is on. +//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK','1'); // Do not check style html tag into posted data +//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Do not check anti POST attack test +//if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU','1'); // If there is no need to load and show top and left menu +//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1'); // If we don't need to load the html.form.class.php +//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); // Do not load ajax.lib.php library +//if (! defined("NOLOGIN")) define("NOLOGIN",'1'); // If this page is public (can be called outside logged session) + + +// Load Dolibarr environment +require '../main.inc.php'; +require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'); +require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/assets.class.php'; + +// Load traductions files requiredby by page +$langs->loadLangs(array("assets")); + +$action = GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... +$massaction = GETPOST('massaction','alpha'); // The bulk action (combo box choice into lists) +$show_files = GETPOST('show_files','int'); // Show files area generated by bulk actions ? +$confirm = GETPOST('confirm','alpha'); // Result of a confirmation +$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button +$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'assetslist'; // To manage different context of search +$backtopage = GETPOST('backtopage','alpha'); // Go back to a dedicated page +$optioncss = GETPOST('optioncss','aZ'); // Option for the css output (always '' except when 'print') + +$id = GETPOST('id','int'); + +// Load variable for pagination +$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit; +$sortfield = GETPOST('sortfield','alpha'); +$sortorder = GETPOST('sortorder','alpha'); +$page = GETPOST('page','int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; + +// Initialize technical objects +$object=new Assets($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction=$conf->assets->dir_output . '/temp/massgeneration/'.$user->id; +$hookmanager->initHooks(array('assetslist')); // Note that conf->hooks_modules contains array +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('assets'); +$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); + +// Default sort order (if not yet defined by previous GETPOST) +if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. +if (! $sortorder) $sortorder="ASC"; + +// Protection if external user +$socid=0; +if ($user->societe_id > 0) +{ + //$socid = $user->societe_id; + accessforbidden(); +} +//$result = restrictedArea($user, 'assets', $id,''); + +// Initialize array of search criterias +$search_all=trim(GETPOST("search_all",'alpha')); +$search=array(); +foreach($object->fields as $key => $val) +{ + if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); +} + +// List of fields to search into when doing a "search in all" +$fieldstosearchall = array(); +foreach($object->fields as $key => $val) +{ + if ($val['searchall']) $fieldstosearchall['t.'.$key]=$val['label']; +} + +// Definition of fields for list +$arrayfields=array(); +foreach($object->fields as $key => $val) +{ + // If $val['visible']==0, then we never show the field + if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']); +} +// Extra fields +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) +{ + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($extrafields->attribute_list[$key])) $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>(($extrafields->attribute_list[$key]<0)?0:1), 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>(abs($extrafields->attribute_list[$key])!=3 && $extrafields->attribute_perms[$key])); + } +} +$object->fields = dol_sort_array($object->fields, 'position'); +$arrayfields = dol_sort_array($arrayfields, 'position'); + + + +/* + * Actions + * + * Put here all code to do according to value of "$action" parameter + */ + +if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; } +if (! GETPOST('confirmmassaction','alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; } + +$parameters=array(); +$reshook=$hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +if (empty($reshook)) +{ + // Selection of new fields + include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + + // Purge search criteria + if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers + { + foreach($object->fields as $key => $val) + { + $search[$key]=''; + } + $toselect=''; + $search_array_options=array(); + } + if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha') + || GETPOST('button_search_x','alpha') || GETPOST('button_search.x','alpha') || GETPOST('button_search','alpha')) + { + $massaction=''; // Protection to avoid mass action if we force a new search during a mass action confirmation + } + + // Mass actions + $objectclass='Assets'; + $objectlabel='Assets'; + $permtoread = $user->rights->assets->read; + $permtodelete = $user->rights->assets->delete; + $uploaddir = $conf->assets->dir_output; + include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; +} + + + +/* + * View + * + * Put here all code to render page + */ + +$form=new Form($db); + +$now=dol_now(); + +//$help_url="EN:Module_Assets|FR:Module_Assets_FR|ES:Módulo_Assets"; +$help_url=''; +$title = $langs->trans('ListOf', $langs->transnoentitiesnoconv("Assets")); + + +// Build and execute select +// -------------------------------------------------------------------- +$sql = 'SELECT '; +foreach($object->fields as $key => $val) +{ + $sql.='t.'.$key.', '; +} +// Add fields from extrafields +foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ", ef.".$key.' as options_'.$key : ''); +// Add fields from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +$sql=preg_replace('/, $/','', $sql); +$sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."assets_extrafields as ef on (t.rowid = ef.fk_object)"; +if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity('assets').")"; +else $sql.=" WHERE 1 = 1"; +foreach($search as $key => $val) +{ + $mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0); + if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'status')?2:$mode_search)); +} +if ($search_all) $sql.= natural_search(array_keys($fieldstosearchall), $search_all); +// Add where from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; +// Add where from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; + +/* If a group by is required +$sql.= " GROUP BY " +foreach($object->fields as $key => $val) +{ + $sql.='t.'.$key.', '; +} +// Add fields from extrafields +foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key : ''); +// Add where from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +*/ + +$sql.=$db->order($sortfield,$sortorder); + +// Count total nb of records +$nbtotalofrecords = ''; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) +{ + $result = $db->query($sql); + $nbtotalofrecords = $db->num_rows($result); +} + +$sql.= $db->plimit($limit+1, $offset); + +$resql=$db->query($sql); +if (! $resql) +{ + dol_print_error($db); + exit; +} + +$num = $db->num_rows($resql); + +// Direct jump if only one record found +if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all) +{ + $obj = $db->fetch_object($resql); + $id = $obj->rowid; + header("Location: ".DOL_URL_ROOT.'/assets/card.php?id='.$id); + exit; +} + + +// Output page +// -------------------------------------------------------------------- + +llxHeader('', $title, $help_url); + +// Example : Adding jquery code +print ''; + +$arrayofselected=is_array($toselect)?$toselect:array(); + +$param=''; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); +foreach($search as $key => $val) +{ + $param.= '&search_'.$key.'='.urlencode($search[$key]); +} +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +// Add $param from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; + +// List of mass actions available +$arrayofmassactions = array( + //'presend'=>$langs->trans("SendByMail"), + //'builddoc'=>$langs->trans("PDFMerge"), +); +if ($user->rights->assets->delete) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); +$massactionbutton=$form->selectMassAction('', $arrayofmassactions); + +print '
'; +if ($optioncss != '') print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, '', '', $limit); + +// Add code for pre mass action (confirmation or email presend form) +$topicmail="SendAssetsRef"; +$modelmail="assets"; +$objecttmp=new Assets($db); +$trackid='xxxx'.$object->id; +include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; + +if ($sall) +{ + foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); + print $langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall); +} + +$moreforfilter = ''; +/*$moreforfilter.='
'; +$moreforfilter.= $langs->trans('MyFilter') . ': '; +$moreforfilter.= '
';*/ + +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint; +else $moreforfilter = $hookmanager->resPrint; + +if (! empty($moreforfilter)) +{ + print '
'; + print $moreforfilter; + print '
'; +} + +$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; +$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields +$selectedfields.=(count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); + +print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table +print ''."\n"; + + +// Fields title search +// -------------------------------------------------------------------- +print ''; +foreach($object->fields as $key => $val) +{ + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) print ''; +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; + +// Fields from hook +$parameters=array('arrayfields'=>$arrayfields); +$reshook=$hookmanager->executeHooks('printFieldListOption', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Action column +print ''; +print ''."\n"; + + +// Fields title label +// -------------------------------------------------------------------- +print ''; +foreach($object->fields as $key => $val) +{ + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($align?'class="'.$align.'"':''), $sortfield, $sortorder, $align.' ')."\n"; +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; +// Hook fields +$parameters=array('arrayfields'=>$arrayfields); +$reshook=$hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],'','','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; +print ''."\n"; + + +// Detect if we need a fetch on each output line +$needToFetchEachLine=0; +foreach ($extrafields->attribute_computed as $key => $val) +{ + if (preg_match('/\$object/',$val)) $needToFetchEachLine++; // There is at least one compute field that use $object +} + + +// Loop on record +// -------------------------------------------------------------------- +$i=0; +$totalarray=array(); +while ($i < min($num, $limit)) +{ + $obj = $db->fetch_object($resql); + if (empty($obj)) break; // Should not happen + + // Store properties in $object + $object->id = $obj->rowid; + foreach($object->fields as $key => $val) + { + if (isset($obj->$key)) $object->$key = $obj->$key; + } + + // Show here line of result + print ''; + foreach($object->fields as $key => $val) + { + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) + { + print ''; + print $object->showOutputField($val, $key, $obj->$key, ''); + print ''; + if (! $i) $totalarray['nbfield']++; + if (! empty($val['isameasure'])) + { + if (! $i) $totalarray['pos'][$totalarray['nbfield']]='t.'.$key; + $totalarray['val']['t.'.$key] += $obj->$key; + } + } + } + // Extra fields + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); + $reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $object); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Action column + print ''; + if (! $i) $totalarray['nbfield']++; + + print ''; + + $i++; +} + +// Show total line +if (isset($totalarray['pos'])) +{ + print ''; + $i=0; + while ($i < $totalarray['nbfield']) + { + $i++; + if (! empty($totalarray['pos'][$i])) print ''; + else + { + if ($i == 1) + { + if ($num < $limit) print ''; + else print ''; + } + else print ''; + } + } + print ''; +} + +// If no record found +if ($num == 0) +{ + $colspan=1; + foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; } + print ''; +} + + +$db->free($resql); + +$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql); +$reshook=$hookmanager->executeHooks('printFieldListFooter', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +print '
'; +$searchpicto=$form->showFilterButtons(); +print $searchpicto; +print '
'; + if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + { + $selected=0; + if (in_array($obj->rowid, $arrayofselected)) $selected=1; + print ''; + } + print '
'.price($totalarray['val'][$totalarray['pos'][$i]]).''.$langs->trans("Total").''.$langs->trans("Totalforthispage").'
'.$langs->trans("NoRecordFound").'
'."\n"; +print '
'."\n"; + +print '
'."\n"; + +if (in_array('builddoc',$arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords)) +{ + if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) + { + require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'); + $formfile = new FormFile($db); + + // Show list of available documents + $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder; + $urlsource.=str_replace('&','&',$param); + + $filedir=$diroutputmassaction; + $genallowed=$user->rights->assets->read; + $delallowed=$user->rights->assets->create; + + print $formfile->showdocuments('massfilesarea_assets','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,''); + } + else + { + print '
'.$langs->trans("ShowTempMassFilesArea").''; + } +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/assets/note.php b/htdocs/assets/note.php new file mode 100644 index 00000000000..b0db48e2abe --- /dev/null +++ b/htdocs/assets/note.php @@ -0,0 +1,149 @@ + + * Copyright (C) 2018 Alexandre Spangaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file note.php + * \ingroup assets + * \brief Card with notes on Assets + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/assets.class.php'; + +// Load traductions files requiredby by page +$langs->loadLangs(array("assets","companies")); + +// Get parameters +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'alpha'); +$cancel = GETPOST('cancel', 'aZ09'); +$backtopage = GETPOST('backtopage', 'alpha'); + +// Initialize technical objects +$object=new Assets($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction=$conf->assets->dir_output . '/temp/massgeneration/'.$user->id; +$hookmanager->initHooks(array('assetsnote')); // Note that conf->hooks_modules contains array +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('assets'); + +// Security check - Protection if external user +//if ($user->societe_id > 0) access_forbidden(); +//if ($user->societe_id > 0) $socid = $user->societe_id; +//$result = restrictedArea($user, 'assets', $id); + +// Load object +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals +if ($id > 0 || ! empty($ref)) $upload_dir = $conf->assets->multidir_output[$object->entity] . "/" . $object->id; + +$permissionnote=1; +//$permissionnote=$user->rights->assets->creer; // Used by the include of actions_setnotes.inc.php + + +/* + * Actions + */ + +include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not include_once + + +/* + * View + */ + +$form = new Form($db); + +//$help_url='EN:Customers_Orders|FR:Commandes_Clients|ES:Pedidos de clientes'; +$help_url=''; +llxHeader('',$langs->trans('Assets'),$help_url); + +if ($id > 0 || ! empty($ref)) +{ + $object->fetch_thirdparty(); + + $head = AssetsPrepareHead($object); + + dol_fiche_head($head, 'note', $langs->trans("Asset"), -1, 'generic'); + + // Object card + // ------------------------------------------------------------ + $linkback = '' . $langs->trans("BackToList") . ''; + + $morehtmlref='
'; + /* + // Ref customer + $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); + $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); + // Thirdparty + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + // Project + if (! empty($conf->projet->enabled)) + { + $langs->load("projects"); + $morehtmlref.='
'.$langs->trans('Project') . ' '; + if ($user->rights->assets->creer) + { + if ($action != 'classify') + //$morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ' : '; + $morehtmlref.=' : '; + if ($action == 'classify') { + //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); + $morehtmlref.='
'; + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); + $morehtmlref.=''; + $morehtmlref.='
'; + } else { + $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); + } + } else { + if (! empty($object->fk_project)) { + $proj = new Project($db); + $proj->fetch($object->fk_project); + $morehtmlref.=''; + $morehtmlref.=$proj->ref; + $morehtmlref.=''; + } else { + $morehtmlref.=''; + } + } + }*/ + $morehtmlref.='
'; + + + dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); + + + print '
'; + print '
'; + + + $cssclass="titlefield"; + include DOL_DOCUMENT_ROOT.'/core/tpl/notes.tpl.php'; + + print '
'; + + dol_fiche_end(); +} + + +llxFooter(); +$db->close(); diff --git a/htdocs/assets/type.php b/htdocs/assets/type.php new file mode 100644 index 00000000000..51445f86254 --- /dev/null +++ b/htdocs/assets/type.php @@ -0,0 +1,763 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/assets/type.php + * \ingroup assets + * \brief Asset's type setup + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/assets.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/asset.class.php'; +require_once DOL_DOCUMENT_ROOT.'/assets/class/asset_type.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; + +$langs->load("assets"); + +$rowid = GETPOST('rowid','int'); +$action = GETPOST('action','alpha'); +$cancel = GETPOST('cancel','alpha'); +$backtopage = GETPOST('backtopage','alpha'); + +$type = GETPOST('type','alpha'); + +$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit; +$sortfield = GETPOST("sortfield",'alpha'); +$sortorder = GETPOST("sortorder",'alpha'); +$page = GETPOST("page",'int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +$offset = $limit * $page ; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (! $sortorder) { $sortorder="DESC"; } +if (! $sortfield) { $sortfield="d.lastname"; } + +$label=GETPOST("label","alpha"); +$comment=GETPOST("comment"); + +// Security check +$result=restrictedArea($user,'assets',$rowid,'asset_type'); + +$object = new AssetType($db); + +$extrafields = new ExtraFields($db); + +// fetch optionals attributes and labels +$extralabels=$extrafields->fetch_name_optionals_label('asset_type'); + +if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers +{ + $type=""; + $sall=""; +} + + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('assettypecard','globalcard')); + + +/* + * Actions + */ + +if ($cancel) { + + $action=''; + + if (! empty($backtopage)) + { + header("Location: ".$backtopage); + exit; + } +} + +if ($action == 'add' && $user->rights->assets->write) +{ + $object->label = trim($label); + $object->accountancy_code_asset = trim($accountancy_code_asset); + $object->accountancy_code_depreciation_asset = trim($accountancy_code_depreciation_asset); + $object->accountancy_code_depreciation_expense = trim($accountancy_code_depreciation_expense); + $object->note = trim($comment); + + // Fill array 'array_options' with data from add form + $ret = $extrafields->setOptionalsFromPost($extralabels,$object); + if ($ret < 0) $error++; + + if (empty($object->label)) { + $error++; + setEventMessages($langs->trans("ErrorFieldRequired",$langs->transnoentities("Label")), null, 'errors'); + } + else { + $sql = "SELECT label FROM ".MAIN_DB_PREFIX."asset_type WHERE label='".$db->escape($object->label)."'"; + $result = $db->query($sql); + if ($result) { + $num = $db->num_rows($result); + } + if ($num) { + $error++; + $langs->load("errors"); + setEventMessages($langs->trans("ErrorLabelAlreadyExists",$login), null, 'errors'); + } + } + + if (! $error) + { + $id=$object->create($user); + if ($id > 0) + { + header("Location: ".$_SERVER["PHP_SELF"]); + exit; + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + $action = 'create'; + } + } + else + { + $action = 'create'; + } +} + +if ($action == 'update' && $user->rights->assets->write) +{ + $object->fetch($rowid); + + $object->oldcopy = clone $object; + + $object->label = trim($label); + $object->accountancy_code_asset = trim($accountancy_code_asset); + $object->accountancy_code_depreciation_asset = trim($accountancy_code_depreciation_asset); + $object->accountancy_code_depreciation_expense = trim($accountancy_code_depreciation_expense); + $object->note = trim($comment); + + // Fill array 'array_options' with data from add form + $ret = $extrafields->setOptionalsFromPost($extralabels,$object); + if ($ret < 0) $error++; + + $ret=$object->update($user); + + if ($ret >= 0 && ! count($object->errors)) + { + setEventMessages($langs->trans("AssetsTypeModified"), null, 'mesgs'); + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + } + + header("Location: ".$_SERVER["PHP_SELF"]."?rowid=".$object->id); + exit; +} + +if ($action == 'confirm_delete' && $user->rights->assets->write) +{ + $object->fetch($rowid); + $res=$object->delete(); + + if ($res > 0) + { + setEventMessages($langs->trans("AssetsTypeDeleted"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; + } + else + { + setEventMessages($langs->trans("AssetsTypeCanNotBeDeleted"), null, 'errors'); + $action=''; + } +} + + +/* + * View + */ + +$form=new Form($db); +$helpurl=''; +llxHeader('',$langs->trans("AssetsTypeSetup"),$helpurl); + + +// List of asset type +if (! $rowid && $action != 'create' && $action != 'edit') +{ + //dol_fiche_head(''); + + $sql = "SELECT d.rowid, d.label as label, d.accountancy_code_asset, d.accountancy_code_depreciation_asset, d.accountancy_code_depreciation_expense, d.note"; + $sql.= " FROM ".MAIN_DB_PREFIX."asset_type as d"; + $sql.= " WHERE d.entity IN (".getEntity('asset_type').")"; + + $result = $db->query($sql); + if ($result) + { + $num = $db->num_rows($result); + $nbtotalofrecords = $num; + + $i = 0; + + $param = ''; + + print '
'; + if ($optioncss != '') print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + print_barre_liste($langs->trans("AssetsTypes"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_generic.png', 0, '', '', $limit); + + $moreforfilter = ''; + + print '
'; + print ''."\n"; + + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print "\n"; + + $assettype = new AssetType($db); + + while ($i < $num) + { + $objp = $db->fetch_object($result); + + $assettype->id = $objp->rowid; + $assettype->ref = $objp->rowid; + $assettype->label = $objp->rowid; + + print ''; + print ''; + print ''; + print ''; + print ''; + if ($user->rights->adherent->configurer) + print ''; + else + print ''; + print ""; + $i++; + } + print "
'.$langs->trans("Ref").''.$langs->trans("Label").''.$langs->trans("SubscriptionRequired").''.$langs->trans("VoteAllowed").' 
'; + print $assettype->getNomUrl(1); + //'.img_object($langs->trans("ShowType"),'group').' '.$objp->rowid.' + print ''.dol_escape_htmltag($objp->label).''.yn($objp->subscription).''.yn($objp->vote).'rowid.'">'.img_edit().' 
"; + print '
'; + + print '
'; + } + else + { + dol_print_error($db); + } +} + + +/* ************************************************************************** */ +/* */ +/* Creation mode */ +/* */ +/* ************************************************************************** */ +if ($action == 'create') +{ + $object = new AdherentType($db); + + print load_fiche_titre($langs->trans("NewMemberType")); + + print '
'; + print ''; + print ''; + + dol_fiche_head(''); + + print ''; + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + // Other attributes + $parameters=array(); + $reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$act,$action); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + if (empty($reshook) && ! empty($extrafields->attribute_label)) + { + print $object->showOptionals($extrafields,'edit'); + } + print ''; + print "
'.$langs->trans("Label").'
'.$langs->trans("SubscriptionRequired").''; + print $form->selectyesno("subscription",1,1); + print '
'.$langs->trans("VoteAllowed").''; + print $form->selectyesno("vote",0,1); + print '
'.$langs->trans("Description").''; + print '
'.$langs->trans("WelcomeEMail").''; + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor=new DolEditor('mail_valid',$object->mail_valid,'',280,'dolibarr_notes','',false,true,$conf->fckeditor->enabled,15,'90%'); + $doleditor->Create(); + print '
\n"; + + dol_fiche_end(); + + print '
'; + print ''; + print '     '; + print ''; + print '
'; + + print "
\n"; +} + +/* ************************************************************************** */ +/* */ +/* View mode */ +/* */ +/* ************************************************************************** */ +if ($rowid > 0) +{ + if ($action != 'edit') + { + $object = new AdherentType($db); + $object->fetch($rowid); + $object->fetch_optionals(); + + /* + * Confirmation suppression + */ + if ($action == 'delete') + { + print $form->formconfirm($_SERVER['PHP_SELF']."?rowid=".$object->id,$langs->trans("DeleteAMemberType"),$langs->trans("ConfirmDeleteMemberType",$object->label),"confirm_delete", '',0,1); + } + + $head = member_type_prepare_head($object); + + dol_fiche_head($head, 'card', $langs->trans("MemberType"), -1, 'group'); + + $linkback = ''.$langs->trans("BackToList").''; + + dol_banner_tab($object, 'rowid', $linkback); + + print '
'; + print '
'; + + print ''; + + print ''; + + print ''; + + print '"; + + print '"; + + // Other attributes + include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php'; + + print '
'.$langs->trans("SubscriptionRequired").''; + print yn($object->subscription); + print '
'.$langs->trans("VoteAllowed").''; + print yn($object->vote); + print '
'.$langs->trans("Description").''; + print nl2br($object->note)."
'.$langs->trans("WelcomeEMail").''; + print nl2br($object->mail_valid)."
'; + print '
'; + + dol_fiche_end(); + + /* + * Buttons + */ + + print '
'; + + // Edit + if ($user->rights->adherent->configurer) + { + print ''; + } + + // Add + print ''; + + // Delete + if ($user->rights->adherent->configurer) + { + print ''; + } + + print "
"; + + + // Show list of members (nearly same code than in page list.php) + + $assettypestatic=new AssetType($db); + + $now=dol_now(); + + $sql = "SELECT a.rowid, d.login, d.firstname, d.lastname, d.societe, "; + $sql.= " d.datefin,"; + $sql.= " a.fk_asset_type as type_id,"; + $sql.= " t.label as type"; + $sql.= " FROM ".MAIN_DB_PREFIX."asset as a, ".MAIN_DB_PREFIX."asset_type as t"; + $sql.= " WHERE a.fk_asset_type = t.rowid "; + $sql.= " AND a.entity IN (".getEntity('asset').")"; + $sql.= " AND t.rowid = ".$object->id; + if ($sall) + { + $sql.=natural_search(array("f.firstname","d.lastname","d.societe","d.email","d.login","d.address","d.town","d.note_public","d.note_private"), $sall); + } + if ($status != '') + { + $sql.= natural_search('d.statut', $status, 2); + } + if ($action == 'search') + { + if (GETPOST('search','alpha')) + { + $sql.= natural_search(array("d.firstname","d.lastname"), GETPOST('search','alpha')); + } + } + if (! empty($search_lastname)) + { + $sql.= natural_search(array("d.firstname","d.lastname"), $search_lastname); + } + if (! empty($search_login)) + { + $sql.= natural_search("d.login", $search_login); + } + if (! empty($search_email)) + { + $sql.= natural_search("d.email", $search_email); + } + if ($filter == 'uptodate') + { + $sql.=" AND datefin >= '".$db->idate($now)."'"; + } + if ($filter == 'outofdate') + { + $sql.=" AND datefin < '".$db->idate($now)."'"; + } + // Count total nb of records + $nbtotalofrecords = ''; + if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) + { + $resql = $db->query($sql); + if ($resql) $nbtotalofrecords = $db->num_rows($result); + else dol_print_error($db); + } + // Add order and limit + $sql.= " ".$db->order($sortfield,$sortorder); + $sql.= " ".$db->plimit($conf->liste_limit+1, $offset); + + $resql = $db->query($sql); + if ($resql) + { + $num = $db->num_rows($resql); + $i = 0; + + $titre=$langs->trans("AssetsList"); + if ($status != '') + { + if ($status == '-1,1') { $titre=$langs->trans("MembersListQualified"); } + else if ($status == '-1') { $titre=$langs->trans("MembersListToValid"); } + else if ($status == '1' && ! $filter) { $titre=$langs->trans("MembersListValid"); } + else if ($status == '1' && $filter=='uptodate') { $titre=$langs->trans("MembersListUpToDate"); } + else if ($status == '1' && $filter=='outofdate') { $titre=$langs->trans("MembersListNotUpToDate"); } + else if ($status == '0') { $titre=$langs->trans("MembersListResiliated"); } + } + elseif ($action == 'search') + { + $titre=$langs->trans("MembersListQualified"); + } + + if ($type > 0) + { + $assettype=new AssetType($db); + $result=$assettype->fetch($type); + $titre.=" (".$assettype->label.")"; + } + + $param="&rowid=".$object->id; + if (! empty($status)) $param.="&status=".$status; + if (! empty($search_lastname)) $param.="&search_lastname=".$search_lastname; + if (! empty($search_firstname)) $param.="&search_firstname=".$search_firstname; + if (! empty($search_login)) $param.="&search_login=".$search_login; + if (! empty($search_email)) $param.="&search_email=".$search_email; + if (! empty($filter)) $param.="&filter=".$filter; + + if ($sall) + { + print $langs->trans("Filter")." (".$langs->trans("Lastname").", ".$langs->trans("Firstname").", ".$langs->trans("EMail").", ".$langs->trans("Address")." ".$langs->trans("or")." ".$langs->trans("Town")."): ".$sall; + } + + print '
'; + print ''; + + print '
'; + print_barre_liste('',$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords); + + $moreforfilter = ''; + + print '
'; + print ''."\n"; + + // Lignes des champs de filtre + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print "\n"; + + print ''; + print_liste_field_titre( $langs->trans("Name")." / ".$langs->trans("Company"),$_SERVER["PHP_SELF"],"d.lastname",$param,"","",$sortfield,$sortorder); + print_liste_field_titre("Login",$_SERVER["PHP_SELF"],"d.login",$param,"","",$sortfield,$sortorder); + print_liste_field_titre("Nature",$_SERVER["PHP_SELF"],"d.morphy",$param,"","",$sortfield,$sortorder); + print_liste_field_titre("EMail",$_SERVER["PHP_SELF"],"d.email",$param,"","",$sortfield,$sortorder); + print_liste_field_titre("Status",$_SERVER["PHP_SELF"],"d.statut,d.datefin",$param,"","",$sortfield,$sortorder); + print_liste_field_titre("EndSubscription",$_SERVER["PHP_SELF"],"d.datefin",$param,"",'align="center"',$sortfield,$sortorder); + print_liste_field_titre("Action",$_SERVER["PHP_SELF"],"",$param,"",'width="60" align="center"',$sortfield,$sortorder); + print "\n"; + + while ($i < $num && $i < $conf->liste_limit) + { + $objp = $db->fetch_object($resql); + + $datefin=$db->jdate($objp->datefin); + + $adh=new Adherent($db); + $adh->lastname=$objp->lastname; + $adh->firstname=$objp->firstname; + + // Lastname + print ''; + if ($objp->societe != '') + { + print ''."\n"; + } + else + { + print ''."\n"; + } + + // Login + print "\n"; + + // Type + /*print ''; + */ + + // Moral/Physique + print "\n"; + + // EMail + print "\n"; + + // Statut + print '"; + + // Date end subscription + if ($datefin) + { + print ''; + } + else + { + print ''; + } + + // Actions + print '"; + + print "\n"; + $i++; + } + + print "
'; + print ''; + print ' '; + print ' '; + print ''; + print '  '; + print ''; + print '
'.img_object($langs->trans("ShowMember"),"user").' '.$adh->getFullName($langs,0,-1,20).' / '.dol_trunc($objp->societe,12).''.img_object($langs->trans("ShowMember"),"user").' '.$adh->getFullName($langs,0,-1,32).'".$objp->login."'; + $assettypestatic->id=$objp->type_id; + $assettypestatic->label=$objp->type; + print $assettypestatic->getNomUrl(1,12); + print '".$adh->getmorphylib($objp->morphy)."".dol_print_email($objp->email,0,0,1)."'; + print $adh->LibStatut($objp->statut,$objp->subscription,$datefin,2); + print "'; + if ($datefin < dol_now() && $objp->statut > 0) + { + print dol_print_date($datefin,'day')." ".img_warning($langs->trans("SubscriptionLate")); + } + else + { + print dol_print_date($datefin,'day'); + } + print ''; + if ($objp->subscription == 'yes') + { + print $langs->trans("SubscriptionNotReceived"); + if ($objp->statut > 0) print " ".img_warning(); + } + else + { + print ' '; + } + print ''; + if ($user->rights->adherent->creer) + { + print ''.img_edit().''; + } + print ' '; + if ($user->rights->adherent->supprimer) + { + print ''.img_picto($langs->trans("Resiliate"),'disable.png').''; + } + print "
\n"; + print '
'; + print '
'; + + if ($num > $conf->liste_limit) + { + print_barre_liste('',$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$nbtotalofrecords,''); + } + } + else + { + dol_print_error($db); + } + + } + + /* ************************************************************************** */ + /* */ + /* Edition mode */ + /* */ + /* ************************************************************************** */ + + if ($action == 'edit') + { + $object = new AdherentType($db); + $object->fetch($rowid); + $object->fetch_optionals(); + + $head = member_type_prepare_head($object); + + print '
'; + print ''; + print ''; + print ''; + + dol_fiche_head($head, 'card', $langs->trans("MemberType"), 0, 'group'); + + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print '"; + + // Other attributes + $parameters=array(); + $reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$act,$action); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + if (empty($reshook) && ! empty($extrafields->attribute_label)) + { + print $object->showOptionals($extrafields,'edit'); + } + + print '
'.$langs->trans("Ref").''.$object->id.'
'.$langs->trans("Label").'
'.$langs->trans("SubscriptionRequired").''; + print $form->selectyesno("subscription",$object->subscription,1); + print '
'.$langs->trans("VoteAllowed").''; + print $form->selectyesno("vote",$object->vote,1); + print '
'.$langs->trans("Description").''; + print '
'.$langs->trans("WelcomeEMail").''; + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor=new DolEditor('mail_valid',$object->mail_valid,'',280,'dolibarr_notes','',false,true,$conf->fckeditor->enabled,15,'90%'); + $doleditor->Create(); + print "
'; + + // Extra field + if (empty($reshook) && ! empty($extrafields->attribute_label)) + { + print '

'; + foreach($extrafields->attribute_label as $key=>$label) + { + if (isset($_POST["options_" . $key])) { + if (is_array($_POST["options_" . $key])) { + // $_POST["options"] is an array but following code expects a comma separated string + $value = implode(",", $_POST["options_" . $key]); + } else { + $value = $_POST["options_" . $key]; + } + } else { + $value = $adht->array_options["options_" . $key]; + } + print '\n"; + } + print '
'.$label.''; + print $extrafields->showInputField($key,$value); + print "


'; + } + + dol_fiche_end(); + + print '
'; + print ''; + print '     '; + print ''; + print '
'; + + print "
"; + } +} + + +llxFooter(); + +$db->close(); diff --git a/htdocs/barcode/codeinit.php b/htdocs/barcode/codeinit.php index 1ebd512e772..5e684b4091d 100644 --- a/htdocs/barcode/codeinit.php +++ b/htdocs/barcode/codeinit.php @@ -1,5 +1,6 @@ + * Copyright (C) 2018 Ferran Marcet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -143,7 +144,7 @@ if ($action == 'initbarcodeproducts') $nextvalue=$modBarCodeProduct->getNextValue($productstatic,''); //print 'Set value '.$nextvalue.' to product '.$productstatic->id." ".$productstatic->ref." ".$productstatic->type."
\n"; - $result=$productstatic->setValueFrom('barcode', $nextvalue, '', '', 'date', '', $user, 'PRODUCT_MODIFY'); + $result=$productstatic->setValueFrom('barcode', $nextvalue, '', '', 'text', '', $user, 'PRODUCT_MODIFY'); $nbtry++; if ($result > 0) $nbok++; diff --git a/htdocs/blockedlog/admin/blockedlog_list.php b/htdocs/blockedlog/admin/blockedlog_list.php index d508d1edceb..e8b2dc8b73b 100644 --- a/htdocs/blockedlog/admin/blockedlog_list.php +++ b/htdocs/blockedlog/admin/blockedlog_list.php @@ -196,7 +196,7 @@ else if (GETPOST('downloadcsv','alpha')) $block_static->user_fullname = $obj->user_fullname; $block_static->fk_user = $obj->fk_user; $block_static->signature = $obj->signature; - $block_static->object_data = unserialize($obj->object_data); + $block_static->object_data = $block_static->dolDecodeBlockedData($obj->object_data); $checksignature = $block_static->checkSignature($previoushash); // If $previoushash is not defined, checkSignature will search it @@ -474,7 +474,7 @@ if (is_array($blocks)) print ''.$block->ref_object.''; // Link to source object - print ''.$object_link.''; + print ''.$object_link.''; // Amount print ''.price($block->amounts).''; @@ -501,7 +501,7 @@ if (is_array($blocks)) print ''; - // Status note + // Note print ''; if (! $checkresult[$block->id] || ($loweridinerror && $block->id >= $loweridinerror)) // If error { diff --git a/htdocs/blockedlog/ajax/block-info.php b/htdocs/blockedlog/ajax/block-info.php index c8a9cbf70be..d4d4eef4e07 100644 --- a/htdocs/blockedlog/ajax/block-info.php +++ b/htdocs/blockedlog/ajax/block-info.php @@ -85,10 +85,16 @@ function formatObject($objtoshow, $prefix) { if (! is_object($val) && ! is_array($val)) { + // TODO $val can be '__PHP_Incomplete_Class', the is_object return false $s.=''.($prefix?$prefix.' > ':'').$key.''; $s.=''; - if (in_array($key, array('date','datef'))) + if (in_array($key, array('date','datef','dateh','datec','datem','datep'))) { + /*var_dump(is_object($val)); + var_dump(is_array($val)); + var_dump(is_array($val)); + var_dump(@get_class($val)); + var_dump($val);*/ $s.=dol_print_date($val, 'dayhour'); } else diff --git a/htdocs/blockedlog/class/blockedlog.class.php b/htdocs/blockedlog/class/blockedlog.class.php index 3dc3ade52e5..b580b947e6b 100644 --- a/htdocs/blockedlog/class/blockedlog.class.php +++ b/htdocs/blockedlog/class/blockedlog.class.php @@ -18,10 +18,23 @@ * See https://medium.com/@lhartikk/a-blockchain-in-200-lines-of-code-963cc1cc0e54 */ + + + +/*ini_set('unserialize_callback_func', 'mycallback'); + +function mycallback($classname) +{ + //var_dump($classname); + include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + +}*/ + + + /** * Class to manage Blocked Log */ - class BlockedLog { /** @@ -90,6 +103,7 @@ class BlockedLog public $ref_object = ''; public $object_data = null; + public $user_fullname=''; /** * Array of tracked event codes @@ -239,6 +253,17 @@ class BlockedLog $this->error++; } } + else if($this->element === 'subscription') { + require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php'; + + $object = new Subscription($this->db); + if ($object->fetch($this->fk_object)>0) { + return $object->getNomUrl(1); + } + else{ + $this->error++; + } + } else if ($this->action == 'MODULE_SET') { return 'System to track events into unalterable logs were enabled'; @@ -313,6 +338,10 @@ class BlockedLog { $this->date_object = $object->datepaid?$object->datepaid:$object->datep; } + elseif ($object->element=='subscription') + { + $this->date_object = $object->dateh; + } else { $this->date_object = $object->date; } @@ -323,7 +352,10 @@ class BlockedLog // id of object $this->fk_object = $object->id; + + // Set object_data $this->object_data=new stdClass(); + $arrayoffieldstoexclude = array('table_element','fields','ref_previous','ref_next','origin','origin_id','oldcopy','picto','error','modelpdf','table_element_line','linkedObjectsIds','linkedObjects','fk_delivery_address'); // Add thirdparty info if (empty($object->thirdparty) && method_exists($object, 'fetch_thirdparty')) $object->fetch_thirdparty(); @@ -333,7 +365,7 @@ class BlockedLog foreach($object->thirdparty as $key=>$value) { - if (in_array($key, array('fields'))) continue; // Discard some properties + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties if (! in_array($key, array( 'name','name_alias','ref_ext','address','zip','town','state_code','country_code','idprof1','idprof2','idprof3','idprof4','idprof5','idprof6','phone','fax','email','barcode', 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur' @@ -349,7 +381,7 @@ class BlockedLog foreach($mysoc as $key=>$value) { - if (in_array($key, array('fields'))) continue; // Discard some properties + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties if (! in_array($key, array( 'name','name_alias','ref_ext','address','zip','town','state_code','country_code','idprof1','idprof2','idprof3','idprof4','idprof5','idprof6','phone','fax','email','barcode', 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur' @@ -366,12 +398,11 @@ class BlockedLog } // Field specific to object - if ($this->element == 'facture') { foreach($object as $key=>$value) { - if (in_array($key, array('fields'))) continue; // Discard some properties + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties if (! in_array($key, array( 'ref','facnumber','ref_client','ref_supplier','date','datef','type','total_ht','total_tva','total_ttc','localtax1','localtax2','revenuestamp','datepointoftax','note_public','lines' ))) continue; // Discard if not into a dedicated list @@ -402,7 +433,7 @@ class BlockedLog { foreach($object as $key=>$value) { - if (in_array($key, array('fields'))) continue; // Discard some properties + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties if (! in_array($key, array( 'ref','facnumber','ref_client','ref_supplier','date','datef','type','total_ht','total_tva','total_ttc','localtax1','localtax2','revenuestamp','datepointoftax','note_public' ))) continue; // Discard if not into a dedicated list @@ -498,7 +529,7 @@ class BlockedLog $paymentpart->thirdparty = new stdClass(); foreach($tmpobject->thirdparty as $key=>$value) { - if (in_array($key, array('fields'))) continue; // Discard some properties + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties if (! in_array($key, array( 'name','name_alias','ref_ext','address','zip','town','state_code','country_code','idprof1','idprof2','idprof3','idprof4','idprof5','idprof6','phone','fax','email','barcode', 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur' @@ -515,7 +546,7 @@ class BlockedLog { foreach($tmpobject as $key=>$value) { - if (in_array($key, array('fields'))) continue; // Discard some properties + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties if (! in_array($key, array( 'ref','facnumber','ref_client','ref_supplier','date','datef','type','total_ht','total_tva','total_ttc','localtax1','localtax2','revenuestamp','datepointoftax','note_public' ))) continue; // Discard if not into a dedicated list @@ -542,6 +573,29 @@ class BlockedLog if (! empty($object->newref)) $this->object_data->ref = $object->newref; } + elseif($this->element == 'subscription') + { + foreach($object as $key=>$value) + { + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties + if (! in_array($key, array( + 'id','datec','dateh','datef','fk_adherent','amount','import_key','statut','note' + ))) continue; // Discard if not into a dedicated list + if (!is_object($value)) $this->object_data->{$key} = $value; + } + + if (! empty($object->newref)) $this->object_data->ref = $object->newref; + } + else // Generic case + { + foreach($object as $key=>$value) + { + if (in_array($key, $arrayoffieldstoexclude)) continue; // Discard some properties + if (!is_object($value)) $this->object_data->{$key} = $value; + } + + if (! empty($object->newref)) $this->object_data->ref = $object->newref; + } return 1; } @@ -596,7 +650,7 @@ class BlockedLog $this->fk_user = $obj->fk_user; $this->user_fullname = $obj->user_fullname; - $this->object_data = unserialize($obj->object_data); + $this->object_data = $this->dolDecodeBlockedData($obj->object_data); $this->signature = $obj->signature; $this->signature_line = $obj->signature_line; @@ -618,6 +672,31 @@ class BlockedLog } + + /** + * Decode data + * + * @param string $data Data to unserialize + * @param string $mode 0=unserialize, 1=json_decode + * @return string Value unserialized + */ + public function dolDecodeBlockedData($data, $mode=0) + { + try + { + //include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + //include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + $aaa = unserialize($data); + //$aaa = unserialize($data); + } + catch(Exception $e) + { + //print $e->getErrs); + } + return $aaa; + } + + /** * Set block certified by authority * @@ -667,11 +746,12 @@ class BlockedLog return -2; } - if (empty($this->action) || empty($this->fk_user) || empty($this->user_fullname)) { + if (empty($this->action)) { $this->error=$langs->trans("BadParameterWhenCallingCreateOfBlockedLog"); dol_syslog($this->error, LOG_WARNING); return -3; } + if (empty($this->fk_user)) $this->user_fullname='(Anonymous)'; $this->date_creation = dol_now(); diff --git a/htdocs/bookmarks/admin/bookmark.php b/htdocs/bookmarks/admin/bookmark.php index 14b6dabf5e5..9b3bea82af1 100644 --- a/htdocs/bookmarks/admin/bookmark.php +++ b/htdocs/bookmarks/admin/bookmark.php @@ -58,7 +58,7 @@ if ($action == 'setvalue') llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BookmarkSetup"),$linkback,'title_setup'); print $langs->trans("BookmarkDesc")."
\n"; diff --git a/htdocs/bookmarks/card.php b/htdocs/bookmarks/card.php index dcc6d8cdeab..54512a8049e 100644 --- a/htdocs/bookmarks/card.php +++ b/htdocs/bookmarks/card.php @@ -35,7 +35,7 @@ if (! $user->rights->bookmark->lire) { restrictedArea($user, 'bookmarks'); } -$id=GETPOST("id"); +$id=GETPOST("id",'int'); $action=GETPOST("action","alpha"); $title=GETPOST("title","alpha"); $url=GETPOST("url","alpha"); diff --git a/htdocs/bookmarks/list.php b/htdocs/bookmarks/list.php index 2aa3024920f..b1262b7a554 100644 --- a/htdocs/bookmarks/list.php +++ b/htdocs/bookmarks/list.php @@ -81,7 +81,9 @@ $userstatic=new User($db); llxHeader('', $langs->trans("ListOfBookmarks")); -print load_fiche_titre($langs->trans("ListOfBookmarks")); +$newcardbutton=''.$langs->trans('NewBookmark').''; + +print_barre_liste($langs->trans("ListOfBookmarks"), $page, $_SERVER['PHP_SELF'], $param, $sortfield, $sortorder, '', -1, '', 'title_generic.png', 0, $newcardbutton); $sql = "SELECT b.rowid, b.dateb, b.fk_user, b.url, b.target, b.title, b.favicon, b.position,"; $sql.= " u.login, u.lastname, u.firstname"; @@ -204,16 +206,6 @@ else } - -print "
\n"; - -if ($user->rights->bookmark->creer) -{ - print ''.$langs->trans("NewBookmark").''; -} - -print '
'; - llxFooter(); $db->close(); diff --git a/htdocs/cashdesk/admin/cashdesk.php b/htdocs/cashdesk/admin/cashdesk.php index 79e8c8f83e8..5a68b37caac 100644 --- a/htdocs/cashdesk/admin/cashdesk.php +++ b/htdocs/cashdesk/admin/cashdesk.php @@ -85,7 +85,7 @@ $formproduct=new FormProduct($db); llxHeader('',$langs->trans("CashDeskSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CashDeskSetup"),$linkback,'title_setup'); print '
'; diff --git a/htdocs/cashdesk/class/Facturation.class.php b/htdocs/cashdesk/class/Facturation.class.php index 8bfd1d3082c..769a69a23fa 100644 --- a/htdocs/cashdesk/class/Facturation.class.php +++ b/htdocs/cashdesk/class/Facturation.class.php @@ -233,7 +233,7 @@ class Facturation $this->prix_total_localtax2 = $total_localtax2; $this->montant_tva = $total_ttc - $total_ht; - //print $this->prix_total_ttc.'eeee'; exit; + //print 'total: '.$this->prix_total_ttc; exit; } /** diff --git a/htdocs/cashdesk/tpl/facturation1.tpl.php b/htdocs/cashdesk/tpl/facturation1.tpl.php index aed83d641f3..146ae3d40b6 100644 --- a/htdocs/cashdesk/tpl/facturation1.tpl.php +++ b/htdocs/cashdesk/tpl/facturation1.tpl.php @@ -183,7 +183,7 @@ $langs->load("cashdesk"); else print ''; print ''; print '
'; - if (empty($_SESSION['CASHDESK_ID_BANKACCOUNT_CHEQUE']) || $_SESSION['CASHDESK_ID_BANKACCOUNT_CHEQUE'] < 0) + if (empty($_SESSION['CASHDESK_ID_BANKACCOUNT_CB']) || $_SESSION['CASHDESK_ID_BANKACCOUNT_CB'] < 0) { $langs->load("errors"); print ''; @@ -191,7 +191,7 @@ $langs->load("cashdesk"); else print ''; print '
'; print '
'; - if (empty($_SESSION['CASHDESK_ID_BANKACCOUNT_CB']) || $_SESSION['CASHDESK_ID_BANKACCOUNT_CB'] < 0) + if (empty($_SESSION['CASHDESK_ID_BANKACCOUNT_CHEQUE']) || $_SESSION['CASHDESK_ID_BANKACCOUNT_CHEQUE'] < 0) { $langs->load("errors"); print ''; diff --git a/htdocs/cashdesk/validation_verif.php b/htdocs/cashdesk/validation_verif.php index 399e2984ea8..431f921a1fb 100644 --- a/htdocs/cashdesk/validation_verif.php +++ b/htdocs/cashdesk/validation_verif.php @@ -324,7 +324,7 @@ switch ($action) { // We set status to payed $result=$invoice->set_paid($user); - //print 'eeeee';exit; + //print 'set paid';exit; } } diff --git a/htdocs/categories/admin/categorie.php b/htdocs/categories/admin/categorie.php index 55006a8b171..c1564e1b892 100644 --- a/htdocs/categories/admin/categorie.php +++ b/htdocs/categories/admin/categorie.php @@ -73,11 +73,11 @@ if (preg_match('/del_(.*)/',$action,$reg)) */ $help_url='EN:Module Categories|FR:Module Catégories|ES:Módulo Categorías'; -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; llxHeader('',$langs->trans("Categories"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CategoriesSetup"),$linkback,'title_setup'); diff --git a/htdocs/categories/admin/categorie_extrafields.php b/htdocs/categories/admin/categorie_extrafields.php index c20dff5db42..3e5a252264f 100644 --- a/htdocs/categories/admin/categorie_extrafields.php +++ b/htdocs/categories/admin/categorie_extrafields.php @@ -63,7 +63,7 @@ $help_url='EN:Module Categories|FR:Module Catégories|ES:Módulo Categorías'; llxHeader('',$langs->trans("Categories"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CategoriesSetup"),$linkback,'title_setup'); $head = categoriesadmin_prepare_head(); diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php index 5b824d8c959..d8d5b10a2b5 100644 --- a/htdocs/categories/class/categorie.class.php +++ b/htdocs/categories/class/categorie.class.php @@ -653,18 +653,6 @@ class Categorie extends CommonObject if ($this->id == -1) return -2; - // For backward compatibility - if ($type == 'societe') - { - $type = 'customer'; - dol_syslog(get_class($this) . "::add_type(): type 'societe' is deprecated, please use 'customer' instead", LOG_WARNING); - } - elseif ($type == 'fournisseur') - { - $type = 'supplier'; - dol_syslog(get_class($this) . "::add_type(): type 'fournisseur' is deprecated, please use 'supplier' instead", LOG_WARNING); - } - $this->db->begin(); $sql = "INSERT INTO " . MAIN_DB_PREFIX . "categorie_" . $this->MAP_CAT_TABLE[$type]; @@ -715,11 +703,11 @@ class Categorie extends CommonObject } } - // Save object we want to link category to into category instance to provide information to trigger - $this->linkto=$obj; + // Call trigger - $result=$this->call_trigger('CATEGORY_LINK',$user); + $this->context=array('linkto'=>$obj); // Save object we want to link category to into category instance to provide information to trigger + $result=$this->call_trigger('CATEGORY_LINK',$user); if ($result < 0) { $error++; } // End call triggers @@ -1203,6 +1191,8 @@ class Categorie extends CommonObject */ function get_all_categories($type=null, $parent=false) { + if (! is_numeric($type)) $type = $this->MAP_ID[$type]; + $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie"; $sql.= " WHERE entity IN (".getEntity('category').")"; if (! is_null($type)) diff --git a/htdocs/comm/action/card.php b/htdocs/comm/action/card.php index 2f59df43803..d0d97f83435 100644 --- a/htdocs/comm/action/card.php +++ b/htdocs/comm/action/card.php @@ -1044,7 +1044,7 @@ if ($id > 0) } // Title - print 'global->AGENDA_USE_EVENT_TYPE)?' class="fieldrequired"':'').'>'.$langs->trans("Title").''; + print ''.$langs->trans("Title").''; // Full day event print ''.$langs->trans("EventOnFullDay").'fulldayevent?' checked':'').'>'; diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index 5f094856e1c..7960bbd764d 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -164,6 +164,7 @@ class ActionComm extends CommonObject // Properties for links to other objects var $fk_element; // Id of record + var $elementid; // Id of record alternative for API var $elementtype; // Type of record. This if property ->element of object linked to. // Ical @@ -212,7 +213,7 @@ class ActionComm extends CommonObject $now=dol_now(); // Check parameters - if (empty($this->userownerid)) + if (! isset($this->userownerid) || $this->userownerid === '') // $this->userownerid may be 0 (anonymous event) of > 0 { dol_syslog("You tried to create an event but mandatory property ownerid was not defined", LOG_WARNING); $this->errors[]='ErrorPropertyUserowneridNotDefined'; @@ -550,7 +551,7 @@ class ActionComm extends CommonObject $sql.= " a.fk_user_author, a.fk_user_mod,"; $sql.= " a.fk_user_action, a.fk_user_done,"; $sql.= " a.fk_contact, a.percent as percentage,"; - $sql.= " a.fk_element, a.elementtype,"; + $sql.= " a.fk_element as elementid, a.elementtype,"; $sql.= " a.priority, a.fulldayevent, a.location, a.punctual, a.transparency,"; $sql.= " c.id as type_id, c.code as type_code, c.libelle as type_label, c.color as type_color, c.picto as type_picto,"; $sql.= " s.nom as socname,"; @@ -591,7 +592,6 @@ class ActionComm extends CommonObject $this->label = $obj->label; $this->datep = $this->db->jdate($obj->datep); $this->datef = $this->db->jdate($obj->datep2); -// $this->durationp = $this->durationp; // deprecated $this->datec = $this->db->jdate($obj->datec); $this->datem = $this->db->jdate($obj->datem); @@ -624,7 +624,8 @@ class ActionComm extends CommonObject $this->societe->id = $obj->fk_soc; // deprecated $this->contact->id = $obj->fk_contact; // deprecated - $this->fk_element = $obj->fk_element; + $this->fk_element = $obj->elementid; + $this->elementid = $obj->elementid; $this->elementtype = $obj->elementtype; $this->fetchResources(); @@ -1236,8 +1237,12 @@ class ActionComm extends CommonObject $result=''; - // Set label of typ - $labeltype = ($langs->transnoentities("Action".$this->type_code) != "Action".$this->type_code)?$langs->transnoentities("Action".$this->type_code):$this->type_label; + // Set label of type + $labeltype = ''; + if ($this->type_code) + { + $labeltype = ($langs->transnoentities("Action".$this->type_code) != "Action".$this->type_code)?$langs->transnoentities("Action".$this->type_code):$this->type_label; + } if (empty($conf->global->AGENDA_USE_EVENT_TYPE)) { if ($this->type_code != 'AC_OTH_AUTO') $labeltype = $langs->trans('ActionAC_MANUAL'); @@ -1267,7 +1272,7 @@ class ActionComm extends CommonObject $linkclose.=' title="'.dol_escape_htmltag($tooltip, 1).'"'; $linkclose.=' class="'.$classname.' classfortooltip"'; - if (! is_object($hookmanager)) + /*if (! is_object($hookmanager)) { include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $hookmanager=new HookManager($this->db); @@ -1276,6 +1281,7 @@ class ActionComm extends CommonObject $parameters=array('id'=>$this->id); $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks $linkclose = ($hookmanager->resPrint ? $hookmanager->resPrint : $linkclose); + */ } else $linkclose.=' class="'.$classname.'"'; @@ -1289,7 +1295,7 @@ class ActionComm extends CommonObject $linkstart.=$linkclose.'>'; $linkend=''; - //print 'rrr'.$this->libelle.'-'.$withpicto; + //print 'rrr'.$this->libelle.'rrr'.$this->label.'rrr'.$withpicto; if ($withpicto == 2) { @@ -1309,7 +1315,10 @@ class ActionComm extends CommonObject { if (! empty($conf->global->AGENDA_USE_EVENT_TYPE)) // Add code into () { - $libelle.=(($this->type_code && $libelle!=$langs->transnoentities("Action".$this->type_code) && $langs->transnoentities("Action".$this->type_code)!="Action".$this->type_code)?' ('.$langs->transnoentities("Action".$this->type_code).')':''); + if ($labeltype) + { + $libelle.=(preg_match('/'.preg_quote($labeltype,'/').'/', $libelle)?'':' ('.$langs->transnoentities("Action".$this->type_code).')'); + } } } @@ -1318,6 +1327,18 @@ class ActionComm extends CommonObject $result.=$libelleshort; $result.=$linkend; + global $action; + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('actiondao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + return $result; } @@ -1329,7 +1350,7 @@ class ActionComm extends CommonObject * @param string $type 'event' or 'journal' * @param int $cachedelay Do not rebuild file if date older than cachedelay seconds * @param string $filename Force filename - * @param array $filters Array of filters + * @param array $filters Array of filters. Exemple array('notolderthan'=>99, 'year'=>..., 'idfrom'=>..., 'notactiontype'=>'systemauto', 'project'=>123, ...) * @return int <0 if error, nb of events in new file if ok */ function build_exportfile($format,$type,$cachedelay,$filename,$filters) @@ -1410,7 +1431,9 @@ class ActionComm extends CommonObject if ($key == 'idfrom') $sql.=" AND a.id >= ".(is_numeric($value)?$value:0); if ($key == 'idto') $sql.=" AND a.id <= ".(is_numeric($value)?$value:0); if ($key == 'project') $sql.=" AND a.fk_project=".(is_numeric($value)?$value:0); - // We must filter on assignement table + if ($key == 'actiontype') $sql.=" AND c.type = '".$this->db->escape($value)."'"; + if ($key == 'notactiontype') $sql.=" AND c.type <> '".$this->db->escape($value)."'"; + // We must filter on assignement table if ($key == 'logint') $sql.= " AND ar.fk_actioncomm = a.id AND ar.element_type='user'"; if ($key == 'logina') { @@ -1631,7 +1654,8 @@ class ActionComm extends CommonObject { global $conf, $langs; - $this->output = ''; + $error = 0; + $this->output = ''; $this->error=''; if (empty($conf->global->AGENDA_REMINDER_EMAIL)) @@ -1645,6 +1669,8 @@ class ActionComm extends CommonObject dol_syslog(__METHOD__, LOG_DEBUG); + $this->db->begin(); + // TODO Scan events of type 'email' into table llx_actioncomm_reminder with status todo, send email, then set status to done @@ -1653,7 +1679,9 @@ class ActionComm extends CommonObject $sql = "DELETE FROM ".MAIN_DB_PREFIX."actioncomm_reminder WHERE dateremind < '".$this->db->jdate($now - (3600 * 24 * 32))."'"; $this->db->query($sql); - return 0; + $this->db->commit(); + + return $error; } } diff --git a/htdocs/comm/action/class/api_agendaevents.class.php b/htdocs/comm/action/class/api_agendaevents.class.php index 58deec46f5e..b507d9c72b9 100644 --- a/htdocs/comm/action/class/api_agendaevents.class.php +++ b/htdocs/comm/action/class/api_agendaevents.class.php @@ -80,6 +80,8 @@ class AgendaEvents extends DolibarrApi throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); } + $result = $this->actioncomm->fetch_optionals(); + $this->actioncomm->fetchObjectLinked(); return $this->_cleanObjectDatas($this->actioncomm); } @@ -113,13 +115,17 @@ class AgendaEvents extends DolibarrApi // If the internal user must only see his customers, force searching by him $search_sale = 0; if (! DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) $search_sale = DolibarrApiAccess::$user->id; + if (empty($conf->societe->enabled)) $search_sale = 0; // If module thirdparty not enabled, sale representative is something that does not exists $sql = "SELECT t.id as rowid"; - if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects) + if (! empty($conf->societe->enabled)) + if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects) $sql.= " FROM ".MAIN_DB_PREFIX."actioncomm as t"; - if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale + if (! empty($conf->societe->enabled)) + if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale $sql.= ' WHERE t.entity IN ('.getEntity('agenda').')'; - if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= " AND t.fk_soc = sc.fk_soc"; + if (! empty($conf->societe->enabled)) + if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) $sql.= " AND t.fk_soc = sc.fk_soc"; if ($user_ids) $sql.=" AND t.fk_user_action IN (".$user_ids.")"; if ($socid > 0) $sql.= " AND t.fk_soc = ".$socid; // Insert sale filter @@ -319,7 +325,6 @@ class AgendaEvents extends DolibarrApi unset($object->usermod); unset($object->libelle); - unset($object->array_options); unset($object->context); unset($object->canvas); unset($object->contact); diff --git a/htdocs/comm/action/index.php b/htdocs/comm/action/index.php index efdad6a3738..9fae2dd47a9 100644 --- a/htdocs/comm/action/index.php +++ b/htdocs/comm/action/index.php @@ -589,6 +589,7 @@ if ($resql) $event->type_color=$obj->type_color; $event->libelle=$obj->label; + $event->label=$obj->label; $event->percentage=$obj->percent; $event->authorid=$obj->fk_user_author; // user id of creator $event->userownerid=$obj->fk_user_action; // user id of owner @@ -601,7 +602,9 @@ if ($resql) $event->elementtype=$obj->elementtype; $event->societe->id=$obj->fk_soc; + $event->thirdparty_id=$obj->fk_soc; $event->contact->id=$obj->fk_contact; + $event->contact_id=$obj->fk_contact; // Defined date_start_in_calendar and date_end_in_calendar property // They are date start and end of action but modified to not be outside calendar view. @@ -1176,19 +1179,72 @@ else // View by day $timestamp=dol_mktime(12,0,0,$month,$day,$year); $arraytimestamp=dol_getdate($timestamp); - print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table - echo ''; - echo ' '; - echo ' \n"; - echo " \n"; - echo " \n"; - echo ' \n"; - echo " \n"; + //echo '
'.$langs->trans("Day".$arraytimestamp['wday'])."
'; - $maxnbofchar=80; - show_day_events($db, $day, $month, $year, $month, $style, $eventarray, 0, $maxnbofchar, $newparam, 1, 300); - echo "
'; + echo '
'; + + echo ' '; + echo ' '; + echo ' \n"; + echo " \n"; + + /* + echo '
'; + echo '
'; + echo '
'; + echo show_day_events($db, $day, $month, $year, $month, $style, $eventarray, 0, $maxnbofchar, $newparam, 1, 300, -1); + echo '
'."\n"; + echo "
\n"; + */ + echo '
'.$langs->trans("Day".$arraytimestamp['wday'])."
'; - print '
'; + + /* WIP View per hour */ + $useviewhour = 0; + if ($useviewhour) + { + print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table + + $maxheightwin=(isset($_SESSION["dol_screenheight"]) && $_SESSION["dol_screenheight"] > 500)?($_SESSION["dol_screenheight"]-200):660; // Also into index.php file + + echo '
'; + echo '
'; + + $maxnbofchar=80; + + $tmp = explode('-', $conf->global->MAIN_DEFAULT_WORKING_HOURS); + $minhour = round($tmp[0],0); + $maxhour = round($tmp[1],0); + if ($minhour > 23) $minhour = 23; + if ($maxhour < 1) $maxhour = 1; + if ($maxhour <= $minhour) { $maxhour = $minhour + 1; } + + $i = 0; + $j = 0; + while ($i < 24) + { + echo '
'."\n"; + echo '
'.dol_print_date($i*3600, 'hour', 'gmt').'
'; + echo '
'; + echo "
\n"; + echo "
\n"; + $i++; + $j++; + } + + echo '
'; + + show_day_events($db, $day, $month, $year, $month, $style, $eventarray, 0, $maxnbofchar, $newparam, 1, 300, 1); + + print '
'; + } + else + { + print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table + + show_day_events($db, $day, $month, $year, $month, $style, $eventarray, 0, $maxnbofchar, $newparam, 1, 300, 0); + + print '
'; + } } print "\n".''; @@ -1213,9 +1269,10 @@ $db->close(); * @param string $newparam Parameters on current URL * @param int $showinfo Add extended information (used by day and week view) * @param int $minheight Minimum height for each event. 60px by default. + * @param string $nonew 0=Add "new entry button", 1=No "new entry button", -1=Only "new entry button" * @return void */ -function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventarray, $maxprint=0, $maxnbofchar=16, $newparam='', $showinfo=0, $minheight=60) +function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventarray, $maxprint=0, $maxnbofchar=16, $newparam='', $showinfo=0, $minheight=60, $nonew=0) { global $user, $conf, $langs; global $action, $filter, $filtert, $status, $actioncode; // Filters used into search form @@ -1230,26 +1287,35 @@ function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventa $curtime = dol_mktime(0, 0, 0, $month, $day, $year); print '
'."\n"; - print '
'; - if ($user->rights->agenda->myactions->create || $user->rights->agenda->allactions->create) + if ($nonew <= 0) { - $newparam.='&month='.str_pad($month, 2, "0", STR_PAD_LEFT).'&year='.$year; + print '
'; + if ($user->rights->agenda->myactions->create || $user->rights->agenda->allactions->create) + { + $newparam.='&month='.str_pad($month, 2, "0", STR_PAD_LEFT).'&year='.$year; - //$param='month='.$monthshown.'&year='.$year; - $hourminsec='100000'; - print ''; - print img_picto($langs->trans("NewAction"),'edit_add.png'); - print ''; + //$param='month='.$monthshown.'&year='.$year; + $hourminsec='100000'; + print ''; + print img_picto($langs->trans("NewAction"),'edit_add.png'); + print ''; + } + print '
'."\n"; + } + + if ($nonew < 0) + { + print '
'; + return; } - print '
'."\n"; // Line with td contains all div of each events print '
'; @@ -1390,6 +1456,7 @@ function show_day_events($db, $day, $month, $year, $monthshown, $style, &$eventa //print ' position: absolute; top: 40px; width: 50%;'; //print '"'; print '>'; + //var_dump($event->userassigned); //var_dump($event->transparency); print 'load("companies"); -$langs->load("agenda"); -$langs->load("commercial"); +$langs->loadLangs(array("users","companies","agenda","commercial")); $action=GETPOST('action','alpha'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'actioncommlist'; // To manage different context of search $resourceid=GETPOST("resourceid","int"); $year=GETPOST("year",'int'); $month=GETPOST("month",'int'); $day=GETPOST("day",'int'); $pid=GETPOST("projectid",'int',3); $status=GETPOST("status",'alpha'); -$type=GETPOST('type'); +$type=GETPOST('type','alphanohtml'); $optioncss = GETPOST('optioncss','alpha'); // Set actioncode (this code must be same for setting actioncode into peruser, listacton and index) if (GETPOST('actioncode','array')) @@ -57,6 +55,7 @@ else $actioncode=GETPOST("actioncode","alpha",3)?GETPOST("actioncode","alpha",3):(GETPOST("actioncode")=='0'?'0':(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE)); } if ($actioncode == '' && empty($actioncodearray)) $actioncode=(empty($conf->global->AGENDA_DEFAULT_FILTER_TYPE)?'':$conf->global->AGENDA_DEFAULT_FILTER_TYPE); +$search_id=GETPOST('search_id','alpha'); $search_title=GETPOST('search_title','alpha'); $dateselect=dol_mktime(0, 0, 0, GETPOST('dateselectmonth','int'), GETPOST('dateselectday','int'), GETPOST('dateselectyear','int')); @@ -70,8 +69,6 @@ $filtert = GETPOST("filtert","int",3); $usergroup = GETPOST("usergroup","int",3); $showbirthday = empty($conf->use_javascript_ajax)?GETPOST("showbirthday","int"):1; -$contextpage='actioncommlist'; - $extrafields = new ExtraFields($db); // fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('actioncomm'); @@ -116,6 +113,7 @@ if (! $user->rights->agenda->allactions->read || $filter=='mine') // If no permi } // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$object = new ActionComm($db); $hookmanager->initHooks(array('agendalist')); $arrayfields=array( @@ -170,7 +168,8 @@ include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers { //$actioncode=''; - $search_title=''; + $search_id=''; + $search_title=''; $datestart=''; $dateend=''; $status=''; @@ -199,24 +198,25 @@ llxHeader('',$langs->trans("Agenda"),$help_url); $listofextcals=array(); $param=''; -if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; -if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); if ($actioncode != '') { if(is_array($actioncode)) { - foreach($actioncode as $str_action) $param.="&actioncode[]=".$str_action; - } else $param.="&actioncode=".$actioncode; + foreach($actioncode as $str_action) $param.="&actioncode[]=".urlencode($str_action); + } else $param.="&actioncode=".urlencode($actioncode); } -if ($resourceid > 0) $param.="&resourceid=".$resourceid; -if ($status != '' && $status > -1) $param.="&status=".$status; -if ($filter) $param.="&filter=".$filter; -if ($filtert) $param.="&filtert=".$filtert; -if ($socid) $param.="&socid=".$socid; +if ($resourceid > 0) $param.="&resourceid=".urlencode($resourceid); +if ($status != '' && $status > -1) $param.="&status=".urlencode($status); +if ($filter) $param.="&filter=".urlencode($filter); +if ($filtert) $param.="&filtert=".urlencode($filtert); +if ($socid) $param.="&socid=".urlencode($socid); if ($showbirthday) $param.="&showbirthday=1"; -if ($pid) $param.="&projectid=".$pid; -if ($type) $param.="&type=".$type; -if ($usergroup) $param.="&usergroup=".$usergroup; -if ($optioncss != '') $param.='&optioncss='.$optioncss; -if ($search_title != '') $param.='&search_title='.$search_title; +if ($pid) $param.="&projectid=".urlencode($pid); +if ($type) $param.="&type=".urlencode($type); +if ($usergroup) $param.="&usergroup=".urlencode($usergroup); +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +if ($search_id != '') $param.='&search_title='.urlencode($search_id); +if ($search_title != '') $param.='&search_title='.urlencode($search_title); if (GETPOST('datestartday','int')) $param.='&datestartday='.GETPOST('datestartday','int'); if (GETPOST('datestartmonth','int')) $param.='&datestartmonth='.GETPOST('datestartmonth','int'); if (GETPOST('datestartyear','int')) $param.='&datestartyear='.GETPOST('datestartyear','int'); @@ -234,7 +234,7 @@ $sql.= ' a.fk_user_author,a.fk_user_action,'; $sql.= " a.fk_contact, a.note, a.percent as percent,"; $sql.= " a.fk_element, a.elementtype,"; $sql.= " c.code as type_code, c.libelle as type_label,"; -$sql.= " sp.lastname, sp.firstname"; +$sql.= " sp.lastname, sp.firstname, sp.email, sp.phone, sp.address, sp.phone as phone_pro, sp.phone_mobile, sp.phone_perso, sp.fk_pays as country_id"; // Add fields from extrafields foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key.' as options_'.$key : ''); $sql.= " FROM ".MAIN_DB_PREFIX."actioncomm as a"; @@ -293,6 +293,7 @@ if ($status == '50') { $sql.= " AND (a.percent > 0 AND a.percent < 100)"; } // R if ($status == '100') { $sql.= " AND a.percent = 100"; } if ($status == 'done' || $status == '100') { $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep2 <= '".$db->idate($now)."'))"; } if ($status == 'todo') { $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep2 > '".$db->idate($now)."'))"; } +if ($search_id) $sql.=natural_search("a.id", $search_id, 1); if ($search_title) $sql.=natural_search("a.label", $search_title); // We must filter on assignement table if ($filtert > 0 || $usergroup > 0) @@ -331,13 +332,9 @@ if ($resql) $num = $db->num_rows($resql); - /*$title=$langs->trans("DoneAndToDoActions"); - if ($status == 'done') $title=$langs->trans("DoneActions"); - if ($status == 'todo') $title=$langs->trans("ToDoActions"); - */ - $title=$langs->trans("ListOfEvents"); - - $newtitle=$langs->trans($title); + // Local calendar + $newtitle ='
' . $langs->trans("LocalAgenda").'  
'; + //$newtitle=$langs->trans($title); $tabactive='cardlist'; @@ -358,7 +355,7 @@ if ($resql) //if ($actioncode) $nav.=''; //if ($resourceid) $nav.=''; if ($filter) $nav.=''; - if ($filtert) $nav.=''; + //if ($filtert) $nav.=''; //if ($socid) $nav.=''; if ($showbirthday) $nav.=''; //if ($pid) $nav.=''; @@ -425,17 +422,17 @@ if ($resql) print '
'."\n"; print ''; - if (! empty($arrayfields['a.id']['checked'])) print ''; + if (! empty($arrayfields['a.id']['checked'])) print ''; if (! empty($arrayfields['owner']['checked'])) print ''; if (! empty($arrayfields['c.libelle']['checked'])) print ''; if (! empty($arrayfields['a.label']['checked'])) print ''; if (! empty($arrayfields['a.datep']['checked'])) { - print ''; } if (! empty($arrayfields['a.datep2']['checked'])) { - print ''; } @@ -496,7 +493,6 @@ if ($resql) $caction=new CActionComm($db); $arraylist=$caction->liste_array(1, 'code', '', (empty($conf->global->AGENDA_USE_EVENT_TYPE)?1:0), '', 1); - $var=true; while ($i < min($num,$limit)) { $obj = $db->fetch_object($resql); @@ -517,8 +513,8 @@ if ($resql) print ''; + // Ref if (! empty($arrayfields['a.id']['checked'])) { - // Ref print ''; @@ -606,9 +602,14 @@ if ($resql) print '
'; + print ''; print $form->select_date($datestart, 'datestart', 0, 0, 1, '', 1, 0, 1); print ''; + print ''; print $form->select_date($dateend, 'dateend', 0, 0, 1, '', 1, 0, 1); print '
'; print $actionstatic->getNomUrl(1,-1); print ''; if ($obj->fk_contact > 0) { + $contactstatic->id=$obj->fk_contact; + $contactstatic->email=$obj->email; $contactstatic->lastname=$obj->lastname; $contactstatic->firstname=$obj->firstname; - $contactstatic->id=$obj->fk_contact; + $contactstatic->phone_pro=$obj->phone_pro; + $contactstatic->phone_mobile=$obj->phone_mobile; + $contactstatic->phone_perso=$obj->phone_perso; + $contactstatic->country_id=$obj->country_id; print $contactstatic->getNomUrl(1,'',28); } else diff --git a/htdocs/comm/action/pertype.php b/htdocs/comm/action/pertype.php index cc3688a4793..1657166bd3d 100644 --- a/htdocs/comm/action/pertype.php +++ b/htdocs/comm/action/pertype.php @@ -468,10 +468,8 @@ if ($resql) $event->datef=$datep2; $event->type_code=$obj->code; $event->type_color=$obj->color; - //$event->libelle=$obj->label; // deprecated $event->label=$obj->label; $event->percentage=$obj->percent; - //$event->author->id=$obj->fk_user_author; // user id of creator $event->authorid=$obj->fk_user_author; // user id of creator $event->userownerid=$obj->fk_user_action; // user id of owner $event->priority=$obj->priority; @@ -483,8 +481,6 @@ if ($resql) $event->socid=$obj->fk_soc; $event->contactid=$obj->fk_contact; - //$event->societe->id=$obj->fk_soc; // deprecated - //$event->contact->id=$obj->fk_contact; // deprecated $event->fk_element=$obj->fk_element; $event->elementtype=$obj->elementtype; diff --git a/htdocs/comm/action/peruser.php b/htdocs/comm/action/peruser.php index a4154f5a9e4..f6939ddc2b6 100644 --- a/htdocs/comm/action/peruser.php +++ b/htdocs/comm/action/peruser.php @@ -205,24 +205,24 @@ if ($status == 'todo') $title=$langs->trans("ToDoActions"); $param=''; if ($actioncode || isset($_GET['actioncode']) || isset($_POST['actioncode'])) { if(is_array($actioncode)) { - foreach($actioncode as $str_action) $param.="&actioncode[]=".$str_action; - } else $param.="&actioncode=".$actioncode; + foreach($actioncode as $str_action) $param.="&actioncode[]=".urlencode($str_action); + } else $param.="&actioncode=".urlencode($actioncode); } -if ($resourceid > 0) $param.="&resourceid=".$resourceid; -if ($status || isset($_GET['status']) || isset($_POST['status'])) $param.="&status=".$status; -if ($filter) $param.="&filter=".$filter; -if ($filtert) $param.="&filtert=".$filtert; -if ($usergroup) $param.="&usergroup=".$usergroup; -if ($socid) $param.="&socid=".$socid; +if ($resourceid > 0) $param.="&resourceid=".urlencode($resourceid); +if ($status || isset($_GET['status']) || isset($_POST['status'])) $param.="&status=".urlencode($status); +if ($filter) $param.="&filter=".urlencode($filter); +if ($filtert) $param.="&filtert=".urlencode($filtert); +if ($usergroup) $param.="&usergroup=".urlencode($usergroup); +if ($socid) $param.="&socid=".urlencode($socid); if ($showbirthday) $param.="&showbirthday=1"; -if ($pid) $param.="&projectid=".$pid; -if ($type) $param.="&type=".$type; -if ($action == 'show_day' || $action == 'show_week' || $action == 'show_month' || $action != 'show_peruser') $param.='&action='.$action; -if ($begin_h != '') $param.='&begin_h='.$begin_h; -if ($end_h != '') $param.='&end_h='.$end_h; -if ($begin_d != '') $param.='&begin_d='.$begin_d; -if ($end_d != '') $param.='&end_d='.$end_d; -$param.="&maxprint=".$maxprint; +if ($pid) $param.="&projectid=".urlencode($pid); +if ($type) $param.="&type=".urlencode($type); +if ($action == 'show_day' || $action == 'show_week' || $action == 'show_month' || $action != 'show_peruser') $param.='&action='.urlencode($action); +if ($begin_h != '') $param.='&begin_h='.urlencode($begin_h); +if ($end_h != '') $param.='&end_h='.urlencode($end_h); +if ($begin_d != '') $param.='&begin_d='.urlencode($begin_d); +if ($end_d != '') $param.='&end_d='.urlencode($end_d); +$param.="&maxprint=".urlencode($maxprint); $prev = dol_get_first_day_week($day, $month, $year); @@ -245,7 +245,9 @@ $next_day = $next['day']; // Define firstdaytoshow and lastdaytoshow (warning: lastdaytoshow is last second to show + 1) $firstdaytoshow=dol_mktime(0,0,0,$first_month,$first_day,$first_year); -$lastdaytoshow=dol_time_plus_duree($firstdaytoshow, 7, 'd'); + +$nb_weeks_to_show = (! empty($conf->global->AGENDA_NB_WEEKS_IN_VIEW_PER_USER)) ? ((int) $conf->global->AGENDA_NB_WEEKS_IN_VIEW_PER_USER * 7) : 7; +$lastdaytoshow=dol_time_plus_duree($firstdaytoshow, $nb_weeks_to_show, 'd'); //print $firstday.'-'.$first_month.'-'.$first_year; //print dol_print_date($firstdaytoshow,'dayhour'); //print dol_print_date($lastdaytoshow,'dayhour'); @@ -282,12 +284,11 @@ $nav.=' query($sql); @@ -495,10 +496,8 @@ if ($resql) $event->datef=$datep2; $event->type_code=$obj->code; $event->type_color=$obj->color; - //$event->libelle=$obj->label; // deprecated $event->label=$obj->label; $event->percentage=$obj->percent; - //$event->author->id=$obj->fk_user_author; // user id of creator $event->authorid=$obj->fk_user_author; // user id of creator $event->userownerid=$obj->fk_user_action; // user id of owner $event->priority=$obj->priority; @@ -510,8 +509,6 @@ if ($resql) $event->socid=$obj->fk_soc; $event->contactid=$obj->fk_contact; - //$event->societe->id=$obj->fk_soc; // deprecated - //$event->contact->id=$obj->fk_contact; // deprecated $event->fk_element=$obj->fk_element; $event->elementtype=$obj->elementtype; @@ -619,183 +616,193 @@ echo '' ; //print "begin_d=".$begin_d." end_d=".$end_d; - +$currentdaytoshow = $firstdaytoshow; echo '
'; -echo ''; -echo ''; -echo ''; -$i=0; // 0 = sunday, -while ($i < 7) -{ - if (($i + 1) < $begin_d || ($i + 1) > $end_d) - { - $i++; - continue; - } - echo '\n"; - $i++; -} -echo "\n"; +while($currentdaytoshow<$lastdaytoshow) { -echo ''; -echo ''; -$i=0; -while ($i < 7) -{ - if (($i + 1) < $begin_d || ($i + 1) > $end_d) - { - $i++; - continue; - } - for ($h = $begin_h; $h < $end_h; $h++) - { - echo '"; - } - echo "\n"; - $i++; -} -echo "\n"; + echo '
'; - echo $langs->trans("Day".(($i+(isset($conf->global->MAIN_START_WEEK)?$conf->global->MAIN_START_WEEK:1)) % 7)); - print "
"; - if ($i) print dol_print_date(dol_time_plus_duree($firstdaytoshow, $i, 'd'),'day'); - else print dol_print_date($firstdaytoshow,'day'); - echo "
'; - print ''.sprintf("%02d",$h).''; - print "
'; - -// Define $usernames -$usernames = array(); //init -$usernamesid = array(); -/* Use this to have list of users only if users have events */ -if (! empty($conf->global->AGENDA_SHOWOWNERONLY_ONPERUSERVIEW)) -{ - foreach ($eventarray as $daykey => $notused) - { - // Get all assigned users for each event - foreach ($eventarray[$daykey] as $index => $event) - { - $event->fetch_userassigned(); - $listofuserid=$event->userassigned; - foreach($listofuserid as $userid => $tmp) - { - if (! in_array($userid, $usernamesid)) $usernamesid[$userid] = $userid; - } - } - } -} -/* Use this list to have for all users */ -else -{ - $sql = "SELECT u.rowid, u.lastname as lastname, u.firstname, u.statut, u.login, u.admin, u.entity"; - $sql.= " FROM ".MAIN_DB_PREFIX."user as u"; - if ($usergroup > 0) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug ON u.rowid = ug.fk_user"; - $sql.= " WHERE u.statut = 1 AND u.entity IN (".getEntity('user').")"; - if ($usergroup > 0) $sql.= " AND ug.fk_usergroup = ".$usergroup; - //print $sql; - $resql=$db->query($sql); - if ($resql) - { - $num = $db->num_rows($resql); - $i = 0; - if ($num) - { - while ($i < $num) - { - $obj = $db->fetch_object($resql); - $usernamesid[$obj->rowid]=$obj->rowid; - $i++; - } - } - } - else dol_print_error($db); -} -//var_dump($usernamesid); -foreach($usernamesid as $id) -{ - $tmpuser=new User($db); - $result=$tmpuser->fetch($id); - $usernames[]=$tmpuser; -} - -/* -if ($filtert > 0) -{ - $tmpuser = new User($db); - $tmpuser->fetch($filtert); - $usernames[] = $tmpuser; -} -else if ($usergroup) -{ - $tmpgroup = new UserGroup($db); - $tmpgroup->fetch($usergroup); - $usernames = $tmpgroup->listUsersForGroup(); -} -else -{ - $tmpgroup = new UserGroup($db); - //$tmpgroup->fetch($usergroup); No fetch, we want all users for all groups - $usernames = $tmpgroup->listUsersForGroup(); -}*/ - -// Load array of colors by type -$colorsbytype=array(); -$labelbytype=array(); -$sql="SELECT code, color, libelle FROM ".MAIN_DB_PREFIX."c_actioncomm ORDER BY position"; -$resql=$db->query($sql); -while ($obj = $db->fetch_object($resql)) -{ - $colorsbytype[$obj->code]=$obj->color; - $labelbytype[$obj->code]=$obj->libelle; -} - -// Loop on each user to show calendar -$todayarray=dol_getdate($now,'fast'); -$sav = $tmpday; -$showheader = true; -$var = false; -foreach ($usernames as $username) -{ - $var = ! $var; - echo ""; - echo ''; - $tmpday = $sav; - - // Lopp on each day of week - $i = 0; - for ($iter_day = 0; $iter_day < 8; $iter_day++) + echo ''; + echo ''; + $i=0; // 0 = sunday, + while ($i < 7) { if (($i + 1) < $begin_d || ($i + 1) > $end_d) { $i++; continue; } - - // Show days of the current week - $curtime = dol_time_plus_duree($firstdaytoshow, $iter_day, 'd'); - $tmparray = dol_getdate($curtime,'fast'); - $tmpday = $tmparray['mday']; - $tmpmonth = $tmparray['mon']; - $tmpyear = $tmparray['year']; - - $style='cal_current_month'; - if ($iter_day == 6) $style.=' cal_other_month'; - $today=0; - if ($todayarray['mday']==$tmpday && $todayarray['mon']==$tmpmonth && $todayarray['year']==$tmpyear) $today=1; - if ($today) $style='cal_today_peruser'; - - show_day_events2($username, $tmpday, $tmpmonth, $tmpyear, $monthshown, $style, $eventarray, 0, $maxnbofchar, $newparam, 1, 300, $showheader, $colorsbytype, $var); - + echo '\n"; $i++; } echo "\n"; - $showheader = false; + + echo ''; + echo ''; + $i=0; + while ($i < 7) + { + if (($i + 1) < $begin_d || ($i + 1) > $end_d) + { + $i++; + continue; + } + for ($h = $begin_h; $h < $end_h; $h++) + { + echo '"; + } + echo "\n"; + $i++; + } + echo "\n"; + + + // Define $usernames + $usernames = array(); //init + $usernamesid = array(); + /* Use this to have list of users only if users have events */ + if (! empty($conf->global->AGENDA_SHOWOWNERONLY_ONPERUSERVIEW)) + { + foreach ($eventarray as $daykey => $notused) + { + // Get all assigned users for each event + foreach ($eventarray[$daykey] as $index => $event) + { + $event->fetch_userassigned(); + $listofuserid=$event->userassigned; + foreach($listofuserid as $userid => $tmp) + { + if (! in_array($userid, $usernamesid)) $usernamesid[$userid] = $userid; + } + } + } + } + /* Use this list to have for all users */ + else + { + $sql = "SELECT u.rowid, u.lastname as lastname, u.firstname, u.statut, u.login, u.admin, u.entity"; + $sql.= " FROM ".MAIN_DB_PREFIX."user as u"; + if ($usergroup > 0) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug ON u.rowid = ug.fk_user"; + $sql.= " WHERE u.statut = 1 AND u.entity IN (".getEntity('user').")"; + if ($usergroup > 0) $sql.= " AND ug.fk_usergroup = ".$usergroup; + //print $sql; + $resql=$db->query($sql); + if ($resql) + { + $num = $db->num_rows($resql); + $i = 0; + if ($num) + { + while ($i < $num) + { + $obj = $db->fetch_object($resql); + $usernamesid[$obj->rowid]=$obj->rowid; + $i++; + } + } + } + else dol_print_error($db); + } + //var_dump($usernamesid); + foreach($usernamesid as $id) + { + $tmpuser=new User($db); + $result=$tmpuser->fetch($id); + $usernames[]=$tmpuser; + } + + /* + if ($filtert > 0) + { + $tmpuser = new User($db); + $tmpuser->fetch($filtert); + $usernames[] = $tmpuser; + } + else if ($usergroup) + { + $tmpgroup = new UserGroup($db); + $tmpgroup->fetch($usergroup); + $usernames = $tmpgroup->listUsersForGroup(); + } + else + { + $tmpgroup = new UserGroup($db); + //$tmpgroup->fetch($usergroup); No fetch, we want all users for all groups + $usernames = $tmpgroup->listUsersForGroup(); + }*/ + + // Load array of colors by type + $colorsbytype=array(); + $labelbytype=array(); + $sql="SELECT code, color, libelle FROM ".MAIN_DB_PREFIX."c_actioncomm ORDER BY position"; + $resql=$db->query($sql); + while ($obj = $db->fetch_object($resql)) + { + $colorsbytype[$obj->code]=$obj->color; + $labelbytype[$obj->code]=$obj->libelle; + } + + // Loop on each user to show calendar + $todayarray=dol_getdate($now,'fast'); + $sav = $tmpday; + $showheader = true; + $var = false; + foreach ($usernames as $username) + { + $var = ! $var; + echo ""; + echo ''; + $tmpday = $sav; + + // Lopp on each day of week + $i = 0; + for ($iter_day = 0; $iter_day < 8; $iter_day++) + { + + if (($i + 1) < $begin_d || ($i + 1) > $end_d) + { + $i++; + continue; + } + + // Show days of the current week + $curtime = dol_time_plus_duree($currentdaytoshow, $iter_day, 'd'); + $tmparray = dol_getdate($curtime,'fast'); + $tmpday = $tmparray['mday']; + $tmpmonth = $tmparray['mon']; + $tmpyear = $tmparray['year']; + + $style='cal_current_month'; + if ($iter_day == 6) $style.=' cal_other_month'; + $today=0; + if ($todayarray['mday']==$tmpday && $todayarray['mon']==$tmpmonth && $todayarray['year']==$tmpyear) $today=1; + if ($today) $style='cal_today_peruser'; + + show_day_events2($username, $tmpday, $tmpmonth, $tmpyear, $monthshown, $style, $eventarray, 0, $maxnbofchar, $newparam, 1, 300, $showheader, $colorsbytype, $var); + + $i++; + } + echo "\n"; + $showheader = false; + } + + echo "
'; - print $username->getNomUrl(-1,'',0,0,20,1,''); - print '
'; + echo $langs->trans("Day".(($i+(isset($conf->global->MAIN_START_WEEK)?$conf->global->MAIN_START_WEEK:1)) % 7)); + print "
"; + if ($i) print dol_print_date(dol_time_plus_duree($currentdaytoshow, $i, 'd'),'day'); + else print dol_print_date($currentdaytoshow,'day'); + echo "
'; + print ''.sprintf("%02d",$h).''; + print "
'; + print $username->getNomUrl(-1,'',0,0,20,1,''); + print '
\n"; + echo "
"; + + $currentdaytoshow = dol_time_plus_duree($currentdaytoshow, 7, 'd'); + } -echo "
\n"; echo '
'; if (! empty($conf->global->AGENDA_USE_EVENT_TYPE) && ! empty($conf->global->AGENDA_USE_COLOR_PER_EVENT_TYPE)) diff --git a/htdocs/comm/admin/propal_extrafields.php b/htdocs/comm/admin/propal_extrafields.php index 33c38282847..3c95c39c88d 100644 --- a/htdocs/comm/admin/propal_extrafields.php +++ b/htdocs/comm/admin/propal_extrafields.php @@ -63,7 +63,7 @@ $textobject=$langs->transnoentitiesnoconv("Proposals"); llxHeader('',$langs->trans("PropalSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("PropalSetup"),$linkback,'title_setup'); diff --git a/htdocs/comm/admin/propaldet_extrafields.php b/htdocs/comm/admin/propaldet_extrafields.php index 270274327e6..9c85504d458 100644 --- a/htdocs/comm/admin/propaldet_extrafields.php +++ b/htdocs/comm/admin/propaldet_extrafields.php @@ -71,7 +71,7 @@ $textobject=$langs->transnoentitiesnoconv("Proposals"); llxHeader('',$langs->trans("PropalSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("PropalSetup"),$linkback,'title_setup'); $head = propal_admin_prepare_head(); diff --git a/htdocs/comm/card.php b/htdocs/comm/card.php index 506b0c6fa8d..eee4d529751 100644 --- a/htdocs/comm/card.php +++ b/htdocs/comm/card.php @@ -82,7 +82,7 @@ $extrafields = new ExtraFields($db); $extralabels=$extrafields->fetch_name_optionals_label($object->table_element); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('commcard','globalcard')); +$hookmanager->initHooks(array('thirdpartycomm','globalcard')); /* @@ -189,9 +189,11 @@ if (empty($reshook)) if ($action == 'update_extras') { $object->fetch($id); + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) { @@ -276,29 +278,38 @@ if ($object->id > 0) print ''; } + // This fields are used to know VAT to include in an invoice when the thirdparty is making a sale, so when it is a supplier. + // We don't need them into customer profile. + // Except for spain and localtax where localtax depends on buyer and not seller + // VAT is used + /* print ''; - print ''.$langs->trans('VATIsUsed').''; + print ''; + print $form->textwithpicto($langs->trans('VATIsUsed'),$langs->trans('VATIsUsedWhenSelling')); + print ''; print ''; print yn($object->tva_assuj); print ''; print ''; + */ - // Local Taxes - // TODO Move this on same record than VATIsUsed - if ($mysoc->localtax1_assuj=="1") + if ($mysoc->country_code == 'ES') { - print ''.$langs->transcountry("LocalTax1IsUsed", $mysoc->country_code).''; - print yn($object->localtax1_assuj); - print ''; + // Local Taxes + if ($mysoc->localtax1_assuj=="1") + { + print ''.$langs->transcountry("LocalTax1IsUsed", $mysoc->country_code).''; + print yn($object->localtax1_assuj); + print ''; + } + if ($mysoc->localtax1_assuj=="1") + { + print ''.$langs->transcountry("LocalTax2IsUsed", $mysoc->country_code).''; + print yn($object->localtax2_assuj); + print ''; + } } - if ($mysoc->localtax1_assuj=="1") - { - print ''.$langs->transcountry("LocalTax2IsUsed", $mysoc->country_code).''; - print yn($object->localtax2_assuj); - print ''; - } - // TVA Intra print ''.$langs->trans('VATIntra').''; @@ -548,7 +559,7 @@ if ($object->id > 0) } print '
'; - + print '
'; $boxstat = ''; @@ -557,7 +568,7 @@ if ($object->id > 0) // Lien recap $boxstat.='
'; - $boxstat.=''; + $boxstat.='
'; $boxstat.=''; print ''; } + $i = 0; while ($i < $num && $i < $MAXLIST) { - $contrat=new Contrat($db); - $objp = $db->fetch_object($resql); + $contrat->id=$objp->id; + $contrat->ref=$objp->ref?$objp->ref:$objp->id; + $contrat->ref_customer=$objp->refcus; + $contrat->ref_supplier=$objp->refsup; + print ''; print '\n"; print '\n"; @@ -933,12 +947,11 @@ if ($object->id > 0) $sql.= " AND f.entity = ".$conf->entity; $sql.= " ORDER BY f.tms DESC"; - $fichinter_static=new Fichinter($db); - $resql=$db->query($sql); if ($resql) { - $var=true; + $fichinter_static=new Fichinter($db); + $num = $db->num_rows($resql); if ($num > 0) { @@ -949,8 +962,8 @@ if ($object->id > 0) print ''; print '
'; if (! empty($conf->propal->enabled)) @@ -649,8 +660,6 @@ if ($object->id > 0) */ if (! empty($conf->propal->enabled) && $user->rights->propal->lire) { - $propal_static = new Propal($db); - $sql = "SELECT s.nom, s.rowid, p.rowid as propalid, p.fk_statut, p.total_ht"; $sql.= ", p.tva as total_tva"; $sql.= ", p.total as total_ttc"; @@ -665,9 +674,9 @@ if ($object->id > 0) $resql=$db->query($sql); if ($resql) { - $var=true; - $num = $db->num_rows($resql); + $propal_static = new Propal($db); + $num = $db->num_rows($resql); if ($num > 0) { print ''; @@ -716,8 +725,6 @@ if ($object->id > 0) */ if (! empty($conf->commande->enabled) && $user->rights->commande->lire) { - $commande_static=new Commande($db); - $sql = "SELECT s.nom, s.rowid"; $sql.= ", c.rowid as cid, c.total_ht"; $sql.= ", c.tva as total_tva"; @@ -733,14 +740,14 @@ if ($object->id > 0) $resql=$db->query($sql); if ($resql) { - $var=true; - $num = $db->num_rows($resql); + $commande_static=new Commande($db); + $num = $db->num_rows($resql); if ($num > 0) { // Check if there are orders billable $sql2 = 'SELECT s.nom, s.rowid as socid, s.client, c.rowid, c.ref, c.total_ht, c.ref_client,'; - $sql2.= ' c.date_valid, c.date_commande, c.date_livraison, c.fk_statut, c.facture as facturee'; + $sql2.= ' c.date_valid, c.date_commande, c.date_livraison, c.fk_statut, c.facture as billed'; $sql2.= ' FROM '.MAIN_DB_PREFIX.'societe as s'; $sql2.= ', '.MAIN_DB_PREFIX.'commande as c'; $sql2.= ' WHERE c.fk_soc = s.rowid'; @@ -768,14 +775,16 @@ if ($object->id > 0) { $objp = $db->fetch_object($resql); + $commande_static->id = $objp->cid; + $commande_static->ref = $objp->ref; + $commande_static->ref_client=$objp->ref_client; + $commande_static->total_ht = $objp->total_ht; + $commande_static->total_tva = $objp->total_tva; + $commande_static->total_ttc = $objp->total_ttc; + $commande_static->billed = $objp->billed; + print ''; print '\n"; print ''; @@ -795,9 +804,8 @@ if ($object->id > 0) /* * Last shipments */ - if (! empty($conf->expedition->enabled) && $user->rights->expedition->lire) { - $sendingstatic = new Expedition($db); - + if (! empty($conf->expedition->enabled) && $user->rights->expedition->lire) + { $sql = 'SELECT e.rowid as id'; $sql.= ', e.ref'; $sql.= ', e.date_creation'; @@ -816,10 +824,11 @@ if ($object->id > 0) $sql.= " ORDER BY e.date_creation DESC"; $resql = $db->query($sql); - if ($resql) { - $var = true; - $num = $db->num_rows($resql); - $i = 0; + if ($resql) + { + $sendingstatic = new Expedition($db); + + $num = $db->num_rows($resql); if ($num > 0) { print '
'; - $commande_static->id = $objp->cid; - $commande_static->ref = $objp->ref; - $commande_static->ref_client=$objp->ref_client; - $commande_static->total_ht = $objp->total_ht; - $commande_static->total_tva = $objp->total_tva; - $commande_static->total_ttc = $objp->total_ttc; print $commande_static->getNomUrl(1); print ''.dol_print_date($db->jdate($objp->dc),'day')."'.price($objp->total_ht).'
'; @@ -830,12 +839,16 @@ if ($object->id > 0) print ''; } - while ($i < $num && $i < $MAXLIST) { + $i = 0; + while ($i < $num && $i < $MAXLIST) + { $objp = $db->fetch_object($resql); - print ''; - print ''; + print ''; if ($objp->date_creation > 0) { @@ -862,9 +875,7 @@ if ($object->id > 0) */ if (! empty($conf->contrat->enabled) && $user->rights->contrat->lire) { - $contratstatic=new Contrat($db); - - $sql = "SELECT s.nom, s.rowid, c.rowid as id, c.ref as ref, c.statut, c.datec as dc, c.date_contrat as dcon, c.ref_supplier as refsup"; + $sql = "SELECT s.nom, s.rowid, c.rowid as id, c.ref as ref, c.statut, c.datec as dc, c.date_contrat as dcon, c.ref_customer as refcus, c.ref_supplier as refsup"; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."contrat as c"; $sql.= " WHERE c.fk_soc = s.rowid "; $sql.= " AND s.rowid = ".$object->id; @@ -874,9 +885,10 @@ if ($object->id > 0) $resql=$db->query($sql); if ($resql) { - $var=true; + $contrat=new Contrat($db); + $num = $db->num_rows($resql); - if ($num >0 ) + if ($num >0) { print '
'; + $sendingstatic->id = $objp->id; $sendingstatic->ref = $objp->ref; + + print '
'; print $sendingstatic->getNomUrl(1); print '
'; @@ -887,17 +899,19 @@ if ($object->id > 0) print '
'; - $contrat->id=$objp->id; - $contrat->ref=$objp->ref?$objp->ref:$objp->id; print $contrat->getNomUrl(1,12); print "'.dol_trunc($objp->refsup,12)."'.img_picto($langs->trans("Statistics"),'stats').'
'; print ''; - } + $i = 0; while ($i < $num && $i < $MAXLIST) { @@ -983,8 +996,6 @@ if ($object->id > 0) */ if (! empty($conf->facture->enabled) && $user->rights->facture->lire) { - $invoicetemplate = new FactureRec($db); - $sql = 'SELECT f.rowid as id, f.titre as ref, f.amount'; $sql.= ', f.total as total_ht'; $sql.= ', f.tva as total_tva'; @@ -1007,9 +1018,9 @@ if ($object->id > 0) $resql=$db->query($sql); if ($resql) { - $var=true; + $invoicetemplate = new FactureRec($db); + $num = $db->num_rows($resql); - $i = 0; if ($num > 0) { print ''; @@ -1020,12 +1031,11 @@ if ($object->id > 0) print ''; } + $i = 0; while ($i < $num && $i < $MAXLIST) { $objp = $db->fetch_object($resql); - print ''; - print ''; + print ''; if ($objp->frequency && $objp->date_last_gen > 0) @@ -1084,8 +1097,6 @@ if ($object->id > 0) */ if (! empty($conf->facture->enabled) && $user->rights->facture->lire) { - $facturestatic = new Facture($db); - $sql = 'SELECT f.rowid as facid, f.facnumber, f.type, f.amount'; $sql.= ', f.total as total_ht'; $sql.= ', f.tva as total_tva'; @@ -1105,9 +1116,9 @@ if ($object->id > 0) $resql=$db->query($sql); if ($resql) { - $var=true; + $facturestatic = new Facture($db); + $num = $db->num_rows($resql); - $i = 0; if ($num > 0) { print '
'; $invoicetemplate->id = $objp->id; $invoicetemplate->ref = $objp->ref; $invoicetemplate->suspended = $objp->suspended; @@ -1034,6 +1044,9 @@ if ($object->id > 0) $invoicetemplate->total_ht = $objp->total_ht; $invoicetemplate->total_tva = $objp->total_tva; $invoicetemplate->total_ttc = $objp->total_ttc; + + print '
'; print $invoicetemplate->getNomUrl(1); print '
'; @@ -1119,18 +1130,20 @@ if ($object->id > 0) print ''; } + $i = 0; while ($i < $num && $i < $MAXLIST) { $objp = $db->fetch_object($resql); - print ''; - print ''; + print ''; if ($objp->df > 0) diff --git a/htdocs/comm/index.php b/htdocs/comm/index.php index 91004c86f67..255bb036c93 100644 --- a/htdocs/comm/index.php +++ b/htdocs/comm/index.php @@ -266,7 +266,7 @@ if (! empty($conf->supplier_proposal->enabled) && $user->rights->supplier_propos $companystatic->code_client = $obj->code_client; $companystatic->code_fournisseur = $obj->code_fournisseur; $companystatic->canvas=$obj->canvas; - print $companystatic->getNomUrl(1,'customer',16); + print $companystatic->getNomUrl(1,'supplier',16); print ''; print ''; $i++; @@ -676,8 +676,8 @@ if (! empty($conf->propal->enabled) && $user->rights->propal->lire) { $langs->load("propal"); - $sql = "SELECT s.nom as name, s.rowid, p.rowid as propalid, p.total as total_ttc, p.total_ht, p.tva as total_tva, p.ref, p.ref_client, p.fk_statut, p.datep as dp, p.fin_validite as dfv"; - $sql.= ", s.code_client"; + $sql = "SELECT s.nom as name, s.rowid, s.code_client"; + $sql.= ", p.rowid as propalid, p.entity, p.total as total_ttc, p.total_ht, p.tva as total_tva, p.ref, p.ref_client, p.fk_statut, p.datep as dp, p.fin_validite as dfv"; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s"; $sql.= ", ".MAIN_DB_PREFIX."propal as p"; if (! $user->rights->societe->client->voir && ! $socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; @@ -726,7 +726,7 @@ if (! empty($conf->propal->enabled) && $user->rights->propal->lire) print ''; print '
'; $facturestatic->id = $objp->facid; $facturestatic->ref = $objp->facnumber; $facturestatic->type = $objp->type; - $facturestatic->total_ht = $objp->total_ht; - $facturestatic->total_tva = $objp->total_tva; - $facturestatic->total_ttc = $objp->total_ttc; + $facturestatic->total_ht = $objp->total_ht; + $facturestatic->total_tva = $objp->total_tva; + $facturestatic->total_ttc = $objp->total_ttc; + + print '
'; print $facturestatic->getNomUrl(1); print ''.price($obj->total_ht).'
'; $filename=dol_sanitizeFileName($obj->ref); - $filedir=$conf->propal->dir_output . '/' . dol_sanitizeFileName($obj->ref); + $filedir=$conf->propal->multidir_output[$obj->entity] . '/' . dol_sanitizeFileName($obj->ref); $urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->propalid; print $formfile->getDocumentsLink($propalstatic->element, $filename, $filedir); print '
'; diff --git a/htdocs/comm/mailing/card.php b/htdocs/comm/mailing/card.php index db19e943b4b..a8bbb05fb84 100644 --- a/htdocs/comm/mailing/card.php +++ b/htdocs/comm/mailing/card.php @@ -967,7 +967,7 @@ else * Boutons d'action */ - if (GETPOST('cancel','alpha') || $confirm=='no' || $action == '' || in_array($action,array('settodraft', 'valid','delete','sendall','clone'))) + if (GETPOST('cancel','alpha') || $confirm=='no' || $action == '' || in_array($action,array('settodraft','valid','delete','sendall','clone','test'))) { print "\n\n
\n"; @@ -1071,7 +1071,9 @@ else print '
'; print load_fiche_titre($langs->trans("TestMailing")); - // Create l'objet formulaire mail + dol_fiche_head(null, '', '', -1); + + // Create l'objet formulaire mail include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; $formmail = new FormMail($db); $formmail->fromname = $object->email_from; @@ -1099,6 +1101,10 @@ else print $formmail->get_form(); print '
'; + + dol_fiche_end(); + + print dol_set_focus('#sendto'); } @@ -1114,7 +1120,7 @@ else dol_fiche_head('', '', '', -1); - print ''; + print '
'; // Subject print ''; @@ -1260,7 +1266,7 @@ else dol_fiche_head(null, '', '', -1); - print '
'.$langs->trans("MailTopic").''.$object->sujet.'
'; + print '
'; // Subject print ''; diff --git a/htdocs/comm/mailing/cibles.php b/htdocs/comm/mailing/cibles.php index 57af9f5c601..48949881659 100644 --- a/htdocs/comm/mailing/cibles.php +++ b/htdocs/comm/mailing/cibles.php @@ -260,7 +260,7 @@ if ($object->fetch($id) >= 0) print load_fiche_titre($langs->trans("ToAddRecipientsChooseHere"), ($user->admin?info_admin($langs->trans("YouCanAddYourOwnPredefindedListHere"),1):''), 'title_generic'); //print '
'.$langs->trans("MailTopic").'
'; - print '
'; + print '
'; //print '
'; print '
'; diff --git a/htdocs/comm/mailing/list.php b/htdocs/comm/mailing/list.php index f2491b09336..f9fad78a1f0 100644 --- a/htdocs/comm/mailing/list.php +++ b/htdocs/comm/mailing/list.php @@ -103,12 +103,13 @@ if ($result) $title=$langs->trans("ListOfEMailings"); if ($filteremail) $title.=' ('.$langs->trans("SentTo",$filteremail).')'; - print_barre_liste($title, $page, $_SERVER["PHP_SELF"],"",$sortfield,$sortorder,"",$num); + + $newcardbutton=''.$langs->trans('NewMailing').''; $i = 0; - $param = "&sall=".urlencode($sall); - if ($filteremail) $param.='&filteremail='.urlencode($filteremail); + $param = "&sall=".urlencode($sall); + if ($filteremail) $param.='&filteremail='.urlencode($filteremail); print '
'; if ($optioncss != '') print ''; @@ -118,7 +119,9 @@ if ($result) print ''; print ''; - $moreforfilter = ''; + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, '',$num, '', 'title_generic.png', 0, $newcardbutton); + + $moreforfilter = ''; print '
'; print '
'."\n"; diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index 380edc5a472..8be7fa249b8 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -201,7 +201,7 @@ if (empty($reshook)) { $result = $object->delete($user); if ($result > 0) { - header('Location: ' . DOL_URL_ROOT . '/comm/propal/list.php'); + header('Location: ' . DOL_URL_ROOT . '/comm/propal/list.php?restore_lastsearch_values=1'); exit(); } else { $langs->load("errors"); @@ -616,7 +616,7 @@ if (empty($reshook)) // Close proposal else if ($action == 'setstatut' && $user->rights->propal->cloturer && ! GETPOST('cancel','alpha')) { - if (! GETPOST('statut','int')) { + if (! (GETPOST('statut','int') > 0)) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CloseAs")), null, 'errors'); $action = 'statut'; } else { @@ -1228,9 +1228,11 @@ if (empty($reshook)) } else if ($action == 'update_extras') { + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) { @@ -1290,7 +1292,7 @@ if (empty($reshook)) } // Actions to build doc - $upload_dir = $conf->propal->dir_output; + $upload_dir = $conf->propal->multidir_output[$object->entity]; $permissioncreate=$user->rights->propal->creer; include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; @@ -1369,7 +1371,7 @@ if ($action == 'create') $soc = $objectsrc->thirdparty; - $cond_reglement_id = (! empty($objectsrc->cond_reglement_id)?$objectsrc->cond_reglement_id:(! empty($soc->cond_reglement_id)?$soc->cond_reglement_id:1)); + $cond_reglement_id = (! empty($objectsrc->cond_reglement_id)?$objectsrc->cond_reglement_id:(! empty($soc->cond_reglement_id)?$soc->cond_reglement_id:0)); // TODO maybe add default value option $mode_reglement_id = (! empty($objectsrc->mode_reglement_id)?$objectsrc->mode_reglement_id:(! empty($soc->mode_reglement_id)?$soc->mode_reglement_id:0)); $remise_percent = (! empty($objectsrc->remise_percent)?$objectsrc->remise_percent:(! empty($soc->remise_percent)?$soc->remise_percent:0)); $remise_absolue = (! empty($objectsrc->remise_absolue)?$objectsrc->remise_absolue:(! empty($soc->remise_absolue)?$soc->remise_absolue:0)); @@ -1456,17 +1458,13 @@ if ($action == 'create') // Ligne info remises tiers print ''; } @@ -1826,7 +1824,7 @@ if ($action == 'create') $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, $user->rights->propal->creer, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, $user->rights->propal->creer, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); if (empty($conf->global->MAIN_DISABLE_OTHER_LINK) && $object->thirdparty->id > 0) $morehtmlref.=' ('.$langs->trans("OtherProposals").')'; // Project if (! empty($conf->projet->enabled)) @@ -1873,31 +1871,26 @@ if ($action == 'create') print '
' . $langs->trans('Discounts') . ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", $soc->remise_percent); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); + $absolute_discount = $soc->getAvailableDiscounts(); - print '. '; - if ($absolute_discount) - print $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount, 0, $langs, 1, -1, -1, $conf->currency)); - else - print $langs->trans("CompanyHasNoAbsoluteDiscount"); - print '.'; + + $thirdparty = $soc; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . GETPOST('origin') . '&originid=' . GETPOST('originid')); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; print '
'; // Link for thirdparty discounts + if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + } else { + $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')"; + $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')"; + } + print ''; // Date of proposal @@ -1919,7 +1912,7 @@ if ($action == 'create') print ''; } else { if ($object->date) { - print dol_print_date($object->date, 'daytext'); + print dol_print_date($object->date, 'day'); } else { print ' '; } @@ -1945,7 +1938,7 @@ if ($action == 'create') print ''; } else { if (! empty($object->fin_validite)) { - print dol_print_date($object->fin_validite, 'daytext'); + print dol_print_date($object->fin_validite, 'day'); if ($object->statut == Propal::STATUS_VALIDATED && $object->fin_validite < ($now - $conf->propal->cloture->warning_delay)) print img_warning($langs->trans("Late")); } else { @@ -2346,9 +2339,9 @@ if ($action == 'create') // Send if ($object->statut == Propal::STATUS_VALIDATED || $object->statut == Propal::STATUS_SIGNED) { if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->propal->propal_advance->send) { - print ''; + print ''; } else - print ''; + print ''; } // Create an order @@ -2423,7 +2416,7 @@ if ($action == 'create') * Documents generes */ $filename = dol_sanitizeFileName($object->ref); - $filedir = $conf->propal->dir_output . "/" . dol_sanitizeFileName($object->ref); + $filedir = $conf->propal->multidir_output[$object->entity] . "/" . dol_sanitizeFileName($object->ref); $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id; $genallowed = $user->rights->propal->lire; $delallowed = $user->rights->propal->creer; @@ -2464,7 +2457,7 @@ if ($action == 'create') // Presend form $modelmail='propal_send'; $defaulttopic='SendPropalRef'; - $diroutput = $conf->propal->dir_output; + $diroutput = $conf->propal->multidir_output[$object->entity]; $trackid = 'pro'.$object->id; include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php'; diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 2b714bbeff8..6e98329a4cc 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -1316,7 +1316,7 @@ class Propal extends CommonObject function fetch($rowid,$ref='') { - $sql = "SELECT p.rowid, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc"; + $sql = "SELECT p.rowid, p.ref, p.entity, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc"; $sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht"; $sql.= ", p.datec"; $sql.= ", p.date_valid as datev"; @@ -1343,8 +1343,8 @@ class Propal extends CommonObject $sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc"; $sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement"; $sql.= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."propal as p"; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid AND cr.entity IN ('.getEntity('c_payment_term').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid'; @@ -1362,6 +1362,7 @@ class Propal extends CommonObject $obj = $this->db->fetch_object($resql); $this->id = $obj->rowid; + $this->entity = $obj->entity; $this->ref = $obj->ref; $this->ref_client = $obj->ref_client; @@ -1748,8 +1749,8 @@ class Propal extends CommonObject // to not lose the linked files $oldref = dol_sanitizeFileName($this->ref); $newref = dol_sanitizeFileName($num); - $dirsource = $conf->propal->dir_output.'/'.$oldref; - $dirdest = $conf->propal->dir_output.'/'.$newref; + $dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref; + $dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref; if (file_exists($dirsource)) { @@ -1758,7 +1759,7 @@ class Propal extends CommonObject { dol_syslog("Rename ok"); // Rename docs starting with $oldref with $newref - $listoffiles=dol_dir_list($conf->propal->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref,'/')); + $listoffiles=dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref,'/')); foreach($listoffiles as $fileentry) { $dirsource=$fileentry['name']; @@ -2527,21 +2528,6 @@ class Propal extends CommonObject } } - /** - * Class invoiced the Propal - * - * @return int <0 si ko, >0 si ok - * @deprecated - * @see classifyBilled() - */ - function classer_facturee() - { - global $user; - dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING); - - return $this->classifyBilled($user); - } - /** * Set draft status * @@ -2814,9 +2800,9 @@ class Propal extends CommonObject { // We remove directory $ref = dol_sanitizeFileName($this->ref); - if ($conf->propal->dir_output && !empty($this->ref)) + if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) { - $dir = $conf->propal->dir_output . "/" . $ref ; + $dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref ; $file = $dir . "/" . $ref . ".pdf"; if (file_exists($file)) { diff --git a/htdocs/comm/propal/contact.php b/htdocs/comm/propal/contact.php index 01e851769a7..eff22ba4358 100644 --- a/htdocs/comm/propal/contact.php +++ b/htdocs/comm/propal/contact.php @@ -164,7 +164,7 @@ if ($object->id > 0) $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); // Project if (! empty($conf->projet->enabled)) { diff --git a/htdocs/comm/propal/document.php b/htdocs/comm/propal/document.php index ba373c78f44..8ef875f0a23 100644 --- a/htdocs/comm/propal/document.php +++ b/htdocs/comm/propal/document.php @@ -75,7 +75,7 @@ $object->fetch($id,$ref); if ($object->id > 0) { $object->fetch_thirdparty(); - $upload_dir = $conf->propal->dir_output.'/'.dol_sanitizeFileName($object->ref); + $upload_dir = $conf->propal->multidir_output[$object->entity].'/'.dol_sanitizeFileName($object->ref); include_once DOL_DOCUMENT_ROOT . '/core/actions_linkedfiles.inc.php'; } @@ -90,7 +90,7 @@ $form = new Form($db); if ($object->id > 0) { - $upload_dir = $conf->propal->dir_output.'/'.dol_sanitizeFileName($object->ref); + $upload_dir = $conf->propal->multidir_output[$object->entity].'/'.dol_sanitizeFileName($object->ref); $head = propal_prepare_head($object); dol_fiche_head($head, 'document', $langs->trans('Proposal'), -1, 'propal'); @@ -114,7 +114,7 @@ if ($object->id > 0) $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); // Project if (! empty($conf->projet->enabled)) { diff --git a/htdocs/comm/propal/index.php b/htdocs/comm/propal/index.php index a313ab299f0..e9dce10ea0b 100644 --- a/htdocs/comm/propal/index.php +++ b/htdocs/comm/propal/index.php @@ -216,7 +216,7 @@ $max=5; * Last modified proposals */ -$sql = "SELECT c.rowid, c.ref, c.fk_statut, s.nom as socname, s.rowid as socid, s.canvas, s.client,"; +$sql = "SELECT c.rowid, c.entity, c.ref, c.fk_statut, s.nom as socname, s.rowid as socid, s.canvas, s.client,"; $sql.= " date_cloture as datec"; $sql.= " FROM ".MAIN_DB_PREFIX."propal as c"; $sql.= ", ".MAIN_DB_PREFIX."societe as s"; @@ -262,7 +262,7 @@ if ($resql) print '
' . $langs->trans('Discounts') . ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", $soc->remise_percent); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); - print '. '; - $absolute_discount = $soc->getAvailableDiscounts('', 'fk_facture_source IS NULL'); - $absolute_creditnote = $soc->getAvailableDiscounts('', 'fk_facture_source IS NOT NULL'); + + $absolute_discount = $soc->getAvailableDiscounts('', $filterabsolutediscount); + $absolute_creditnote = $soc->getAvailableDiscounts('', $filtercreditnote); $absolute_discount = price2num($absolute_discount, 'MT'); $absolute_creditnote = price2num($absolute_creditnote, 'MT'); - if ($absolute_discount) { - if ($object->statut > Propal::STATUS_DRAFT) { - print $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount, 0, $langs, 0, 0, -1, $conf->currency)); - } else { - // Remise dispo de type non avoir - $filter = 'fk_facture_source IS NULL'; - print '
'; - $form->form_remise_dispo($_SERVER["PHP_SELF"] . '?id=' . $object->id, 0, 'remise_id', $soc->id, $absolute_discount, $filter, 0, '', 1); - } - } - if ($absolute_creditnote) { - print $langs->trans("CompanyHasCreditNote", price($absolute_creditnote, 0, $langs, 0, 0, -1, $conf->currency)) . '. '; - } - if (! $absolute_discount && ! $absolute_creditnote) - print $langs->trans("CompanyHasNoAbsoluteDiscount") . '.'; + + $thirdparty = $soc; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?id=' . $object->id); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print '
'; $filename=dol_sanitizeFileName($obj->ref); - $filedir=$conf->propal->dir_output . '/' . dol_sanitizeFileName($obj->ref); + $filedir=$conf->propal->multidir_output[$obj->entity] . '/' . dol_sanitizeFileName($obj->ref); $urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->rowid; print $formfile->getDocumentsLink($propalstatic->element, $filename, $filedir); print '
'; @@ -296,7 +296,8 @@ if (! empty($conf->propal->enabled) && $user->rights->propale->lire) $now=dol_now(); - $sql = "SELECT s.nom as socname, s.rowid as socid, s.canvas, s.client, p.rowid as propalid, p.total as total_ttc, p.total_ht, p.ref, p.fk_statut, p.datep as dp, p.fin_validite as dfv"; + $sql = "SELECT s.nom as socname, s.rowid as socid, s.canvas, s.client"; + $sql.= ", p.rowid as propalid, p.entity, p.total as total_ttc, p.total_ht, p.ref, p.fk_statut, p.datep as dp, p.fin_validite as dfv"; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s"; $sql.= ", ".MAIN_DB_PREFIX."propal as p"; if (!$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; @@ -341,7 +342,7 @@ if (! empty($conf->propal->enabled) && $user->rights->propale->lire) print ''; print ''; $filename=dol_sanitizeFileName($obj->ref); - $filedir=$conf->propal->dir_output . '/' . dol_sanitizeFileName($obj->ref); + $filedir=$conf->propal->multidir_output[$obj->entity] . '/' . dol_sanitizeFileName($obj->ref); $urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->propalid; print $formfile->getDocumentsLink($propalstatic->element, $filename, $filedir); print ''; diff --git a/htdocs/comm/propal/list.php b/htdocs/comm/propal/list.php index 5de43a75b9d..3259654ebcc 100644 --- a/htdocs/comm/propal/list.php +++ b/htdocs/comm/propal/list.php @@ -12,6 +12,7 @@ * Copyright (C) 2015 Jean-François Ferry * Copyright (C) 2016 Ferran Marcet * Copyright (C) 2017 Charlene Benke + * Copyright (C) 2018 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,6 +53,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'proposallist'; $search_user=GETPOST('search_user','int'); $search_sale=GETPOST('search_sale','int'); @@ -94,9 +96,6 @@ $pagenext = $page + 1; if (! $sortfield) $sortfield='p.ref'; if (! $sortorder) $sortorder='DESC'; -// Initialize technical object to manage context to save list fields -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'proposallist'; - // Security check $module='propal'; $dbtable=''; @@ -110,9 +109,10 @@ if (! empty($socid)) } $result = restrictedArea($user, $module, $objectid, $dbtable); -$diroutputmassaction=$conf->propal->dir_output . '/temp/massgeneration/'.$user->id; +$diroutputmassaction=$conf->propal->multidir_output[$conf->entity] . '/temp/massgeneration/'.$user->id; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$object = new Propal($db); $hookmanager->initHooks(array('propallist')); $extrafields = new ExtraFields($db); @@ -214,7 +214,8 @@ if (empty($reshook)) $objectlabel='Proposals'; $permtoread = $user->rights->propal->lire; $permtodelete = $user->rights->propal->supprimer; - $uploaddir = $conf->propal->dir_output; + $permtoclose = $user->rights->propal->cloturer; + $uploaddir = $conf->propal->multidir_output[$conf->entity]; include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; } @@ -242,7 +243,7 @@ if ($sall || $search_product_category > 0) $sql = 'SELECT DISTINCT'; $sql.= ' s.rowid as socid, s.nom as name, s.email, s.town, s.zip, s.fk_pays, s.client, s.code_client, '; $sql.= " typent.code as typent_code,"; $sql.= " state.code_departement as state_code, state.nom as state_name,"; -$sql.= ' p.rowid, p.note_private, p.total_ht, p.tva as total_vat, p.total as total_ttc, p.localtax1, p.localtax2, p.ref, p.ref_client, p.fk_statut, p.fk_user_author, p.datep as dp, p.fin_validite as dfv,'; +$sql.= ' p.rowid, p.entity, p.note_private, p.total_ht, p.tva as total_vat, p.total as total_ttc, p.localtax1, p.localtax2, p.ref, p.ref_client, p.fk_statut, p.fk_user_author, p.datep as dp, p.fin_validite as dfv,'; $sql.= ' p.datec as date_creation, p.tms as date_update,'; $sql.= " pr.rowid as project_id, pr.ref as project_ref,"; if (! $user->rights->societe->client->voir && ! $socid) $sql .= " sc.fk_soc, sc.fk_user,"; @@ -389,9 +390,16 @@ if ($resql) 'builddoc'=>$langs->trans("PDFMerge"), ); if ($user->rights->propal->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); - if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); + if ($user->rights->propal->cloturer) $arrayofmassactions['closed']=$langs->trans("Closed"); + if (in_array($massaction, array('presend','predelete','closed'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); + $newcardbutton=''; + if ($user->rights->propal->creer) + { + $newcardbutton=''.$langs->trans('NewPropal').''; + } + // Lignes des champs de filtre print '
'; if ($optioncss != '') print ''; @@ -403,7 +411,7 @@ if ($resql) print ''; print ''; - print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, '', '', $limit); + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, $newcardbutton, '', $limit); $topicmail="SendPropalRef"; $modelmail="proposal_send"; @@ -462,7 +470,7 @@ if ($resql) $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields - if ($massactionbutton) $selectedfields.=$form->showCheckAddButtons('checkforselect', 1); + $selectedfields.=(count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); print '
'; print ''."\n"; @@ -614,7 +622,7 @@ if ($resql) // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields - $parameters=array('arrayfields'=>$arrayfields); + $parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['p.datec']['checked'])) print_liste_field_titre($arrayfields['p.datec']['label'],$_SERVER["PHP_SELF"],"p.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); @@ -663,7 +671,7 @@ if ($resql) // Other picto tool print '
'; $filename=dol_sanitizeFileName($obj->ref); - $filedir=$conf->propal->dir_output . '/' . dol_sanitizeFileName($obj->ref); + $filedir=$conf->propal->multidir_output[$obj->entity] . '/' . dol_sanitizeFileName($obj->ref); $urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->rowid; print $formfile->getDocumentsLink($objectstatic->element, $filename, $filedir); print '
'; diff --git a/htdocs/comm/propal/tpl/linkedobjectblock.tpl.php b/htdocs/comm/propal/tpl/linkedobjectblock.tpl.php index cc8c6fe0547..a13d80c083c 100644 --- a/htdocs/comm/propal/tpl/linkedobjectblock.tpl.php +++ b/htdocs/comm/propal/tpl/linkedobjectblock.tpl.php @@ -52,18 +52,18 @@ foreach($linkedObjectBlock as $key => $objectlink) $trclass=($var?'pair':'impair'); if ($ilink == count($linkedObjectBlock) && empty($noMoreLinkedObjectBlockAfter) && count($linkedObjectBlock) <= 1) $trclass.=' liste_sub_total'; ?> - - trans("Proposal"); ?> - getNomUrl(1); ?> - ref_client; ?> - date,'day'); ?> - " data-element="element; ?>" data-id="id; ?>" > + trans("Proposal"); ?> + getNomUrl(1); ?> + ref_client; ?> + date,'day'); ?> + rights->propale->lire) { $total = $total + $objectlink->total_ht; echo price($objectlink->total_ht); } ?> - getLibStatut(3); ?> - ">transnoentitiesnoconv("RemoveLink")); ?> + getLibStatut(3); ?> + ">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?> load("bills"); $id=GETPOST("id",'int'); -$socid = GETPOST('id','int'); +$socid = GETPOST('id','int')?GETPOST('id','int'):GETPOST('socid','int'); // Security check if ($user->societe_id > 0) { @@ -56,7 +56,14 @@ if (GETPOST('action','aZ09') == 'setremise') { $object = new Societe($db); $object->fetch($id); - $result=$object->set_remise_client(price2num(GETPOST("remise")),GETPOST("note"),$user); + + $discount_type = GETPOST('discount_type', 'int'); + + if(! empty($discount_type)) { + $result=$object->set_remise_supplier(price2num(GETPOST("remise")),GETPOST("note"),$user); + } else { + $result=$object->set_remise_client(price2num(GETPOST("remise")),GETPOST("note"),$user); + } if ($result > 0) { @@ -100,34 +107,73 @@ if ($socid > 0) $head = societe_prepare_head($object); - + $isCustomer = $object->client == 1 || $object->client == 3; + $isSupplier = $object->fournisseur == 1; print ''; print ''; print ''; print ''; - dol_fiche_head($head, 'relativediscount', $langs->trans("ThirdParty"), 0, 'company'); + dol_fiche_head($head, 'relativediscount', $langs->trans("ThirdParty"), -1, 'company'); dol_banner_tab($object, 'socid', '', ($user->societe_id?0:1), 'rowid', 'nom'); print '
'; print '
'; + + if(! $isCustomer && ! $isSupplier) { + print '

'.$langs->trans('ThirdpartyIsNeitherCustomerNorClientSoCannotHaveDiscounts').'

'; + + dol_fiche_end(); + + print ''; + + llxFooter(); + $db->close(); + exit; + } + print ''; - // Discount - print '"; + if($isCustomer) { + // Customer discount + print '"; + } + + if($isSupplier) { + // Supplier discount + print '"; + } + print '
'; - print $langs->trans("CustomerRelativeDiscount").''.price2num($object->remise_percent)."%
'; + print $langs->trans("CustomerRelativeDiscount").''.price2num($object->remise_percent)."%
'; + print $langs->trans("SupplierRelativeDiscount").''.price2num($object->remise_supplier_percent)."%
'; print '
'; print load_fiche_titre($langs->trans("NewRelativeDiscount"),'',''); print '
'; - + + if($isCustomer && ! $isSupplier) { + print ''; + } + + if(! $isCustomer && $isSupplier) { + print ''; + } + print ''; + if($isCustomer && $isSupplier) { + // Discount type + print ''; + print ''; + } + // New value print ''; @@ -155,57 +201,128 @@ if ($socid > 0) print '
'; + if($isCustomer) { + if($isSupplier) { + print '
'; + print '
'; + print load_fiche_titre($langs->trans("CustomerDiscounts"), '', ''); + } - /* - * List log of all percent discounts - */ - $sql = "SELECT rc.rowid, rc.remise_client as remise_percent, rc.note, rc.datec as dc,"; - $sql.= " u.login, u.rowid as user_id"; - $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise as rc, ".MAIN_DB_PREFIX."user as u"; - $sql.= " WHERE rc.fk_soc = " . $object->id; - $sql.= " AND rc.entity = " . $conf->entity; - $sql.= " AND u.rowid = rc.fk_user_author"; - $sql.= " ORDER BY rc.datec DESC"; + /* + * List log of all customer percent discounts + */ + $sql = "SELECT rc.rowid, rc.remise_client as remise_percent, rc.note, rc.datec as dc,"; + $sql.= " u.login, u.rowid as user_id"; + $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise as rc, ".MAIN_DB_PREFIX."user as u"; + $sql.= " WHERE rc.fk_soc = " . $object->id; + $sql.= " AND rc.entity = " . $conf->entity; + $sql.= " AND u.rowid = rc.fk_user_author"; + $sql.= " ORDER BY rc.datec DESC"; - $resql=$db->query($sql); - if ($resql) - { - print '
'.$langs->trans('DiscountType').' '; + print ' '; + print '
'; print $langs->trans("NewValue").'%
'; - $tag = !$tag; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - $num = $db->num_rows($resql); - if ($num > 0) - { - $i = 0; - while ($i < $num) - { - $obj = $db->fetch_object($resql); - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - $i++; - } + $resql=$db->query($sql); + if ($resql) + { + print '
'.$langs->trans("Date").''.$langs->trans("CustomerRelativeDiscountShort").''.$langs->trans("NoteReason").''.$langs->trans("User").'
'.dol_print_date($db->jdate($obj->dc),"dayhour").''.price2num($obj->remise_percent).'%'.$obj->note.''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.'
'; + $tag = !$tag; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $num = $db->num_rows($resql); + if ($num > 0) + { + $i = 0; + while ($i < $num) + { + $obj = $db->fetch_object($resql); + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $i++; + } + } + else + { + print ''; + } + $db->free($resql); + print "
'.$langs->trans("Date").''.$langs->trans("CustomerRelativeDiscountShort").''.$langs->trans("NoteReason").''.$langs->trans("User").'
'.dol_print_date($db->jdate($obj->dc),"dayhour").''.price2num($obj->remise_percent).'%'.$obj->note.''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.'
'.$langs->trans("None").'
"; } else { - print ''.$langs->trans("None").''; - } - $db->free($resql); - print ""; - } - else - { - dol_print_error($db); + dol_print_error($db); + } } + if($isSupplier) { + if($isCustomer) { + print '
'; // class="fichehalfleft" + print '
'; + print '
'; + print load_fiche_titre($langs->trans("SupplierDiscounts"), '', ''); + } + + /* + * List log of all supplier percent discounts + */ + $sql = "SELECT rc.rowid, rc.remise_supplier as remise_percent, rc.note, rc.datec as dc,"; + $sql.= " u.login, u.rowid as user_id"; + $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise_supplier as rc, ".MAIN_DB_PREFIX."user as u"; + $sql.= " WHERE rc.fk_soc = " . $object->id; + $sql.= " AND rc.entity = " . $conf->entity; + $sql.= " AND u.rowid = rc.fk_user_author"; + $sql.= " ORDER BY rc.datec DESC"; + + $resql=$db->query($sql); + if ($resql) + { + print ''; + $tag = !$tag; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $num = $db->num_rows($resql); + if ($num > 0) + { + $i = 0; + while ($i < $num) + { + $obj = $db->fetch_object($resql); + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $i++; + } + } + else + { + print ''; + } + $db->free($resql); + print "
'.$langs->trans("Date").''.$langs->trans("CustomerRelativeDiscountShort").''.$langs->trans("NoteReason").''.$langs->trans("User").'
'.dol_print_date($db->jdate($obj->dc),"dayhour").''.price2num($obj->remise_percent).'%'.$obj->note.''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.'
'.$langs->trans("None").'
"; + } + else + { + dol_print_error($db); + } + + if($isCustomer) { + print '
'; // class="ficheaddleft" + print '
'; // class="fichehalfright" + print '
'; // class="fichecenter" + } + } } llxFooter(); diff --git a/htdocs/comm/remx.php b/htdocs/comm/remx.php index 0127b8f15be..b580684af7e 100644 --- a/htdocs/comm/remx.php +++ b/htdocs/comm/remx.php @@ -26,6 +26,7 @@ require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php'; $langs->load("orders"); @@ -92,6 +93,12 @@ if ($action == 'confirm_split' && GETPOST("confirm") == 'yes') $newdiscount2->fk_facture=$discount->fk_facture; $newdiscount1->fk_facture_line=$discount->fk_facture_line; $newdiscount2->fk_facture_line=$discount->fk_facture_line; + $newdiscount1->fk_invoice_supplier_source=$discount->fk_invoice_supplier_source; + $newdiscount2->fk_invoice_supplier_source=$discount->fk_invoice_supplier_source; + $newdiscount1->fk_invoice_supplier=$discount->fk_invoice_supplier; + $newdiscount2->fk_invoice_supplier=$discount->fk_invoice_supplier; + $newdiscount1->fk_invoice_supplier_line=$discount->fk_invoice_supplier_line; + $newdiscount2->fk_invoice_supplier_line=$discount->fk_invoice_supplier_line; if ($discount->description == '(CREDIT_NOTE)' || $discount->description == '(DEPOSIT)') { $newdiscount1->description=$discount->description; @@ -106,6 +113,8 @@ if ($action == 'confirm_split' && GETPOST("confirm") == 'yes') $newdiscount2->fk_user=$discount->fk_user; $newdiscount1->fk_soc=$discount->fk_soc; $newdiscount2->fk_soc=$discount->fk_soc; + $newdiscount1->discount_type=$discount->discount_type; + $newdiscount2->discount_type=$discount->discount_type; $newdiscount1->datec=$discount->datec; $newdiscount2->datec=$discount->datec; $newdiscount1->tva_tx=$discount->tva_tx; @@ -143,6 +152,7 @@ if ($action == 'setremise' && $user->rights->societe->creer) $amount_ht=GETPOST('amount_ht'); $desc=GETPOST('desc','alpha'); $tva_tx=GETPOST('tva_tx','alpha'); + $discount_type=! empty($_POST['discount_type'])?GETPOST('discount_type','alpha'):0; if (price2num($amount_ht) > 0) { @@ -157,7 +167,7 @@ if ($action == 'setremise' && $user->rights->societe->creer) { $soc = new Societe($db); $soc->fetch($id); - $discountid=$soc->set_remise_except($amount_ht,$user,$desc,$tva_tx); + $discountid=$soc->set_remise_except($amount_ht,$user,$desc,$tva_tx,$discount_type); if ($discountid > 0) { @@ -215,6 +225,7 @@ if (GETPOST('action','aZ09') == 'confirm_remove' && GETPOST("confirm")=='yes') $form=new Form($db); $facturestatic=new Facture($db); +$facturefournstatic=new FactureFournisseur($db); llxHeader('',$langs->trans("GlobalDiscount")); @@ -224,12 +235,14 @@ if ($socid > 0) $object = new Societe($db); $object->fetch($socid); + $isCustomer = $object->client == 1 || $object->client == 3; + $isSupplier = $object->fournisseur == 1; + /* * Display tabs */ $head = societe_prepare_head($object); - print '
'; print ''; print ''; @@ -242,36 +255,85 @@ if ($socid > 0) print '
'; print '
'; + + if(! $isCustomer && ! $isSupplier) { + print '

'.$langs->trans('ThirdpartyIsNeitherCustomerNorClientSoCannotHaveDiscounts').'

'; + + dol_fiche_end(); + + print ''; + + llxFooter(); + $db->close(); + exit; + } + + print ''; - // Calcul avoirs en cours - $remise_all=$remise_user=0; - $sql = "SELECT SUM(rc.amount_ht) as amount, rc.fk_user"; - $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise_except as rc"; - $sql.= " WHERE rc.fk_soc = " . $object->id; - $sql.= " AND rc.entity = " . $conf->entity; - $sql.= " AND (fk_facture_line IS NULL AND fk_facture IS NULL)"; - $sql.= " GROUP BY rc.fk_user"; - $resql=$db->query($sql); - if ($resql) - { - $obj = $db->fetch_object($resql); - $remise_all+=$obj->amount; - if ($obj->fk_user == $user->id) $remise_user+=$obj->amount; - } - else - { - dol_print_error($db); + if($isCustomer) { // Calcul avoirs client en cours + $remise_all=$remise_user=0; + $sql = "SELECT SUM(rc.amount_ht) as amount, rc.fk_user"; + $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql.= " WHERE rc.fk_soc = " . $object->id; + $sql.= " AND rc.entity = " . $conf->entity; + $sql.= " AND discount_type = 0"; // Exclude supplier discounts + $sql.= " AND (fk_facture_line IS NULL AND fk_facture IS NULL)"; + $sql.= " GROUP BY rc.fk_user"; + $resql=$db->query($sql); + if ($resql) + { + $obj = $db->fetch_object($resql); + $remise_all+=$obj->amount; + if ($obj->fk_user == $user->id) $remise_user+=$obj->amount; + } + else + { + dol_print_error($db); + } + + print ''; + print ''; + + if (! empty($user->fk_soc)) // No need to show this for external users + { + print ''; + print ''; + } } - print ''; - print ''; - - if (! empty($user->fk_soc)) // No need to show this for external users - { - print ''; - print ''; + if($isSupplier) { + // Calcul avoirs fournisseur en cours + $remise_all=$remise_user=0; + $sql = "SELECT SUM(rc.amount_ht) as amount, rc.fk_user"; + $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql.= " WHERE rc.fk_soc = " . $object->id; + $sql.= " AND rc.entity = " . $conf->entity; + $sql.= " AND discount_type = 1"; // Exclude customer discounts + $sql.= " AND (fk_invoice_supplier_line IS NULL AND fk_invoice_supplier IS NULL)"; + $sql.= " GROUP BY rc.fk_user"; + $resql=$db->query($sql); + if ($resql) + { + $obj = $db->fetch_object($resql); + $remise_all+=$obj->amount; + if ($obj->fk_user == $user->id) $remise_user+=$obj->amount; + } + else + { + dol_print_error($db); + } + + print ''; + print ''; + + if (! empty($user->fk_soc)) // No need to show this for external users + { + print ''; + print ''; + } } + print '
'.$langs->trans("CustomerAbsoluteDiscountAllUsers").''.$remise_all.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("CustomerAbsoluteDiscountMy").''.$remise_user.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("CustomerAbsoluteDiscountAllUsers").''.$remise_all.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("CustomerAbsoluteDiscountMy").''.$remise_user.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("SupplierAbsoluteDiscountAllUsers").''.$remise_all.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'.$langs->trans("SupplierAbsoluteDiscountMy").''.$remise_user.' '.$langs->trans("Currency".$conf->currency).' '.$langs->trans("HT").'
'; print '
'; @@ -283,7 +345,22 @@ if ($socid > 0) print load_fiche_titre($langs->trans("NewGlobalDiscount"),'',''); print '
'; + + if($isCustomer && ! $isSupplier) { + print ''; + } + + if(! $isCustomer && $isSupplier) { + print ''; + } + print ''; + if($isCustomer && $isSupplier) { + print ''; + print ''; + } print ''; print ''; @@ -321,132 +398,287 @@ if ($socid > 0) print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&remid='.GETPOST('remid'), $langs->trans('RemoveDiscount'), $langs->trans('ConfirmRemoveDiscount'), 'confirm_remove', '', 0, 1); } + /* - * Liste remises fixes restant en cours (= liees a aucune facture ni ligne de facture) + * Liste remises fixes client restant en cours (= liees a aucune facture ni ligne de facture) */ - $sql = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; - $sql.= " rc.datec as dc, rc.description,"; - $sql.= " rc.fk_facture_source,"; - $sql.= " u.login, u.rowid as user_id,"; - $sql.= " fa.facnumber as ref, fa.type as type"; - $sql.= " FROM ".MAIN_DB_PREFIX."user as u, ".MAIN_DB_PREFIX."societe_remise_except as rc"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fa ON rc.fk_facture_source = fa.rowid"; - $sql.= " WHERE rc.fk_soc = " . $object->id; - $sql.= " AND rc.entity = " . $conf->entity; - $sql.= " AND u.rowid = rc.fk_user"; - $sql.= " AND (rc.fk_facture_line IS NULL AND rc.fk_facture IS NULL)"; - $sql.= " ORDER BY rc.datec DESC"; + + print load_fiche_titre($langs->trans("DiscountStillRemaining")); - $resql=$db->query($sql); - if ($resql) - { - print load_fiche_titre($langs->trans("DiscountStillRemaining")); - print '
'.$langs->trans('DiscountType').' '; + print ' '; + print '
'.$langs->trans("AmountHT").''; print ' '.$langs->trans("Currency".$conf->currency).'
'; - print ''; - print ''; // Need 120+ for format with AM/PM - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; + if($isCustomer) { + if($isSupplier) { + print '
'; + print '
'; + print load_fiche_titre($langs->trans("CustomerDiscounts"), '', ''); + } - $showconfirminfo=array(); - - $i = 0; - $num = $db->num_rows($resql); - if ($num > 0) + $sql = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; + $sql.= " rc.datec as dc, rc.description,"; + $sql.= " rc.fk_facture_source,"; + $sql.= " u.login, u.rowid as user_id,"; + $sql.= " fa.facnumber as ref, fa.type as type"; + $sql.= " FROM ".MAIN_DB_PREFIX."user as u, ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fa ON rc.fk_facture_source = fa.rowid"; + $sql.= " WHERE rc.fk_soc = " . $object->id; + $sql.= " AND rc.entity = " . $conf->entity; + $sql.= " AND u.rowid = rc.fk_user"; + $sql.= " AND rc.discount_type = 0"; // Eliminate supplier discounts + $sql.= " AND (rc.fk_facture_line IS NULL AND rc.fk_facture IS NULL)"; + $sql.= " ORDER BY rc.datec DESC"; + + $resql=$db->query($sql); + if ($resql) { - while ($i < $num) - { - $obj = $db->fetch_object($resql); - - print '
'; - print ''; - if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) - { - print ''; - } - elseif (preg_match('/\(DEPOSIT\)/',$obj->description)) - { - print ''; - } - elseif (preg_match('/\(EXCESS RECEIVED\)/',$obj->description)) - { - print ''; - } - else - { - print ''; - } - print ''; - print ''; - print ''; - print ''; - print ''; - if ($user->rights->societe->creer || $user->rights->facture->creer) - { - print ''; - } - else print ''; - print ''; - - if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid) - { - $showconfirminfo['rowid']=$obj->rowid; - $showconfirminfo['amount_ttc']=$obj->amount_ttc; - } - $i++; - } + print '
'.$langs->trans("Date").''.$langs->trans("ReasonDiscount").''.$langs->trans("ConsumedBy").''.$langs->trans("AmountHT").''.$langs->trans("VATRate").''.$langs->trans("AmountTTC").''.$langs->trans("DiscountOfferedBy").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').''; - $facturestatic->id=$obj->fk_facture_source; - $facturestatic->ref=$obj->ref; - $facturestatic->type=$obj->type; - print preg_replace('/\(CREDIT_NOTE\)/',$langs->trans("CreditNote"),$obj->description).' '.$facturestatic->getNomURl(1); - print ''; - $facturestatic->id=$obj->fk_facture_source; - $facturestatic->ref=$obj->ref; - $facturestatic->type=$obj->type; - print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturestatic->getNomURl(1); - print ''; - $facturestatic->id=$obj->fk_facture_source; - $facturestatic->ref=$obj->ref; - $facturestatic->type=$obj->type; - print preg_replace('/\(EXCESS RECEIVED\)/',$langs->trans("ExcessReceived"),$obj->description).' '.$facturestatic->getNomURl(1); - print ''; - print $obj->description; - print ''.$langs->trans("NotConsumed").''.price($obj->amount_ht).''.price2num($obj->tva_tx,'MU').'%'.price($obj->amount_ttc).''; - print ''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.''; - print ''; - print 'rowid.($backtopage?'&backtopage='.urlencode($backtopage):'').'">'.img_split($langs->trans("SplitDiscount")).''; - print '   '; - print 'rowid.($backtopage?'&backtopage='.urlencode($backtopage):'').'">'.img_delete($langs->trans("RemoveDiscount")).''; - print ' 
'; + print ''; + print ''; // Need 120+ for format with AM/PM + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + $showconfirminfo=array(); + + $i = 0; + $num = $db->num_rows($resql); + if ($num > 0) + { + while ($i < $num) + { + $obj = $db->fetch_object($resql); + + print ''; + print ''; + if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(DEPOSIT\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(EXCESS RECEIVED\)/',$obj->description)) + { + print ''; + } + else + { + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + if ($user->rights->societe->creer || $user->rights->facture->creer) + { + print ''; + } + else print ''; + print ''; + + if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid) + { + $showconfirminfo['rowid']=$obj->rowid; + $showconfirminfo['amount_ttc']=$obj->amount_ttc; + } + $i++; + } + } + else + { + print ''; + } + $db->free($resql); + print "
'.$langs->trans("Date").''.$langs->trans("ReasonDiscount").''.$langs->trans("ConsumedBy").''.$langs->trans("AmountHT").''.$langs->trans("VATRate").''.$langs->trans("AmountTTC").''.$langs->trans("DiscountOfferedBy").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(CREDIT_NOTE\)/',$langs->trans("CreditNote"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(EXCESS RECEIVED\)/',$langs->trans("ExcessReceived"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + print $obj->description; + print ''.$langs->trans("NotConsumed").''.price($obj->amount_ht).''.price2num($obj->tva_tx,'MU').'%'.price($obj->amount_ttc).''; + print ''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.''; + print ''; + print 'rowid.($backtopage?'&backtopage='.urlencode($backtopage):'').'">'.img_split($langs->trans("SplitDiscount")).''; + print '   '; + print 'rowid.($backtopage?'&backtopage='.urlencode($backtopage):'').'">'.img_delete($langs->trans("RemoveDiscount")).''; + print ' 
'.$langs->trans("None").'
"; + + if (count($showconfirminfo)) + { + $amount1=price2num($showconfirminfo['amount_ttc']/2,'MT'); + $amount2=($showconfirminfo['amount_ttc']-$amount1); + $formquestion=array( + 'text' => $langs->trans('TypeAmountOfEachNewDiscount'), + array('type' => 'text', 'name' => 'amount_ttc_1', 'label' => $langs->trans("AmountTTC").' 1', 'value' => $amount1, 'size' => '5'), + array('type' => 'text', 'name' => 'amount_ttc_2', 'label' => $langs->trans("AmountTTC").' 2', 'value' => $amount2, 'size' => '5') + ); + $langs->load("dict"); + print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&remid='.$showconfirminfo['rowid'].($backtopage?'&backtopage='.urlencode($backtopage):''), $langs->trans('SplitDiscount'), $langs->trans('ConfirmSplitDiscount',price($showconfirminfo['amount_ttc']),$langs->transnoentities("Currency".$conf->currency)), 'confirm_split', $formquestion, 0, 0); + } } else { - print ''.$langs->trans("None").''; - } - $db->free($resql); - print ""; - - if (count($showconfirminfo)) - { - $amount1=price2num($showconfirminfo['amount_ttc']/2,'MT'); - $amount2=($showconfirminfo['amount_ttc']-$amount1); - $formquestion=array( - 'text' => $langs->trans('TypeAmountOfEachNewDiscount'), - array('type' => 'text', 'name' => 'amount_ttc_1', 'label' => $langs->trans("AmountTTC").' 1', 'value' => $amount1, 'size' => '5'), - array('type' => 'text', 'name' => 'amount_ttc_2', 'label' => $langs->trans("AmountTTC").' 2', 'value' => $amount2, 'size' => '5') - ); - $langs->load("dict"); - print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&remid='.$showconfirminfo['rowid'].($backtopage?'&backtopage='.urlencode($backtopage):''), $langs->trans('SplitDiscount'), $langs->trans('ConfirmSplitDiscount',price($showconfirminfo['amount_ttc']),$langs->transnoentities("Currency".$conf->currency)), 'confirm_split', $formquestion, 0, 0); + dol_print_error($db); } } - else - { - dol_print_error($db); + + if($isSupplier) { + if($isCustomer) { + print '
'; // class="fichehalfleft" + print '
'; + print '
'; + print load_fiche_titre($langs->trans("SupplierDiscounts"), '', ''); + } + + /* + * Liste remises fixes fournisseur restant en cours (= liees a aucune facture ni ligne de facture) + */ + $sql = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; + $sql.= " rc.datec as dc, rc.description,"; + $sql.= " rc.fk_invoice_supplier_source,"; + $sql.= " u.login, u.rowid as user_id,"; + $sql.= " fa.ref, fa.type as type"; + $sql.= " FROM ".MAIN_DB_PREFIX."user as u, ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as fa ON rc.fk_invoice_supplier_source = fa.rowid"; + $sql.= " WHERE rc.fk_soc = " . $object->id; + $sql.= " AND rc.entity = " . $conf->entity; + $sql.= " AND u.rowid = rc.fk_user"; + $sql.= " AND rc.discount_type = 1"; // Eliminate customer discounts + $sql.= " AND (rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_line IS NULL)"; + $sql.= " ORDER BY rc.datec DESC"; + + $resql=$db->query($sql); + if ($resql) + { + print ''; + print ''; + print ''; // Need 120+ for format with AM/PM + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + $showconfirminfo=array(); + + $i = 0; + $num = $db->num_rows($resql); + if ($num > 0) + { + while ($i < $num) + { + $obj = $db->fetch_object($resql); + + print ''; + print ''; + if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(DEPOSIT\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(EXCESS PAID\)/',$obj->description)) + { + print ''; + } + else + { + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + if ($user->rights->societe->creer || $user->rights->facture->creer) + { + print ''; + } + else print ''; + print ''; + + if ($_GET["action"]=='split' && GETPOST('remid') == $obj->rowid) + { + $showconfirminfo['rowid']=$obj->rowid; + $showconfirminfo['amount_ttc']=$obj->amount_ttc; + } + $i++; + } + } + else + { + print ''; + } + $db->free($resql); + print "
'.$langs->trans("Date").''.$langs->trans("ReasonDiscount").''.$langs->trans("ConsumedBy").''.$langs->trans("AmountHT").''.$langs->trans("VATRate").''.$langs->trans("AmountTTC").''.$langs->trans("DiscountOfferedBy").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').''; + $facturefournstatic->id=$obj->fk_invoice_supplier_source; + $facturefournstatic->ref=$obj->ref; + $facturefournstatic->type=$obj->type; + print preg_replace('/\(CREDIT_NOTE\)/',$langs->trans("CreditNote"),$obj->description).' '.$facturefournstatic->getNomURl(1); + print ''; + $facturefournstatic->id=$obj->fk_invoice_supplier_source; + $facturefournstatic->ref=$obj->ref; + $facturefournstatic->type=$obj->type; + print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturefournstatic->getNomURl(1); + print ''; + $facturefournstatic->id=$obj->fk_invoice_supplier_source; + $facturefournstatic->ref=$obj->ref; + $facturefournstatic->type=$obj->type; + print preg_replace('/\(EXCESS PAID\)/',$langs->trans("ExcessPaid"),$obj->description).' '.$facturefournstatic->getNomURl(1); + print ''; + print $obj->description; + print ''.$langs->trans("NotConsumed").''.price($obj->amount_ht).''.price2num($obj->tva_tx,'MU').'%'.price($obj->amount_ttc).''; + print ''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.''; + print ''; + print 'rowid.($backtopage?'&backtopage='.urlencode($backtopage):'').'">'.img_split($langs->trans("SplitDiscount")).''; + print '   '; + print 'rowid.($backtopage?'&backtopage='.urlencode($backtopage):'').'">'.img_delete($langs->trans("RemoveDiscount")).''; + print ' 
'.$langs->trans("None").'
"; + + if (count($showconfirminfo)) + { + $amount1=price2num($showconfirminfo['amount_ttc']/2,'MT'); + $amount2=($showconfirminfo['amount_ttc']-$amount1); + $formquestion=array( + 'text' => $langs->trans('TypeAmountOfEachNewDiscount'), + array('type' => 'text', 'name' => 'amount_ttc_1', 'label' => $langs->trans("AmountTTC").' 1', 'value' => $amount1, 'size' => '5'), + array('type' => 'text', 'name' => 'amount_ttc_2', 'label' => $langs->trans("AmountTTC").' 2', 'value' => $amount2, 'size' => '5') + ); + $langs->load("dict"); + print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&remid='.$showconfirminfo['rowid'].($backtopage?'&backtopage='.urlencode($backtopage):''), $langs->trans('SplitDiscount'), $langs->trans('ConfirmSplitDiscount',price($showconfirminfo['amount_ttc']),$langs->transnoentities("Currency".$conf->currency)), 'confirm_split', $formquestion, 0, 0); + } + } + else + { + dol_print_error($db); + } + + if($isCustomer) { + print '
'; // class="ficheaddleft" + print '
'; // class="fichehalfright" + print '
'; // class="fichecenter" + } } print '
'; @@ -454,150 +686,317 @@ if ($socid > 0) /* * List discount consumed (=liees a une ligne de facture ou facture) */ + + print load_fiche_titre($langs->trans("DiscountAlreadyCounted")); - // Remises liees a lignes de factures - $sql = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; - $sql.= " rc.datec as dc, rc.description, rc.fk_facture_line, rc.fk_facture,"; - $sql.= " rc.fk_facture_source,"; - $sql.= " u.login, u.rowid as user_id,"; - $sql.= " f.rowid, f.facnumber,"; - $sql.= " fa.facnumber as ref, fa.type as type"; - $sql.= " FROM ".MAIN_DB_PREFIX."facture as f"; - $sql.= " , ".MAIN_DB_PREFIX."user as u"; - $sql.= " , ".MAIN_DB_PREFIX."facturedet as fc"; - $sql.= " , ".MAIN_DB_PREFIX."societe_remise_except as rc"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fa ON rc.fk_facture_source = fa.rowid"; - $sql.= " WHERE rc.fk_soc =". $object->id; - $sql.= " AND rc.fk_facture_line = fc.rowid"; - $sql.= " AND fc.fk_facture = f.rowid"; - $sql.= " AND rc.fk_user = u.rowid"; - $sql.= " ORDER BY dc DESC"; - //$sql.= " UNION "; - // Remises liees a factures - $sql2 = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; - $sql2.= " rc.datec as dc, rc.description, rc.fk_facture_line, rc.fk_facture,"; - $sql2.= " rc.fk_facture_source,"; - $sql2.= " u.login, u.rowid as user_id,"; - $sql2.= " f.rowid, f.facnumber,"; - $sql2.= " fa.facnumber as ref, fa.type as type"; - $sql2.= " FROM ".MAIN_DB_PREFIX."facture as f"; - $sql2.= " , ".MAIN_DB_PREFIX."user as u"; - $sql2.= " , ".MAIN_DB_PREFIX."societe_remise_except as rc"; - $sql2.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fa ON rc.fk_facture_source = fa.rowid"; - $sql2.= " WHERE rc.fk_soc =". $object->id; - $sql2.= " AND rc.fk_facture = f.rowid"; - $sql2.= " AND rc.fk_user = u.rowid"; - - $sql2.= " ORDER BY dc DESC"; - - $resql=$db->query($sql); - $resql2=null; - if ($resql) $resql2=$db->query($sql2); - if ($resql2) - { - print load_fiche_titre($langs->trans("DiscountAlreadyCounted")); - print ''; - print ''; - print ''; // Need 120+ for format with AM/PM - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - - $tab_sqlobj=array(); - $tab_sqlobjOrder=array(); - $num = $db->num_rows($resql); - if ($num > 0) - { - for ($i = 0;$i < $num; $i++) - { - $sqlobj = $db->fetch_object($resql); - $tab_sqlobj[] = $sqlobj; - $tab_sqlobjOrder[]=$db->jdate($sqlobj->dc); - } + if($isCustomer) { + if($isSupplier) { + print '
'; + print '
'; + print load_fiche_titre($langs->trans("CustomerDiscounts"), '', ''); } - $db->free($resql); - $num = $db->num_rows($resql2); - for ($i = 0;$i < $num;$i++) + // Remises liees a lignes de factures + $sql = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; + $sql.= " rc.datec as dc, rc.description, rc.fk_facture_line, rc.fk_facture,"; + $sql.= " rc.fk_facture_source,"; + $sql.= " u.login, u.rowid as user_id,"; + $sql.= " f.rowid, f.facnumber,"; + $sql.= " fa.facnumber as ref, fa.type as type"; + $sql.= " FROM ".MAIN_DB_PREFIX."facture as f"; + $sql.= " , ".MAIN_DB_PREFIX."user as u"; + $sql.= " , ".MAIN_DB_PREFIX."facturedet as fc"; + $sql.= " , ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fa ON rc.fk_facture_source = fa.rowid"; + $sql.= " WHERE rc.fk_soc =". $object->id; + $sql.= " AND rc.fk_facture_line = fc.rowid"; + $sql.= " AND fc.fk_facture = f.rowid"; + $sql.= " AND rc.fk_user = u.rowid"; + $sql.= " AND rc.discount_type = 0"; // Eliminate supplier discounts + $sql.= " ORDER BY dc DESC"; + //$sql.= " UNION "; + // Remises liees a factures + $sql2 = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; + $sql2.= " rc.datec as dc, rc.description, rc.fk_facture_line, rc.fk_facture,"; + $sql2.= " rc.fk_facture_source,"; + $sql2.= " u.login, u.rowid as user_id,"; + $sql2.= " f.rowid, f.facnumber,"; + $sql2.= " fa.facnumber as ref, fa.type as type"; + $sql2.= " FROM ".MAIN_DB_PREFIX."facture as f"; + $sql2.= " , ".MAIN_DB_PREFIX."user as u"; + $sql2.= " , ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql2.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fa ON rc.fk_facture_source = fa.rowid"; + $sql2.= " WHERE rc.fk_soc =". $object->id; + $sql2.= " AND rc.fk_facture = f.rowid"; + $sql2.= " AND rc.fk_user = u.rowid"; + $sql2.= " AND rc.discount_type = 0"; // Eliminate supplier discounts + $sql2.= " ORDER BY dc DESC"; + + $resql=$db->query($sql); + $resql2=null; + if ($resql) $resql2=$db->query($sql2); + if ($resql2) { - $sqlobj = $db->fetch_object($resql2); - $tab_sqlobj[] = $sqlobj; - $tab_sqlobjOrder[]= $db->jdate($sqlobj->dc); - } - $db->free($resql2); - array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj); - - $num = count($tab_sqlobj); - if ($num > 0) - { - $i = 0 ; - while ($i < $num ) - { - $obj = array_shift($tab_sqlobj); - print '
'; - print ''; - if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) - { - print ''; - } - elseif (preg_match('/\(DEPOSIT\)/',$obj->description)) - { - print ''; - } - elseif (preg_match('/\(EXCESS RECEIVED\)/',$obj->description)) - { - print ''; - } - else - { - print ''; - } - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - $i++; - } + print '
'.$langs->trans("Date").''.$langs->trans("ReasonDiscount").''.$langs->trans("ConsumedBy").''.$langs->trans("AmountHT").''.$langs->trans("VATRate").''.$langs->trans("AmountTTC").''.$langs->trans("Author").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').''; - $facturestatic->id=$obj->fk_facture_source; - $facturestatic->ref=$obj->ref; - $facturestatic->type=$obj->type; - print preg_replace('/\(CREDIT_NOTE\)/',$langs->trans("CreditNote"),$obj->description).' '.$facturestatic->getNomURl(1); - print ''; - $facturestatic->id=$obj->fk_facture_source; - $facturestatic->ref=$obj->ref; - $facturestatic->type=$obj->type; - print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturestatic->getNomURl(1); - print ''; - $facturestatic->id=$obj->fk_facture_source; - $facturestatic->ref=$obj->ref; - $facturestatic->type=$obj->type; - print preg_replace('/\(EXCESS RECEIVED\)/',$langs->trans("Invoice"),$obj->description).' '.$facturestatic->getNomURl(1); - print ''; - print $obj->description; - print ''.img_object($langs->trans("ShowBill"),'bill').' '.$obj->facnumber.''.price($obj->amount_ht).''.price2num($obj->tva_tx,'MU').'%'.price($obj->amount_ttc).''; - print ''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.''; - print ' 
'; + print ''; + print ''; // Need 120+ for format with AM/PM + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + $tab_sqlobj=array(); + $tab_sqlobjOrder=array(); + $num = $db->num_rows($resql); + if ($num > 0) + { + for ($i = 0;$i < $num; $i++) + { + $sqlobj = $db->fetch_object($resql); + $tab_sqlobj[] = $sqlobj; + $tab_sqlobjOrder[]=$db->jdate($sqlobj->dc); + } + } + $db->free($resql); + + $num = $db->num_rows($resql2); + for ($i = 0;$i < $num;$i++) + { + $sqlobj = $db->fetch_object($resql2); + $tab_sqlobj[] = $sqlobj; + $tab_sqlobjOrder[]= $db->jdate($sqlobj->dc); + } + $db->free($resql2); + array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj); + + $num = count($tab_sqlobj); + if ($num > 0) + { + $i = 0 ; + while ($i < $num ) + { + $obj = array_shift($tab_sqlobj); + print ''; + print ''; + if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(DEPOSIT\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(EXCESS RECEIVED\)/',$obj->description)) + { + print ''; + } + else + { + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $i++; + } + } + else + { + print ''; + } + + print "
'.$langs->trans("Date").''.$langs->trans("ReasonDiscount").''.$langs->trans("ConsumedBy").''.$langs->trans("AmountHT").''.$langs->trans("VATRate").''.$langs->trans("AmountTTC").''.$langs->trans("Author").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(CREDIT_NOTE\)/',$langs->trans("CreditNote"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + $facturestatic->id=$obj->fk_facture_source; + $facturestatic->ref=$obj->ref; + $facturestatic->type=$obj->type; + print preg_replace('/\(EXCESS RECEIVED\)/',$langs->trans("Invoice"),$obj->description).' '.$facturestatic->getNomURl(1); + print ''; + print $obj->description; + print ''.img_object($langs->trans("ShowBill"),'bill').' '.$obj->facnumber.''.price($obj->amount_ht).''.price2num($obj->tva_tx,'MU').'%'.price($obj->amount_ttc).''; + print ''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.''; + print ' 
'.$langs->trans("None").'
"; } else { - print ''.$langs->trans("None").''; + dol_print_error($db); + } + } + + if($isSupplier) { + if($isCustomer) { + print '
'; // class="fichehalfleft" + print '
'; + print '
'; + print load_fiche_titre($langs->trans("SupplierDiscounts"), '', ''); } - print ""; - } - else - { - dol_print_error($db); - } + // Remises liees a lignes de factures + $sql = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; + $sql.= " rc.datec as dc, rc.description, rc.fk_invoice_supplier_line, rc.fk_invoice_supplier,"; + $sql.= " rc.fk_invoice_supplier_source,"; + $sql.= " u.login, u.rowid as user_id,"; + $sql.= " f.rowid, f.ref as facnumber,"; + $sql.= " fa.ref, fa.type as type"; + $sql.= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; + $sql.= " , ".MAIN_DB_PREFIX."user as u"; + $sql.= " , ".MAIN_DB_PREFIX."facture_fourn_det as fc"; + $sql.= " , ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as fa ON rc.fk_invoice_supplier_source = fa.rowid"; + $sql.= " WHERE rc.fk_soc =". $object->id; + $sql.= " AND rc.fk_invoice_supplier_line = fc.rowid"; + $sql.= " AND fc.fk_facture_fourn = f.rowid"; + $sql.= " AND rc.fk_user = u.rowid"; + $sql.= " AND rc.discount_type = 1"; // Eliminate customer discounts + $sql.= " ORDER BY dc DESC"; + //$sql.= " UNION "; + // Remises liees a factures + $sql2 = "SELECT rc.rowid, rc.amount_ht, rc.amount_tva, rc.amount_ttc, rc.tva_tx,"; + $sql2.= " rc.datec as dc, rc.description, rc.fk_invoice_supplier_line, rc.fk_invoice_supplier,"; + $sql2.= " rc.fk_invoice_supplier_source,"; + $sql2.= " u.login, u.rowid as user_id,"; + $sql2.= " f.rowid, f.ref as facnumber,"; + $sql2.= " fa.ref, fa.type as type"; + $sql2.= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; + $sql2.= " , ".MAIN_DB_PREFIX."user as u"; + $sql2.= " , ".MAIN_DB_PREFIX."societe_remise_except as rc"; + $sql2.= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as fa ON rc.fk_invoice_supplier_source = fa.rowid"; + $sql2.= " WHERE rc.fk_soc =". $object->id; + $sql2.= " AND rc.fk_invoice_supplier = f.rowid"; + $sql2.= " AND rc.fk_user = u.rowid"; + $sql2.= " AND rc.discount_type = 1"; // Eliminate customer discounts + $sql2.= " ORDER BY dc DESC"; + + $resql=$db->query($sql); + $resql2=null; + if ($resql) $resql2=$db->query($sql2); + if ($resql2) + { + print ''; + print ''; + print ''; // Need 120+ for format with AM/PM + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + $tab_sqlobj=array(); + $tab_sqlobjOrder=array(); + $num = $db->num_rows($resql); + if ($num > 0) + { + for ($i = 0;$i < $num; $i++) + { + $sqlobj = $db->fetch_object($resql); + $tab_sqlobj[] = $sqlobj; + $tab_sqlobjOrder[]=$db->jdate($sqlobj->dc); + } + } + $db->free($resql); + + $num = $db->num_rows($resql2); + for ($i = 0;$i < $num;$i++) + { + $sqlobj = $db->fetch_object($resql2); + $tab_sqlobj[] = $sqlobj; + $tab_sqlobjOrder[]= $db->jdate($sqlobj->dc); + } + $db->free($resql2); + array_multisort($tab_sqlobjOrder,SORT_DESC,$tab_sqlobj); + + $num = count($tab_sqlobj); + if ($num > 0) + { + $i = 0 ; + while ($i < $num ) + { + $obj = array_shift($tab_sqlobj); + print ''; + print ''; + if (preg_match('/\(CREDIT_NOTE\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(DEPOSIT\)/',$obj->description)) + { + print ''; + } + elseif (preg_match('/\(EXCESS PAID\)/',$obj->description)) + { + print ''; + } + else + { + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $i++; + } + } + else + { + print ''; + } + + print "
'.$langs->trans("Date").''.$langs->trans("ReasonDiscount").''.$langs->trans("ConsumedBy").''.$langs->trans("AmountHT").''.$langs->trans("VATRate").''.$langs->trans("AmountTTC").''.$langs->trans("Author").' 
'.dol_print_date($db->jdate($obj->dc),'dayhour').''; + $facturefournstatic->id=$obj->fk_invoice_supplier_source; + $facturefournstatic->ref=$obj->ref; + $facturefournstatic->type=$obj->type; + print preg_replace('/\(CREDIT_NOTE\)/',$langs->trans("CreditNote"),$obj->description).' '.$facturefournstatic->getNomURl(1); + print ''; + $facturefournstatic->id=$obj->fk_invoice_supplier_source; + $facturefournstatic->ref=$obj->ref; + $facturefournstatic->type=$obj->type; + print preg_replace('/\(DEPOSIT\)/',$langs->trans("InvoiceDeposit"),$obj->description).' '.$facturefournstatic->getNomURl(1); + print ''; + $facturefournstatic->id=$obj->fk_invoice_supplier_source; + $facturefournstatic->ref=$obj->ref; + $facturefournstatic->type=$obj->type; + print preg_replace('/\(EXCESS PAID\)/',$langs->trans("Invoice"),$obj->description).' '.$facturefournstatic->getNomURl(1); + print ''; + print $obj->description; + print ''.img_object($langs->trans("ShowBill"),'bill').' '.$obj->facnumber.''.price($obj->amount_ht).''.price2num($obj->tva_tx,'MU').'%'.price($obj->amount_ttc).''; + print ''.img_object($langs->trans("ShowUser"),'user').' '.$obj->login.''; + print ' 
'.$langs->trans("None").'
"; + } + else + { + dol_print_error($db); + } + if($isCustomer) { + print '
'; // class="ficheaddleft" + print '
'; // class="fichehalfright" + print '
'; // class="fichecenter" + } + } } llxFooter(); diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index 21975439872..57aa7ce8b04 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -56,15 +56,7 @@ if (!empty($conf->variants->enabled)) { require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php'; } -$langs->load('orders'); -$langs->load('sendings'); -$langs->load('companies'); -$langs->load('bills'); -$langs->load('propal'); -$langs->load('deliveries'); -$langs->load('sendings'); -$langs->load('products'); -$langs->load('other'); +$langs->loadLangs(array('orders','sendings','companies','bills','propal','deliveries','products','other')); if (!empty($conf->incoterm->enabled)) $langs->load('incoterm'); if (! empty($conf->margin->enabled)) $langs->load('margins'); if (! empty($conf->productbatch->enabled)) $langs->load("productbatch"); @@ -185,7 +177,7 @@ if (empty($reshook)) $result = $object->delete($user); if ($result > 0) { - header('Location: index.php'); + header('Location: list.php?restore_lastsearch_values=1'); exit; } else @@ -1268,9 +1260,11 @@ if (empty($reshook)) if ($action == 'update_extras') { + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) @@ -1448,7 +1442,7 @@ if ($action == 'create' && $user->rights->commande->creer) $ref_client = (! empty($objectsrc->ref_client) ? $objectsrc->ref_client : ''); $soc = $objectsrc->thirdparty; - $cond_reglement_id = (!empty($objectsrc->cond_reglement_id)?$objectsrc->cond_reglement_id:(!empty($soc->cond_reglement_id)?$soc->cond_reglement_id:1)); + $cond_reglement_id = (!empty($objectsrc->cond_reglement_id)?$objectsrc->cond_reglement_id:(!empty($soc->cond_reglement_id)?$soc->cond_reglement_id:0)); // TODO maybe add default value option $mode_reglement_id = (!empty($objectsrc->mode_reglement_id)?$objectsrc->mode_reglement_id:(!empty($soc->mode_reglement_id)?$soc->mode_reglement_id:0)); $fk_account = (! empty($objectsrc->fk_account)?$objectsrc->fk_account:(! empty($soc->fk_account)?$soc->fk_account:0)); $availability_id = (!empty($objectsrc->availability_id)?$objectsrc->availability_id:(!empty($soc->availability_id)?$soc->availability_id:0)); @@ -1556,17 +1550,14 @@ if ($action == 'create' && $user->rights->commande->creer) // Ligne info remises tiers print '' . $langs->trans('Discounts') . ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", $soc->remise_percent); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); - print '. '; + $absolute_discount = $soc->getAvailableDiscounts(); - if ($absolute_discount) - print $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount), $langs->trans("Currency" . $conf->currency)); - else - print $langs->trans("CompanyHasNoAbsoluteDiscount"); - print '.'; + + $thirdparty = $soc; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . GETPOST('origin') . '&originid=' . GETPOST('originid')); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print ''; } // Date @@ -1989,17 +1980,17 @@ if ($action == 'create' && $user->rights->commande->creer) { if ($action != 'classify') $morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ' : '; - if ($action == 'classify') { - //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); - $morehtmlref.='
'; - $morehtmlref.=''; - $morehtmlref.=''; - $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); - $morehtmlref.=''; - $morehtmlref.='
'; - } else { - $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); - } + if ($action == 'classify') { + //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); + $morehtmlref.='
'; + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); + $morehtmlref.=''; + $morehtmlref.='
'; + } else { + $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); + } } else { if (! empty($object->fk_project)) { $proj = new Project($db); @@ -2038,12 +2029,11 @@ if ($action == 'create' && $user->rights->commande->creer) // Relative and absolute discounts if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { - $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final - // invoice + $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice } else { - $filterabsolutediscount = "fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND description LIKE '(DEPOSIT)%')"; - $filtercreditnote = "fk_facture_source IS NOT NULL AND description NOT LIKE '(DEPOSIT)%'"; + $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')"; + $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')"; } $addrelativediscount = '' . $langs->trans("EditRelativeDiscounts") . ''; @@ -2051,29 +2041,17 @@ if ($action == 'create' && $user->rights->commande->creer) $addcreditnote = '' . $langs->trans("AddCreditNote") . ''; print '' . $langs->trans('Discounts') . ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", $soc->remise_percent); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); - print '. '; - $absolute_discount = $soc->getAvailableDiscounts('', 'fk_facture_source IS NULL'); - $absolute_creditnote = $soc->getAvailableDiscounts('', 'fk_facture_source IS NOT NULL'); + + $absolute_discount = $soc->getAvailableDiscounts('', $filterabsolutediscount); + $absolute_creditnote = $soc->getAvailableDiscounts('', $filtercreditnote); $absolute_discount = price2num($absolute_discount, 'MT'); $absolute_creditnote = price2num($absolute_creditnote, 'MT'); - if ($absolute_discount) { - if ($object->statut > Commande::STATUS_DRAFT) { - print $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount), $langs->transnoentities("Currency" . $conf->currency)); - } else { - // Remise dispo de type remise fixe (not credit note) - print '
'; - $form->form_remise_dispo($_SERVER["PHP_SELF"] . '?id=' . $object->id, 0, 'remise_id', $soc->id, $absolute_discount, $filterabsolutediscount, 0, '', 1); - } - } - if ($absolute_creditnote) { - print $langs->trans("CompanyHasCreditNote", price($absolute_creditnote), $langs->transnoentities("Currency" . $conf->currency)) . '. '; - } - if (! $absolute_discount && ! $absolute_creditnote) - print $langs->trans("CompanyHasNoAbsoluteDiscount") . '.'; + + $thirdparty = $soc; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?id=' . $object->id); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print ''; // Date @@ -2473,7 +2451,7 @@ if ($action == 'create' && $user->rights->commande->creer) /* * Form to add new line */ - if ($object->statut == Commande::STATUS_DRAFT && $user->rights->commande->creer) + if ($object->statut == Commande::STATUS_DRAFT && $user->rights->commande->creer && $action != 'selectlines') { if ($action != 'editline') { @@ -2504,9 +2482,9 @@ if ($action == 'create' && $user->rights->commande->creer) // Send if ($object->statut > Commande::STATUS_DRAFT) { if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->commande->order_advance->send)) { - print ''; + print ''; } else - print ''; + print ''; } // Valid diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index b7ba839b144..dd0e41ee836 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -79,10 +79,9 @@ class Commande extends CommonOrder */ public $statut; /** - * @deprecated - * @see billed + * Billed + * @var int */ - public $facturee; public $billed; // billed or not public $brouillon; @@ -545,7 +544,6 @@ class Commande extends CommonOrder { $this->statut = self::STATUS_VALIDATED; $this->billed = 0; - $this->facturee = 0; // deprecated $this->db->commit(); return 1; @@ -1400,7 +1398,6 @@ class Commande extends CommonOrder $this->line->total_localtax1=$total_localtax1; $this->line->total_localtax2=$total_localtax2; $this->line->total_ttc=$total_ttc; - $this->line->product_type=$type; $this->line->special_code=$special_code; $this->line->origin=$origin; $this->line->origin_id=$origin_id; @@ -1533,8 +1530,8 @@ class Commande extends CommonOrder { $prod = new Product($this->db); $prod->fetch($idproduct); - $prod -> get_sousproduits_arbo (); - $prods_arbo = $prod->get_each_prod(); + $prod -> get_sousproduits_arbo(); + $prods_arbo = $prod->get_arbo_each_prod(); if(count($prods_arbo) > 0) { foreach($prods_arbo as $key => $value) @@ -1567,7 +1564,7 @@ class Commande extends CommonOrder // Check parameters if (empty($id) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1; - $sql = 'SELECT c.rowid, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_statut'; + $sql = 'SELECT c.rowid, c.entity, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_statut'; $sql.= ', c.amount_ht, c.total_ht, c.total_ttc, c.tva as total_tva, c.localtax1 as total_localtax1, c.localtax2 as total_localtax2, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_availability, c.fk_input_reason'; $sql.= ', c.fk_account'; $sql.= ', c.date_commande'; @@ -1584,8 +1581,8 @@ class Commande extends CommonOrder $sql.= ', ca.code as availability_code, ca.label as availability_label'; $sql.= ', dr.code as demand_reason_code'; $sql.= ' FROM '.MAIN_DB_PREFIX.'commande as c'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid AND cr.entity IN ('.getEntity('c_payment_term').')'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id AND p.entity IN ('.getEntity('c_paiement').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = ca.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid'; @@ -1597,12 +1594,14 @@ class Commande extends CommonOrder dol_syslog(get_class($this)."::fetch", LOG_DEBUG); $result = $this->db->query($sql); - if ($result) - { - $obj = $this->db->fetch_object($result); - if ($obj) - { - $this->id = $obj->rowid; + if ($result) + { + $obj = $this->db->fetch_object($result); + if ($obj) + { + $this->id = $obj->rowid; + $this->entity = $obj->entity; + $this->ref = $obj->ref; $this->ref_client = $obj->ref_client; $this->ref_customer = $obj->ref_client; @@ -1623,7 +1622,6 @@ class Commande extends CommonOrder $this->remise_percent = $obj->remise_percent; $this->remise_absolue = $obj->remise_absolue; $this->source = $obj->source; - $this->facturee = $obj->billed; // deprecated $this->billed = $obj->billed; $this->note = $obj->note_private; // deprecated $this->note_private = $obj->note_private; @@ -2700,7 +2698,6 @@ class Commande extends CommonOrder if (! $error) { $this->oldcopy= clone $this; - $this->facturee=1; // deprecated $this->billed=1; } @@ -2736,21 +2733,6 @@ class Commande extends CommonOrder } } - /** - * Classify the order as invoiced - * - * @return int <0 if ko, >0 if ok - * @deprecated - * @see classifyBilled() - */ - function classer_facturee() - { - global $user; - dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING); - - return $this->classifyBilled($user); - } - /** * Classify the order as not invoiced * @@ -2772,7 +2754,6 @@ class Commande extends CommonOrder if (! $error) { $this->oldcopy= clone $this; - $this->facturee=1; // deprecated $this->billed=1; } @@ -2783,7 +2764,6 @@ class Commande extends CommonOrder if (! $error) { - $this->facturee=0; // deprecated $this->billed=0; $this->db->commit(); @@ -3134,6 +3114,12 @@ class Commande extends CommonOrder // End call triggers } + if ($this->nb_expedition() != 0) + { + $this->errors[] = $langs->trans('SomeShipmentExists'); + $error++; + } + if (! $error) { // Delete order details @@ -3312,7 +3298,6 @@ class Commande extends CommonOrder */ function getLibStatut($mode) { - if ($this->facturee && empty($this->billed)) $this->billed=$this->facturee; // For backward compatibility return $this->LibStatut($this->statut, $this->billed, $mode); } @@ -4065,7 +4050,7 @@ class OrderLine extends CommonOrderLine $sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").','; $sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null").','; $sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit); - $sql.= ", ".$this->fk_multicurrency; + $sql.= ", ".(! empty($this->fk_multicurrency) ? $this->fk_multicurrency : 'NULL'); $sql.= ", '".$this->db->escape($this->multicurrency_code)."'"; $sql.= ", ".$this->multicurrency_subprice; $sql.= ", ".$this->multicurrency_total_ht; diff --git a/htdocs/commande/document.php b/htdocs/commande/document.php index ee688bed307..5a3f0c1973f 100644 --- a/htdocs/commande/document.php +++ b/htdocs/commande/document.php @@ -69,6 +69,7 @@ $object = new Commande($db); /* * Actions */ + if ($object->fetch($id)) { $object->fetch_thirdparty(); diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php index fe8461c3a41..8048d5a618f 100644 --- a/htdocs/commande/list.php +++ b/htdocs/commande/list.php @@ -51,6 +51,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'orderlist'; $search_orderyear=GETPOST("search_orderyear","int"); $search_ordermonth=GETPOST("search_ordermonth","int"); @@ -95,11 +96,9 @@ $pagenext = $page + 1; if (! $sortfield) $sortfield='c.ref'; if (! $sortorder) $sortorder='DESC'; -// Initialize technical object to manage context to save list fields -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'orderlist'; - // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Commande($db); +$hookmanager->initHooks(array('orderlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -420,6 +419,12 @@ if ($resql) if (in_array($massaction, array('presend','predelete','createbills'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); + $newcardbutton=''; + if ($contextpage == 'orderlist' && $user->rights->commande->creer) + { + $newcardbutton=''.$langs->trans('NewOrder').''; + } + // Lines of title fields print '
'; if ($optioncss != '') print ''; @@ -432,7 +437,7 @@ if ($resql) print ''; print ''; - print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, '', '', $limit); + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, $newcardbutton, '', $limit); $topicmail="SendOrderRef"; $modelmail="order_send"; @@ -670,7 +675,7 @@ if ($resql) // Status billed if (! empty($arrayfields['c.facture']['checked'])) { - print ''; + print ''; print $form->selectyesno('billed', $billed, 1, 0, 1); print ''; } @@ -701,7 +706,7 @@ if ($resql) // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields - $parameters=array('arrayfields'=>$arrayfields); + $parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['c.datec']['checked'])) print_liste_field_titre($arrayfields['c.datec']['label'],$_SERVER["PHP_SELF"],"c.date_creation","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); diff --git a/htdocs/commande/orderstoinvoice.php b/htdocs/commande/orderstoinvoice.php index a15598ee82d..bebcaa93097 100644 --- a/htdocs/commande/orderstoinvoice.php +++ b/htdocs/commande/orderstoinvoice.php @@ -545,7 +545,7 @@ if (($action != 'create' && $action != 'add') || ($action == 'create' && $error) rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; @@ -695,7 +695,7 @@ if (($action != 'create' && $action != 'add') || ($action == 'create' && $error) print ''; // Statut - print ''.$generic_commande->LibStatut($objp->fk_statut,$objp->facturee,5).''; + print ''.$generic_commande->LibStatut($objp->fk_statut,$objp->billed,5).''; // Checkbox print ''; diff --git a/htdocs/commande/tpl/linkedobjectblock.tpl.php b/htdocs/commande/tpl/linkedobjectblock.tpl.php index 39479ba220b..d481ac32d90 100644 --- a/htdocs/commande/tpl/linkedobjectblock.tpl.php +++ b/htdocs/commande/tpl/linkedobjectblock.tpl.php @@ -47,23 +47,25 @@ foreach($linkedObjectBlock as $key => $objectlink) $trclass=($var?'pair':'impair'); if ($ilink == count($linkedObjectBlock) && empty($noMoreLinkedObjectBlockAfter) && count($linkedObjectBlock) <= 1) $trclass.=' liste_sub_total'; ?> - - trans("CustomerOrder"); ?> - getNomUrl(1); ?> - ref_client; ?> - date,'day'); ?> - " > + trans("CustomerOrder"); ?> + global->MAIN_ENABLE_IMPORT_LINKED_OBJECT_LINES) print ' + + getNomUrl(1); ?> + ref_client; ?> + date,'day'); ?> + rights->commande->lire) { $total = $total + $objectlink->total_ht; echo price($objectlink->total_ht); } ?> - getLibStatut(3); ?> - + getLibStatut(3); ?> + element != 'shipping') { ?> - ">transnoentitiesnoconv("RemoveLink")); ?> + ">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?> diff --git a/htdocs/compta/bank/bankentries_list.php b/htdocs/compta/bank/bankentries_list.php index 692df2fcb22..c583e9180ff 100644 --- a/htdocs/compta/bank/bankentries_list.php +++ b/htdocs/compta/bank/bankentries_list.php @@ -54,6 +54,7 @@ $ref = GETPOST('ref','alpha'); $action=GETPOST('action','alpha'); $cancel=GETPOST('cancel','alpha'); $confirm=GETPOST('confirm','alpha'); +$contextpage='banktransactionlist'.(empty($object->ref)?'':'-'.$object->id); // Security check $fieldvalue = (! empty($id) ? $id : (! empty($ref) ? $ref :'')); @@ -73,16 +74,16 @@ $description=GETPOST("description",'alpha'); $dateop = dol_mktime(12,0,0,GETPOST("opmonth",'int'),GETPOST("opday",'int'),GETPOST("opyear",'int')); $debit=GETPOST("debit",'alpha'); $credit=GETPOST("credit",'alpha'); -$type=GETPOST("type",'alpha'); -$account=GETPOST("account",'int'); -$accountancy_code=GETPOST('accountancy_code', 'alpha'); -$bid=GETPOST("bid","int"); +$search_type=GETPOST("search_type",'alpha'); +$search_account=GETPOST("search_account",'int')?GETPOST("search_account",'int'):GETPOST("account",'int'); +$search_accountancy_code=GETPOST('search_accountancy_code', 'alpha')?GETPOST('search_accountancy_code', 'alpha'):GETPOST('accountancy_code', 'alpha'); +$search_bid=GETPOST("search_bid","int")?GETPOST("search_bid","int"):GETPOST("bid","int"); $search_ref=GETPOST('search_ref','alpha'); $search_dt_start = dol_mktime(0, 0, 0, GETPOST('search_start_dtmonth', 'int'), GETPOST('search_start_dtday', 'int'), GETPOST('search_start_dtyear', 'int')); $search_dt_end = dol_mktime(0, 0, 0, GETPOST('search_end_dtmonth', 'int'), GETPOST('search_end_dtday', 'int'), GETPOST('search_end_dtyear', 'int')); $search_dv_start = dol_mktime(0, 0, 0, GETPOST('search_start_dvmonth', 'int'), GETPOST('search_start_dvday', 'int'), GETPOST('search_start_dvyear', 'int')); $search_dv_end = dol_mktime(0, 0, 0, GETPOST('search_end_dvmonth', 'int'), GETPOST('search_end_dvday', 'int'), GETPOST('search_end_dvyear', 'int')); -$search_thirdparty=GETPOST("thirdparty",'alpha'); +$search_thirdparty=GETPOST("search_thirdparty",'alpha')?GETPOST("search_thirdparty",'alpha'):GETPOST("thirdparty",'alpha'); $search_req_nb=GETPOST("req_nb",'alpha'); $search_num_releve=GETPOST("search_num_releve",'alpha'); $search_conciliated=GETPOST("search_conciliated",'int'); @@ -108,7 +109,7 @@ $mode_balance_ok=false; if (($sortfield == 'b.datev' || $sortfield == 'b.datev,b.dateo,b.rowid')) { $sortfield = 'b.datev,b.dateo,b.rowid'; - if ($id > 0 || ! empty($ref) || $account > 0) $mode_balance_ok = true; + if ($id > 0 || ! empty($ref) || $search_account > 0) $mode_balance_ok = true; } if (strtolower($sortorder) == 'desc') $mode_balance_ok = false; @@ -116,14 +117,10 @@ $object = new Account($db); if ($id > 0 || ! empty($ref)) { $result=$object->fetch($id, $ref); - $account = $object->id; // Force the search field on id of account + $search_account = $object->id; // Force the search field on id of account } -// Initialize technical object to manage context to save list fields -$contextpage='banktransactionlist'.(empty($object->ref)?'':'-'.$object->id); -//var_dump($contextpage); - // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('banktransactionlist', $contextpage)); $extrafields = new ExtraFields($db); @@ -179,10 +176,10 @@ if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x', $search_dv_start=''; $search_dv_end=''; $description=""; - $type=""; + $search_type=""; $debit=""; $credit=""; - $bid=""; + $search_bid=""; $search_ref=""; $search_req_nb=''; $search_thirdparty=''; @@ -190,8 +187,8 @@ if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x', $search_conciliated=''; $thirdparty=''; - $account=""; - if ($id > 0 || ! empty($ref)) $account=$object->id; + $search_account=""; + if ($id > 0 || ! empty($ref)) $search_account=$object->id; } if (empty($reshook)) @@ -297,7 +294,7 @@ if (GETPOST('save') && ! $cancel && $user->rights->banque->modifier) $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BankAccount")), null, 'errors'); } - /*if (! empty($conf->accounting->enabled) && (empty($accountancy_code) || $accountancy_code == '-1')) + /*if (! empty($conf->accounting->enabled) && (empty($search_accountancy_code) || $search_accountancy_code == '-1')) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("AccountAccounting")), null, 'errors'); $error++; @@ -307,7 +304,7 @@ if (GETPOST('save') && ! $cancel && $user->rights->banque->modifier) { $objecttmp = new Account($db); $objecttmp->fetch($bankaccountid); - $insertid = $objecttmp->addline($dateop, $operation, $label, $amount, $num_chq, ($cat1 > 0 ? $cat1 : 0), $user, '', '', $accountancy_code); + $insertid = $objecttmp->addline($dateop, $operation, $label, $amount, $num_chq, ($cat1 > 0 ? $cat1 : 0), $user, '', '', $search_accountancy_code); if ($insertid > 0) { setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); @@ -364,26 +361,28 @@ $now = dol_now(); // Must be before button action $param=''; -if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; -if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); if ($id > 0) $param.='&id='.urlencode($id); if (!empty($ref)) $param.='&ref='.urlencode($ref); if (!empty($search_ref)) $param.='&search_ref='.urlencode($search_ref); if (!empty($description)) $param.='&description='.urlencode($description); -if (!empty($type)) $param.='&type='.urlencode($type); -if (!empty($debit)) $param.='&debit='.$debit; -if (!empty($credit)) $param.='&credit='.$credit; -if (!empty($account)) $param.='&account='.$account; +if (!empty($search_type)) $param.='&type='.urlencode($search_type); +if (!empty($search_thirdparty)) $param.='&search_thirdparty='.urlencode($search_thirdparty); +if (!empty($debit)) $param.='&debit='.urlencode($debit); +if (!empty($credit)) $param.='&credit='.urlencode($credit); +if (!empty($search_account)) $param.='&search_account='.urlencode($search_account); if (!empty($search_num_releve)) $param.='&search_num_releve='.urlencode($search_num_releve); if ($search_conciliated != '' && $search_conciliated != '-1') $param.='&search_conciliated='.urlencode($search_conciliated); -if (!empty($bid)) $param.='&bid='.$bid; +if ($search_bid > 0) $param.='&search_bid='.urlencode($search_bid); if (dol_strlen($search_dt_start) > 0) $param .= '&search_start_dtmonth=' . GETPOST('search_start_dtmonth', 'int') . '&search_start_dtday=' . GETPOST('search_start_dtday', 'int') . '&search_start_dtyear=' . GETPOST('search_start_dtyear', 'int'); if (dol_strlen($search_dt_end) > 0) $param .= '&search_end_dtmonth=' . GETPOST('search_end_dtmonth', 'int') . '&search_end_dtday=' . GETPOST('search_end_dtday', 'int') . '&search_end_dtyear=' . GETPOST('search_end_dtyear', 'int'); if (dol_strlen($search_dv_start) > 0) $param .= '&search_start_dvmonth=' . GETPOST('search_start_dvmonth', 'int') . '&search_start_dvday=' . GETPOST('search_start_dvday', 'int') . '&search_start_dvyear=' . GETPOST('search_start_dvyear', 'int'); if (dol_strlen($search_dv_end) > 0) $param .= '&search_end_dvmonth=' . GETPOST('search_end_dvmonth', 'int') . '&search_end_dvday=' . GETPOST('search_end_dvday', 'int') . '&search_end_dvyear=' . GETPOST('search_end_dvyear', 'int'); -if ($search_req_nb) $param.='&req_nb='.urlencode($search_req_nb); -if (GETPOST("thirdparty")) $param.='&thirdparty='.urlencode(GETPOST("thirdparty")); -if ($optioncss != '') $param.='&optioncss='.$optioncss; +if ($search_req_nb) $param.='&req_nb='.urlencode($search_req_nb); +if (GETPOST("search_thirdparty",'int')) $param.='&thirdparty='.urlencode(GETPOST("search_thirdparty",'int')); +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +if ($action == 'reconcile') $param.='&action=reconcile'; // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; @@ -422,19 +421,17 @@ if ($id > 0 || ! empty($ref)) if ($action != 'reconcile') { - //print '
'; - if ($object->canBeConciliated() > 0) { // If not cash account and can be reconciliate if ($user->rights->banque->consolidate) { - $buttonreconcile = ''.$langs->trans("Conciliate").''; + $newparam = $param; + $newparam = preg_replace('/search_conciliated=\d+/i','',$newparam); + $buttonreconcile = ''.$langs->trans("Conciliate").''; } else { $buttonreconcile = ''.$langs->trans("Conciliate").''; } } - - //print '
'; } } else @@ -454,7 +451,7 @@ $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // Note that $action and $object may have been modified by hook $sql.=$hookmanager->resPrint; $sql.= " FROM "; -if ($bid) $sql.= MAIN_DB_PREFIX."bank_class as l,"; +if ($search_bid>0) $sql.= MAIN_DB_PREFIX."bank_class as l,"; $sql.= " ".MAIN_DB_PREFIX."bank_account as ba,"; $sql.= " ".MAIN_DB_PREFIX."bank as b"; if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_extrafields as ef on (b.rowid = ef.fk_object)"; @@ -462,21 +459,21 @@ $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_url as bu ON bu.fk_bank = b.rowid AND $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON bu.url_id = s.rowid"; $sql.= " WHERE b.fk_account = ba.rowid"; $sql.= " AND ba.entity IN (".getEntity('bank_account').")"; -if ($account > 0) $sql.=" AND b.fk_account = ".$account; +if ($search_account > 0) $sql.=" AND b.fk_account = ".$search_account; // Search period criteria if (dol_strlen($search_dt_start)>0) $sql .= " AND b.dateo >= '" . $db->idate($search_dt_start) . "'"; if (dol_strlen($search_dt_end)>0) $sql .= " AND b.dateo <= '" . $db->idate($search_dt_end) . "'"; // Search period criteria if (dol_strlen($search_dv_start)>0) $sql .= " AND b.datev >= '" . $db->idate($search_dv_start) . "'"; if (dol_strlen($search_dv_end)>0) $sql .= " AND b.datev <= '" . $db->idate($search_dv_end) . "'"; -if ($search_ref) $sql.=natural_search("b.rowid", $search_ref); +if ($search_ref) $sql.=natural_search("b.rowid", $search_ref, 1); if ($search_req_nb) $sql.= natural_search("b.num_chq", $search_req_nb); if ($search_num_releve) $sql.= natural_search("b.num_releve", $search_num_releve); if ($search_conciliated != '' && $search_conciliated != '-1') $sql.= " AND b.rappro = ".$search_conciliated; if ($search_thirdparty) $sql.= natural_search("s.nom", $search_thirdparty); if ($description) $sql.= natural_search("b.label", $description); // Warning some text are just translation keys, not translated strings -if ($bid) $sql.= " AND b.rowid=l.lineid AND l.fk_categ=".$bid; -if (! empty($type)) $sql.= " AND b.fk_type = '".$db->escape($type)."' "; +if ($search_bid > 0) $sql.= " AND b.rowid=l.lineid AND l.fk_categ=".$search_bid; +if (! empty($search_type)) $sql.= " AND b.fk_type = '".$db->escape($search_type)."' "; // Search criteria amount $debit = price2num(str_replace('-','',$debit)); $credit = price2num(str_replace('-','',$credit)); @@ -518,11 +515,11 @@ if ($page >= $nbtotalofpages) } // If not account defined $mode_balance_ok=false -if (empty($account)) $mode_balance_ok=false; +if (empty($search_account)) $mode_balance_ok=false; // If a search is done $mode_balance_ok=false if (! empty($search_ref)) $mode_balance_ok=false; if (! empty($req_nb)) $mode_balance_ok=false; -if (! empty($type)) $mode_balance_ok=false; +if (! empty($search_type)) $mode_balance_ok=false; if (! empty($debit)) $mode_balance_ok=false; if (! empty($credit)) $mode_balance_ok=false; if (! empty($thirdparty)) $mode_balance_ok=false; @@ -643,7 +640,7 @@ if ($resql) print ' '; print ''.$langs->trans("Type").''; print ''.$langs->trans("Numero").''; - //if (! $account > 0) + //if (! $search_account > 0) //{ print ''.$langs->trans("BankAccount").''; //} @@ -672,15 +669,15 @@ if ($resql) print ''; print ' '; print ''; - $form->select_types_paiements((GETPOST('operation')?GETPOST('operation'):($object->courant == Account::TYPE_CASH ? 'LIQ' : '')),'operation','1,2',2,1); + $form->select_types_paiements((GETPOST('operation')?GETPOST('operation'):($object->courant == Account::TYPE_CASH ? 'LIQ' : '')), 'operation', '1,2', 2, 1); print ''; print ''; print ''; print ''; - //if (! $account > 0) + //if (! $search_account > 0) //{ print ''; - $form->select_comptes(GETPOST('add_account','int')?GETPOST('add_account','int'):$account,'add_account',0,'',1, ($id > 0 || ! empty($ref)?' disabled="disabled"':'')); + $form->select_comptes(GETPOST('add_account','int')?GETPOST('add_account','int'):$search_account,'add_account',0,'',1, ($id > 0 || ! empty($ref)?' disabled="disabled"':'')); print ''; //} print ''; @@ -688,7 +685,7 @@ if ($resql) /*if (! empty($conf->accounting->enabled)) { print ''; - print $formaccounting->select_account($accountancy_code, 'accountancy_code', 1, null, 1, 1, ''); + print $formaccounting->select_account($search_accountancy_code, 'search_accountancy_code', 1, null, 1, 1, ''); print ''; }*/ print ''; @@ -733,7 +730,7 @@ if ($resql) if (! empty($conf->global->BANK_USE_VARIOUS_PAYMENT)) // If direct entries is done using miscellaneous payments { if ($user->rights->banque->modifier) { - $addbutton = ''.$langs->trans("AddBankRecord").''; + $addbutton = ''.$langs->trans("AddBankRecord").''; } else { $addbutton = ''.$langs->trans("AddBankRecord").''; } @@ -768,15 +765,8 @@ if ($resql) $picto='title_bank'; if ($id > 0 || ! empty($ref)) $picto=''; - if (GETPOST("bid")) - { - $result=$bankcateg->fetch(GETPOST("bid")); - print_barre_liste($langs->trans("BankTransactionForCategory",$bankcateg->label).' '.($socid?' '.$soc->name:''), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, $picto, 0, $morehtml, '', $limit); - } - else - { - print_barre_liste($langs->trans("BankTransactions"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, $picto, 0, $morehtml, '', $limit); - } + + print_barre_liste($langs->trans("BankTransactions"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, $picto, 0, $morehtml, '', $limit); // We can add page now to param if ($page != '') $param.='&page='.urlencode($page); @@ -799,6 +789,22 @@ if ($resql) $moreforfilter .= '
'.$langs->trans('to') . ' ' . $form->select_date($search_dv_end, 'search_end_dv', 0, 0, 1, "search_form", 1, 0, 1).'
'; $moreforfilter .= ''; + if (! empty($conf->categorie->enabled)) + { + // Categories + if (! empty($conf->categorie->enabled) && ! empty($user->rights->categorie->lire)) + { + $langs->load('categories'); + + // Bank line + $moreforfilter.='
'; + $moreforfilter.=$langs->trans('RubriquesTransactions').' : '; + $cate_arbo = $form->select_all_categories(Categorie::TYPE_BANK_LINE, $search_bid, 'parent', null, null, 1); + $moreforfilter.=$form->selectarray('search_bid', $cate_arbo, $search_bid, 1); + $moreforfilter.='
'; + } + } + $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters); // Note that $action and $object may have been modified by hook if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint; @@ -842,7 +848,7 @@ if ($resql) if (! empty($arrayfields['type']['checked'])) { print ''; - $form->select_types_paiements(empty($type)?'':$type, 'type', '', 2, 0, 1, 0, 1, 'maxwidth100'); + $form->select_types_paiements(empty($search_type)?'':$search_type, 'search_type', '', 2, 1, 1, 0, 1, 'maxwidth100'); print ''; } if (! empty($arrayfields['b.num_chq']['checked'])) @@ -852,12 +858,12 @@ if ($resql) } if (! empty($arrayfields['bu.label']['checked'])) { - print ''; + print ''; } if (! empty($arrayfields['ba.ref']['checked'])) { print ''; - $form->select_comptes($account,'account',0,'',1, ($id > 0 || ! empty($ref)?' disabled="disabled"':'')); + $form->select_comptes($search_account,'search_account',0,'',1, ($id > 0 || ! empty($ref)?' disabled="disabled"':'')); print ''; } if (! empty($arrayfields['b.debit']['checked'])) @@ -947,7 +953,7 @@ if ($resql) // If we are in a situation where we need/can show balance, we calculate the start of balance if (! $balancecalculated && (! empty($arrayfields['balancebefore']['checked']) || ! empty($arrayfields['balance']['checked'])) && $mode_balance_ok) { - if (! $account) + if (! $search_account) { dol_print_error('', 'account is not defined but $mode_balance_ok is true'); exit; @@ -962,7 +968,7 @@ if ($resql) $sqlforbalance.= " ".MAIN_DB_PREFIX."bank as b"; $sqlforbalance.= " WHERE b.fk_account = ba.rowid"; $sqlforbalance.= " AND ba.entity IN (".getEntity('bank_account').")"; - $sqlforbalance.= " AND b.fk_account = ".$account; + $sqlforbalance.= " AND b.fk_account = ".$search_account; $sqlforbalance.= " AND (b.datev < '" . $db->idate($db->jdate($objp->dv)) . "' OR (b.datev = '" . $db->idate($db->jdate($objp->dv)) . "' AND (b.dateo < '".$db->idate($db->jdate($objp->do))."' OR (b.dateo = '".$db->idate($db->jdate($objp->do))."' AND b.rowid < ".$objp->rowid."))))"; $resqlforbalance = $db->query($sqlforbalance); //print $sqlforbalance; @@ -983,12 +989,12 @@ if ($resql) { $tmpnbfieldbeforebalance=0; $tmpnbfieldafterbalance=0; - $balancefieldfound=false; + $balancefieldfound=0; foreach($arrayfields as $key => $val) { if ($key == 'balancebefore' || $key == 'balance') { - $balancefieldfound=true; + $balancefieldfound++; continue; } if (! empty($arrayfields[$key]['checked'])) @@ -1019,10 +1025,29 @@ if ($resql) print ''; print ''; } - print ''; - print price(price2num($balance, 'MT'), 1, $langs); + + if (! empty($arrayfields['balancebefore']['checked'])) + { + print ''; + print price(price2num($balance, 'MT'), 1, $langs); + print ''; + } + if (! empty($arrayfields['balance']['checked'])) + { + print ''; + print price(price2num($balance, 'MT'), 1, $langs); + print ''; + } + + print ''; + print ''; + print ' '; print ''; - print ''; + print ''; print ''; print ''; } @@ -1048,7 +1073,7 @@ if ($resql) if (! empty($arrayfields['b.rowid']['checked'])) { print ''; - print "rowid.'">'.img_object($langs->trans("ShowPayment").': '.$objp->rowid, 'account', 'class="classfortooltip"').' '.$objp->rowid."   "; + print "rowid.'&save_lastsearch_values=1">'.img_object($langs->trans("ShowPayment").': '.$objp->rowid, 'account', 'class="classfortooltip"').' '.$objp->rowid."   "; print ''; if (! $i) $totalarray['nbfield']++; } @@ -1447,7 +1472,7 @@ if ($resql) elseif ($totalarray['totalcredfield'] == $i) print ''.price($totalarray['totalcred']).''; elseif ($i == $posconciliatecol) { - print ''; + print ''; if ($user->rights->banque->consolidate && $action == 'reconcile') print ''; print ''; } diff --git a/htdocs/compta/bank/card.php b/htdocs/compta/bank/card.php index c43ee812953..00db08f9d4d 100644 --- a/htdocs/compta/bank/card.php +++ b/htdocs/compta/bank/card.php @@ -803,15 +803,15 @@ else print ''; print ''."\n\n"; - dol_fiche_head(''); + dol_fiche_head(array(), 0, '' ,0); - print '
'; + //print '
'; print ''; // Ref print ''; - print ''; + print ''; // Label print ''; diff --git a/htdocs/compta/bank/class/account.class.php b/htdocs/compta/bank/class/account.class.php index 719a48c3e3c..c773ef242ad 100644 --- a/htdocs/compta/bank/class/account.class.php +++ b/htdocs/compta/bank/class/account.class.php @@ -332,7 +332,7 @@ class Account extends CommonObject * @param int $fk_bank To search using bank transaction id * @param int $url_id To search using link to * @param string $type To search using type - * @return array|-1 Array of links or -1 on error + * @return array|-1 Array of links array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> ) or -1 on error */ function get_url($fk_bank='', $url_id='', $type='') { @@ -505,10 +505,12 @@ class Account extends CommonObject * @param int $notrigger 1=Disable triggers * @return int < 0 if KO, > 0 if OK */ - function create(User $user = null, $notrigger=0) + function create(User $user, $notrigger=0) { global $langs,$conf, $hookmanager; + $error=0; + // Clean parameters if (! $this->min_allowed) $this->min_allowed=0; if (! $this->min_desired) $this->min_desired=0; @@ -668,7 +670,7 @@ class Account extends CommonObject * @param int $notrigger 1=Disable triggers * @return int <0 if KO, >0 if OK */ - function update(User $user = null, $notrigger = 0) + function update(User $user, $notrigger = 0) { global $langs,$conf, $hookmanager; @@ -1135,6 +1137,8 @@ class Account extends CommonObject */ function solde($option=0) { + $solde=0; + $sql = "SELECT sum(amount) as amount"; $sql.= " FROM ".MAIN_DB_PREFIX."bank"; $sql.= " WHERE fk_account = ".$this->id; @@ -1149,8 +1153,13 @@ class Account extends CommonObject $solde = $obj->amount; } $this->db->free($resql); - return $solde; + } else { + $this->errors[]=$this->db->lasterror; + return -1; } + + return $solde; + } /** @@ -1593,7 +1602,7 @@ class Account extends CommonObject $this->code_banque = '123'; $this->code_guichet = '456'; $this->number = 'ABC12345'; - $this->cle_rib = 50; + $this->cle_rib = '50'; $this->bic = 'AA12'; $this->iban = 'FR999999999'; $this->domiciliation = 'My bank address'; @@ -1904,7 +1913,7 @@ class AccountLine extends CommonObject */ function update_conciliation(User $user, $cat) { - global $conf; + global $conf,$langs; $this->db->begin(); @@ -2132,9 +2141,10 @@ class AccountLine extends CommonObject * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto * @param int $maxlen Longueur max libelle * @param string $option Option ('showall') + * @param int $notooltip 1=Disable tooltip * @return string Chaine avec URL */ - function getNomUrl($withpicto=0,$maxlen=0,$option='') + function getNomUrl($withpicto=0,$maxlen=0,$option='',$notooltip=0) { global $langs; diff --git a/htdocs/compta/bank/ligne.php b/htdocs/compta/bank/ligne.php index f6740ed6fda..05f331eba99 100644 --- a/htdocs/compta/bank/ligne.php +++ b/htdocs/compta/bank/ligne.php @@ -437,6 +437,7 @@ if ($result) // Type of payment / Number print ""; if ($user->rights->banque->modifier || $user->rights->banque->consolidate) { @@ -459,22 +460,10 @@ if ($result) } print ""; - // Bank of cheque - print ""; - if ($user->rights->banque->modifier || $user->rights->banque->consolidate) - { - print ''; - } - else - { - print ''; - } - print ""; - // Transmitter - print ""; + print ""; if ($user->rights->banque->modifier || $user->rights->banque->consolidate) { print '"; + // Bank of cheque + print ""; + if ($user->rights->banque->modifier || $user->rights->banque->consolidate) + { + print ''; + } + else + { + print ''; + } + print ""; + // Date ope print ''; if ($user->rights->banque->modifier || $user->rights->banque->consolidate) diff --git a/htdocs/compta/bank/list.php b/htdocs/compta/bank/list.php index 817d6dc84e9..e3c2a5b94a9 100644 --- a/htdocs/compta/bank/list.php +++ b/htdocs/compta/bank/list.php @@ -1,4 +1,6 @@ * Copyright (C) 2004-2016 Laurent Destailleur * Copyright (C) 2005-2012 Regis Houssin @@ -42,6 +44,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'bankaccountlist'; // To manage different context of search $search_ref=GETPOST('search_ref','alpha'); $search_label=GETPOST('search_label','alpha'); @@ -51,7 +54,8 @@ $optioncss = GETPOST('optioncss','alpha'); // Security check if ($user->societe_id) $socid=$user->societe_id; -$result=restrictedArea($user,'banque'); +if (! empty($user->rights->accounting->chartofaccount)) $allowed=1; // Dictionary with list of banks accounting account allowed to manager of chart account +if (! $allowed) $result=restrictedArea($user,'banque'); $diroutputmassaction=$conf->bank->dir_output . '/temp/massgeneration/'.$user->id; @@ -66,11 +70,9 @@ $pagenext = $page + 1; if (! $sortfield) $sortfield='b.label'; if (! $sortorder) $sortorder='ASC'; -// Initialize technical object to manage context to save list fields -$contextpage='bankaccountlist'; - // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Account($db); +$hookmanager->initHooks(array('bankaccountlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -89,8 +91,8 @@ $arrayfields=array( 'accountype'=>array('label'=>$langs->trans("Type"), 'checked'=>1), 'b.label'=>array('label'=>$langs->trans("Label"), 'checked'=>1), 'b.number'=>array('label'=>$langs->trans("AccountIdShort"), 'checked'=>1), - 'b.account_number'=>array('label'=>$langs->trans("AccountAccounting"), 'checked'=>$conf->accountancy->enabled), - 'b.fk_accountancy_journal'=>array('label'=>$langs->trans("AccountancyJournal"), 'checked'=>$conf->accountancy->enabled), + 'b.account_number'=>array('label'=>$langs->trans("AccountAccounting"), 'checked'=>(! empty($conf->accounting->enabled) || ! empty($conf->accounting->enabled))), + 'b.fk_accountancy_journal'=>array('label'=>$langs->trans("AccountancyJournal"), 'checked'=>(! empty($conf->accounting->enabled) || ! empty($conf->accounting->enabled))), 'toreconcile'=>array('label'=>$langs->trans("TransactionsToConciliate"), 'checked'=>1), 'b.currency_code'=>array('label'=>$langs->trans("Currency"), 'checked'=>0), 'b.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500), diff --git a/htdocs/compta/bank/releve.php b/htdocs/compta/bank/releve.php index a1e69cdc651..6a6557c5aef 100644 --- a/htdocs/compta/bank/releve.php +++ b/htdocs/compta/bank/releve.php @@ -534,11 +534,10 @@ else $title=$langs->trans("AccountStatement").' '.$numref.' - '.$langs->trans("BankAccount").' '.$object->getNomUrl(1, 'receipts'); print load_fiche_titre($title, $mesprevnext, 'title_bank.png'); //print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, 0, $nbtotalofrecords, 'title_bank.png', 0, '', '', 0, 1); - print '
'; print ""; print ''; - print ""; + print ''; print '
'; print '
'.$langs->trans("Ref").'ref).'">
ref).'">
'.$langs->trans("Label").'
".$langs->trans("Type")." / ".$langs->trans("Numero"); + print ' ('.$langs->trans("ChequeOrTransferNumber").')'; print "
".$langs->trans("Bank")."'; - print ''; - print ''.$objp->banque.'
".$langs->trans("CheckTransmitter")."
".$langs->trans("CheckTransmitter"); + print ' ('.$langs->trans("ChequeMaker").')'; + print "'; @@ -487,6 +476,22 @@ if ($result) } print "
".$langs->trans("Bank"); + print ' ('.$langs->trans("ChequeBank").')'; + print "'; + print ''; + print ''.$objp->banque.'
'.$langs->trans("DateOperation").'
'; @@ -753,7 +752,7 @@ else while ($ii < $numc) { $objc = $db->fetch_object($resc); - print "
$objc->label"; + print "
".$objc->label.""; $ii++; } } @@ -776,7 +775,7 @@ else print '\n"; } - print '\n"; + print '\n"; if ($user->rights->banque->modifier || $user->rights->banque->consolidate) { @@ -798,8 +797,9 @@ else print "\n".'"; // Line Balance - print "\n"; - print '"; + print "\n"; + print ""; + print '"; print "\n"; print "
 '.price($objp->amount)."'.price($total)."'.price(price2num($total, 'MT'))."
'.$langs->trans("Total")." :".price($totald)."".price($totalc)."  
 ".$langs->trans("EndBankBalance")." :'.price($total)." 
 ".$langs->trans("EndBankBalance")." :'.price(price2num($total, 'MT'))." 
"; print ""; diff --git a/htdocs/compta/bank/transfer.php b/htdocs/compta/bank/transfer.php index 61a4f524a1a..e50d4822da7 100644 --- a/htdocs/compta/bank/transfer.php +++ b/htdocs/compta/bank/transfer.php @@ -49,8 +49,8 @@ if ($action == 'add') $dateo = dol_mktime(12,0,0,GETPOST('remonth','int'),GETPOST('reday','int'),GETPOST('reyear','int')); $label = GETPOST('label','alpha'); - $amount= GETPOST('amount'); - $amountto= GETPOST('amountto'); + $amount= GETPOST('amount','alpha'); + $amountto= GETPOST('amountto','alpha'); if (! $label) { @@ -125,7 +125,7 @@ if ($action == 'add') if (! $error) { - $mesgs = $langs->trans("TransferFromToDone",''.$accountfrom->label."",''.$accountto->label."",$amount,$langs->transnoentities("Currency".$conf->currency)); + $mesgs = $langs->trans("TransferFromToDone", ''.$accountfrom->label."", ''.$accountto->label."", $amount, $langs->transnoentities("Currency".$conf->currency)); setEventMessages($mesgs, null, 'mesgs'); $db->commit(); } @@ -153,6 +153,12 @@ llxHeader(); print ' '; @@ -210,12 +218,12 @@ $account_to=''; $label=''; $amount=''; -if($error) +if ($error) { $account_from = GETPOST('account_from','int'); $account_to = GETPOST('account_to','int'); $label = GETPOST('label','alpha'); - $amount = GETPOST('amount','int'); + $amount = GETPOST('amount','alpha'); } print load_fiche_titre($langs->trans("MenuBankInternalTransfer"), '', 'title_bank.png'); @@ -246,9 +254,9 @@ print "\n"; print ""; $form->select_date((! empty($dateo)?$dateo:''),'','','','','add'); print "\n"; -print ''; -print ''; -print ''; +print ''; +print ''; +print ''; print ""; diff --git a/htdocs/compta/bank/various_payment/index.php b/htdocs/compta/bank/various_payment/index.php index d52cbeb7595..aa47d714bd9 100644 --- a/htdocs/compta/bank/various_payment/index.php +++ b/htdocs/compta/bank/various_payment/index.php @@ -102,7 +102,7 @@ $sql = "SELECT v.rowid, v.sens, v.amount, v.label, v.datep as datep, v.datev as $sql.= " ba.rowid as bid, ba.ref as bref, ba.number as bnumber, ba.account_number as bank_account_number, ba.fk_accountancy_journal as accountancy_journal, ba.label as blabel,"; $sql.= " pst.code as payment_code"; $sql.= " FROM ".MAIN_DB_PREFIX."payment_various as v"; -$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON v.fk_typepayment = pst.id AND pst.entity IN (" . getEntity('c_paiement').")"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON v.fk_typepayment = pst.id"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON v.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid"; $sql.= " WHERE v.entity IN (".getEntity('payment_various').")"; @@ -153,6 +153,8 @@ if ($result) if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); + $newcardbutton=''.$langs->trans('MenuNewVariousPayment').''; + print ''; if ($optioncss != '') print ''; @@ -163,7 +165,7 @@ if ($result) print ''; print ''; - print_barre_liste($langs->trans("VariousPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num, $totalnboflines, 'title_accountancy.png', 0, '', '', $limit); + print_barre_liste($langs->trans("VariousPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num, $totalnboflines, 'title_accountancy.png', 0, $newcardbutton, '', $limit); print '
'; print ''."\n"; @@ -187,7 +189,7 @@ if ($result) // Type print ''; // Account diff --git a/htdocs/compta/charges/index.php b/htdocs/compta/charges/index.php index 6e765cdacb0..321fad5baf6 100644 --- a/htdocs/compta/charges/index.php +++ b/htdocs/compta/charges/index.php @@ -139,7 +139,7 @@ if (! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) $sql.= " FROM ".MAIN_DB_PREFIX."c_chargesociales as c,"; $sql.= " ".MAIN_DB_PREFIX."chargesociales as cs"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiementcharge as pc ON pc.fk_charge = cs.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON pc.fk_typepaiement = pct.id AND pct.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON pc.fk_typepaiement = pct.id"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON pc.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid"; $sql.= " WHERE cs.fk_type = c.id"; @@ -260,7 +260,7 @@ if (! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) $sql.= " FROM ".MAIN_DB_PREFIX."tva as pv"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON pv.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON pv.fk_typepayment = pct.id AND pct.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON pv.fk_typepayment = pct.id"; $sql.= " WHERE pv.entity IN (".getEntity("tax").")"; if ($year > 0) { @@ -474,7 +474,7 @@ if (! empty($conf->salaries->enabled) && $user->rights->salaries->read) $sql.= " FROM ".MAIN_DB_PREFIX."payment_salary as s"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON s.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON s.fk_typepayment = pct.id AND pct.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON s.fk_typepayment = pct.id"; $sql.= " , ".MAIN_DB_PREFIX."user as u"; $sql.= " WHERE s.entity IN (".getEntity('user').")"; $sql.= " AND u.rowid = s.fk_user"; diff --git a/htdocs/compta/facture/admin/facture_cust_extrafields.php b/htdocs/compta/facture/admin/facture_cust_extrafields.php index 2e985c8a009..7e6b069b0d9 100644 --- a/htdocs/compta/facture/admin/facture_cust_extrafields.php +++ b/htdocs/compta/facture/admin/facture_cust_extrafields.php @@ -64,7 +64,7 @@ $textobject=strtolower($langs->transnoentitiesnoconv("BillsCustomers")); llxHeader('',$langs->trans("BillsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup'); diff --git a/htdocs/compta/facture/admin/facture_rec_cust_extrafields.php b/htdocs/compta/facture/admin/facture_rec_cust_extrafields.php index 0cbea0c8d89..76284e7fe73 100644 --- a/htdocs/compta/facture/admin/facture_rec_cust_extrafields.php +++ b/htdocs/compta/facture/admin/facture_rec_cust_extrafields.php @@ -65,7 +65,7 @@ $textobject=strtolower($langs->transnoentitiesnoconv("BillsCustomers")); llxHeader('',$langs->trans("BillsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup'); diff --git a/htdocs/compta/facture/admin/facturedet_cust_extrafields.php b/htdocs/compta/facture/admin/facturedet_cust_extrafields.php index b17e3c416d2..d42139e4ec0 100644 --- a/htdocs/compta/facture/admin/facturedet_cust_extrafields.php +++ b/htdocs/compta/facture/admin/facturedet_cust_extrafields.php @@ -65,7 +65,7 @@ $textobject=strtolower($langs->transnoentitiesnoconv("BillsCustomers")); llxHeader('',$langs->trans("BillsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup'); diff --git a/htdocs/compta/facture/admin/facturedet_rec_cust_extrafields.php b/htdocs/compta/facture/admin/facturedet_rec_cust_extrafields.php index 0951dff721b..cc33b8dc026 100644 --- a/htdocs/compta/facture/admin/facturedet_rec_cust_extrafields.php +++ b/htdocs/compta/facture/admin/facturedet_rec_cust_extrafields.php @@ -65,7 +65,7 @@ $textobject=strtolower($langs->transnoentitiesnoconv("BillsCustomers")); llxHeader('',$langs->trans("BillsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup'); diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 9d2205fc98e..6d0c6fffc1a 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -191,7 +191,7 @@ if (empty($reshook)) $result = $object->delete($user, 0, $idwarehouse); if ($result > 0) { - header('Location: ' . DOL_URL_ROOT . '/compta/facture/list.php'); + header('Location: ' . DOL_URL_ROOT . '/compta/facture/list.php?restore_lastsearch_values=1'); exit(); } else { setEventMessages($object->error, $object->errors, 'errors'); @@ -553,7 +553,7 @@ if (empty($reshook)) || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->invoice_advance->unvalidate))) ) { - $idwarehouse = GETPOST('idwarehouse'); + $idwarehouse = GETPOST('idwarehouse','int'); $object->fetch($id); $object->fetch_thirdparty(); @@ -640,8 +640,8 @@ if (empty($reshook)) else if ($action == 'confirm_paid_partially' && $confirm == 'yes' && $user->rights->facture->paiement) { $object->fetch($id); - $close_code = $_POST["close_code"]; - $close_note = $_POST["close_note"]; + $close_code = GETPOST("close_code",'none'); + $close_note = GETPOST("close_note",'none'); if ($close_code) { $result = $object->set_paid($user, $close_code, $close_note); if ($result<0) setEventMessages($object->error, $object->errors, 'errors'); @@ -651,8 +651,8 @@ if (empty($reshook)) } // Classify "abandoned" else if ($action == 'confirm_canceled' && $confirm == 'yes') { $object->fetch($id); - $close_code = $_POST["close_code"]; - $close_note = $_POST["close_note"]; + $close_code = GETPOST("close_code",'none'); + $close_note = GETPOST("close_note",'none'); if ($close_code) { $result = $object->set_canceled($user, $close_code, $close_note); if ($result<0) setEventMessages($object->error, $object->errors, 'errors'); @@ -716,7 +716,7 @@ if (empty($reshook)) $sql = 'SELECT SUM(pf.amount) as total_paiements'; $sql.= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id AND c.entity IN (' . getEntity('c_paiement') . ')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id'; $sql.= ' WHERE pf.fk_facture = '.$object->id; $sql.= ' AND pf.fk_paiement = p.rowid'; $sql.= ' AND p.entity IN (' . getEntity('facture').')'; @@ -916,15 +916,21 @@ if (empty($reshook)) $id = $object->create($user); + $facture_source = new Facture($db); // fetch origin object if (GETPOST('invoiceAvoirWithLines', 'int')==1 && $id>0) { - $facture_source = new Facture($db); // fetch origin object if ($facture_source->fetch($object->fk_facture_source)>0) { $fk_parent_line = 0; foreach($facture_source->lines as $line) { + // Extrafields + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && method_exists($line, 'fetch_optionals')) { + // load extrafields + $line->fetch_optionals(); + } + // Reset fk_parent_line for no child products and special product if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) { $fk_parent_line = 0; @@ -963,7 +969,6 @@ if (empty($reshook)) if(GETPOST('invoiceAvoirWithPaymentRestAmount', 'int')==1 && $id>0) { - $facture_source = new Facture($db); // fetch origin object if not previously defined if ($facture_source->fetch($object->fk_facture_source)>0) { $totalpaye = $facture_source->getSommePaiement(); @@ -974,6 +979,20 @@ if (empty($reshook)) $object->addline($langs->trans('invoiceAvoirLineWithPaymentRestAmount'),$remain_to_pay,1,0,0,0,0,0,'','','TTC'); } } + + // Add link between credit note and origin + if(! empty($object->fk_facture_source)) { + $facture_source->fetch($object->fk_facture_source); + } + $facture_source->fetchObjectLinked(); + + if(! empty($facture_source->linkedObjectsIds)) { + $linkedObjectIds = $facture_source->linkedObjectsIds; + $sourcetype = key($linkedObjectIds); + $fk_origin = current($facture_source->linkedObjectsIds[$sourcetype]); + + $object->add_object_linked($sourcetype, $fk_origin); + } } } @@ -1413,6 +1432,7 @@ if (empty($reshook)) } } + // Situation invoices if (GETPOST('type') == Facture::TYPE_SITUATION && (!empty($_POST['situations']))) { $datefacture = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); @@ -1449,16 +1469,16 @@ if (empty($reshook)) $object->fetch_thirdparty(); $object->date = $datefacture; $object->date_pointoftax = $date_pointoftax; - $object->note_public = trim($_POST['note_public']); - $object->note = trim($_POST['note']); - $object->ref_client = $_POST['ref_client']; - $object->ref_int = $_POST['ref_int']; - $object->modelpdf = $_POST['model']; - $object->fk_project = $_POST['projectid']; - $object->cond_reglement_id = $_POST['cond_reglement_id']; - $object->mode_reglement_id = $_POST['mode_reglement_id']; - $object->remise_absolue = $_POST['remise_absolue']; - $object->remise_percent = $_POST['remise_percent']; + $object->note_public = trim(GETPOST('note_public','none')); + $object->note = trim(GETPOST('note','none')); + $object->ref_client = GETPOST('ref_client','alpha'); + $object->ref_int = GETPOST('ref_int','alpha'); + $object->modelpdf = GETPOST('model','alpha'); + $object->fk_project = GETPOST('projectid','int'); + $object->cond_reglement_id = GETPOST('cond_reglement_id','int'); + $object->mode_reglement_id = GETPOST('mode_reglement_id','int'); + $object->remise_absolue = GETPOST('remise_absolue','int'); + $object->remise_percent = GETPOST('remise_percent','int'); // Proprietes particulieres a facture de remplacement @@ -1527,14 +1547,14 @@ if (empty($reshook)) // Set if we used free entry or predefined product $predef=''; - $product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):''); + $product_desc=(GETPOST('dp_desc','none')?GETPOST('dp_desc','none'):''); $price_ht = GETPOST('price_ht'); $price_ht_devise = GETPOST('multicurrency_price_ht'); $prod_entry_mode = GETPOST('prod_entry_mode','alpha'); if ($prod_entry_mode == 'free') { $idprod=0; - $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); + $tva_tx = (GETPOST('tva_tx','alpha') ? GETPOST('tva_tx','alpha') : 0); } else { @@ -2088,6 +2108,82 @@ if (empty($reshook)) exit(); } + // add lines from objectlinked + elseif($action == 'import_lines_from_object' + && $user->rights->facture->creer + && $object->statut == Facture::STATUS_DRAFT + && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA || $object->type == Facture::TYPE_SITUATION)) + { + $fromElement = GETPOST('fromelement'); + $fromElementid = GETPOST('fromelementid'); + $importLines = GETPOST('line_checkbox'); + + if(!empty($importLines) && is_array($importLines) && !empty($fromElement) && ctype_alpha($fromElement) && !empty($fromElementid)) + { + if($fromElement == 'commande') + { + dol_include_once('/'.$fromElement.'/class/'.$fromElement.'.class'); + $lineClassName = 'OrderLine'; + } + $nextRang = count($object->lines) + 1; + $importCount = 0; + $error = 0; + foreach($importLines as $lineId) + { + $lineId = intval($lineId); + $originLine = new $lineClassName($db); + if(intval($fromElementid) > 0 && $originLine->fetch( $lineId ) > 0) + { + $originLine->fetch_optionals($lineId); + $desc = $originLine->desc; + $pu_ht = $originLine->subprice; + $qty = $originLine->qty; + $txtva = $originLine->tva_tx; + $txlocaltax1 = $originLine->localtax1_tx; + $txlocaltax2 = $originLine->localtax2_tx; + $fk_product = $originLine->fk_product; + $remise_percent = $originLine->remise_percent; + $date_start = $originLine->date_start; + $date_end = $originLine->date_end; + $ventil = 0; + $info_bits = $originLine->info_bits; + $fk_remise_except = $originLine->fk_remise_except; + $price_base_type='HT'; + $pu_ttc=0; + $type = $originLine->product_type; + $rang=$nextRang++; + $special_code = $originLine->special_code; + $origin = $originLine->element; + $origin_id = $originLine->id; + $fk_parent_line=0; + $fk_fournprice=$originLine->fk_fournprice; + $pa_ht = $originLine->pa_ht; + $label = $originLine->label; + $array_options = $originLine->array_options; + $situation_percent = 100; + $fk_prev_id = ''; + $fk_unit = $originLine->fk_unit; + $pu_ht_devise = $originLine->multicurrency_subprice; + + $res = $object->addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $ventil, $info_bits, $fk_remise_except, $price_base_type, $pu_ttc, $type, $rang, $special_code, $origin, $origin_id, $fk_parent_line, $fk_fournprice, $pa_ht, $label, $array_options, $situation_percent, $fk_prev_id, $fk_unit,$pu_ht_devise); + if($res > 0){ + $importCount++; + }else{ + $error++; + } + } + else{ + $error++; + } + } + + if($error) + { + setEventMessage($langs->trans('ErrorsOnXLines',$error), 'errors'); + } + } + } + // Actions when printing a doc from card include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php'; @@ -2106,9 +2202,11 @@ if (empty($reshook)) if ($action == 'update_extras') { + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from add form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) { @@ -2729,19 +2827,12 @@ if ($action == 'create') { // Discounts for third party print ''; } @@ -3032,7 +3123,7 @@ else if ($id > 0 || ! empty($ref)) $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice } else { - $filterabsolutediscount = "fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%'))"; + $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')"; $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')"; } @@ -3090,7 +3181,7 @@ else if ($id > 0 || ! empty($ref)) array('type' => 'other','name' => 'idwarehouse','label' => $label,'value' => $formproduct->selectWarehouses(GETPOST('idwarehouse')?GETPOST('idwarehouse'):'ifone', 'idwarehouse', '', 1, 0, 0, $langs->trans("NoStockAction")))); $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', $formquestion, "yes", 1); } else { - $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', '', 1); + $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('DeleteBill'), $text, 'confirm_delete', '', 'no', 1); } } @@ -3201,31 +3292,31 @@ else if ($id > 0 || ! empty($ref)) if ($action == 'paid' && $resteapayer > 0) { // Code $i = 0; - $close [$i] ['code'] = 'discount_vat'; // escompte + $close [$i]['code'] = 'discount_vat'; // escompte $i ++; - $close [$i] ['code'] = 'badcustomer'; + $close [$i]['code'] = 'badcustomer'; $i ++; // Help $i = 0; - $close [$i] ['label'] = $langs->trans("HelpEscompte") . '

' . $langs->trans("ConfirmClassifyPaidPartiallyReasonDiscountVatDesc"); + $close [$i]['label'] = $langs->trans("HelpEscompte") . '

' . $langs->trans("ConfirmClassifyPaidPartiallyReasonDiscountVatDesc"); $i ++; - $close [$i] ['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc"); + $close [$i]['label'] = $langs->trans("ConfirmClassifyPaidPartiallyReasonBadCustomerDesc"); $i ++; // Texte $i = 0; - $close [$i] ['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonDiscount", $resteapayer, $langs->trans("Currency" . $conf->currency)), $close [$i] ['label'], 1); + $close [$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonDiscount", $resteapayer, $langs->trans("Currency" . $conf->currency)), $close[$i]['label'], 1); $i ++; - $close [$i] ['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $resteapayer, $langs->trans("Currency" . $conf->currency)), $close [$i] ['label'], 1); + $close [$i]['reason'] = $form->textwithpicto($langs->transnoentities("ConfirmClassifyPaidPartiallyReasonBadCustomer", $resteapayer, $langs->trans("Currency" . $conf->currency)), $close[$i]['label'], 1); $i ++; // arrayreasons[code]=reason foreach ($close as $key => $val) { - $arrayreasons [$close [$key] ['code']] = $close [$key] ['reason']; + $arrayreasons[$close [$key]['code']] = $close[$key]['reason']; } // Cree un tableau formulaire - $formquestion = array('text' => $langs->trans("ConfirmClassifyPaidPartiallyQuestion"),array('type' => 'radio','name' => 'close_code','label' => $langs->trans("Reason"),'values' => $arrayreasons),array('type' => 'text','name' => 'close_note','label' => $langs->trans("Comment"),'value' => '','size' => '100')); + $formquestion = array('text' => $langs->trans("ConfirmClassifyPaidPartiallyQuestion"),array('type' => 'radio','name' => 'close_code','label' => $langs->trans("Reason"),'values' => $arrayreasons),array('type' => 'text','name' => 'close_note','label' => $langs->trans("Comment"),'value' => '','morecss' => 'minwidth300')); // Paiement incomplet. On demande si motif = escompte ou autre - $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?facid=' . $object->id, $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidPartially', $object->ref), 'confirm_paid_partially', $formquestion, "yes"); + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?facid=' . $object->id, $langs->trans('ClassifyPaid'), $langs->trans('ConfirmClassifyPaidPartially', $object->ref), 'confirm_paid_partially', $formquestion, "yes", 1, 250); } // Confirmation du classement abandonne @@ -3254,16 +3345,16 @@ else if ($id > 0 || ! empty($ref)) $arrayreasons [$close [2] ['code']] = $close [2] ['reason']; // Cree un tableau formulaire - $formquestion = array('text' => $langs->trans("ConfirmCancelBillQuestion"),array('type' => 'radio','name' => 'close_code','label' => $langs->trans("Reason"),'values' => $arrayreasons),array('type' => 'text','name' => 'close_note','label' => $langs->trans("Comment"),'value' => '','size' => '100')); + $formquestion = array('text' => $langs->trans("ConfirmCancelBillQuestion"),array('type' => 'radio','name' => 'close_code','label' => $langs->trans("Reason"),'values' => $arrayreasons),array('type' => 'text','name' => 'close_note','label' => $langs->trans("Comment"),'value' => '','morecss' => 'minwidth300')); - $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('CancelBill'), $langs->trans('ConfirmCancelBill', $object->ref), 'confirm_canceled', $formquestion, "yes"); + $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('CancelBill'), $langs->trans('ConfirmCancelBill', $object->ref), 'confirm_canceled', $formquestion, "yes", 1, 250); } } if ($action == 'deletepaiement') { $payment_id = GETPOST('paiement_id'); - $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&paiement_id='.$payment_id, $langs->trans('DeletePayment'), $langs->trans('ConfirmDeletePayment'), 'confirm_delete_paiement', '', 0, 1); + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&paiement_id='.$payment_id, $langs->trans('DeletePayment'), $langs->trans('ConfirmDeletePayment'), 'confirm_delete_paiement', '', 'no', 1); } @@ -3304,7 +3395,7 @@ else if ($id > 0 || ! empty($ref)) $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, $user->rights->facture->creer, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, $user->rights->facture->creer, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); if (empty($conf->global->MAIN_DISABLE_OTHER_LINK) && $object->thirdparty->id > 0) $morehtmlref.=' ('.$langs->trans("OtherBills").')'; // Project if (! empty($conf->projet->enabled)) @@ -3395,84 +3486,14 @@ else if ($id > 0 || ! empty($ref)) print ''; // Relative and absolute discounts - $addrelativediscount = '' . $langs->trans("EditRelativeDiscounts") . ''; - $addabsolutediscount = '' . $langs->trans("EditGlobalDiscounts") . ''; - $addcreditnote = '' . $langs->trans("AddCreditNote") . ''; - $viewabsolutediscount = '' . $langs->trans("ViewAvailableGlobalDiscounts") . ''; - print ''; // Date invoice @@ -4082,7 +4103,7 @@ else if ($id > 0 || ! empty($ref)) if (($object->statut == Facture::STATUS_CLOSED || $object->statut == Facture::STATUS_ABANDONED) && $object->close_code == 'discount_vat') { print ''; + print ''; $resteapayeraffiche = 0; $cssforamountpaymentcomplete = ''; } @@ -4090,7 +4111,7 @@ else if ($id > 0 || ! empty($ref)) if (($object->statut == Facture::STATUS_CLOSED || $object->statut == Facture::STATUS_ABANDONED) && $object->close_code == 'badcustomer') { print ''; + print ''; // $resteapayeraffiche=0; $cssforamountpaymentcomplete = ''; } @@ -4098,7 +4119,7 @@ else if ($id > 0 || ! empty($ref)) if (($object->statut == Facture::STATUS_CLOSED || $object->statut == Facture::STATUS_ABANDONED) && $object->close_code == 'product_returned') { print ''; + print ''; $resteapayeraffiche = 0; $cssforamountpaymentcomplete = ''; } @@ -4109,7 +4130,7 @@ else if ($id > 0 || ! empty($ref)) if ($object->close_note) $text .= '

' . $langs->trans("Reason") . ':' . $object->close_note; print $form->textwithpicto($langs->trans("Abandoned") . ':', $text, - 1); - print ''; + print ''; $resteapayeraffiche = 0; $cssforamountpaymentcomplete = ''; } @@ -4264,7 +4285,7 @@ else if ($id > 0 || ! empty($ref)) // Form to add new line if ($object->statut == 0 && $user->rights->facture->creer && $action != 'valid' && $action != 'editline' && ($object->is_first() || !$object->situation_cycle_ref)) { - if ($action != 'editline') + if ($action != 'editline' && $action != 'selectlines') { // Add free products/services $object->formAddObjectLine(1, $mysoc, $soc); @@ -4306,7 +4327,7 @@ else if ($id > 0 || ! empty($ref)) if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->creer)) || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->invoice_advance->unvalidate))) { - print ''; + print ''; } else { print '
' . $langs->trans('Modify') . '
'; } @@ -4335,7 +4356,7 @@ else if ($id > 0 || ! empty($ref)) { if (! $objectidnext && $object->close_code != 'replaced') // Not replaced by another invoice { - print ''; + print ''; } else { print '
' . $langs->trans('ReOpen') . '
'; } @@ -4346,19 +4367,19 @@ else if ($id > 0 || ! empty($ref)) if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->creer)) || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->facture->invoice_advance->validate))) { - print ''; + print ''; } } // Send by mail if (($object->statut == Facture::STATUS_VALIDATED || $object->statut == Facture::STATUS_CLOSED) || ! empty($conf->global->FACTURE_SENDBYEMAIL_FOR_ALL_STATUS)) { if ($objectidnext) { - print '
' . $langs->trans('SendByMail') . '
'; + print '
' . $langs->trans('SendMail') . '
'; } else { if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->facture->invoice_advance->send) { - print ''; + print ''; } else - print ''; + print ''; } } @@ -4418,16 +4439,16 @@ else if ($id > 0 || ! empty($ref)) // For standard invoice with excess received if ($object->type == Facture::TYPE_STANDARD && empty($object->paye) && ($object->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits) < 0 && $user->rights->facture->creer && empty($discount->id)) { - print ''; + print ''; } // For credit note if ($object->type == Facture::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0 && $user->rights->facture->creer && $object->getSommePaiement() == 0) { - print ''; + print ''; } // For deposit invoice if ($object->type == Facture::TYPE_DEPOSIT && $user->rights->facture->creer && empty($discount->id)) { - print ''; + print ''; } } @@ -4436,7 +4457,7 @@ else if ($id > 0 || ! empty($ref)) || ($object->type == Facture::TYPE_DEPOSIT && $object->paye == 0 && $object->total_ttc > 0 && $resteapayer == 0 && $user->rights->facture->paiement && empty($discount->id)) ) { - print ''; + print ''; } // Classify 'closed not completely paid' (possible si validee et pas encore classee payee) @@ -4446,7 +4467,7 @@ else if ($id > 0 || ! empty($ref)) if ($totalpaye > 0 || $totalcreditnotes > 0) { // If one payment or one credit note was linked to this invoice - print ''; + print ''; } else { @@ -4458,7 +4479,7 @@ else if ($id > 0 || ! empty($ref)) } else { - print ''; + print ''; } } } @@ -4467,7 +4488,7 @@ else if ($id > 0 || ! empty($ref)) // Clone if (($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA) && $user->rights->facture->creer) { - print ''; + print ''; } // Clone as predefined / Create template @@ -4526,7 +4547,7 @@ else if ($id > 0 || ! empty($ref)) } else { - print ''; + print ''; } } else { print ''; @@ -4556,8 +4577,16 @@ else if ($id > 0 || ! empty($ref)) // Show links to link elements $linktoelem = $form->showLinkToObjectBlock($object, null, array('invoice')); - $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); - + + $compatibleImportElementsList = false; + if($user->rights->facture->creer + && $object->statut == Facture::STATUS_DRAFT + && ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_PROFORMA || $object->type == Facture::TYPE_SITUATION) ) + { + $compatibleImportElementsList = array('commande'); // import from linked elements + } + $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem,$compatibleImportElementsList); + // Show online payment link $useonlinepayment = (! empty($conf->paypal->enabled) || ! empty($conf->stripe->enabled) || ! empty($conf->paybox->enabled)); diff --git a/htdocs/compta/facture/class/api_invoices.class.php b/htdocs/compta/facture/class/api_invoices.class.php index c1f8774095a..a114829d43c 100644 --- a/htdocs/compta/facture/class/api_invoices.class.php +++ b/htdocs/compta/facture/class/api_invoices.class.php @@ -937,8 +937,6 @@ class Invoices extends DolibarrApi throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); } - $request_data = (object) $payment_data; - if (! empty($conf->banque->enabled)) { if(empty($accountid)) { throw new RestException(400, 'Account ID is mandatory'); @@ -962,6 +960,10 @@ class Invoices extends DolibarrApi $resteapayer = price2num($this->invoice->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT'); $this->db->begin(); + + $amounts = array(); + $multicurrency_amounts = array(); + // Clean parameters amount if payment is for a credit note if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) { $resteapayer = price2num($resteapayer,'MT'); diff --git a/htdocs/compta/facture/class/facture-rec.class.php b/htdocs/compta/facture/class/facture-rec.class.php index 7a08cc6c97e..8759f2eebf3 100644 --- a/htdocs/compta/facture/class/facture-rec.class.php +++ b/htdocs/compta/facture/class/facture-rec.class.php @@ -68,6 +68,13 @@ class FactureRec extends CommonInvoice var $usenewprice=0; + var $suspended; // status + + const STATUS_NOTSUSPENDED = 0; + const STATUS_SUSPENDED = 1; + + + /** * Constructor * @@ -95,6 +102,7 @@ class FactureRec extends CommonInvoice // Clean parameters $this->titre=trim($this->titre); $this->usenewprice=empty($this->usenewprice)?0:$this->usenewprice; + if (empty($this->suspended)) $this->suspended=0; // No frequency defined then no next date to execution if (empty($this->frequency)) @@ -147,6 +155,7 @@ class FactureRec extends CommonInvoice $sql.= ", fk_multicurrency"; $sql.= ", multicurrency_code"; $sql.= ", multicurrency_tx"; + $sql.= ", suspended"; $sql.= ") VALUES ("; $sql.= "'".$this->db->escape($this->titre)."'"; $sql.= ", ".$facsrc->socid; @@ -167,13 +176,14 @@ class FactureRec extends CommonInvoice $sql.= ", '".$this->db->escape($this->unit_frequency)."'"; $sql.= ", ".(!empty($this->date_when)?"'".$this->db->idate($this->date_when)."'":'NULL'); $sql.= ", ".(!empty($this->date_last_gen)?"'".$this->db->idate($this->date_last_gen)."'":'NULL'); - $sql.= ", ".$this->nb_gen_done; - $sql.= ", ".$this->nb_gen_max; - $sql.= ", ".$this->auto_validate; - $sql.= ", ".$this->generate_pdf; - $sql.= ", ".$facsrc->fk_multicurrency; - $sql.= ", '".$facsrc->multicurrency_code."'"; - $sql.= ", ".$facsrc->multicurrency_tx; + $sql.= ", ".$this->db->escape($this->nb_gen_done); + $sql.= ", ".$this->db->escape($this->nb_gen_max); + $sql.= ", ".$this->db->escape($this->auto_validate); + $sql.= ", ".$this->db->escape($this->generate_pdf); + $sql.= ", ".$this->db->escape($facsrc->fk_multicurrency); + $sql.= ", '".$this->db->escape($facsrc->multicurrency_code)."'"; + $sql.= ", ".$this->db->escape($facsrc->multicurrency_tx); + $sql.= ", ".$this->db->escape($this->suspended); $sql.= ")"; if ($this->db->query($sql)) @@ -304,8 +314,8 @@ class FactureRec extends CommonInvoice $sql.= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc'; //$sql.= ', el.fk_source'; $sql.= ' FROM '.MAIN_DB_PREFIX.'facture_rec as f'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid AND c.entity IN ('.getEntity('c_payment_term').')'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id AND p.entity IN ('.getEntity('c_paiement').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id'; //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = f.rowid AND el.targettype = 'facture'"; $sql.= ' WHERE f.entity IN ('.getEntity('facture').')'; if ($rowid) $sql.= ' AND f.rowid='.$rowid; @@ -439,7 +449,7 @@ class FactureRec extends CommonInvoice $sql = 'SELECT l.rowid, l.fk_product, l.product_type, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.vat_src_code, l.tva_tx, '; $sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise, l.remise_percent, l.subprice,'; - $sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_ttc,'; + $sql.= ' l.info_bits, l.date_start_fill, l.date_end_fill, l.total_ht, l.total_tva, l.total_ttc,'; //$sql.= ' l.situation_percent, l.fk_prev_id,'; //$sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise_percent, l.fk_remise_except, l.subprice,'; $sql.= ' l.rang, l.special_code,'; @@ -487,6 +497,8 @@ class FactureRec extends CommonInvoice $line->remise_percent = $objp->remise_percent; $line->fk_remise_except = $objp->fk_remise_except; $line->fk_product = $objp->fk_product; + $line->date_start_fill = $objp->date_start_fill; + $line->date_end_fill = $objp->date_end_fill; $line->info_bits = $objp->info_bits; $line->total_ht = $objp->total_ht; $line->total_tva = $objp->total_tva; @@ -602,15 +614,17 @@ class FactureRec extends CommonInvoice * @param string $label Label of the line * @param string $fk_unit Unit * @param double $pu_ht_devise Unit price in currency + * @param int $date_start_fill 1=Flag to fill start date when generating invoice + * @param int $date_end_fill 1=Flag to fill end date when generating invoice * @return int <0 if KO, Id of line if OK */ - function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null, $pu_ht_devise=0) + function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null, $pu_ht_devise=0, $date_start_fill=0, $date_end_fill=0) { global $mysoc; $facid=$this->id; - dol_syslog(get_class($this)."::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,txlocaltax1=$txlocaltax1,txlocaltax2=$txlocaltax2,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit,pu_ht_devise=$pu_ht_devise", LOG_DEBUG); + dol_syslog(get_class($this)."::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,txlocaltax1=$txlocaltax1,txlocaltax2=$txlocaltax2,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit,pu_ht_devise=$pu_ht_devise,date_start_fill=$date_start_fill,date_end_fill=$date_end_fill", LOG_DEBUG); include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php'; // Check parameters @@ -700,6 +714,8 @@ class FactureRec extends CommonInvoice $sql.= ", total_localtax1"; $sql.= ", total_localtax2"; $sql.= ", total_ttc"; + $sql.= ", date_start_fill"; + $sql.= ", date_end_fill"; $sql.= ", info_bits"; $sql.= ", rang"; $sql.= ", special_code"; @@ -727,6 +743,8 @@ class FactureRec extends CommonInvoice $sql.= ", ".price2num($total_localtax1); $sql.= ", ".price2num($total_localtax2); $sql.= ", ".price2num($total_ttc); + $sql.= ", ".(int) $date_start_fill; + $sql.= ", ".(int) $date_end_fill; $sql.= ", ".$info_bits; $sql.= ", ".$rang; $sql.= ", ".$special_code; @@ -778,9 +796,11 @@ class FactureRec extends CommonInvoice * @param string $fk_unit Unit * @param double $pu_ht_devise Unit price in currency * @param int $notrigger disable line update trigger + * @param int $date_start_fill 1=Flag to fill start date when generating invoice + * @param int $date_end_fill 1=Flag to fill end date when generating invoice * @return int <0 if KO, Id of line if OK */ - function updateline($rowid, $desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null, $pu_ht_devise = 0, $notrigger=0) + function updateline($rowid, $desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $info_bits=0, $fk_remise_except='', $pu_ttc=0, $type=0, $rang=-1, $special_code=0, $label='', $fk_unit=null, $pu_ht_devise = 0, $notrigger=0, $date_start_fill=0, $date_end_fill=0) { global $mysoc; @@ -883,6 +903,8 @@ class FactureRec extends CommonInvoice $sql.= ", total_localtax1='".price2num($total_localtax1)."'"; $sql.= ", total_localtax2='".price2num($total_localtax2)."'"; $sql.= ", total_ttc='".price2num($total_ttc)."'"; + $sql.= ", date_start_fill=".((int) $date_start_fill); + $sql.= ", date_end_fill=".((int) $date_end_fill); $sql.= ", info_bits=".$info_bits; $sql.= ", rang=".$rang; $sql.= ", special_code=".$special_code; @@ -948,7 +970,7 @@ class FactureRec extends CommonInvoice * Create all recurrents invoices (for all entities if multicompany is used). * A result may also be provided into this->output. * - * WARNING: This method change context $conf->entity to be in correct context for each recurring invoice found. + * WARNING: This method change temporarly context $conf->entity to be in correct context for each recurring invoice found. * * @return int 0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK) */ @@ -1006,7 +1028,7 @@ class FactureRec extends CommonInvoice $facture->type = self::TYPE_STANDARD; $facture->brouillon = 1; - $facture->date = $facturerec->date_when; // We could also use dol_now here but we prefer date_when so invoice has real date when we would like even if we generate later. + $facture->date = (empty($facturerec->date_when)?$now:$facturerec->date_when); // We could also use dol_now here but we prefer date_when so invoice has real date when we would like even if we generate later. $facture->socid = $facturerec->socid; $invoiceidgenerated = $facture->create($user); @@ -1137,49 +1159,69 @@ class FactureRec extends CommonInvoice $prefix=''; if ($recur) { - if ($status == 1) return $langs->trans('Disabled'); // credit note + if ($status == self::STATUS_SUSPENDED) return $langs->trans('Disabled'); else return $langs->trans('Active'); } - else return $langs->trans("Draft"); + else + { + if ($status == self::STATUS_SUSPENDED) return $langs->trans('Disabled'); + else return $langs->trans("Draft"); + } } if ($mode == 1) { $prefix='Short'; if ($recur) { - if ($status == 1) return $langs->trans('Disabled'); + if ($status == self::STATUS_SUSPENDED) return $langs->trans('Disabled'); else return $langs->trans('Active'); } - else return $langs->trans("Draft"); + else + { + if ($status == self::STATUS_SUSPENDED) return $langs->trans('Disabled'); + else return $langs->trans("Draft"); + } } if ($mode == 2) { if ($recur) { - if ($status == 1) return img_picto($langs->trans('Disabled'),'statut6').' '.$langs->trans('Disabled'); + if ($status == self::STATUS_SUSPENDED) return img_picto($langs->trans('Disabled'),'statut6').' '.$langs->trans('Disabled'); else return img_picto($langs->trans('Active'),'statut4').' '.$langs->trans('Active'); } - else return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); + else + { + if ($status == self::STATUS_SUSPENDED) return img_picto($langs->trans('Disabled'),'statut6').' '.$langs->trans('Disabled'); + else return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); + } } if ($mode == 3) { if ($recur) { $prefix='Short'; - if ($status == 1) return img_picto($langs->trans('Disabled'),'statut6'); + if ($status == self::STATUS_SUSPENDED) return img_picto($langs->trans('Disabled'),'statut6'); else return img_picto($langs->trans('Active'),'statut4'); } - else return img_picto($langs->trans('Draft'),'statut0'); + else + { + if ($status == self::STATUS_SUSPENDED) return img_picto($langs->trans('Disabled'),'statut6'); + else return img_picto($langs->trans('Draft'),'statut0'); + } } if ($mode == 4) { $prefix=''; if ($recur) { - if ($status == 1) return img_picto($langs->trans('Disabled'),'statut6').' '.$langs->trans('Disabled'); + if ($status == self::STATUS_SUSPENDED) return img_picto($langs->trans('Disabled'),'statut6').' '.$langs->trans('Disabled'); else return img_picto($langs->trans('Active'),'statut4').' '.$langs->trans('Active'); } - else return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); + else + { + if ($status == self::STATUS_SUSPENDED) return img_picto($langs->trans('Disabled'),'statut6').' '.$langs->trans('Disabled'); + else return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); + } } if ($mode == 5 || $mode == 6) { @@ -1187,10 +1229,14 @@ class FactureRec extends CommonInvoice if ($mode == 5) $prefix='Short'; if ($recur) { - if ($status == 1) return ''.$langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut6'); + if ($status == self::STATUS_SUSPENDED) return ''.$langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut6'); else return ''.$langs->trans('Active').' '.img_picto($langs->trans('Active'),'statut4'); } - else return $langs->trans('Draft').' '.img_picto($langs->trans('Active'),'statut0'); + else + { + if ($status == self::STATUS_SUSPENDED) return ''.$langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut6'); + else return $langs->trans('Draft').' '.img_picto($langs->trans('Active'),'statut0'); + } } } @@ -1561,6 +1607,10 @@ class FactureLigneRec extends CommonInvoiceLine public $element='facturedetrec'; public $table_element='facturedet_rec'; + var $date_start_fill; + var $date_end_fill; + + /** * Delete line in database * @@ -1615,7 +1665,7 @@ class FactureLigneRec extends CommonInvoiceLine { $sql = 'SELECT l.rowid, l.fk_facture ,l.fk_product, l.product_type, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.vat_src_code, l.tva_tx,'; $sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise, l.remise_percent, l.subprice,'; - $sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_ttc,'; + $sql.= ' l.date_start_fill, l.date_end_fill, l.info_bits, l.total_ht, l.total_tva, l.total_ttc,'; $sql.= ' l.rang, l.special_code,'; $sql.= ' l.fk_unit, l.fk_contract_line,'; $sql.= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc'; @@ -1655,6 +1705,8 @@ class FactureLigneRec extends CommonInvoiceLine $this->remise_percent = $objp->remise_percent; $this->fk_remise_except = $objp->fk_remise_except; $this->fk_product = $objp->fk_product; + $this->date_start_fill = $objp->date_start_fill; + $this->date_end_fill = $objp->date_end_fill; $this->info_bits = $objp->info_bits; $this->total_ht = $objp->total_ht; $this->total_tva = $objp->total_tva; @@ -1711,6 +1763,9 @@ class FactureLigneRec extends CommonInvoiceLine $sql.= ", product_type=".$this->product_type; $sql.= ", remise_percent='".price2num($this->remise_percent)."'"; $sql.= ", subprice='".price2num($this->subprice)."'"; + $sql.= ", info_bits='".price2num($this->info_bits)."'"; + $sql.= ", date_start_fill=".(int) $this->date_start_fill; + $sql.= ", date_end_fill=".(int) $this->date_end_fill; $sql.= ", total_ht='".price2num($this->total_ht)."'"; $sql.= ", total_tva='".price2num($this->total_tva)."'"; $sql.= ", total_localtax1='".price2num($this->total_localtax1)."'"; diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index ce3fb569ded..c0285a65fdf 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -13,8 +13,9 @@ * Copyright (C) 2012 Cédric Salvador * Copyright (C) 2012-2014 Raphaël Doursenaud * Copyright (C) 2013 Cedric Gross - * Copyright (C) 2013 Florian Henry - * Copyright (C) 2016 Ferran Marcet + * Copyright (C) 2013 Florian Henry + * Copyright (C) 2016 Ferran Marcet + * Copyright (C) 2018 Alexandre Spangaro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,6 +44,9 @@ require_once DOL_DOCUMENT_ROOT.'/societe/class/client.class.php'; require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php'; require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php'; + /** * Class to manage invoices */ @@ -140,7 +144,7 @@ class Facture extends CommonInvoice public $situation_counter; /** - * @var bool Final situation flag + * @var int Final situation flag */ public $situation_final; @@ -239,7 +243,7 @@ class Facture extends CommonInvoice * @param int $forceduedate 1=Do not recalculate due date from payment condition but force it with value * @return int <0 if KO, >0 if OK */ - function create($user,$notrigger=0,$forceduedate=0) + function create(User $user, $notrigger=0, $forceduedate=0) { global $langs,$conf,$mysoc,$hookmanager; $error=0; @@ -265,13 +269,13 @@ class Facture extends CommonInvoice $this->multicurrency_tx = 1; } - dol_syslog(get_class($this)."::create user=".$user->id); + dol_syslog(get_class($this)."::create user=".$user->id." date=".$this->date); // Check parameters - if (empty($this->date) || empty($user->id)) + if (empty($this->date)) { - $this->error="ErrorBadParameter"; - dol_syslog(get_class($this)."::create Try to create an invoice with an empty parameter (user, date, ...)", LOG_ERR); + $this->error="Try to create an invoice with an empty parameter (date)"; + dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR); return -3; } $soc = new Societe($this->db); @@ -287,6 +291,10 @@ class Facture extends CommonInvoice $this->db->begin(); + $originaldatewhen=null; + $nextdatewhen=null; + $previousdaynextdatewhen=null; + // Create invoice from a template invoice if ($this->fac_rec > 0) { @@ -297,19 +305,22 @@ class Facture extends CommonInvoice $result=$_facrec->fetch($this->fac_rec); $result=$_facrec->fetchObjectLinked(); // This load $_facrec->linkedObjectsIds + // Define some dates $originaldatewhen = $_facrec->date_when; + $nextdatewhen=dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency); + $previousdaynextdatewhen=dol_time_plus_duree($nextdatewhen, -1, 'd'); $this->socid = $_facrec->socid; // Invoice created on same thirdparty than template $this->entity = $_facrec->entity; // Invoice created in same entity than template // Fields coming from GUI (priority on template). TODO Value of template should be used as default value on GUI so we can use here always value from GUI - $this->fk_project = GETPOST('projectid','int') > 0 ? GETPOST('projectid','int') : $_facrec->fk_project; + $this->fk_project = GETPOST('projectid','int') > 0 ? ((int) GETPOST('projectid','int')) : $_facrec->fk_project; $this->note_public = GETPOST('note_public','none') ? GETPOST('note_public','none') : $_facrec->note_public; $this->note_private = GETPOST('note_private','none') ? GETPOST('note_private','none') : $_facrec->note_private; - $this->modelpdf = GETPOST('model') ? GETPOST('model') : $_facrec->modelpdf; - $this->cond_reglement_id = GETPOST('cond_reglement_id') > 0 ? GETPOST('cond_reglement_id') : $_facrec->cond_reglement_id; - $this->mode_reglement_id = GETPOST('mode_reglement_id') > 0 ? GETPOST('mode_reglement_id') : $_facrec->mode_reglement_id; - $this->fk_account = GETPOST('fk_account') > 0 ? GETPOST('fk_account') : $_facrec->fk_account; + $this->modelpdf = GETPOST('model','alpha') ? GETPOST('model','apha') : $_facrec->modelpdf; + $this->cond_reglement_id = GETPOST('cond_reglement_id','int') > 0 ? ((int) GETPOST('cond_reglement_id','int')) : $_facrec->cond_reglement_id; + $this->mode_reglement_id = GETPOST('mode_reglement_id','int') > 0 ? ((int) GETPOST('mode_reglement_id','int')) : $_facrec->mode_reglement_id; + $this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account; // Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result $this->total_ht = $_facrec->total_ht; @@ -374,7 +385,8 @@ class Facture extends CommonInvoice $substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for tempalte invoice $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = dol_print_date($originaldatewhen, 'dayhour'); - $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date(dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency), 'dayhour'); + $substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($nextdatewhen, 'dayhour'); + $substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = dol_print_date($previousdaynextdatewhen, 'dayhour'); //var_dump($substitutionarray);exit; @@ -494,6 +506,7 @@ class Facture extends CommonInvoice } } + // Propagate contacts if (! $error && $this->id && ! empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && ! empty($this->origin) && ! empty($this->origin_id)) // Get contact from origin object { $originforcontact = $this->origin; @@ -531,11 +544,10 @@ class Facture extends CommonInvoice else dol_print_error($resqlcontact); } - /* - * Insert lines of invoices into database + * Insert lines of invoices, if not from template invoice, into database */ - if (count($this->lines) && is_object($this->lines[0])) // If this->lines is array of InvoiceLines (preferred mode) + if (! $error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) // If this->lines is array of InvoiceLines (preferred mode) { $fk_parent_line = 0; @@ -545,9 +557,18 @@ class Facture extends CommonInvoice $newinvoiceline=$this->lines[$i]; $newinvoiceline->fk_facture=$this->id; - // TODO This seems not used. Here we put origin 'facture' but after, we put an id of object ! - $newinvoiceline->origin = $this->element; - $newinvoiceline->origin_id = $this->lines[$i]->id; + $newinvoiceline->origin = $this->lines[$i]->element; + $newinvoiceline->origin_id = $this->lines[$i]->id; + + // Auto set date of service ? + if ($this->lines[$i]->date_start_fill == 1 && $originaldatewhen) // $originaldatewhen is defined when generating from recurring invoice only + { + $newinvoiceline->date_start = $originaldatewhen; + } + if ($this->lines[$i]->date_end_fill == 1 && $previousdaynextdatewhen) // $previousdaynextdatewhen is defined when generating from recurring invoice only + { + $newinvoiceline->date_end = $previousdaynextdatewhen; + } if ($result >= 0) { @@ -557,6 +578,15 @@ class Facture extends CommonInvoice } $newinvoiceline->fk_parent_line=$fk_parent_line; + + if($this->type === Facture::TYPE_REPLACEMENT && $newinvoiceline->fk_remise_except){ + $discount = new DiscountAbsolute($this->db); + $discount->fetch($newinvoiceline->fk_remise_except); + + $discountId = $soc->set_remise_except($discount->amount_ht, $user, $discount->description, $discount->tva_tx); + $newinvoiceline->fk_remise_except = $discountId; + } + $result=$newinvoiceline->insert(); // Defined the new fk_parent_line @@ -572,7 +602,7 @@ class Facture extends CommonInvoice } } } - else // If this->lines is an array of invoice line arrays + elseif (! $error && empty($this->fac_rec)) // If this->lines is an array of invoice line arrays { $fk_parent_line = 0; @@ -680,7 +710,9 @@ class Facture extends CommonInvoice $localtax2_tx, $_facrec->lines[$i]->fk_product, $_facrec->lines[$i]->remise_percent, - '','',0, + ($_facrec->lines[$i]->date_start_fill == 1 && $originaldatewhen)?$originaldatewhen:'', + ($_facrec->lines[$i]->date_end_fill == 1 && $previousdaynextdatewhen)?$previousdaynextdatewhen:'', + 0, $tva_npr, '', 'HT', @@ -737,10 +769,16 @@ class Facture extends CommonInvoice } else if ($reshook < 0) $error++;*/ - // Call trigger - $result=$this->call_trigger('BILL_CREATE',$user); - if ($result < 0) $error++; - // End call triggers + if (! $error) + { + if (! $notrigger) + { + // Call trigger + $result=$this->call_trigger('BILL_CREATE',$user); + if ($result < 0) $error++; + // End call triggers + } + } if (! $error) { @@ -851,7 +889,7 @@ class Facture extends CommonInvoice } elseif ($this->type == self::TYPE_SITUATION && !empty($conf->global->INVOICE_USE_SITUATION)) { - $this->fetchObjectLinked('', '', $object->id, 'facture'); + $this->fetchObjectLinked('', '', $facture->id, 'facture'); foreach ($this->linkedObjectsIds as $typeObject => $Tfk_object) { @@ -1228,7 +1266,7 @@ class Facture extends CommonInvoice if (empty($rowid) && empty($ref) && empty($ref_ext) && empty($ref_int)) return -1; - $sql = 'SELECT f.rowid,f.facnumber,f.ref_client,f.ref_ext,f.ref_int,f.type,f.fk_soc,f.amount'; + $sql = 'SELECT f.rowid,f.entity,f.facnumber,f.ref_client,f.ref_ext,f.ref_int,f.type,f.fk_soc,f.amount'; $sql.= ', f.tva, f.localtax1, f.localtax2, f.total, f.total_ttc, f.revenuestamp'; $sql.= ', f.remise_percent, f.remise_absolue, f.remise'; $sql.= ', f.datef as df, f.date_pointoftax'; @@ -1247,8 +1285,8 @@ class Facture extends CommonInvoice $sql.= ', f.fk_incoterms, f.location_incoterms'; $sql.= ", i.libelle as libelle_incoterms"; $sql.= ' FROM '.MAIN_DB_PREFIX.'facture as f'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid AND c.entity IN (' . getEntity('c_payment_term').')'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id AND p.entity IN ('.getEntity('c_paiement').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON f.fk_incoterms = i.rowid'; $sql.= ' WHERE f.entity IN ('.getEntity('facture').')'; if ($rowid) $sql.= " AND f.rowid=".$rowid; @@ -1265,6 +1303,8 @@ class Facture extends CommonInvoice $obj = $this->db->fetch_object($result); $this->id = $obj->rowid; + $this->entity = $obj->entity; + $this->ref = $obj->facnumber; $this->ref_client = $obj->ref_client; $this->ref_ext = $obj->ref_ext; @@ -1446,6 +1486,9 @@ class Facture extends CommonInvoice $line->fk_prev_id = $objp->fk_prev_id; $line->fk_unit = $objp->fk_unit; + // Accountancy + $line->fk_accounting_account = $objp->fk_code_ventilation; + // Multicurrency $line->fk_multicurrency = $objp->fk_multicurrency; $line->multicurrency_code = $objp->multicurrency_code; @@ -1508,7 +1551,7 @@ class Facture extends CommonInvoice * @param int $notrigger 0=launch triggers after, 1=disable triggers * @return int <0 if KO, >0 if OK */ - function update($user=null, $notrigger=0) + function update(User $user, $notrigger=0) { $error=0; @@ -1524,36 +1567,24 @@ class Facture extends CommonInvoice if (isset($this->note_public)) $this->note_public=trim($this->note_public); if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf); if (isset($this->import_key)) $this->import_key=trim($this->import_key); - if (empty($this->situation_cycle_ref)) { - $this->situation_cycle_ref = 'null'; - } - - if (empty($this->situation_counter)) { - $this->situation_counter = 'null'; - } - - if (empty($this->situation_final)) { - $this->situation_final = '0'; - } // Check parameters // Put here code to add control on parameters values // Update request $sql = "UPDATE ".MAIN_DB_PREFIX."facture SET"; - $sql.= " facnumber=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").","; - $sql.= " type=".(isset($this->type)?$this->type:"null").","; + $sql.= " type=".(isset($this->type)?$this->db->escape($this->type):"null").","; $sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").","; $sql.= " increment=".(isset($this->increment)?"'".$this->db->escape($this->increment)."'":"null").","; - $sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").","; + $sql.= " fk_soc=".(isset($this->socid)?$this->db->escape($this->socid):"null").","; $sql.= " datec=".(strval($this->date_creation)!='' ? "'".$this->db->idate($this->date_creation)."'" : 'null').","; $sql.= " datef=".(strval($this->date)!='' ? "'".$this->db->idate($this->date)."'" : 'null').","; $sql.= " date_pointoftax=".(strval($this->date_pointoftax)!='' ? "'".$this->db->idate($this->date_pointoftax)."'" : 'null').","; $sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').","; - $sql.= " paye=".(isset($this->paye)?$this->paye:"null").","; - $sql.= " remise_percent=".(isset($this->remise_percent)?$this->remise_percent:"null").","; - $sql.= " remise_absolue=".(isset($this->remise_absolue)?$this->remise_absolue:"null").","; + $sql.= " paye=".(isset($this->paye)?$this->db->escape($this->paye):"null").","; + $sql.= " remise_percent=".(isset($this->remise_percent)?$this->db->escape($this->remise_percent):"null").","; + $sql.= " remise_absolue=".(isset($this->remise_absolue)?$this->db->escape($this->remise_absolue):"null").","; $sql.= " close_code=".(isset($this->close_code)?"'".$this->db->escape($this->close_code)."'":"null").","; $sql.= " close_note=".(isset($this->close_note)?"'".$this->db->escape($this->close_note)."'":"null").","; $sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").","; @@ -1561,23 +1592,22 @@ class Facture extends CommonInvoice $sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").","; $sql.= " total=".(isset($this->total_ht)?$this->total_ht:"null").","; $sql.= " total_ttc=".(isset($this->total_ttc)?$this->total_ttc:"null").","; - $sql.= " revenuestamp=".((isset($this->revenuestamp) && $this->revenuestamp != '')?$this->revenuestamp:"null").","; - $sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").","; - $sql.= " fk_user_author=".(isset($this->user_author)?$this->user_author:"null").","; - $sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->fk_user_valid:"null").","; - $sql.= " fk_facture_source=".(isset($this->fk_facture_source)?$this->fk_facture_source:"null").","; - $sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").","; - $sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").","; - $sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").","; + $sql.= " revenuestamp=".((isset($this->revenuestamp) && $this->revenuestamp != '')?$this->db->escape($this->revenuestamp):"null").","; + $sql.= " fk_statut=".(isset($this->statut)?$this->db->escape($this->statut):"null").","; + $sql.= " fk_user_author=".(isset($this->user_author)?$this->db->escape($this->user_author):"null").","; + $sql.= " fk_user_valid=".(isset($this->fk_user_valid)?$this->db->escape($this->fk_user_valid):"null").","; + $sql.= " fk_facture_source=".(isset($this->fk_facture_source)?$this->db->escape($this->fk_facture_source):"null").","; + $sql.= " fk_projet=".(isset($this->fk_project)?$this->db->escape($this->fk_project):"null").","; + $sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->db->escape($this->cond_reglement_id):"null").","; + $sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->db->escape($this->mode_reglement_id):"null").","; $sql.= " date_lim_reglement=".(strval($this->date_lim_reglement)!='' ? "'".$this->db->idate($this->date_lim_reglement)."'" : 'null').","; $sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").","; $sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").","; $sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").","; - $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null"); - $sql.= ", situation_cycle_ref=".$this->situation_cycle_ref; - $sql.= ", situation_counter=".$this->situation_counter; - $sql.= ", situation_final=".$this->situation_final; - + $sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null").","; + $sql.= " situation_cycle_ref=".(empty($this->situation_cycle_ref)?"null":$this->db->escape($this->situation_cycle_ref)).","; + $sql.= " situation_counter=".(empty($this->situation_counter)?"null":$this->db->escape($this->situation_counter)).","; + $sql.= " situation_final=".(empty($this->situation_counter)?"0":$this->db->escape($this->situation_counter)); $sql.= " WHERE rowid=".$this->id; $this->db->begin(); @@ -1799,9 +1829,9 @@ class Facture extends CommonInvoice dol_syslog(get_class($this)."::delete rowid=".$rowid.", ref=".$this->ref.", thirdparty=".$this->thirdparty->name, LOG_DEBUG); // Test to avoid invoice deletion (allowed if draft) - $test = $this->is_erasable(); + $result = $this->is_erasable(); - if ($test <= 0) return 0; + if ($result <= 0) return 0; $error=0; @@ -2231,7 +2261,7 @@ class Facture extends CommonInvoice // Validate $sql = 'UPDATE '.MAIN_DB_PREFIX.'facture'; - $sql.= " SET facnumber='".$num."', fk_statut = ".self::STATUS_VALIDATED.", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'"; + $sql.= " SET facnumber='".$num."', fk_statut = ".self::STATUS_VALIDATED.", fk_user_valid = ".($user->id > 0 ? $user->id : "null").", date_valid = '".$this->db->idate($now)."'"; if (! empty($conf->global->FAC_FORCE_DATE_VALIDATION)) // If option enabled, we force invoice date { $sql.= ", datef='".$this->db->idate($this->date)."'"; @@ -2354,9 +2384,12 @@ class Facture extends CommonInvoice $final = ($this->lines[$i]->situation_percent == 100); $i++; } - if ($final) { - $this->setFinal($user); - } + + if (empty($final)) $this->situation_final = 0; + else $this->situation_final = 1; + + $this->setFinal($user); + } } } @@ -2519,8 +2552,8 @@ class Facture extends CommonInvoice * @param double $txlocaltax2 Local tax 2 rate (deprecated, use instead txtva with code inside) * @param int $fk_product Id of predefined product/service * @param double $remise_percent Percent of discount on line - * @param int $date_start Date start of service - * @param int $date_end Date end of service + * @param int $date_start Date start of service + * @param int $date_end Date end of service * @param int $ventil Code of dispatching into accountancy * @param int $info_bits Bits de type de lignes * @param int $fk_remise_except Id discount used @@ -2542,7 +2575,7 @@ class Facture extends CommonInvoice * @param double $pu_ht_devise Unit price in currency * @return int <0 if KO, Id of line if OK */ - function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $date_start='', $date_end='', $ventil=0, $info_bits=0, $fk_remise_except='', $price_base_type='HT', $pu_ttc=0, $type=self::TYPE_STANDARD, $rang=-1, $special_code=0, $origin='', $origin_id=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=0, $situation_percent=100, $fk_prev_id='', $fk_unit = null, $pu_ht_devise = 0) + function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $date_start='', $date_end='', $ventil=0, $info_bits=0, $fk_remise_except='', $price_base_type='HT', $pu_ttc=0, $type=self::TYPE_STANDARD, $rang=-1, $special_code=0, $origin='', $origin_id=0, $fk_parent_line=0, $fk_fournprice=null, $pa_ht=0, $label='', $array_options=0, $situation_percent=100, $fk_prev_id=0, $fk_unit = null, $pu_ht_devise = 0) { // Deprecation warning if ($label) { @@ -3047,7 +3080,7 @@ class Facture extends CommonInvoice // For triggers $result = $line->fetch($rowid); - if (! ($result > 0)) dol_print_error($db, $line->error, $line->errors); + if (! ($result > 0)) dol_print_error($this->db, $line->error, $line->errors); if ($line->delete($user) > 0) { @@ -3203,62 +3236,6 @@ class Facture extends CommonInvoice } } - /** - * Return list of payments - * - * @param string $filtertype 1 to filter on type of payment == 'PRE' - * @return array Array with list of payments - */ - function getListOfPayments($filtertype='') - { - $retarray=array(); - - $table='paiement_facture'; - $table2='paiement'; - $field='fk_facture'; - $field2='fk_paiement'; - $sharedentity='facture'; - if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') - { - $table='paiementfourn_facturefourn'; - $table2='paiementfourn'; - $field='fk_facturefourn'; - $field2='fk_paiementfourn'; - $sharedentity='facture_fourn'; - } - - $sql = 'SELECT p.ref, pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code'; - $sql.= ' FROM '.MAIN_DB_PREFIX.$table.' as pf, '.MAIN_DB_PREFIX.$table2.' as p, '.MAIN_DB_PREFIX.'c_paiement as t'; - $sql.= ' WHERE pf.'.$field.' = '.$this->id; - $sql.= ' AND pf.'.$field2.' = p.rowid'; - $sql.= ' AND p.fk_paiement = t.id'; - $sql.= ' AND p.entity IN (' . getEntity($sharedentity).')'; - if ($filtertype) $sql.=" AND t.code='PRE'"; - - dol_syslog(get_class($this)."::getListOfPayments", LOG_DEBUG); - $resql=$this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i=0; - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - $retarray[]=array('amount'=>$obj->amount,'type'=>$obj->code, 'date'=>$obj->datep, 'num'=>$obj->num, 'ref'=>$obj->ref); - $i++; - } - $this->db->free($resql); - return $retarray; - } - else - { - $this->error=$this->db->lasterror(); - dol_print_error($this->db); - return array(); - } - } - - /** * Return next reference of customer invoice not already used (or last reference) * according to numbering module defined into constant FACTURE_ADDON @@ -3831,6 +3808,7 @@ class Facture extends CommonInvoice // Initialize parameters $this->id=0; + $this->entity = 1; $this->ref = 'SPECIMEN'; $this->specimen=1; $this->socid = 1; @@ -4117,7 +4095,6 @@ class Facture extends CommonInvoice $this->db->begin(); - $this->situation_final = 1; $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture SET situation_final = ' . $this->situation_final . ' where rowid = ' . $this->id; dol_syslog(__METHOD__, LOG_DEBUG); @@ -4424,7 +4401,7 @@ class FactureLigne extends CommonInvoiceLine if (empty($this->subprice)) $this->subprice=0; if (empty($this->special_code)) $this->special_code=0; if (empty($this->fk_parent_line)) $this->fk_parent_line=0; - if (empty($this->fk_prev_id)) $this->fk_prev_id = 'null'; + if (empty($this->fk_prev_id)) $this->fk_prev_id = 0; if (! isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') $this->situation_percent = 100; if (empty($this->pa_ht)) $this->pa_ht=0; @@ -4507,7 +4484,7 @@ class FactureLigne extends CommonInvoiceLine $sql.= " ".price2num($this->total_localtax1).","; $sql.= " ".price2num($this->total_localtax2); $sql.= ", " . $this->situation_percent; - $sql.= ", " . $this->fk_prev_id; + $sql.= ", " . (!empty($this->fk_prev_id)?$this->fk_prev_id:"null"); $sql.= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit); $sql.= ", ".$user->id; $sql.= ", ".$user->id; diff --git a/htdocs/compta/facture/class/paymentterm.class.php b/htdocs/compta/facture/class/paymentterm.class.php index bc13b0d5876..3697a61672b 100644 --- a/htdocs/compta/facture/class/paymentterm.class.php +++ b/htdocs/compta/facture/class/paymentterm.class.php @@ -57,7 +57,6 @@ class PaymentTerm // extends CommonObject function __construct($db) { $this->db = $db; - return 1; } @@ -90,7 +89,6 @@ class PaymentTerm // extends CommonObject // Insert request $sql = "INSERT INTO ".MAIN_DB_PREFIX."c_payment_term("; - $sql.= "rowid,"; $sql.= "entity,"; $sql.= "code,"; $sql.= "sortorder,"; @@ -101,7 +99,6 @@ class PaymentTerm // extends CommonObject $sql.= "nbjour,"; $sql.= "decalage"; $sql.= ") VALUES ("; - $sql.= " ".(! isset($this->rowid)?'NULL':"'".$this->db->escape($this->rowid)."'").","; $sql.= " ".(! isset($this->entity)?getEntity('c_payment_term'):"'".$this->db->escape($this->entity)."'").","; $sql.= " ".(! isset($this->code)?'NULL':"'".$this->db->escape($this->code)."'").","; $sql.= " ".(! isset($this->sortorder)?'NULL':"'".$this->db->escape($this->sortorder)."'").","; @@ -181,7 +178,6 @@ class PaymentTerm // extends CommonObject $sql.= " FROM ".MAIN_DB_PREFIX."c_payment_term as t"; $sql.= " WHERE t.rowid = ".$id; - $sql.= " AND t.entity = " . getEntity('c_payment_term'); dol_syslog(get_class($this)."::fetch", LOG_DEBUG); $resql=$this->db->query($sql); @@ -221,36 +217,36 @@ class PaymentTerm // extends CommonObject * * @return int <0 if KO, >0 if OK */ - function getDefaultId() - { - global $langs; + function getDefaultId() + { + global $langs; - $ret=0; + $ret=0; - $sql = "SELECT"; + $sql = "SELECT"; $sql.= " t.rowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."c_payment_term as t"; - $sql.= " WHERE t.code = 'RECEP'"; - $sql.= " AND t.entity = " . getEntity('c_payment_term'); + $sql.= " FROM ".MAIN_DB_PREFIX."c_payment_term as t"; + $sql.= " WHERE t.code = 'RECEP'"; + $sql.= " AND t.entity IN (".getEntity('c_payment_term').")"; - dol_syslog(get_class($this)."::getDefaultId", LOG_DEBUG); - $resql=$this->db->query($sql); - if ($resql) - { - if ($this->db->num_rows($resql)) - { - $obj = $this->db->fetch_object($resql); - if ($obj) $ret=$obj->rowid; - } - $this->db->free($resql); - return $ret; - } - else - { - $this->error="Error ".$this->db->lasterror(); - return -1; - } - } + dol_syslog(get_class($this)."::getDefaultId", LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + if ($this->db->num_rows($resql)) + { + $obj = $this->db->fetch_object($resql); + if ($obj) $ret=$obj->rowid; + } + $this->db->free($resql); + return $ret; + } + else + { + $this->error="Error ".$this->db->lasterror(); + return -1; + } + } /** @@ -293,7 +289,6 @@ class PaymentTerm // extends CommonObject $sql.= " nbjour=".(isset($this->nbjour)?$this->nbjour:"null").","; $sql.= " decalage=".(isset($this->decalage)?$this->decalage:"null").""; $sql.= " WHERE rowid = " . $this->id; - $sql.= " AND entity = " . getEntity('c_payment_term'); $this->db->begin(); @@ -336,11 +331,11 @@ class PaymentTerm // extends CommonObject } - /** + /** * Delete object in database * - * @param User $user User that delete - * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @param User $user User that delete + * @param int $notrigger 0=launch triggers after, 1=disable triggers * @return int <0 if KO, >0 if OK */ function delete($user, $notrigger=0) @@ -350,7 +345,6 @@ class PaymentTerm // extends CommonObject $sql = "DELETE FROM ".MAIN_DB_PREFIX."c_payment_term"; $sql.= " WHERE rowid = " . $this->id; - $sql.= " AND t.entity = " . getEntity('c_payment_term'); $this->db->begin(); diff --git a/htdocs/compta/facture/contact.php b/htdocs/compta/facture/contact.php index 2c95366f9a9..61f37d221f8 100644 --- a/htdocs/compta/facture/contact.php +++ b/htdocs/compta/facture/contact.php @@ -155,7 +155,7 @@ if ($id > 0 || ! empty($ref)) $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); // Project if (! empty($conf->projet->enabled)) { diff --git a/htdocs/compta/facture/document.php b/htdocs/compta/facture/document.php index 8b76f9b167f..a9396e445c9 100644 --- a/htdocs/compta/facture/document.php +++ b/htdocs/compta/facture/document.php @@ -125,7 +125,7 @@ if ($id > 0 || ! empty($ref)) $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); // Project if (! empty($conf->projet->enabled)) { diff --git a/htdocs/compta/facture/fiche-rec.php b/htdocs/compta/facture/fiche-rec.php index f869c01aa53..6f96e74d345 100644 --- a/htdocs/compta/facture/fiche-rec.php +++ b/htdocs/compta/facture/fiche-rec.php @@ -66,22 +66,8 @@ if ($action == "create" || $action == "add") $objecttype = ''; $result = restrictedArea($user, 'facture', $id, $objecttype); $projectid = GETPOST('projectid','int'); -$search_ref=GETPOST('search_ref'); -$search_societe=GETPOST('search_societe'); -$search_montant_ht=GETPOST('search_montant_ht'); -$search_montant_vat=GETPOST('search_montant_vat'); -$search_montant_ttc=GETPOST('search_montant_ttc'); -$search_payment_mode=GETPOST('search_payment_mode'); -$search_payment_term=GETPOST('search_payment_term'); -$day=GETPOST('day'); -$year=GETPOST('year'); -$month=GETPOST('month'); -$day_date_when=GETPOST('day_date_when'); $year_date_when=GETPOST('year_date_when'); $month_date_when=GETPOST('month_date_when'); -$search_recurring=GETPOST('search_recurring','int'); -$search_frequency=GETPOST('search_frequency','alpha'); -$search_unit_frequency=GETPOST('search_unit_frequency','alpha'); $limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit; $sortfield = GETPOST("sortfield",'alpha'); @@ -116,6 +102,8 @@ $permissionnote = $user->rights->facture->creer; // Used by the include of actio $permissiondellink=$user->rights->facture->creer; // Used by the include of actions_dellink.inc.php $permissiontoedit = $user->rights->facture->creer; // Used by the include of actions_lineupdonw.inc.php +$now = dol_now(); + /* * Actions @@ -142,28 +130,6 @@ if (empty($reshook)) include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be include, not include_once - // Do we click on purge search criteria ? - if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha')) // All test are required to be compatible with all browsers - { - $search_ref=''; - $search_societe=''; - $search_montant_ht=''; - $search_montant_vat=''; - $search_montant_ttc=''; - $search_montant_mode=''; - $search_montant_term=''; - $day=''; - $year=''; - $month=''; - $day_date_when=''; - $year_date_when=''; - $month_date_when=''; - $search_recurring=''; - $search_frequency=''; - $search_unit_frequency=''; - $search_array_options=array(); - } - // Mass actions /*$objectclass='MyObject'; $objectlabel='MyObject'; @@ -444,9 +410,11 @@ if (empty($reshook)) } else if ($action == 'update_extras') { + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) { @@ -682,6 +650,9 @@ if (empty($reshook)) $fk_unit= GETPOST('units', 'alpha'); } + $date_start_fill = GETPOST('date_start_fill','int'); + $date_end_fill = GETPOST('date_end_fill','int'); + // Margin $fournprice = price2num(GETPOST('fournprice' . $predef) ? GETPOST('fournprice' . $predef) : ''); $buyingprice = price2num(GETPOST('buying_price' . $predef) != '' ? GETPOST('buying_price' . $predef) : ''); // If buying_price is '0', we must keep this value @@ -702,7 +673,7 @@ if (empty($reshook)) else { // Insert line - $result = $object->addline($desc, $pu_ht, $qty, $tva_tx,$localtax1_tx, $localtax2_tx, $idprod, $remise_percent, $price_base_type, $info_bits, '', $pu_ttc, $type, - 1, $special_code, $label, $fk_unit); + $result = $object->addline($desc, $pu_ht, $qty, $tva_tx,$localtax1_tx, $localtax2_tx, $idprod, $remise_percent, $price_base_type, $info_bits, '', $pu_ttc, $type, - 1, $special_code, $label, $fk_unit, 0, $date_start_fill, $date_end_fill); if ($result > 0) { @@ -758,6 +729,9 @@ if (empty($reshook)) unset($_POST['date_endmonth']); unset($_POST['date_endyear']); + unset($_POST['date_start_fill']); + unset($_POST['date_end_fill']); + unset($_POST['situations']); unset($_POST['progress']); } @@ -879,6 +853,9 @@ if (empty($reshook)) $error ++; } + $date_start_fill = GETPOST('date_start_fill','int'); + $date_end_fill = GETPOST('date_end_fill','int'); + // Update line if (! $error) { @@ -901,7 +878,10 @@ if (empty($reshook)) $special_code, $label, GETPOST('units'), - $pu_ht_devise + $pu_ht_devise, + 0, + $date_start_fill, + $date_end_fill ); if ($result >= 0) @@ -1131,7 +1111,7 @@ if ($action == 'create') print " ".$form->selectarray('unit_frequency', array('d'=>$langs->trans('Day'), 'm'=>$langs->trans('Month'), 'y'=>$langs->trans('Year')), (GETPOST('unit_frequency')?GETPOST('unit_frequency'):'m')); print ""; - // First date of execution for cron + // Date next run print "
'; - $form->select_types_paiements($typeid,'typeid','',0,0,1,16); + $form->select_types_paiements($typeid,'typeid','',0,1,1,16); print '
' . $langs->trans('Discounts') . ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", '' . $soc->remise_percent . ''); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); - print ' (' . $langs->trans("EditRelativeDiscount") . ')'; - print '. '; - print '
'; - if ($absolute_discount) - print $langs->trans("CompanyHasAbsoluteDiscount", '' . price($absolute_discount) . '', $langs->trans("Currency" . $conf->currency)); - else - print $langs->trans("CompanyHasNoAbsoluteDiscount"); - print ' (' . $langs->trans("EditGlobalDiscounts") . ')'; - print '.'; + + $thirdparty = $soc; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . GETPOST('origin') . '&originid=' . GETPOST('originid')); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print '
' . $langs->trans('Discounts'); - print ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", $soc->remise_percent); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); - // print ' ('.$addrelativediscount.')'; - // Is there is commercial discount or down payment available ? - if ($absolute_discount > 0) { - print '. '; - if ($object->statut > 0 || $object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT) { - if ($object->statut == 0) { - print $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount), $langs->transnoentities("Currency" . $conf->currency)); - print '. '; - } else { - if ($object->statut < 1 || $object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT) { - $text = $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount), $langs->transnoentities("Currency" . $conf->currency)); - print '
' . $text . '.
'; - } else { - $text = $langs->trans("CompanyHasAbsoluteDiscount", price($absolute_discount), $langs->transnoentities("Currency" . $conf->currency)); - $text2 = $langs->trans("AbsoluteDiscountUse"); - print $form->textwithpicto($text, $text2); - } - } - } else { - // Discount available of type fixed amount (not credit note) - print '
'; - $form->form_remise_dispo($_SERVER["PHP_SELF"] . '?facid=' . $object->id, GETPOST('discountid'), 'remise_id', $soc->id, $absolute_discount, $filterabsolutediscount, $resteapayer, ' (' . $addabsolutediscount . ')'); - } - } else { - if ($absolute_creditnote > 0) // If not, link will be added later - { - if ($object->statut == Facture::STATUS_DRAFT && $object->type != Facture::TYPE_CREDIT_NOTE && $object->type != Facture::TYPE_DEPOSIT) - print ' (' . $addabsolutediscount . ')
'; - else - print '. '; - } else - print '. '; - } - // Is there credit notes availables ? - if ($absolute_creditnote > 0) - { - // If validated, we show link "add credit note to payment" - if ($object->statut != Facture::STATUS_VALIDATED || $object->type == Facture::TYPE_CREDIT_NOTE) { - if ($object->statut == 0 && $object->type != Facture::TYPE_DEPOSIT) { - $text = $langs->trans("CompanyHasCreditNote", price($absolute_creditnote), $langs->transnoentities("Currency" . $conf->currency)); - print $form->textwithpicto($text, $langs->trans("CreditNoteDepositUse")); - } else { - print $langs->trans("CompanyHasCreditNote", price($absolute_creditnote), $langs->transnoentities("Currency" . $conf->currency)) . '.'; - } - } else { // We can add a credit note on a down payment or standard invoice or situation invoice - // There is credit notes discounts available - if (! $absolute_discount) print '
'; - // $form->form_remise_dispo($_SERVER["PHP_SELF"].'?facid='.$object->id, 0, 'remise_id_for_payment', $soc->id, $absolute_creditnote, $filtercreditnote, $resteapayer); - $more=' ('.$addcreditnote. (($addcreditnote && $viewabsolutediscount) ? ' - ' : '') . $viewabsolutediscount . ')'; - $form->form_remise_dispo($_SERVER["PHP_SELF"] . '?facid=' . $object->id, 0, 'remise_id_for_payment', $soc->id, $absolute_creditnote, $filtercreditnote, 0, $more); // We allow credit note even if amount is higher - } - } - if (! $absolute_discount && ! $absolute_creditnote) { - print $langs->trans("CompanyHasNoAbsoluteDiscount"); - if ($object->statut == Facture::STATUS_DRAFT && $object->type != Facture::TYPE_CREDIT_NOTE && $object->type != Facture::TYPE_DEPOSIT) - print ' (' . $addabsolutediscount . ')
'; - else - print '. '; - } - // if ($object->statut == 0 && $object->type != 2 && $object->type != 3) - // { - // if (! $absolute_discount && ! $absolute_creditnote) print '
'; - // print '   -   '; - // print $addabsolutediscount; - // print '   -   '.$addcreditnote; // We disbale link to credit note - // } + print '
'; + $thirdparty = $soc; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?facid=' . $object->id); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print '
'; print $form->textwithpicto($langs->trans("Discount") . ':', $langs->trans("HelpEscompte"), - 1); - print '' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . ' 
' . price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye, 'MT')) . ' 
'; print $form->textwithpicto($langs->trans("Abandoned") . ':', $langs->trans("HelpAbandonBadCustomer"), - 1); - print '' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . ' 
' . price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye, 'MT')) . ' 
'; print $form->textwithpicto($langs->trans("ProductReturned") . ':', $langs->trans("HelpAbandonProductReturned"), - 1); - print '' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . ' 
' . price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye, 'MT')) . ' 
' . price($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye) . ' 
' . price(price2num($object->total_ttc - $creditnoteamount - $depositamount - $totalpaye, 'MT')) . ' 
".$langs->trans('NextDateToExecution').""; $date_next_execution = isset($date_next_execution) ? $date_next_execution : (GETPOST('remonth') ? dol_mktime(12, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')) : -1); print $form->select_date($date_next_execution, '', 1, 1, '', "add", 1, 1, 1); @@ -1185,7 +1165,7 @@ if ($action == 'create') $disableedit=1; $disablemove=1; $disableremove=1; - $ret = $object->printObjectLines('', $mysoc, $object->thirdparty, $lineid, 0); // No date selector for template invoice + $object->printObjectLines('', $mysoc, $object->thirdparty, $lineid, 0); // No date selector for template invoice } print "
\n"; @@ -1531,6 +1511,8 @@ else { print $form->editfieldval($langs->trans("NextDateToExecution"), 'date_when', $object->date_when, $object, $user->rights->facture->creer, 'day', $object->date_when, null, '', '', 0, 'strikeIfMaxNbGenReached'); } + //var_dump(dol_print_date($object->date_when+60, 'dayhour').' - '.dol_print_date($now, 'dayhour')); + if ($action != 'editdate_when' && $object->frequency > 0 && $object->date_when && $object->date_when < $now) print img_warning($langs->trans("Late")); print ''; print ''; diff --git a/htdocs/compta/facture/info.php b/htdocs/compta/facture/info.php index 00272fee9b3..2347178b05d 100644 --- a/htdocs/compta/facture/info.php +++ b/htdocs/compta/facture/info.php @@ -69,7 +69,7 @@ $morehtmlref='
'; $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); // Thirdparty -$morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); +$morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); // Project if (! empty($conf->projet->enabled)) { diff --git a/htdocs/compta/facture/invoicetemplate_list.php b/htdocs/compta/facture/invoicetemplate_list.php index d3a7c01b4c0..0a2b9f72d60 100644 --- a/htdocs/compta/facture/invoicetemplate_list.php +++ b/htdocs/compta/facture/invoicetemplate_list.php @@ -72,12 +72,12 @@ $search_montant_vat=GETPOST('search_montant_vat'); $search_montant_ttc=GETPOST('search_montant_ttc'); $search_payment_mode=GETPOST('search_payment_mode'); $search_payment_term=GETPOST('search_payment_term'); -$day=GETPOST('day'); -$year=GETPOST('year'); -$month=GETPOST('month'); -$day_date_when=GETPOST('day_date_when'); -$year_date_when=GETPOST('year_date_when'); -$month_date_when=GETPOST('month_date_when'); +$search_day=GETPOST('search_day','int'); +$search_year=GETPOST('search_year','int'); +$search_month=GETPOST('search_month','int'); +$search_day_date_when=GETPOST('search_day_date_when','int'); +$search_year_date_when=GETPOST('search_year_date_when','int'); +$search_month_date_when=GETPOST('search_month_date_when','int'); $search_recurring=GETPOST('search_recurring','int'); $search_frequency=GETPOST('search_frequency','alpha'); $search_unit_frequency=GETPOST('search_unit_frequency','alpha'); @@ -172,12 +172,12 @@ if (empty($reshook)) $search_montant_ttc=''; $search_payment_mode=''; $search_payment_term=''; - $day=''; - $year=''; - $month=''; - $day_date_when=''; - $year_date_when=''; - $month_date_when=''; + $search_day=''; + $search_year=''; + $search_month=''; + $search_day_date_when=''; + $search_year_date_when=''; + $search_month_date_when=''; $search_recurring=''; $search_frequency=''; $search_unit_frequency=''; @@ -239,38 +239,38 @@ if (! empty($search_payment_term) && $search_payment_term != '-1') $sql .= nat if ($search_recurring == '1') $sql .= ' AND f.frequency > 0'; if ($search_recurring == '0') $sql .= ' AND (f.frequency IS NULL or f.frequency = 0)'; if ($search_frequency != '') $sql .= natural_search('f.frequency', $search_frequency, 1); -if ($search_unit_frequency != '') $sql .= natural_search('f.unit_frequency', $search_unit_frequency); +if ($search_unit_frequency != '') $sql .= ' AND f.frequency > 0'.natural_search('f.unit_frequency', $search_unit_frequency); if ($search_status != '' && $search_status >= -1) { if ($search_status == 0) $sql.= ' AND frequency = 0 AND suspended = 0'; if ($search_status == 1) $sql.= ' AND frequency != 0 AND suspended = 0'; if ($search_status == -1) $sql.= ' AND suspended = 1'; } -if ($month > 0) +if ($search_month > 0) { - if ($year > 0 && empty($day)) - $sql.= " AND f.date_last_gen BETWEEN '".$db->idate(dol_get_first_day($year,$month,false))."' AND '".$db->idate(dol_get_last_day($year,$month,false))."'"; - else if ($year > 0 && ! empty($day)) - $sql.= " AND f.date_last_gen BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month, $day, $year))."' AND '".$db->idate(dol_mktime(23, 59, 59, $month, $day, $year))."'"; + if ($search_year > 0 && empty($search_day)) + $sql.= " AND f.date_last_gen BETWEEN '".$db->idate(dol_get_first_day($search_year,$search_month,false))."' AND '".$db->idate(dol_get_last_day($search_year,$search_month,false))."'"; + else if ($search_year > 0 && ! empty($search_day)) + $sql.= " AND f.date_last_gen BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $search_month, $search_day, $search_year))."' AND '".$db->idate(dol_mktime(23, 59, 59, $search_month, $search_day, $search_year))."'"; else - $sql.= " AND date_format(f.date_last_gen, '%m') = '".$month."'"; + $sql.= " AND date_format(f.date_last_gen, '%m') = '".$db->escape($search_month)."'"; } -else if ($year > 0) +else if ($search_year > 0) { - $sql.= " AND f.date_last_gen BETWEEN '".$db->idate(dol_get_first_day($year,1,false))."' AND '".$db->idate(dol_get_last_day($year,12,false))."'"; + $sql.= " AND f.date_last_gen BETWEEN '".$db->idate(dol_get_first_day($search_year,1,false))."' AND '".$db->idate(dol_get_last_day($search_year,12,false))."'"; } -if ($month_date_when > 0) +if ($search_month_date_when > 0) { - if ($year_date_when > 0 && empty($day_date_when)) - $sql.= " AND f.date_when BETWEEN '".$db->idate(dol_get_first_day($year_date_when,$month_date_when,false))."' AND '".$db->idate(dol_get_last_day($year_date_when,$month_date_when,false))."'"; - else if ($year_date_when > 0 && ! empty($day_date_when)) - $sql.= " AND f.date_date_when_reglement BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month_date_when, $day_date_when, $year_date_when))."' AND '".$db->idate(dol_mktime(23, 59, 59, $month_date_when, $day_date_when, $year_date_when))."'"; + if ($search_year_date_when > 0 && empty($search_day_date_when)) + $sql.= " AND f.date_when BETWEEN '".$db->idate(dol_get_first_day($search_year_date_when,$search_month_date_when,false))."' AND '".$db->idate(dol_get_last_day($search_year_date_when,$search_month_date_when,false))."'"; + else if ($search_year_date_when > 0 && ! empty($search_day_date_when)) + $sql.= " AND f.date_date_when_reglement BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $search_month_date_when, $search_day_date_when, $search_year_date_when))."' AND '".$db->idate(dol_mktime(23, 59, 59, $search_month_date_when, $search_day_date_when, $search_year_date_when))."'"; else - $sql.= " AND date_format(f.date_when, '%m') = '".$month_date_when."'"; + $sql.= " AND date_format(f.date_when, '%m') = '".$db->escape($search_month_date_when)."'"; } -else if ($year_date_when > 0) +else if ($search_year_date_when > 0) { - $sql.= " AND f.date_when BETWEEN '".$db->idate(dol_get_first_day($year_date_when,1,false))."' AND '".$db->idate(dol_get_last_day($year_date_when,12,false))."'"; + $sql.= " AND f.date_when BETWEEN '".$db->idate(dol_get_first_day($search_year_date_when,1,false))."' AND '".$db->idate(dol_get_last_day($search_year_date_when,12,false))."'"; } $nbtotalofrecords = ''; @@ -291,26 +291,26 @@ if ($resql) $param=''; if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); - if ($socid) $param.='&socid='.urlencode($socid); - if ($day) $param.='&day='.urlencode($day); - if ($month) $param.='&month='.urlencode($month); - if ($year) $param.='&year=' .urlencode($year); - if ($day_date_when) $param.='&day_date_when='.urlencode($day_date_when); - if ($month_date_when) $param.='&month_date_when='.urlencode($month_date_when); - if ($year_date_when) $param.='&year_date_when=' .urlencode($year_date_when); - if ($search_ref) $param.='&search_ref=' .urlencode($search_ref); - if ($search_societe) $param.='&search_societe=' .urlencode($search_societe); + if ($socid) $param.='&socid='.urlencode($socid); + if ($search_day) $param.='&search_day='.urlencode($search_day); + if ($search_month) $param.='&search_month='.urlencode($search_month); + if ($search_year) $param.='&search_year=' .urlencode($search_year); + if ($search_day_date_when) $param.='&search_day_date_when='.urlencode($search_day_date_when); + if ($search_month_date_when) $param.='&search_month_date_when='.urlencode($search_month_date_when); + if ($search_year_date_when) $param.='&search_year_date_when=' .urlencode($search_year_date_when); + if ($search_ref) $param.='&search_ref=' .urlencode($search_ref); + if ($search_societe) $param.='&search_societe=' .urlencode($search_societe); if ($search_montant_ht != '') $param.='&search_montant_ht=' .urlencode($search_montant_ht); if ($search_montant_vat != '') $param.='&search_montant_vat='.urlencode($search_montant_vat); if ($search_montant_ttc != '') $param.='&search_montant_ttc='.urlencode($search_montant_ttc); if ($search_payment_mode != '') $param.='&search_payment_mode='.urlencode($search_payment_mode); if ($search_payment_type != '') $param.='&search_payment_type='.urlencode($search_payment_type); if ($search_recurring != '' && $search_recurrning != '-1') $param.='&search_recurring=' .urlencode($search_recurring); - if ($search_frequency > 0) $param.='&search_frequency=' .urlencode($search_frequency); - if ($search_unit_frequency > 0) $param.='&search_unit_frequency='.urlencode($search_unit_frequency); + if ($search_frequency > 0) $param.='&search_frequency=' .urlencode($search_frequency); + if ($search_unit_frequency != '') $param.='&search_unit_frequency='.urlencode($search_unit_frequency); if ($search_status != '') $param.='&search_status='.urlencode($search_status); - if ($option) $param.="&option=".urlencode($option); - if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); + if ($option) $param.="&option=".urlencode($option); + if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; @@ -419,19 +419,19 @@ if ($resql) // Date invoice if (! empty($arrayfields['f.date_last_gen']['checked'])) { - print ''; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - $formother->select_year($year?$year:-1,'year',1, 20, 5); + print ''; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + $formother->select_year($search_year?$search_year:-1,'search_year',1, 20, 5, 0, 0, '', 'witdhauto valignmiddle'); print ''; } - // Date due + // Date next generation if (! empty($arrayfields['f.date_when']['checked'])) { - print ''; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - $formother->select_year($year_date_when?$year_date_when:-1,'year_date_when',1, 20, 5); + print ''; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + $formother->select_year($search_year_date_when?$search_year_date_when:-1,'search_year_date_when',1, 20, 5, 0, 0, '', 'witdhauto valignmiddle'); print ''; } // Extra fields @@ -583,21 +583,26 @@ if ($resql) if (! empty($arrayfields['f.nb_gen_done']['checked'])) { print ''; - print ($objp->frequency ? $objp->nb_gen_done.($objp->nb_gen_max>0?' / '. $objp->nb_gen_max:'') : ''.$langs->trans('NA').''); + print ($objp->frequency > 0 ? $objp->nb_gen_done.($objp->nb_gen_max>0?' / '. $objp->nb_gen_max:'') : ''.$langs->trans('NA').''); print ''; if (! $i) $totalarray['nbfield']++; } + // Date last generation if (! empty($arrayfields['f.date_last_gen']['checked'])) { print ''; - print ($objp->frequency ? dol_print_date($db->jdate($objp->date_last_gen),'day') : ''.$langs->trans('NA').''); + print ($objp->frequency > 0 ? dol_print_date($db->jdate($objp->date_last_gen),'day') : ''.$langs->trans('NA').''); print ''; if (! $i) $totalarray['nbfield']++; } + // Date next generation if (! empty($arrayfields['f.date_when']['checked'])) { print ''; + print '
'; print ($objp->frequency ? ($invoicerectmp->isMaxNbGenReached()?'':'').dol_print_date($db->jdate($objp->date_when),'day').($invoicerectmp->isMaxNbGenReached()?'':'') : ''.$langs->trans('NA').''); + if ($objp->frequency > 0 && $db->jdate($objp->date_when) && $db->jdate($objp->date_when) < $now) print img_warning($langs->trans("Late")); + print '
'; print ''; if (! $i) $totalarray['nbfield']++; } @@ -633,7 +638,7 @@ if ($resql) } else { - print $langs->trans("DateIsNotEnough"); + print $form->textwithpicto('', $langs->trans("DateIsNotEnough")); } } else diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php index 5914f2f06e9..8845d0bc8d7 100644 --- a/htdocs/compta/facture/list.php +++ b/htdocs/compta/facture/list.php @@ -66,6 +66,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'invoicelist'; $lineid=GETPOST('lineid','int'); $userid=GETPOST('userid','int'); @@ -89,14 +90,14 @@ $search_country=GETPOST("search_country",'int'); $search_type_thirdparty=GETPOST("search_type_thirdparty",'int'); $search_user = GETPOST('search_user','int'); $search_sale = GETPOST('search_sale','int'); -$day = GETPOST('day','int'); -$month = GETPOST('month','int'); -$year = GETPOST('year','int'); -$day_lim = GETPOST('day_lim','int'); -$month_lim = GETPOST('month_lim','int'); -$year_lim = GETPOST('year_lim','int'); +$search_day = GETPOST('search_day','int'); +$search_month = GETPOST('search_month','int'); +$search_year = GETPOST('search_year','int'); +$search_day_lim = GETPOST('search_day_lim','int'); +$search_month_lim = GETPOST('search_month_lim','int'); +$search_year_lim = GETPOST('search_year_lim','int'); -$option = GETPOST('option'); +$option = GETPOST('search_option'); if ($option == 'late') { $search_status = '1'; } @@ -114,9 +115,6 @@ if (! $sortfield) $sortfield='f.datef'; $pageprev = $page - 1; $pagenext = $page + 1; -// Initialize technical object to manage context to save list fields -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'invoicelist'; - // Security check $fieldid = (! empty($ref)?'facnumber':'rowid'); if (! empty($user->societe_id)) $socid=$user->societe_id; @@ -129,6 +127,7 @@ $object=new Facture($db); $now=dol_now(); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$object = new Facture($db); $hookmanager->initHooks(array('invoicelist')); $extrafields = new ExtraFields($db); @@ -219,14 +218,14 @@ if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter','a $search_type=''; $search_country=''; $search_type_thirdparty=''; - $day=''; - $year=''; - $month=''; + $search_day=''; + $search_year=''; + $search_month=''; $option=''; $filter=''; - $day_lim=''; - $year_lim=''; - $month_lim=''; + $search_day_lim=''; + $search_year_lim=''; + $search_month_lim=''; $toselect=''; $search_array_options=array(); } @@ -276,7 +275,7 @@ if ($massaction == 'withdrawrequest') $error++; setEventMessages($objecttmp->ref.' '.$langs->trans("AmountMustBePositive"), $objecttmp->errors, 'errors'); } - if(!($objecttmp->statut > Facture::STATUS_DRAFT)){ + if (!($objecttmp->statut > Facture::STATUS_DRAFT)){ $error++; setEventMessages($objecttmp->ref.' '.$langs->trans("Draft"), $objecttmp->errors, 'errors'); } @@ -298,11 +297,11 @@ if ($massaction == 'withdrawrequest') $numprlv = $db->num_rows($result_sql); } - if($numprlv>0){ + if ($numprlv>0){ $error++; setEventMessages($objecttmp->ref.' '.$langs->trans("RequestAlreadyDone"), $objecttmp->errors, 'errors'); } - if(!empty($objecttmp->mode_reglement_id ) && $objecttmp->mode_reglement_id != 3){ + if (!empty($objecttmp->mode_reglement_code ) && $objecttmp->mode_reglement_code != 'PRE'){ $error++; setEventMessages($objecttmp->ref.' '.$langs->trans("BadPaymentMethod"), $objecttmp->errors, 'errors'); } @@ -366,7 +365,7 @@ $sql.= ' s.rowid as socid, s.nom as name, s.email, s.town, s.zip, s.fk_pays, s.c $sql.= " typent.code as typent_code,"; $sql.= " state.code_departement as state_code, state.nom as state_name,"; $sql.= " country.code as country_code,"; -$sql.= " p.rowid as project_id, p.ref as project_ref"; +$sql.= " p.rowid as project_id, p.ref as project_ref, p.title as project_label"; // We need dynamount_payed to be able to sort on status (value is surely wrong because we can count several lines several times due to other left join or link with contacts. But what we need is just 0 or > 0) // TODO Better solution to be able to sort on already payed or remain to pay is to store amount_payed in a denormalized field. if (! $sall) $sql.= ', SUM(pf.amount) as dynamount_payed'; @@ -444,31 +443,31 @@ if ($search_status != '' && $search_status >= 0) if ($search_status == '3') $sql.=" AND f.fk_statut = 3"; // abandonned } if ($search_paymentmode > 0) $sql .= " AND f.fk_mode_reglement = ".$db->escape($search_paymentmode); -if ($month > 0) +if ($search_month > 0) { - if ($year > 0 && empty($day)) - $sql.= " AND f.datef BETWEEN '".$db->idate(dol_get_first_day($year,$month,false))."' AND '".$db->idate(dol_get_last_day($year,$month,false))."'"; - else if ($year > 0 && ! empty($day)) - $sql.= " AND f.datef BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month, $day, $year))."' AND '".$db->idate(dol_mktime(23, 59, 59, $month, $day, $year))."'"; + if ($search_year > 0 && empty($search_day)) + $sql.= " AND f.datef BETWEEN '".$db->idate(dol_get_first_day($search_year,$search_month,false))."' AND '".$db->idate(dol_get_last_day($search_year,$search_month,false))."'"; + else if ($search_year > 0 && ! empty($search_day)) + $sql.= " AND f.datef BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $search_month, $search_day, $search_year))."' AND '".$db->idate(dol_mktime(23, 59, 59, $search_month, $search_day, $serch_year))."'"; else $sql.= " AND date_format(f.datef, '%m') = '".$month."'"; } -else if ($year > 0) +else if ($search_year > 0) { - $sql.= " AND f.datef BETWEEN '".$db->idate(dol_get_first_day($year,1,false))."' AND '".$db->idate(dol_get_last_day($year,12,false))."'"; + $sql.= " AND f.datef BETWEEN '".$db->idate(dol_get_first_day($search_year,1,false))."' AND '".$db->idate(dol_get_last_day($search_year,12,false))."'"; } if ($month_lim > 0) { - if ($year_lim > 0 && empty($day_lim)) - $sql.= " AND f.date_lim_reglement BETWEEN '".$db->idate(dol_get_first_day($year_lim,$month_lim,false))."' AND '".$db->idate(dol_get_last_day($year_lim,$month_lim,false))."'"; - else if ($year_lim > 0 && ! empty($day_lim)) - $sql.= " AND f.date_lim_reglement BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month_lim, $day_lim, $year_lim))."' AND '".$db->idate(dol_mktime(23, 59, 59, $month_lim, $day_lim, $year_lim))."'"; + if ($search_year_lim > 0 && empty($search_day_lim)) + $sql.= " AND f.date_lim_reglement BETWEEN '".$db->idate(dol_get_first_day($search_year_lim,$search_month_lim,false))."' AND '".$db->idate(dol_get_last_day($search_year_lim,$search_month_lim,false))."'"; + else if ($search_year_lim > 0 && ! empty($search_day_lim)) + $sql.= " AND f.date_lim_reglement BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $search_month_lim, $search_day_lim, $search_year_lim))."' AND '".$db->idate(dol_mktime(23, 59, 59, $search_month_lim, $search_day_lim, $search_year_lim))."'"; else - $sql.= " AND date_format(f.date_lim_reglement, '%m') = '".$db->escape($month_lim)."'"; + $sql.= " AND date_format(f.date_lim_reglement, '%m') = '".$db->escape($search_month_lim)."'"; } -else if ($year_lim > 0) +else if ($search_year_lim > 0) { - $sql.= " AND f.date_lim_reglement BETWEEN '".$db->idate(dol_get_first_day($year_lim,1,false))."' AND '".$db->idate(dol_get_last_day($year_lim,12,false))."'"; + $sql.= " AND f.date_lim_reglement BETWEEN '".$db->idate(dol_get_first_day($search_year_lim,1,false))."' AND '".$db->idate(dol_get_last_day($search_year_lim,12,false))."'"; } if ($option == 'late') $sql.=" AND f.date_lim_reglement < '".$db->idate(dol_now() - $conf->facture->client->warning_delay)."'"; if ($search_sale > 0) $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$search_sale; @@ -539,12 +538,12 @@ if ($resql) if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; if ($sall) $param.='&sall='.urlencode($sall); - if ($day) $param.='&day='.urlencode($day); - if ($month) $param.='&month='.urlencode($month); - if ($year) $param.='&year=' .urlencode($year); - if ($day_lim) $param.='&day_lim='.urlencode($day_lim); - if ($month_lim) $param.='&month_lim='.urlencode($month_lim); - if ($year_lim) $param.='&year_lim=' .urlencode($year_lim); + if ($search_day) $param.='&search_day='.urlencode($search_day); + if ($search_month) $param.='&search_month='.urlencode($search_month); + if ($search_year) $param.='&search_year=' .urlencode($search_year); + if ($search_day_lim) $param.='&search_day_lim='.urlencode($search_day_lim); + if ($search_month_lim) $param.='&search_month_lim='.urlencode($search_month_lim); + if ($search_year_lim) $param.='&search_year_lim=' .urlencode($search_year_lim); if ($search_ref) $param.='&search_ref=' .urlencode($search_ref); if ($search_refcustomer) $param.='&search_refcustomer=' .urlencode($search_refcustomer); if ($search_type != '') $param.='&search_type='.urlencode($search_type); @@ -559,9 +558,9 @@ if ($resql) if ($search_montant_ttc != '') $param.='&search_montant_ttc='.urlencode($search_montant_ttc); if ($search_status != '') $param.='&search_status='.urlencode($search_status); if ($search_paymentmode > 0) $param.='search_paymentmode='.urlencode($search_paymentmode); - if ($show_files) $param.='&show_files=' .$show_files; - if ($option) $param.="&option=".$option; - if ($optioncss != '') $param.='&optioncss='.$optioncss; + if ($show_files) $param.='&show_files='.urlencode($show_files); + if ($option) $param.="&search_option=".urlencode($option); + if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; @@ -590,6 +589,12 @@ if ($resql) if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); + $newcardbutton=''; + if($user->rights->facture->creer) + { + $newcardbutton=''.$langs->trans('NewBill').''; + } + $i = 0; print ''."\n"; @@ -603,7 +608,7 @@ if ($resql) print ''; print ''; - print_barre_liste($langs->trans('BillsCustomers').' '.($socid?' '.$soc->name:''), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_accountancy.png', 0, '', '', $limit); + print_barre_liste($langs->trans('BillsCustomers').' '.($socid?' '.$soc->name:''), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_accountancy.png', 0, $newcardbutton, '', $limit); $topicmail="SendBillRef"; $modelmail="facture_send"; @@ -701,20 +706,20 @@ if ($resql) // Date invoice if (! empty($arrayfields['f.date']['checked'])) { - print ''; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - $formother->select_year($year?$year:-1,'year',1, 20, 5); + print ''; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + $formother->select_year($search_year?$search_year:-1,'search_year',1, 20, 5, 0, 0, '', 'widthauto valignmiddle'); print ''; } // Date due if (! empty($arrayfields['f.date_lim_reglement']['checked'])) { - print ''; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - $formother->select_year($year_lim?$year_lim:-1,'year_lim',1, 20, 5); - print '
'.$langs->trans("Late"); + print ''; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + $formother->select_year($search_year_lim?$search_year_lim:-1,'search_year_lim',1, 20, 5, 0, 0, '', 'widthauto valignmiddle'); + print '
'.$langs->trans("Late"); print ''; } // Project @@ -756,7 +761,7 @@ if ($resql) if (! empty($arrayfields['f.fk_mode_reglement']['checked'])) { print ''; - $form->select_types_paiements($search_paymentmode, 'search_paymentmode', '', 0, 0, 1, 10); + $form->select_types_paiements($search_paymentmode, 'search_paymentmode', '', 0, 1, 1, 10); print ''; } if (! empty($arrayfields['f.total_ht']['checked'])) @@ -775,14 +780,14 @@ if ($resql) } if (! empty($arrayfields['f.total_localtax1']['checked'])) { - // Amount + // Localtax1 print ''; print ''; print ''; } if (! empty($arrayfields['f.total_localtax2']['checked'])) { - // Amount + // Localtax2 print ''; print ''; print ''; @@ -862,7 +867,7 @@ if ($resql) // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields - $parameters=array('arrayfields'=>$arrayfields); + $parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['f.datec']['checked'])) print_liste_field_titre($arrayfields['f.datec']['label'],$_SERVER["PHP_SELF"],"f.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); @@ -979,6 +984,7 @@ if ($resql) { $projectstatic->id=$obj->project_id; $projectstatic->ref=$obj->project_ref; + $projectstatic->title=$obj->project_label; print $projectstatic->getNomUrl(1); } print ''; diff --git a/htdocs/compta/facture/note.php b/htdocs/compta/facture/note.php index 755ac413743..356ba3916a0 100644 --- a/htdocs/compta/facture/note.php +++ b/htdocs/compta/facture/note.php @@ -92,7 +92,7 @@ if ($id > 0 || ! empty($ref)) $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1,'customer'); // Project if (! empty($conf->projet->enabled)) { diff --git a/htdocs/compta/facture/prelevement.php b/htdocs/compta/facture/prelevement.php index 989e742afee..e665eaf77d1 100644 --- a/htdocs/compta/facture/prelevement.php +++ b/htdocs/compta/facture/prelevement.php @@ -140,8 +140,16 @@ if ($object->id > 0) if ($object->paye) $resteapayer=0; $resteapayeraffiche=$resteapayer; - $absolute_discount=$object->thirdparty->getAvailableDiscounts('','fk_facture_source IS NULL'); - $absolute_creditnote=$object->thirdparty->getAvailableDiscounts('','fk_facture_source IS NOT NULL'); + if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + } else { + $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')"; + $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')"; + } + + $absolute_discount=$object->thirdparty->getAvailableDiscounts('',$filterabsolutediscount); + $absolute_creditnote=$object->thirdparty->getAvailableDiscounts('',$filtercreditnote); $absolute_discount=price2num($absolute_discount,'MT'); $absolute_creditnote=price2num($absolute_creditnote,'MT'); @@ -253,61 +261,13 @@ if ($object->id > 0) // Discounts print ''.$langs->trans('Discounts').''; - if ($object->thirdparty->remise_percent) print $langs->trans("CompanyHasRelativeDiscount",$object->thirdparty->remise_percent); - else print $langs->trans("CompanyHasNoRelativeDiscount"); - print '. '; - if ($absolute_discount > 0) - { - if ($object->statut > Facture::STATUS_DRAFT || $object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT) - { - if ($object->statut == Facture::STATUS_DRAFT) - { - print $langs->trans("CompanyHasAbsoluteDiscount",price($absolute_discount),$langs->transnoentities("Currency".$conf->currency)).'. '; - } - else - { - if ($object->statut < Facture::STATUS_VALIDATED || $object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT) - { - $text=$langs->trans("CompanyHasAbsoluteDiscount",price($absolute_discount),$langs->transnoentities("Currency".$conf->currency)); - print '
'.$text.'.
'; - } - else - { - $text=$langs->trans("CompanyHasAbsoluteDiscount",price($absolute_discount),$langs->transnoentities("Currency".$conf->currency)); - $text2=$langs->trans("AbsoluteDiscountUse"); - print $form->textwithpicto($text,$text2); - } - } - } - else - { - // Remise dispo de type non avoir - $filter='fk_facture_source IS NULL'; - print '
'; - $form->form_remise_dispo($_SERVER["PHP_SELF"].'?id='.$object->id,0,'remise_id',$object->thirdparty->id,$absolute_discount,$filter,$resteapayer,'',1); - } - } - if ($absolute_creditnote > 0) - { - // If validated, we show link "add credit note to payment" - if ($object->statut != Facture::STATUS_VALIDATED || $object->type == Facture::TYPE_DEPOSIT || $object->type == Facture::TYPE_CREDIT_NOTE) - { - if ($object->statut == Facture::STATUS_DRAFT && $object->type != Facture::TYPE_DEPOSIT) - { - $text=$langs->trans("CompanyHasCreditNote",price($absolute_creditnote),$langs->transnoentities("Currency".$conf->currency)); - print $form->textwithpicto($text,$langs->trans("CreditNoteDepositUse")); - } - else print $langs->trans("CompanyHasCreditNote",price($absolute_creditnote),$langs->transnoentities("Currency".$conf->currency)).'.'; - } - else - { - // Remise dispo de type avoir - $filter='fk_facture_source IS NOT NULL'; - if (! $absolute_discount) print '
'; - $form->form_remise_dispo($_SERVER["PHP_SELF"].'?id='.$object->id,0,'remise_id_for_payment',$object->thirdparty->id,$absolute_creditnote,$filter,$resteapayer,'',1); - } - } - if (! $absolute_discount && ! $absolute_creditnote) print $langs->trans("CompanyHasNoAbsoluteDiscount").'.'; + + $thirdparty = $object->thirdparty; + $discount_type = 0; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?facid=' . $object->id); + $cannotApplyDiscount = 1; + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print ''; // Date invoice diff --git a/htdocs/compta/facture/tpl/linkedobjectblock.tpl.php b/htdocs/compta/facture/tpl/linkedobjectblock.tpl.php index ca3ec7c89c0..3964800fd2b 100644 --- a/htdocs/compta/facture/tpl/linkedobjectblock.tpl.php +++ b/htdocs/compta/facture/tpl/linkedobjectblock.tpl.php @@ -47,12 +47,12 @@ foreach($linkedObjectBlock as $key => $objectlink) $trclass=($var?'pair':'impair'); if ($ilink == count($linkedObjectBlock) && empty($noMoreLinkedObjectBlockAfter) && count($linkedObjectBlock) <= 1) $trclass.=' liste_sub_total'; ?> - - trans("CustomerInvoice"); ?> - getNomUrl(1); ?> - ref_client; ?> - date,'day'); ?> - " data-element="element; ?>" data-id="id; ?>" > + trans("CustomerInvoice"); ?> + getNomUrl(1); ?> + ref_client; ?> + date,'day'); ?> + rights->facture->lire) { $sign = 1; if ($object->type == Facture::TYPE_CREDIT_NOTE) $sign = -1; @@ -66,8 +66,8 @@ foreach($linkedObjectBlock as $key => $objectlink) echo ''.price($objectlink->total_ht).''; } } ?> - getLibStatut(3); ?> - ">transnoentitiesnoconv("RemoveLink")); ?> + getLibStatut(3); ?> + ">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?> 1) } ?> - \ No newline at end of file + diff --git a/htdocs/compta/facture/tpl/linkedobjectblockForRec.tpl.php b/htdocs/compta/facture/tpl/linkedobjectblockForRec.tpl.php index e0f132e2ad5..91056746565 100644 --- a/htdocs/compta/facture/tpl/linkedobjectblockForRec.tpl.php +++ b/htdocs/compta/facture/tpl/linkedobjectblockForRec.tpl.php @@ -62,7 +62,7 @@ foreach($linkedObjectBlock as $key => $objectlink) print $objectlink->getLibStatut(3); ?> - ">transnoentitiesnoconv("RemoveLink")); ?> + ">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?> facture->enabled) && $user->rights->facture->lire) print $facturestatic->getNomUrl(1,''); print ''; print ''; - print $companystatic->getNomUrl(1,'',16); + print $companystatic->getNomUrl(1,'customer',16); print ''; print ''.price($obj->total_ttc).''; print ''; diff --git a/htdocs/compta/localtax/card.php b/htdocs/compta/localtax/card.php index 317d3ae813d..ad409145478 100644 --- a/htdocs/compta/localtax/card.php +++ b/htdocs/compta/localtax/card.php @@ -54,7 +54,7 @@ $hookmanager->initHooks(array('localtaxvatcard','globalcard')); //add payment of localtax if($_POST["cancel"] == $langs->trans("Cancel")){ - header("Location: reglement.php?localTaxType=".$lttype); + header("Location: list.php?localTaxType=".$lttype); exit; } @@ -78,7 +78,7 @@ if ($action == 'add' && $_POST["cancel"] <> $langs->trans("Cancel")) if ($ret > 0) { $db->commit(); - header("Location: reglement.php?localTaxType=".$lttype); + header("Location: list.php?localTaxType=".$lttype); exit; } else @@ -111,7 +111,7 @@ if ($action == 'delete') if ($result >= 0) { $db->commit(); - header("Location: ".DOL_URL_ROOT.'/compta/localtax/reglement.php?localTaxType='.$localtax->ltt); + header("Location: ".DOL_URL_ROOT.'/compta/localtax/list.php?localTaxType='.$localtax->ltt); exit; } else @@ -169,7 +169,7 @@ if ($action == 'create') print ''; print ""; - print ''; diff --git a/htdocs/compta/localtax/class/localtax.class.php b/htdocs/compta/localtax/class/localtax.class.php index 570942590c2..79a88d11c4d 100644 --- a/htdocs/compta/localtax/class/localtax.class.php +++ b/htdocs/compta/localtax/class/localtax.class.php @@ -132,7 +132,7 @@ class Localtax extends CommonObject * @param int $notrigger 0=no, 1=yes (no update trigger) * @return int <0 if KO, >0 if OK */ - function update($user=null, $notrigger=0) + function update(User $user, $notrigger=0) { global $conf, $langs; @@ -149,8 +149,8 @@ class Localtax extends CommonObject $this->db->begin(); // Update request - $sql = "UPDATE ".MAIN_DB_PREFIX."localtax SET"; - $sql.= " localtaxtype=".$this->ltt.","; + $sql = "UPDATE ".MAIN_DB_PREFIX."localtax SET"; + $sql.= " localtaxtype=".$this->ltt.","; $sql.= " tms='".$this->db->idate($this->tms)."',"; $sql.= " datep='".$this->db->idate($this->datep)."',"; $sql.= " datev='".$this->db->idate($this->datev)."',"; @@ -160,7 +160,7 @@ class Localtax extends CommonObject $sql.= " fk_bank=".$this->fk_bank.","; $sql.= " fk_user_creat=".$this->fk_user_creat.","; $sql.= " fk_user_modif=".$this->fk_user_modif; - $sql.= " WHERE rowid=".$this->id; + $sql.= " WHERE rowid=".$this->id; dol_syslog(get_class($this)."::update", LOG_DEBUG); $resql = $this->db->query($sql); @@ -255,12 +255,12 @@ class Localtax extends CommonObject } - /** - * Delete object in database - * - * @param User $user User that delete - * @return int <0 if KO, >0 if OK - */ + /** + * Delete object in database + * + * @param User $user User that delete + * @return int <0 if KO, >0 if OK + */ function delete($user) { // Call trigger @@ -285,11 +285,11 @@ class Localtax extends CommonObject /** - * Initialise an instance with random values. - * Used to build previews or test instances. - * id must be 0 if object instance is a specimen. - * - * @return void + * Initialise an instance with random values. + * Used to build previews or test instances. + * id must be 0 if object instance is a specimen. + * + * @return void */ function initAsSpecimen() { diff --git a/htdocs/compta/localtax/clients.php b/htdocs/compta/localtax/clients.php index 70d52c3d9d3..3dc3e2777c2 100644 --- a/htdocs/compta/localtax/clients.php +++ b/htdocs/compta/localtax/clients.php @@ -28,15 +28,12 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/tax.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/compta/localtax/class/localtax.class.php'; -$langs->load("bills"); -$langs->load("compta"); -$langs->load("companies"); -$langs->load("products"); +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); $local=GETPOST('localTaxType', 'int'); // Date range -$year=GETPOST("year"); +$year=GETPOST("year","int"); if (empty($year)) { $year_current = strftime("%Y",dol_now()); @@ -45,43 +42,48 @@ if (empty($year)) $year_current = $year; $year_start = $year; } -$date_start=dol_mktime(0,0,0,$_REQUEST["date_startmonth"],$_REQUEST["date_startday"],$_REQUEST["date_startyear"]); -$date_end=dol_mktime(23,59,59,$_REQUEST["date_endmonth"],$_REQUEST["date_endday"],$_REQUEST["date_endyear"]); +$date_start=dol_mktime(0,0,0,GETPOST("date_startmonth"),GETPOST("date_startday"),GETPOST("date_startyear")); +$date_end=dol_mktime(23,59,59,GETPOST("date_endmonth"),GETPOST("date_endday"),GETPOST("date_endyear")); // Quarter if (empty($date_start) || empty($date_end)) // We define date_start and date_end { $q=GETPOST("q"); if (empty($q)) { - if (isset($_REQUEST["month"])) { $date_start=dol_get_first_day($year_start,$_REQUEST["month"],false); $date_end=dol_get_last_day($year_start,$_REQUEST["month"],false); } + if (GETPOST("month")) { $date_start=dol_get_first_day($year_start,GETPOST("month"),false); $date_end=dol_get_last_day($year_start,GETPOST("month"),false); } else { - $month_current = strftime("%m",dol_now()); - if ($month_current >= 10) $q=4; - elseif ($month_current >= 7) $q=3; - elseif ($month_current >= 4) $q=2; - else $q=1; + $date_start=dol_get_first_day($year_start,empty($conf->global->SOCIETE_FISCAL_MONTH_START)?1:$conf->global->SOCIETE_FISCAL_MONTH_START,false); + if (empty($conf->global->MAIN_INFO_VAT_RETURN) || $conf->global->MAIN_INFO_VAT_RETURN == 2) $date_end=dol_time_plus_duree($date_start, 3, 'm') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 3) $date_end=dol_time_plus_duree($date_start, 1, 'y') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 1) $date_end=dol_time_plus_duree($date_start, 1, 'm') - 1; } } - if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } - if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } - if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } - if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + else + { + if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } + if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } + if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } + if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + } } -$min = GETPOST("min"); +$min = price2num(GETPOST("min","alpha")); if (empty($min)) $min = 0; // Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit +// 0=normal, 1=option vat for services is on debit, 2=option on payments for products $modetax = $conf->global->TAX_MODE; -if (isset($_REQUEST["modetax"])) $modetax=$_REQUEST["modetax"]; +if (GETPOSTISSET("modetax")) $modetax=GETPOST("modetax",'int'); +if (empty($modetax)) $modetax=0; // Security check $socid = GETPOST('socid','int'); if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); + + /* * View */ @@ -98,6 +100,9 @@ foreach($listofparams as $param) llxHeader('','','','',0,0,'','',$morequerystring); + +$name=$langs->transcountry($local==1?"LT1ReportByCustomers":"LT2ReportByCustomers", $mysoc->country_code); + $fsearch.='
'; $fsearch.=' '; $fsearch.=' '; @@ -108,7 +113,6 @@ $calc=$conf->global->MAIN_INFO_LOCALTAX_CALC.$local; // Affiche en-tete du rapport if ($calc==0 || $calc==1) // Calculate on invoice for goods and services { - $nom=$langs->transcountry($local==1?"LT1ReportByCustomersInInputOutputMode":"LT2ReportByCustomersInInputOutputMode",$mysoc->country_code); $calcmode=$calc==0?$langs->trans("CalcModeLT".$local):$langs->trans("CalcModeLT".$local."Rec"); $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRulesLT",DOL_URL_ROOT.'/admin/company.php').')'; $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); @@ -126,7 +130,6 @@ if ($calc==0 || $calc==1) // Calculate on invoice for goods and services } if ($calc==2) // Invoice for goods, payment for services { - $nom=$langs->transcountry($local==1?"LT1ReportByCustomersInInputOutputMode":"LT2ReportByCustomersInInputOutputMode",$mysoc->country_code); $calcmode=$langs->trans("CalcModeLT2Debt"); $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRulesLT",DOL_URL_ROOT.'/admin/company.php').')'; $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); diff --git a/htdocs/compta/localtax/index.php b/htdocs/compta/localtax/index.php index 5d17688fc68..39d8a1dfa6a 100644 --- a/htdocs/compta/localtax/index.php +++ b/htdocs/compta/localtax/index.php @@ -1,6 +1,7 @@ * Copyright (C) 2014 Ferran Marcet + * Copyright (C) 2018 Laurent Destailleur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,45 +23,67 @@ * \brief Index page of IRPF reports */ require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/report.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/tax.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; -$langs->load("other"); -$langs->load("compta"); -$langs->load("banks"); -$langs->load("bills"); +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); $localTaxType=GETPOST('localTaxType', 'int'); +// Date range $year=GETPOST("year","int"); -if ($year == 0) +if (empty($year)) { - $year_current = strftime("%Y",time()); - $year_start = $year_current; + $year_current = strftime("%Y",dol_now()); + $year_start = $year_current; } else { - $year_current = $year; - $year_start = $year; + $year_current = $year; + $year_start = $year; +} +$date_start=dol_mktime(0,0,0,GETPOST("date_startmonth"),GETPOST("date_startday"),GETPOST("date_startyear")); +$date_end=dol_mktime(23,59,59,GETPOST("date_endmonth"),GETPOST("date_endday"),GETPOST("date_endyear")); +// Quarter +if (empty($date_start) || empty($date_end)) // We define date_start and date_end +{ + $q=GETPOST("q"); + if (empty($q)) + { + if (GETPOST("month")) { $date_start=dol_get_first_day($year_start,GETPOST("month"),false); $date_end=dol_get_last_day($year_start,GETPOST("month"),false); } + else + { + $date_start=dol_get_first_day($year_start, $conf->global->SOCIETE_FISCAL_MONTH_START,false); + $date_end=dol_time_plus_duree($date_start, 1, 'y') - 1; + } + } + else + { + if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } + if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } + if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } + if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + } } +// Define modetax (0 or 1) +// 0=normal, 1=option vat for services is on debit, 2=option on payments for products +$modetax = $conf->global->TAX_MODE; +if (GETPOSTISSET("modetax")) $modetax=GETPOST("modetax",'int'); +if (empty($modetax)) $modetax=0; + // Security check -$socid = isset($_GET["socid"])?$_GET["socid"]:''; -if ($user->societe_id) - $socid=$user->societe_id; +$socid = GETPOST('socid','int'); +if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); -// Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit -$modetax = $conf->global->TAX_MODE; -if (isset($_GET["modetax"])) - $modetax=$_GET["modetax"]; /** * print function * - * @param DoliDB $db Database - * @param string $sql sql - * @param string $date date + * @param DoliDB $db Database handler + * @param string $sql SQL Request + * @param string $date Date * @return void */ function pt ($db, $sql, $date) @@ -78,10 +101,10 @@ function pt ($db, $sql, $date) print ''; print ''."\n"; print "\n"; - $var=True; + while ($i < $num) { $obj = $db->fetch_object($result); - + print ''; print '\n"; $total = $total + $obj->mm; @@ -95,7 +118,8 @@ function pt ($db, $sql, $date) print "
'.$langs->trans("DatePayment").''; + print ''.$langs->trans("DatePayment").''; print $form->select_date($datep,"datep",'','','','add',1,1); print '
'.$langs->trans("Amount").' 
'.$obj->dm."
"; $db->free($result); - } else { + } + else { dol_print_error($db); } } @@ -105,7 +129,9 @@ function pt ($db, $sql, $date) * View */ -llxHeader(); +$form=new Form($db); +$company_static=new Societe($db); +$tva = new Tva($db); if($localTaxType==1) { $LT='LT1'; @@ -123,24 +149,35 @@ if($localTaxType==1) { $CalcLT= $conf->global->MAIN_INFO_LOCALTAX_CALC2; } +$description = ''; -$textprevyear="".img_previous().""; -$textnextyear=" ".img_next().""; +// Show report header +$name = $langs->trans("ReportByMonth"); +$description = $langs->trans($LT); +$calcmode = $langs->trans("LTReportBuildWithOptionDefinedInModule").' '; +$calcmode.= '('.$langs->trans("TaxModuleSetupToModifyRulesLT",DOL_URL_ROOT.'/admin/company.php').')
'; -print load_fiche_titre($langs->transcountry($LT,$mysoc->country_code),"$textprevyear ".$langs->trans("Year")." $year_start $textnextyear", 'title_accountancy.png'); +//if (! empty($conf->global->MAIN_MODULE_ACCOUNTING)) $description.='
'.$langs->trans("ThisIsAnEstimatedValue"); + +$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); + +$builddate=dol_now(); + + +llxHeader('', $name); + +//$textprevyear="".img_previous().""; +//$textnextyear=" ".img_next().""; +//print load_fiche_titre($langs->transcountry($LT,$mysoc->country_code),"$textprevyear ".$langs->trans("Year")." $year_start $textnextyear", 'title_accountancy.png'); + +report_header($name,'',$period,$periodlink,$description,$builddate,$exportlink,array(),$calcmode); +//report_header($name,'',$textprevyear.$langs->trans("Year")." ".$year_start.$textnextyear,'',$description,$builddate,$exportlink,array(),$calcmode); -print $langs->trans("LTReportBuildWithOptionDefinedInModule").'
'; -print '('.$langs->trans("TaxModuleSetupToModifyRulesLT",DOL_URL_ROOT.'/admin/company.php').')
'; print '
'; -print ''; -print ''; +print '
'; -print '
'; -print load_fiche_titre($langs->transcountry($LTSummary,$mysoc->country_code), '', ''); -print ' '; -print load_fiche_titre($langs->transcountry($LTPaid,$mysoc->country_code), '', ''); -print '
'; +print load_fiche_titre($langs->transcountry($LTSummary,$mysoc->country_code), '', ''); print ''; print ''; @@ -155,45 +192,57 @@ if($CalcLT==1) { if($CalcLT==2) { print ""; } - print ""; print "\n"; print "\n"; -$y = $year_current ; +$tmp=dol_getdate($date_start); +$y = $tmp['year']; +$m = $tmp['mon']; +$tmp=dol_getdate($date_end); +$yend = $tmp['year']; +$mend = $tmp['mon']; -$var=True; $total=0; $subtotalcoll=0; $subtotalpaye=0; $subtotal=0; -$i=0; -for ($m = 1 ; $m < 13 ; $m++ ) { - $coll_listsell = vat_by_date($db, $y, 0, 0, 0, $modetax, 'sell', $m); - $coll_listbuy = vat_by_date($db, $y, 0, 0, 0, $modetax, 'buy', $m); - +$i=0; $mcursor=0; +while ((($y < $yend) || ($y == $yend && $m < $mend)) && $mcursor < 1000) // $mcursor is to avoid too large loop +{ + $m = $conf->global->SOCIETE_FISCAL_MONTH_START + ($mcursor % 12); + if ($m == 13) $y++; + if ($m > 12) $m -= 12; + $mcursor++; + + $coll_listsell = tax_by_date('vat', $db, $y, 0, 0, 0, $modetax, 'sell', $m); + $coll_listbuy = tax_by_date('vat', $db, $y, 0, 0, 0, $modetax, 'buy', $m); + $action = "tva"; $object = array(&$coll_listsell, &$coll_listbuy); $parameters["mode"] = $modetax; $parameters["year"] = $y; $parameters["month"] = $m; $parameters["type"] = 'localtax'.$localTaxType; - + // Initialize technical object to manage hooks of expenses. Note that conf->hooks_modules contains array array $hookmanager->initHooks(array('externalbalance')); $reshook=$hookmanager->executeHooks('addVatLine',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks - if (! is_array($coll_listbuy) && $coll_listbuy == -1) { + if (! is_array($coll_listbuy) && $coll_listbuy == -1) + { $langs->load("errors"); print ''; break; } - if (! is_array($coll_listbuy) && $coll_listbuy == -2) { + if (! is_array($coll_listbuy) && $coll_listbuy == -2) + { print ''; break; } - + print ''; - print ''; - if($CalcLT==0) { + print ''; + + if ($CalcLT==0) { $x_coll = 0; foreach($coll_listsell as $vatrate=>$val) { $x_coll+=$val[$localTaxType==1?'localtax1':'localtax2']; @@ -221,7 +270,7 @@ for ($m = 1 ; $m < 13 ; $m++ ) { } $subtotalcoll = $subtotalcoll + $x_coll; print ""; - + } if($CalcLT==0) { @@ -231,7 +280,7 @@ for ($m = 1 ; $m < 13 ; $m++ ) { } elseif($CalcLT==2) { $diff= $x_coll; } - + $total = $total + $diff; $subtotal = $subtotal + $diff; @@ -240,7 +289,8 @@ for ($m = 1 ; $m < 13 ; $m++ ) { print "\n"; $i++; - if ($i > 2) { + if ($i > 2) + { print ''; print ''; if($CalcLT==0) { @@ -265,7 +315,10 @@ print ''; print '
".$langs->transcountry($LTCustomer,$mysoc->country_code)."".$langs->trans("TotalToPay")." 
'.$langs->trans("ErrorNoAccountancyModuleLoaded").'
'.$langs->trans("FeatureNotYetAvailable").'
'.dol_print_date(dol_mktime(0,0,0,$m,1,$y),"%b %Y").''.dol_print_date(dol_mktime(0,0,0,$m,1,$y),"%b %Y").'".price($x_coll)."
'.$langs->trans("SubTotal").':
'; -print '
 '; + +print '
'; + +print load_fiche_titre($langs->transcountry($LTPaid,$mysoc->country_code), '', ''); /* * Payed @@ -274,18 +327,18 @@ print '
 '; $sql = "SELECT SUM(amount) as mm, date_format(f.datev,'%Y-%m') as dm"; $sql.= " FROM ".MAIN_DB_PREFIX."localtax as f"; $sql.= " WHERE f.entity = ".$conf->entity; -$sql.= " AND f.datev >= '".$db->idate(dol_get_first_day($y,1,false))."'"; -$sql.= " AND f.datev <= '".$db->idate(dol_get_last_day($y,12,false))."'"; +$sql.= " AND f.datev >= '".$db->idate($date_start)."'"; +$sql.= " AND f.datev <= '".$db->idate($date_end)."'"; $sql.= " AND localtaxtype=".$localTaxType; $sql.= " GROUP BY dm"; $sql.= " ORDER BY dm ASC"; pt($db, $sql,$langs->trans("Year")." $y"); -print '
'; +print '
'; + +print '
'; -print ''; -print ''; llxFooter(); $db->close(); diff --git a/htdocs/compta/localtax/reglement.php b/htdocs/compta/localtax/list.php similarity index 88% rename from htdocs/compta/localtax/reglement.php rename to htdocs/compta/localtax/list.php index 54183ef72b9..50bc87d4d27 100644 --- a/htdocs/compta/localtax/reglement.php +++ b/htdocs/compta/localtax/list.php @@ -16,7 +16,7 @@ */ /** - * \file htdocs/compta/localtax/reglement.php + * \file htdocs/compta/localtax/list.php * \ingroup tax * \brief List of IRPF payments */ @@ -24,15 +24,15 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/compta/localtax/class/localtax.class.php'; -$langs->load("compta"); $langs->load("compta"); // Security check -$socid = isset($_GET["socid"])?$_GET["socid"]:''; +$socid = GETPOST('socid','int'); if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); $ltt=GETPOST("localTaxType"); + /* * View */ @@ -41,7 +41,13 @@ llxHeader(); $localtax_static = new Localtax($db); -print load_fiche_titre($langs->transcountry($ltt==2?"LT2Payments":"LT1Payments",$mysoc->country_code)); +$newcardbutton=''; +if ($user->rights->tax->charges->creer) +{ + $newcardbutton=''.$langs->trans('NewVATPayment').''; +} + +print load_fiche_titre($langs->transcountry($ltt==2?"LT2Payments":"LT1Payments",$mysoc->country_code), $newcardbutton); $sql = "SELECT rowid, amount, label, f.datev as dm"; $sql.= " FROM ".MAIN_DB_PREFIX."localtax as f "; @@ -66,7 +72,7 @@ if ($result) while ($i < $num) { $obj = $db->fetch_object($result); - + print ''; $localtax_static->id=$obj->rowid; diff --git a/htdocs/compta/localtax/quadri_detail.php b/htdocs/compta/localtax/quadri_detail.php index 5e18c786c18..db316a1d6dc 100644 --- a/htdocs/compta/localtax/quadri_detail.php +++ b/htdocs/compta/localtax/quadri_detail.php @@ -38,10 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php'; -$langs->load("bills"); -$langs->load("compta"); -$langs->load("companies"); -$langs->load("products"); +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); $local=GETPOST('localTaxType', 'int'); // Date range @@ -63,29 +60,31 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end $q=GETPOST("q"); if (empty($q)) { - if (isset($_REQUEST["month"])) { $date_start=dol_get_first_day($year_start,$_REQUEST["month"],false); $date_end=dol_get_last_day($year_start,$_REQUEST["month"],false); } + if (GETPOST("month")) { $date_start=dol_get_first_day($year_start,$_REQUEST["month"],false); $date_end=dol_get_last_day($year_start,GETPOST("month"),false); } else { - $month_current = strftime("%m",dol_now()); - if ($month_current >= 10) $q=4; - elseif ($month_current >= 7) $q=3; - elseif ($month_current >= 4) $q=2; - else $q=1; + $date_start=dol_get_first_day($year_start,empty($conf->global->SOCIETE_FISCAL_MONTH_START)?1:$conf->global->SOCIETE_FISCAL_MONTH_START,false); + if (empty($conf->global->MAIN_INFO_VAT_RETURN) || $conf->global->MAIN_INFO_VAT_RETURN == 2) $date_end=dol_time_plus_duree($date_start, 3, 'm') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 3) $date_end=dol_time_plus_duree($date_start, 1, 'y') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 1) $date_end=dol_time_plus_duree($date_start, 1, 'm') - 1; } } - if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } - if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } - if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } - if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + else + { + if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } + if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } + if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } + if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + } } -$min = GETPOST("min"); +$min = price2num(GETPOST("min","alpha")); if (empty($min)) $min = 0; // Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit +// 0=normal, 1=option vat for services is on debit, 2=option on payments for products $modetax = $conf->global->TAX_MODE; -if (isset($_REQUEST["modetax"])) $modetax=$_REQUEST["modetax"]; +if (GETPOSTISSET("modetax")) $modetax=GETPOST("modetax",'int'); if (empty($modetax)) $modetax=0; // Security check @@ -118,11 +117,11 @@ $fsearch.=' '; $fsearch.=' '; $fsearch.=' '; +$name=$langs->transcountry($local==1?"LT1ReportByQuarters":"LT2ReportByQuarters", $mysoc->country_code); $calc=$conf->global->MAIN_INFO_LOCALTAX_CALC.$local; if ($conf->global->$calc==0 || $conf->global->$calc==1) // Calculate on invoice for goods and services { - $nom=$langs->trans($local==1?"LT1ReportByQuartersInDueDebtMode":"LT2ReportByQuartersInDueDebtMode"); $calcmode=$calc==0?$langs->trans("CalcModeLT".$local):$langs->trans("CalcModeLT".$local."Rec"); $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRulesLT",DOL_URL_ROOT.'/admin/company.php').')'; $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); @@ -151,7 +150,6 @@ if ($conf->global->$calc==0 || $conf->global->$calc==1) // Calculate on invoice } if ($conf->global->$calc==2) // Invoice for goods, payment for services { - $nom=$langs->trans($local==1?"LT1ReportByQuartersInInputOutputMode":"LT2ReportByQuartersInInputOutputMode"); $calcmode=$calc==0?$langs->trans("CalcModeLT".$local):$langs->trans("CalcModeLT".$local."Rec"); $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRulesLT",DOL_URL_ROOT.'/admin/company.php').')'; $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); diff --git a/htdocs/compta/paiement.php b/htdocs/compta/paiement.php index 11751fe258b..77d9cd0a93d 100644 --- a/htdocs/compta/paiement.php +++ b/htdocs/compta/paiement.php @@ -88,11 +88,11 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e if (empty($reshook)) { - if ($action == 'add_paiement' || ($action == 'confirm_paiement' && $confirm=='yes')) + if ($action == 'add_paiement' || ($action == 'confirm_paiement' && $confirm == 'yes')) { $error = 0; - $datepaye = dol_mktime(12, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); + $datepaye = dol_mktime(12, 0, 0, GETPOST('remonth','int'), GETPOST('reday','int'), GETPOST('reyear','int')); $paiement_id = 0; $totalpayment = 0; $multicurrency_totalpayment = 0; @@ -504,18 +504,18 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie print ''.$langs->trans('CheckTransmitter'); print ' ('.$langs->trans("ChequeMaker").')'; print ''; - print ''; + print ''; // Bank name print ''.$langs->trans('Bank'); print ' ('.$langs->trans("ChequeBank").')'; print ''; - print ''; + print ''; // Comments print ''.$langs->trans('Comments').''; print ''; - print ''; + print ''; print ''; @@ -827,7 +827,7 @@ if (! GETPOST('action','aZ09')) $sql = 'SELECT p.datep as dp, p.amount, f.amount as fa_amount, f.facnumber'; $sql.=', f.rowid as facid, c.libelle as paiement_type, p.num_paiement'; - $sql.= ' FROM '.MAIN_DB_PREFIX.'paiement as p LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id AND c.entity IN (' . getEntity('c_paiement').')'; + $sql.= ' FROM '.MAIN_DB_PREFIX.'paiement as p LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id'; $sql.= ', '.MAIN_DB_PREFIX.'facture as f'; $sql.= ' WHERE p.fk_facture = f.rowid'; $sql.= ' AND f.entity IN (' . getEntity('facture').')'; diff --git a/htdocs/compta/paiement/card.php b/htdocs/compta/paiement/card.php index 3ec17c2521d..7c97b75d293 100644 --- a/htdocs/compta/paiement/card.php +++ b/htdocs/compta/paiement/card.php @@ -34,9 +34,7 @@ require_once DOL_DOCUMENT_ROOT .'/core/modules/facture/modules_facture.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; if (! empty($conf->banque->enabled)) require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; -$langs->load('bills'); -$langs->load('banks'); -$langs->load('companies'); +$langs->loadLangs(array('bills','banks','companies')); $id=GETPOST('id','int'); $ref=GETPOST('ref', 'alpha'); @@ -222,57 +220,64 @@ print '
'; print ''."\n"; // Date payment -print ''; // Payment type (VIR, LIQ, ...) $labeltype=$langs->trans("PaymentType".$object->type_code)!=("PaymentType".$object->type_code)?$langs->trans("PaymentType".$object->type_code):$object->type_libelle; -print ''; - -// Payment numero -print ''; - -// Amount -print ''; - -// Note -print ''; +print ''; $disable_delete = 0; // Bank account if (! empty($conf->banque->enabled)) { - if ($object->fk_account > 0) - { - $bankline=new AccountLine($db); - $bankline->fetch($object->bank_line); - if ($bankline->rappro) - { - $disable_delete = 1; - $title_button = dol_escape_htmltag($langs->transnoentitiesnoconv("CantRemoveConciliatedPayment")); - } + if ($object->fk_account > 0) + { + $bankline=new AccountLine($db); + $bankline->fetch($object->bank_line); + if ($bankline->rappro) + { + $disable_delete = 1; + $title_button = dol_escape_htmltag($langs->transnoentitiesnoconv("CantRemoveConciliatedPayment")); + } - print ''; - print ''; - print ''; - print ''; - - print ''; - print ''; - print ''; + print ''; + print ''; - print ''; + print $accountstatic->getNomUrl(1); + print ''; + print ''; + } +} +// Payment numero +/* +$titlefield=$langs->trans('Numero').' ('.$langs->trans("ChequeOrTransferNumber").')'; +print ''; + +// Check transmitter +$titlefield=$langs->trans('CheckTransmitter').' ('.$langs->trans("ChequeMaker").')'; +print ''; + +// Bank name +$titlefield=$langs->trans('Bank').' ('.$langs->trans("ChequeBank").')'; +print ''; +*/ + +// Bank account +if (! empty($conf->banque->enabled)) +{ + if ($object->fk_account > 0) + { if ($object->type_code == 'CHQ' && $bankline->fk_bordereau > 0) { dol_include_once('/compta/paiement/cheque/class/remisecheque.class.php'); @@ -280,15 +285,30 @@ if (! empty($conf->banque->enabled)) $bordereau->fetch($bankline->fk_bordereau); print ''; - print ''; - print ''; + print ''; - print ''; + print ''; + print ''; } - } + } + + print ''; + print ''; + print ''; + print ''; } +// Comments +print ''; + +// Amount +print ''; + print '
'.$form->editfieldkey("Date",'datep',$object->date,$object,$user->rights->facture->paiement).''; +print '
'.$form->editfieldkey("Date",'datep',$object->date,$object,$user->rights->facture->paiement).''; print $form->editfieldval("Date",'datep',$object->date,$object,$user->rights->facture->paiement,'datepicker','',null,$langs->trans('PaymentDateUpdateSucceeded')); print '
'.$langs->trans('PaymentMode').''.$labeltype.'
'.$form->editfieldkey("Numero",'num_paiement',$object->numero,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer).''; -print $form->editfieldval("Numero",'num_paiement',$object->numero,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer,'string','',null,$langs->trans('PaymentNumberUpdateSucceeded')); -print '
'.$langs->trans('Amount').''.price($object->montant,'',$langs,0,0,-1,$conf->currency).'
'.$form->editfieldkey("Note",'note',$object->note,$object,$user->rights->facture->paiement).''; -print $form->editfieldval("Note",'note',$object->note,$object,$user->rights->facture->paiement,'textarea'); -print '
'.$langs->trans('PaymentMode').''.$labeltype.'
'.$langs->trans('BankTransactionLine').''; - print $bankline->getNomUrl(1,0,'showconciliated'); - print '
'.$langs->trans('BankAccount').''; + print '
'.$langs->trans('BankAccount').''; $accountstatic=new Account($db); $accountstatic->fetch($bankline->fk_account); - print $accountstatic->getNomUrl(1); - print '
'.$form->editfieldkey($titlefield,'num_paiement',$object->num_paiement,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer).''; +print $form->editfieldval($titlefield,'num_paiement',$object->num_paiement,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer,'string','',null,$langs->trans('PaymentNumberUpdateSucceeded')); +print '
'.$form->editfieldkey($titlefield,'chqemetteur',$object->,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer).''; +print $form->editfieldval($titlefield,'chqemetteur',$object->aaa,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer,'string','',null,$langs->trans('ChequeMakeUpdateSucceeded')); +print '
'.$form->editfieldkey($titlefield,'chqbank',$object->aaa,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer).''; +print $form->editfieldval($titlefield,'chqbank',$object->aaa,$object,$object->statut == 0 && $user->rights->fournisseur->facture->creer,'string','',null,$langs->trans('ChequeBankUpdateSucceeded')); +print '
'.$langs->trans('CheckReceipt').''; + print ''.$langs->trans('CheckReceipt').''; print $bordereau->getNomUrl(1); - print '
'.$langs->trans('BankTransactionLine').''; + print $bankline->getNomUrl(1,0,'showconciliated'); + print '
'.$form->editfieldkey("Comments",'note',$object->note,$object,$user->rights->facture->paiement).''; +print $form->editfieldval("Note",'note',$object->note,$object,$user->rights->facture->paiement,'textarea:'.ROWS_3.':90%'); +print '
'.$langs->trans('Amount').''.price($object->amount,'',$langs,0,-1,-1,$conf->currency).'
'; print ''; @@ -332,21 +352,22 @@ if ($resql) if ($num > 0) { - $var=True; - while ($i < $num) { $objp = $db->fetch_object($resql); - print ''; + $thirdpartystatic->fetch($objp->socid); - $invoice=new Facture($db); - $invoice->fetch($objp->facid); - $paiement = $invoice->getSommePaiement(); - $creditnotes=$invoice->getSumCreditNotesUsed(); - $deposits=$invoice->getSumDepositsUsed(); - $alreadypayed=price2num($paiement + $creditnotes + $deposits,'MT'); - $remaintopay=price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits,'MT'); + $invoice=new Facture($db); + $invoice->fetch($objp->facid); + + $paiement = $invoice->getSommePaiement(); + $creditnotes=$invoice->getSumCreditNotesUsed(); + $deposits=$invoice->getSumDepositsUsed(); + $alreadypayed=price2num($paiement + $creditnotes + $deposits,'MT'); + $remaintopay=price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits,'MT'); + + print ''; // Invoice print ''; @@ -355,8 +376,6 @@ if ($resql) // Third party print ''; - $thirdpartystatic->id=$objp->socid; - $thirdpartystatic->name=$objp->name; print $thirdpartystatic->getNomUrl(1); print ''; diff --git a/htdocs/compta/paiement/cheque/card.php b/htdocs/compta/paiement/cheque/card.php index c684e041660..8a165ebe811 100644 --- a/htdocs/compta/paiement/cheque/card.php +++ b/htdocs/compta/paiement/cheque/card.php @@ -564,7 +564,7 @@ else $accountstatic=new Account($db); $accountstatic->fetch($object->account_id); - $linkback=''.$langs->trans("BackToList").''; + $linkback=''.$langs->trans("BackToList").''; $morehtmlref=''; dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); diff --git a/htdocs/compta/paiement/cheque/class/remisecheque.class.php b/htdocs/compta/paiement/cheque/class/remisecheque.class.php index 3458e6bc82a..528b01477a6 100644 --- a/htdocs/compta/paiement/cheque/class/remisecheque.class.php +++ b/htdocs/compta/paiement/cheque/class/remisecheque.class.php @@ -979,25 +979,56 @@ class RemiseCheque extends CommonObject } /** - * Return clicable name (with picto eventually) + * Return clicable name (with picto eventually) * - * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto - * @param string $option Sur quoi pointe le lien - * @return string Chaine avec URL + * @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto + * @param string $option Sur quoi pointe le lien + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string Chaine avec URL */ - function getNomUrl($withpicto=0,$option='') + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) { - global $langs; + global $conf, $langs; $result=''; - $label = $langs->trans("ShowCheckReceipt").': '.$this->ref; - $link = ''; + $label = ''.$langs->trans("ShowCheckReceipt").''; + $label.= '
'; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = DOL_URL_ROOT.'/compta/paiement/cheque/card.php?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowCheckReceipt"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = '
'; $linkend=''; - if ($withpicto) $result.=($link.img_object($label, 'payment', 'class="classfortooltip"').$linkend); - if ($withpicto && $withpicto != 2) $result.=' '; - if ($withpicto != 2) $result.=$link.$this->ref.$linkend; + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; return $result; } diff --git a/htdocs/compta/paiement/cheque/index.php b/htdocs/compta/paiement/cheque/index.php index e9f887e37f4..9ddbd0b5cf6 100644 --- a/htdocs/compta/paiement/cheque/index.php +++ b/htdocs/compta/paiement/cheque/index.php @@ -92,10 +92,12 @@ print '
'; $max=10; -$sql = "SELECT bc.rowid, bc.date_bordereau as db, bc.amount, bc.ref as ref"; -$sql.= ", bc.statut, bc.nbcheque"; -$sql.= ", ba.label, ba.rowid as bid"; +$sql = "SELECT bc.rowid, bc.date_bordereau as db, bc.amount, bc.ref as ref,"; +$sql.= " bc.statut, bc.nbcheque,"; +$sql.= " ba.ref, ba.label, ba.rowid as bid, ba.number, ba.currency_code, ba.account_number, ba.accountancy_journal,"; +$sql.= " aj.code"; $sql.= " FROM ".MAIN_DB_PREFIX."bordereau_cheque as bc, ".MAIN_DB_PREFIX."bank_account as ba"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_journal as aj ON aj.rowid = ba.fk_accountancy_journal"; $sql.= " WHERE ba.rowid = bc.fk_bank_account"; $sql.= " AND bc.entity = ".$conf->entity; $sql.= " ORDER BY bc.date_bordereau DESC, rowid DESC"; @@ -122,8 +124,13 @@ if ($resql) $checkdepositstatic->statut=$objp->statut; $accountstatic->id=$objp->bid; + $accountstatic->ref=$objp->ref; $accountstatic->label=$objp->label; - + $accountstatic->number=$objp->number; + $accountstatic->currency_code=$objp->currency_code; + $accountstatic->account_number=$objp->account_number; + $accountstatic->accountancy_journal=$objp->code; + print ''."\n"; print ''.$checkdepositstatic->getNomUrl(1).''; diff --git a/htdocs/compta/paiement/cheque/list.php b/htdocs/compta/paiement/cheque/list.php index 76c52215281..532d2e4c9b8 100644 --- a/htdocs/compta/paiement/cheque/list.php +++ b/htdocs/compta/paiement/cheque/list.php @@ -131,6 +131,12 @@ if ($resql) if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; + $newcardbutton=''; + if ($user->rights->banque->cheque) + { + $newcardbutton = ''.$langs->trans('NewCheckDeposit').''; + } + print ''; if ($optioncss != '') print ''; print ''; @@ -140,7 +146,7 @@ if ($resql) print ''; print ''; - print_barre_liste($langs->trans("MenuChequeDeposits"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_bank.png', '', '', $limit); + print_barre_liste($langs->trans("MenuChequeDeposits"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_bank.png', 0, $newcardbutton, '', $limit); $moreforfilter=''; diff --git a/htdocs/compta/paiement/class/cpaiement.class.php b/htdocs/compta/paiement/class/cpaiement.class.php index b089cac6aab..59f13023b24 100644 --- a/htdocs/compta/paiement/class/cpaiement.class.php +++ b/htdocs/compta/paiement/class/cpaiement.class.php @@ -107,7 +107,7 @@ class Cpaiement // Insert request $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '('; - $sql.= 'id,'; + $sql.= 'entity,'; $sql.= 'code,'; $sql.= 'libelle,'; $sql.= 'type,'; @@ -118,7 +118,7 @@ class Cpaiement $sql .= ') VALUES ('; - $sql .= ' '.(! isset($this->id)?'NULL':$this->id).','; + $sql .= ' '.(! isset($this->entity)?getEntity('c_paiement'):$this->entity).','; $sql .= ' '.(! isset($this->code)?'NULL':"'".$this->db->escape($this->code)."'").','; $sql .= ' '.(! isset($this->libelle)?'NULL':"'".$this->db->escape($this->libelle)."'").','; $sql .= ' '.(! isset($this->type)?'NULL':$this->type).','; @@ -186,7 +186,8 @@ class Cpaiement $sql .= " t.module"; $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t'; if (null !== $ref) { - $sql .= ' WHERE t.code = ' . '\'' . $ref . '\''; + $sql .= ' WHERE t.entity IN ('.getEntity('c_paiement').')'; + $sql .= ' AND t.code = ' . '\'' . $ref . '\''; } else { $sql .= ' WHERE t.id = ' . $id; } diff --git a/htdocs/compta/paiement/class/paiement.class.php b/htdocs/compta/paiement/class/paiement.class.php index 95c366dff0d..8b71cf40064 100644 --- a/htdocs/compta/paiement/class/paiement.class.php +++ b/htdocs/compta/paiement/class/paiement.class.php @@ -59,6 +59,7 @@ class Paiement extends CommonObject // de llx_paiement qui est lie aux types de //paiement de llx_c_paiement var $num_paiement; // Numero du CHQ, VIR, etc... + var $num_payment; // Numero du CHQ, VIR, etc... var $bank_account; // Id compte bancaire du paiement var $bank_line; // Id de la ligne d'ecriture bancaire // fk_paiement dans llx_paiement est l'id du type de paiement (7 pour CHQ, ...) @@ -88,7 +89,7 @@ class Paiement extends CommonObject { $sql = 'SELECT p.rowid, p.ref, p.datep as dp, p.amount, p.statut, p.fk_bank,'; $sql.= ' c.code as type_code, c.libelle as type_libelle,'; - $sql.= ' p.num_paiement, p.note,'; + $sql.= ' p.num_paiement as num_payment, p.note,'; $sql.= ' b.fk_account'; $sql.= ' FROM '.MAIN_DB_PREFIX.'paiement as p LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON p.fk_bank = b.rowid'; @@ -110,7 +111,9 @@ class Paiement extends CommonObject $this->ref = $obj->ref?$obj->ref:$obj->rowid; $this->date = $this->db->jdate($obj->dp); $this->datepaye = $this->db->jdate($obj->dp); - $this->numero = $obj->num_paiement; + $this->numero = $obj->num_payment; // deprecated + $this->num_paiement = $obj->num_payment; // deprecated + $this->num_payment = $obj->num_payment; $this->montant = $obj->amount; // deprecated $this->amount = $obj->amount; $this->note = $obj->note; @@ -209,9 +212,10 @@ class Paiement extends CommonObject $total = $totalamount_converted; // Maybe use price2num with MT for the converted value $mtotal = $totalamount; } + $note = ($this->note_public?$this->note_public:$this->note); $sql = "INSERT INTO ".MAIN_DB_PREFIX."paiement (entity, ref, datec, datep, amount, multicurrency_amount, fk_paiement, num_paiement, note, fk_user_creat)"; - $sql.= " VALUES (".$conf->entity.", '".$this->ref."', '". $this->db->idate($now)."', '".$this->db->idate($this->datepaye)."', '".$total."', '".$mtotal."', ".$this->paiementid.", '".$this->num_paiement."', '".$this->db->escape($this->note)."', ".$user->id.")"; + $sql.= " VALUES (".$conf->entity.", '".$this->ref."', '". $this->db->idate($now)."', '".$this->db->idate($this->datepaye)."', '".$total."', '".$mtotal."', ".$this->paiementid.", '".$this->num_paiement."', '".$this->db->escape($note)."', ".$user->id.")"; dol_syslog(get_class($this)."::Create insert paiement", LOG_DEBUG); $resql = $this->db->query($sql); @@ -530,7 +534,7 @@ class Paiement extends CommonObject { if ($accountid <= 0) { - $this->error='Bad value for parameter accountid'; + $this->error='Bad value for parameter accountid='.$accountid; dol_syslog(get_class($this).'::addPaymentToBank '.$this->error, LOG_ERR); return -1; } @@ -1089,7 +1093,7 @@ class Paiement extends CommonObject $result .= $linkstart; if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); - if ($withpicto && $withpicto != 2) $result.= $this->ref; + if ($withpicto && $withpicto != 2) $result.= ($this->ref?$this->ref:$this->id); $result .= $linkend; return $result; diff --git a/htdocs/compta/paiement/list.php b/htdocs/compta/paiement/list.php index 595c9e868dc..6ca9f29c16e 100644 --- a/htdocs/compta/paiement/list.php +++ b/htdocs/compta/paiement/list.php @@ -138,7 +138,7 @@ else $reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // Note that $action and $object may have been modified by hook $sql.=$hookmanager->resPrint; $sql.= " FROM ".MAIN_DB_PREFIX."paiement as p"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_paiement = c.id AND c.entity IN (" . getEntity('c_paiement') . ")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_paiement = c.id"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON p.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON p.rowid = pf.fk_paiement"; diff --git a/htdocs/compta/paiement/avalider.php b/htdocs/compta/paiement/tovalidate.php similarity index 96% rename from htdocs/compta/paiement/avalider.php rename to htdocs/compta/paiement/tovalidate.php index beb2fb77ecd..7e16b525233 100644 --- a/htdocs/compta/paiement/avalider.php +++ b/htdocs/compta/paiement/tovalidate.php @@ -17,9 +17,9 @@ */ /** - * \file htdocs/compta/paiement/avalider.php + * \file htdocs/compta/paiement/tovalidate.php * \ingroup compta - * \brief Page liste des paiements a valider des factures clients + * \brief Page list payment to validate. Visible in menu when option BILL_ADD_PAYMENT_VALIDATION is on. */ require '../../main.inc.php'; diff --git a/htdocs/compta/prelevement/bons.php b/htdocs/compta/prelevement/bons.php index 9d3da39e5c3..31dfbd5b610 100644 --- a/htdocs/compta/prelevement/bons.php +++ b/htdocs/compta/prelevement/bons.php @@ -101,6 +101,8 @@ if ($result) $selectedfields=''; + $newcardbutton = ''.$langs->trans('NewStandingOrder').''; + // Lines of title fields print ''; if ($optioncss != '') print ''; @@ -112,7 +114,7 @@ if ($result) print ''; print ''; - print_barre_liste($langs->trans("WithdrawalsReceipts"), $page, $_SERVER["PHP_SELF"], $urladd, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_generic', 0, '', '', $limit); + print_barre_liste($langs->trans("WithdrawalsReceipts"), $page, $_SERVER["PHP_SELF"], $urladd, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_generic', 0, $newcardbutton, '', $limit); $moreforfilter=''; diff --git a/htdocs/compta/prelevement/class/bonprelevement.class.php b/htdocs/compta/prelevement/class/bonprelevement.class.php index d0aa788fe48..57cf518db16 100644 --- a/htdocs/compta/prelevement/class/bonprelevement.class.php +++ b/htdocs/compta/prelevement/class/bonprelevement.class.php @@ -4,6 +4,7 @@ * Copyright (C) 2010-2015 Juanjo Menent * Copyright (C) 2010-2014 Laurent Destailleur * Copyright (C) 2014-2016 Ferran Marcet + * Copyright (C) 2018 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -726,16 +727,12 @@ class BonPrelevement extends CommonObject $sql = "SELECT count(f.rowid) as nb"; $sql.= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql.= ", ".MAIN_DB_PREFIX."prelevement_facture_demande as pfd"; - //if ($banque || $agence) $sql.=", ".MAIN_DB_PREFIX."societe_rib as sr"; $sql.= " WHERE f.fk_statut = 1"; $sql.= " AND f.entity = ".$conf->entity; $sql.= " AND f.rowid = pfd.fk_facture"; $sql.= " AND f.paye = 0"; $sql.= " AND pfd.traite = 0"; $sql.= " AND f.total_ttc > 0"; - //if ($banque || $agence) $sql.= " AND f.fk_soc = sr.rowid"; - //if ($banque) $sql.= " AND sr.code_banque = '".$conf->global->PRELEVEMENT_CODE_BANQUE."'"; - //if ($agence) $sql.= " AND sr.code_guichet = '".$conf->global->PRELEVEMENT_CODE_GUICHET."'"; dol_syslog(get_class($this)."::SommeAPrelever"); $resql = $this->db->query($sql); @@ -764,9 +761,10 @@ class BonPrelevement extends CommonObject * @param int $agence dolibarr mysoc bank office (guichet) * @param string $mode real=do action, simu=test only * @param string $format FRST, RCUR or ALL + * @param string $executiondate Date to execute the transfer * @return int <0 if KO, nbre of invoice withdrawed if OK */ - function Create($banque=0, $agence=0, $mode='real', $format='ALL') + function Create($banque=0, $agence=0, $mode='real', $format='ALL',$executiondate='') { global $conf,$langs; @@ -780,6 +778,8 @@ class BonPrelevement extends CommonObject $error = 0; $datetimeprev = time(); + //Choice the date of the execution direct debit + if(!empty($executiondate)) $datetimeprev = $executiondate; $month = strftime("%m", $datetimeprev); $year = strftime("%Y", $datetimeprev); @@ -805,9 +805,8 @@ class BonPrelevement extends CommonObject $sql.= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql.= ", ".MAIN_DB_PREFIX."societe as s"; $sql.= ", ".MAIN_DB_PREFIX."prelevement_facture_demande as pfd"; - //if ($banque || $agence) $sql.= ", ".MAIN_DB_PREFIX."societe_rib as sr"; $sql.= " WHERE f.rowid = pfd.fk_facture"; - $sql.= " AND f.entity = ".$conf->entity; + $sql.= " AND f.entity IN (".getEntity('facture').')'; $sql.= " AND s.rowid = f.fk_soc"; //if ($banque || $agence) $sql.= " AND s.rowid = sr.fk_soc"; $sql.= " AND f.fk_statut = 1"; @@ -1082,7 +1081,7 @@ class BonPrelevement extends CommonObject $this->factures = $factures_prev_id; // Generation of SEPA file $this->filename - $this->generate($format); + $this->generate($format,$executiondate); } dol_syslog(__METHOD__."::End withdraw receipt, file ".$this->filename, LOG_DEBUG); } @@ -1279,9 +1278,10 @@ class BonPrelevement extends CommonObject * File is generated with name this->filename * * @param string $format FRST, RCUR or ALL + * @param string $executiondate Date to execute transfer * @return int 0 if OK, <0 if KO */ - function generate($format='ALL') + function generate($format='ALL',$executiondate='') { global $conf,$langs,$mysoc; @@ -1310,10 +1310,16 @@ class BonPrelevement extends CommonObject */ // SEPA Initialisation $CrLf = "\n"; - $date_actu = dol_now(); + + $now = dol_now(); + + $dateTime_ECMA = dol_print_date($now, '%Y-%m-%dT%H:%M:%S'); + + $date_actu = $now; + if (!empty($executiondate)) $date_actu=$executiondate; + $dateTime_YMD = dol_print_date($date_actu, '%Y%m%d'); $dateTime_YMDHMS = dol_print_date($date_actu, '%Y%m%d%H%M%S'); - $dateTime_ECMA = dol_print_date($date_actu, '%Y-%m-%dT%H:%M:%S'); $fileDebiteurSection = ''; $fileEmetteurSection = ''; $i = 0; @@ -1340,6 +1346,7 @@ class BonPrelevement extends CommonObject $sql.= " AND soc.rowid = f.fk_soc"; $sql.= " AND rib.fk_soc = f.fk_soc"; $sql.= " AND rib.default_rib = 1"; + $sql.= " AND rib.type = 'ban'"; //print $sql; // Define $fileDebiteurSection. One section DrctDbtTxInf per invoice. diff --git a/htdocs/compta/prelevement/create.php b/htdocs/compta/prelevement/create.php index cd3aebf62f4..c94a786917f 100644 --- a/htdocs/compta/prelevement/create.php +++ b/htdocs/compta/prelevement/create.php @@ -3,6 +3,7 @@ * Copyright (C) 2010-2015 Laurent Destailleur * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2010-2012 Juanjo Menent + * Copyright (C) 2018 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,7 +66,9 @@ if ($action == 'create') { // $conf->global->PRELEVEMENT_CODE_BANQUE and $conf->global->PRELEVEMENT_CODE_GUICHET should be empty $bprev = new BonPrelevement($db); - $result=$bprev->create($conf->global->PRELEVEMENT_CODE_BANQUE, $conf->global->PRELEVEMENT_CODE_GUICHET, $mode, $format); + $executiondate = dol_mktime(0, 0, 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); + + $result = $bprev->create($conf->global->PRELEVEMENT_CODE_BANQUE, $conf->global->PRELEVEMENT_CODE_GUICHET, $mode, $format,$executiondate); if ($result < 0) { setEventMessages($bprev->error, $bprev->errors, 'errors'); @@ -90,6 +93,7 @@ if ($action == 'create') /* * View */ +$form = new Form($db); $thirdpartystatic=new Societe($db); $invoicestatic=new Facture($db); @@ -144,23 +148,22 @@ print '
'; if ($mesg) print $mesg; print "
\n"; +print ''; +print ''; +if ($nb) { + if ($pricetowithdraw) { + print $langs->trans('ExecutionDate').' '; + print $form->select_date(); + if ($mysoc->isInEEC()) { + print ''; + print ''; + } else { + print '' . $langs->trans("CreateAll") . "\n"; + } -if ($nb) -{ - if ($pricetowithdraw) - { - if ($mysoc->isInEEC()) - { - print ''.$langs->trans("CreateForSepaFRST")."\n"; - print ''.$langs->trans("CreateForSepaRCUR")."\n"; } else { - print ''.$langs->trans("CreateAll")."\n"; - } - } - else - { if ($mysoc->isInEEC()) { print ''.$langs->trans("CreateForSepaFRST")."\n"; diff --git a/htdocs/compta/prelevement/factures.php b/htdocs/compta/prelevement/factures.php index c829b139436..64811c7604a 100644 --- a/htdocs/compta/prelevement/factures.php +++ b/htdocs/compta/prelevement/factures.php @@ -205,7 +205,7 @@ if ($result) print_liste_field_titre("Bill",$_SERVER["PHP_SELF"],"p.ref",'',$param,'',$sortfield,$sortorder); print_liste_field_titre("ThirdParty",$_SERVER["PHP_SELF"],"s.nom",'',$param,'',$sortfield,$sortorder); print_liste_field_titre("AmountInvoice",$_SERVER["PHP_SELF"],"f.total_ttc","",$param,'align="right"',$sortfield,$sortorder); - print_liste_field_titre("AmountRequested",$_SERVER["PHP_SELF"],"pl.amount_requested","",$param,'align="right"',$sortfield,$sortorder); + print_liste_field_titre("AmountRequested",$_SERVER["PHP_SELF"],"pl.amount","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre("StatusDebitCredit",$_SERVER["PHP_SELF"],"","",$param,'align="center"',$sortfield,$sortorder); print_liste_field_titre(''); print "\n"; diff --git a/htdocs/compta/prelevement/ligne.php b/htdocs/compta/prelevement/ligne.php index 20ac3d18082..bb5598cf81f 100644 --- a/htdocs/compta/prelevement/ligne.php +++ b/htdocs/compta/prelevement/ligne.php @@ -33,13 +33,12 @@ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; $langs->load("banks"); $langs->load("categories"); +$langs->load("bills"); +$langs->load("withdrawals"); // Security check if ($user->societe_id > 0) accessforbidden(); -$langs->load("bills"); -$langs->load("withdrawals"); - // Get supervariables $action = GETPOST('action','alpha'); $id = GETPOST('id','int'); diff --git a/htdocs/compta/resultat/clientfourn.php b/htdocs/compta/resultat/clientfourn.php index fe7d927b6df..5692f3d97bb 100644 --- a/htdocs/compta/resultat/clientfourn.php +++ b/htdocs/compta/resultat/clientfourn.php @@ -107,13 +107,13 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } } -// $date_start and $date_end are defined. We force $start_year and $nbofyear +// $date_start and $date_end are defined. We force $year_start and $nbofyear $tmps=dol_getdate($date_start); -$start_year = $tmps['year']; +$year_start = $tmps['year']; $tmpe=dol_getdate($date_end); $year_end = $tmpe['year']; $nbofyear = ($year_end - $start_year) + 1; -//var_dump($start_year." ".$end_year." ".$nbofyear); +//var_dump("year_start=".$year_start." year_end=".$year_end." nbofyear=".$nbofyear." date_start=".dol_print_date($date_start, 'dayhour')." date_end=".dol_print_date($date_end, 'dayhour')); // Define modecompta ('CREANCES-DETTES' or 'RECETTES-DEPENSES' or 'BOOKKEEPING') $modecompta = $conf->global->ACCOUNTING_MODE; @@ -218,7 +218,7 @@ if ($date_endyear) $param.='&date_endyear='.$date_startyear; print ''; print ''; -print_liste_field_titre("PredefinedGroups", $_SERVER["PHP_SELF"], 's.nom, s.rowid','',$param,'',$sortfield,$sortorder,'width200 '); +print_liste_field_titre("PredefinedGroups", $_SERVER["PHP_SELF"], 'f.thirdparty_code,f.rowid','',$param,'',$sortfield,$sortorder,'width200 '); print_liste_field_titre(''); if ($modecompta == 'BOOKKEEPING') { @@ -253,9 +253,9 @@ if ($modecompta == 'BOOKKEEPING') $sql.= " FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as f"; $sql.= ", ".MAIN_DB_PREFIX."accounting_account as aa"; $sql.= " WHERE f.numero_compte = aa.account_number"; - //$sql.= " AND fk_statut in (1,2)"; $sql.= " AND ".$predefinedgroupwhere; - $sql.= " AND aa.fk_pcg_version = '".$charofaccountstring."'"; + $sql.= " AND fk_pcg_version = '".$db->escape($charofaccountstring)."'"; + $sql.= " AND f.entity = ".$conf->entity; if (! empty($date_start) && ! empty($date_end)) $sql.= " AND f.doc_date >= '".$db->idate($date_start)."' AND f.doc_date <= '".$db->idate($date_end)."'"; $sql.= " GROUP BY pcg_type, pcg_subtype, name, socid"; @@ -295,7 +295,7 @@ if ($modecompta == 'BOOKKEEPING') if ($showaccountdetail != 'no') { $tmppredefinedgroupwhere="pcg_type = '".$db->escape($objp->pcg_type)."' AND pcg_subtype = '".$db->escape($objp->pcg_subtype)."'"; - $tmppredefinedgroupwhere.= " AND fk_pcg_version = '".$charofaccountstring."'"; + $tmppredefinedgroupwhere.= " AND fk_pcg_version = '".$db->escape($charofaccountstring)."'"; //$tmppredefinedgroupwhere.= " AND thirdparty_code = '".$db->escape($objp->name)."'"; // Get cpts of category/group @@ -312,7 +312,7 @@ if ($modecompta == 'BOOKKEEPING') } - if ($showaccountdetail == 'all' || $resultN > 0) + if ($showaccountdetail == 'all' || $resultN <> 0) { print ''; print ''; @@ -822,7 +822,7 @@ else $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as p"; $sql.= " INNER JOIN ".MAIN_DB_PREFIX."user as u ON u.rowid=p.fk_user_author"; $sql.= " INNER JOIN ".MAIN_DB_PREFIX."payment_expensereport as pe ON pe.fk_expensereport = p.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id AND c.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id"; $sql.= " WHERE p.entity IN (".getEntity('expensereport').")"; $sql.= " AND p.fk_statut>=5"; @@ -906,7 +906,7 @@ else $sql = "SELECT p.societe as nom, p.firstname, p.lastname, date_format(p.datedon,'%Y-%m') as dm, sum(p.amount) as amount"; $sql.= " FROM ".MAIN_DB_PREFIX."don as p"; $sql.= " INNER JOIN ".MAIN_DB_PREFIX."payment_donation as pe ON pe.fk_donation = p.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id AND c.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id"; $sql.= " WHERE p.entity IN (".getEntity('donation').")"; $sql.= " AND fk_statut >= 2"; } diff --git a/htdocs/compta/resultat/index.php b/htdocs/compta/resultat/index.php index 156576fa4bc..e8b00fc869b 100644 --- a/htdocs/compta/resultat/index.php +++ b/htdocs/compta/resultat/index.php @@ -85,13 +85,14 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } } -// $date_start and $date_end are defined. We force $start_year and $nbofyear +// $date_start and $date_end are defined. We force $year_start and $nbofyear $tmps=dol_getdate($date_start); -$start_year = $tmps['year']; +$year_start = $tmps['year']; $tmpe=dol_getdate($date_end); $year_end = $tmpe['year']; $nbofyear = ($year_end - $start_year) + 1; -//var_dump($start_year." ".$end_year." ".$nbofyear); +//var_dump("year_start=".$year_start." year_end=".$year_end." nbofyear=".$nbofyear." date_start=".dol_print_date($date_start, 'dayhour')." date_end=".dol_print_date($date_end, 'dayhour')); + // Security check $socid = GETPOST('socid','int'); @@ -376,7 +377,6 @@ if (! empty($conf->tax->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { while ($i < $num) { @@ -409,7 +409,6 @@ if (! empty($conf->tax->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { while ($i < $num) { @@ -443,7 +442,6 @@ if (! empty($conf->tax->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { while ($i < $num) { @@ -474,7 +472,6 @@ if (! empty($conf->tax->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { while ($i < $num) { @@ -537,7 +534,6 @@ if (! empty($conf->tax->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { while ($i < $num) { @@ -600,7 +596,6 @@ if (! empty($conf->tax->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { while ($i < $num) { @@ -647,7 +642,6 @@ if (! empty($conf->salaries->enabled) && ($modecompta == 'CREANCES-DETTES' || $m $result = $db->query($sql); if ($result) { $num = $db->num_rows($result); - $var = false; $i = 0; if ($num) { while ($i < $num) { @@ -698,7 +692,7 @@ if (! empty($conf->expensereport->enabled) && ($modecompta == 'CREANCES-DETTES' $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as p"; $sql.= " INNER JOIN ".MAIN_DB_PREFIX."user as u ON u.rowid=p.fk_user_author"; $sql.= " INNER JOIN ".MAIN_DB_PREFIX."payment_expensereport as pe ON pe.fk_expensereport = p.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id AND c.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id"; $sql.= " WHERE p.entity IN (".getEntity('expensereport').")"; $sql.= " AND p.fk_statut>=5"; @@ -761,7 +755,7 @@ if (! empty($conf->don->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco $sql = "SELECT p.societe as nom, p.firstname, p.lastname, date_format(pe.datep,'%Y-%m') as dm, sum(p.amount) as amount"; $sql.= " FROM ".MAIN_DB_PREFIX."don as p"; $sql.= " INNER JOIN ".MAIN_DB_PREFIX."payment_donation as pe ON pe.fk_donation = p.rowid"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id AND c.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON pe.fk_typepayment = c.id"; $sql.= " WHERE p.entity IN (".getEntity('donation').")"; $sql.= " AND fk_statut >= 2"; if (! empty($date_start) && ! empty($date_end)) @@ -775,7 +769,6 @@ if (! empty($conf->don->enabled) && ($modecompta == 'CREANCES-DETTES' || $modeco if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { @@ -810,33 +803,35 @@ elseif ($modecompta == 'BOOKKEEPING') { if (! empty($conf->accounting->enabled) && ($modecompta == 'BOOKKEEPING')) { - $subtotal_ht = 0; - $subtotal_ttc = 0; + $predefinedgroupwhere = "("; + //$predefinedgroupwhere.= " (pcg_type = 'EXPENSE' and pcg_subtype in ('PRODUCT','SERVICE'))"; + $predefinedgroupwhere.= " (pcg_type = 'EXPENSE')"; + $predefinedgroupwhere.= " OR "; + //$predefinedgroupwhere.= " (pcg_type = 'INCOME' and pcg_subtype in ('PRODUCT','SERVICE'))"; + $predefinedgroupwhere.= " (pcg_type = 'INCOME')"; + $predefinedgroupwhere.= ")"; + + $charofaccountstring = $conf->global->CHARTOFACCOUNTS; + $charofaccountstring=dol_getIdFromCode($db, $conf->global->CHARTOFACCOUNTS, 'accounting_system', 'rowid', 'pcg_version'); $sql = "SELECT b.doc_ref, b.numero_compte, b.subledger_account, b.subledger_label, pcg_type, date_format(b.doc_date,'%Y-%m') as dm, sum(b.debit) as debit, sum(b.credit) as credit, sum(b.montant) as amount"; $sql.= " FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as b, ".MAIN_DB_PREFIX."accounting_account as aa"; $sql.= " WHERE b.numero_compte = aa.account_number AND b.entity = ".$conf->entity; - //$sql.= " AND fk_statut in (1,2)"; - $sql.= " AND ("; - //$sql.= " (pcg_type = 'EXPENSE' and pcg_subtype in ('PRODUCT','SERVICE'))"; - $sql.= " (pcg_type = 'EXPENSE')"; - $sql.= " OR "; - //$sql.= " (pcg_type = 'INCOME' and pcg_subtype in ('PRODUCT','SERVICE'))"; - $sql.= " (pcg_type = 'INCOME')"; - $sql.= ")"; - //$sql.= " AND code_journal in ('VT', 'AC')"; + $sql.= " AND ".$predefinedgroupwhere; + $sql.= " AND fk_pcg_version = '".$db->escape($charofaccountstring)."'"; if (! empty($date_start) && ! empty($date_end)) $sql.= " AND b.doc_date >= '".$db->idate($date_start)."' AND b.doc_date <= '".$db->idate($date_end)."'"; $sql.= " GROUP BY b.doc_ref, b.numero_compte, b.subledger_account, b.subledger_label, pcg_type, dm"; - //print $sql; + $subtotal_ht = 0; + $subtotal_ttc = 0; + dol_syslog("get bookkeeping record"); $result=$db->query($sql); if ($result) { $num = $db->num_rows($result); - $var=false; $i = 0; if ($num) { @@ -846,9 +841,8 @@ if (! empty($conf->accounting->enabled) && ($modecompta == 'BOOKKEEPING')) if (! isset($encaiss[$obj->dm])) $encaiss[$obj->dm]=0; $encaiss[$obj->dm] += $obj->debit; - if (! isset($encaiss_ttc[$obj->dm])) $encaiss_ttc[$obj->dm]=0; - $encaiss_ttc[$obj->dm] += $obj->credit; + $encaiss_ttc[$obj->dm] += 0; $i++; } @@ -909,7 +903,6 @@ for ($annee = $year_start ; $annee <= $year_end ; $annee++) } print ''; -$var=True; // Loop on each month $nb_mois_decalage = $conf->global->SOCIETE_FISCAL_MONTH_START?($conf->global->SOCIETE_FISCAL_MONTH_START-1):0; @@ -927,21 +920,44 @@ for ($mois = 1+$nb_mois_decalage ; $mois <= 12+$nb_mois_decalage ; $mois++) $case = strftime("%Y-%m",dol_mktime(12,0,0,$mois_modulo,1,$annee_decalage)); print '"; print '"; } @@ -952,7 +968,10 @@ for ($mois = 1+$nb_mois_decalage ; $mois <= 12+$nb_mois_decalage ; $mois++) // Total $nbcols=0; -print ''; +print ''; for ($annee = $year_start ; $annee <= $year_end ; $annee++) { $nbcols+=2; diff --git a/htdocs/compta/resultat/result.php b/htdocs/compta/resultat/result.php index 8b9cba79b0c..7854f6656ed 100644 --- a/htdocs/compta/resultat/result.php +++ b/htdocs/compta/resultat/result.php @@ -254,9 +254,33 @@ else if ($modecompta=="RECETTES-DEPENSES") else if ($modecompta=="BOOKKEEPING") { - //All categories - $cats = $AccCat->getCats(); - if ($catsCalcule < 0) dol_print_error($db, $AccCat->error, $AccCat->errors); + // Get array of all report groups that are active + $cats = $AccCat->getCats(); // WARNING: Computed groups must be after group they include + + /* + $sql = 'SELECT DISTINCT t.numero_compte as nb FROM '.MAIN_DB_PREFIX.'accounting_bookkeeping as t, '.MAIN_DB_PREFIX.'accounting_account as aa'; + $sql.= " WHERE t.numero_compte = aa.account_number AND aa.fk_accounting_category = 0"; + if (! empty($date_start) && ! empty($date_end)) + $sql.= " AND t.doc_date >= '".$db->idate($date_start)."' AND t.doc_date <= '".$db->idate($date_end)."'"; + if (! empty($month)) { + $sql .= " AND MONTH(t.doc_date) = " . $month; + } + $resql = $db->query($sql); + if ($resql) + { + $num_rows = $db->num_rows($resql); + if ($num_rows) { + + print '
Warning: There is '.$num_rows.' accounts in your ledger table that are not set into a reporting group
'; + $i = 0; + //while ($i < $num) { + // $obj = $db->fetch_object($resql); + // $i++; + //} + } + } + else dol_print_error($db); + */ $j=1; $sommes = array(); @@ -365,7 +389,7 @@ else if ($modecompta=="BOOKKEEPING") $totCat['M'][$k] = 0; } - // Get cpts of category/group + // Set $cpts of with array of accounts in the category/group $cpts = $AccCat->getCptsCat($cat['rowid']); print ""; @@ -373,7 +397,10 @@ else if ($modecompta=="BOOKKEEPING") // Column group print ''; + + // Label of group + print ''; + if ($modecompta != 'BOOKKEEPING') print ''; + print ''; if ($annee != $year_end) print ''; } print ''; @@ -336,7 +334,9 @@ for ($mois = 1+$nb_mois_decalage ; $mois <= 12+$nb_mois_decalage ; $mois++) if ($cum[$case]) { $now_show_delta=1; // On a trouve le premier mois de la premiere annee generant du chiffre. - print ''.price($cum[$case],1).''; + if ($modecompta != 'BOOKKEEPING') print ''; + print price($cum[$case], 1); + if ($modecompta != 'BOOKKEEPING') print ''; } else { diff --git a/htdocs/compta/tva/card.php b/htdocs/compta/tva/card.php index e76d96e1a5e..2411858a924 100644 --- a/htdocs/compta/tva/card.php +++ b/htdocs/compta/tva/card.php @@ -55,7 +55,7 @@ $hookmanager->initHooks(array('taxvatcard','globalcard')); if ($_POST["cancel"] == $langs->trans("Cancel") && ! $id) { - header("Location: reglement.php"); + header("Location: list.php"); exit; } @@ -119,7 +119,7 @@ if ($action == 'add' && $_POST["cancel"] <> $langs->trans("Cancel")) if ($ret > 0) { $db->commit(); - header("Location: reglement.php"); + header("Location: list.php"); exit; } else @@ -154,7 +154,7 @@ if ($action == 'delete') if ($result >= 0) { $db->commit(); - header("Location: ".DOL_URL_ROOT.'/compta/tva/reglement.php'); + header("Location: ".DOL_URL_ROOT.'/compta/tva/list.php'); exit; } else @@ -306,7 +306,7 @@ if ($id) dol_fiche_head($head, 'card', $langs->trans("VATPayment"), -1, 'payment'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref', $morehtmlref, '', 0, '', ''); diff --git a/htdocs/compta/tva/clients.php b/htdocs/compta/tva/clients.php index 276f801d05b..71376a566e1 100644 --- a/htdocs/compta/tva/clients.php +++ b/htdocs/compta/tva/clients.php @@ -1,7 +1,7 @@ * Copyright (C) 2004 Eric Seigne - * Copyright (C) 2004-2013 Laurent Destailleur + * Copyright (C) 2004-2018 Laurent Destailleur * Copyright (C) 2006 Yannick Warnier * Copyright (C) 2014 Ferran Marcet * @@ -20,9 +20,9 @@ */ /** - * \file htdocs/compta/tva/clients.php - * \ingroup tax - * \brief Page des societes + * \file htdocs/compta/tva/clients.php + * \ingroup tax + * \brief Page of sales taxes */ require '../../main.inc.php'; @@ -32,70 +32,56 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php'; -$langs->load("bills"); -$langs->load("compta"); -$langs->load("companies"); -$langs->load("products"); -$langs->load("other"); +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); // Date range -$year=GETPOST("year"); -if (empty($year)) { +$year=GETPOST("year","int"); +if (empty($year)) +{ $year_current = strftime("%Y",dol_now()); $year_start = $year_current; } else { $year_current = $year; $year_start = $year; } -$date_start=dol_mktime(0,0,0,$_REQUEST["date_startmonth"],$_REQUEST["date_startday"],$_REQUEST["date_startyear"]); -$date_end=dol_mktime(23,59,59,$_REQUEST["date_endmonth"],$_REQUEST["date_endday"],$_REQUEST["date_endyear"]); +$date_start=dol_mktime(0,0,0,GETPOST("date_startmonth"),GETPOST("date_startday"),GETPOST("date_startyear")); +$date_end=dol_mktime(23,59,59,GETPOST("date_endmonth"),GETPOST("date_endday"),GETPOST("date_endyear")); // Quarter -if (empty($date_start) || empty($date_end)) {// We define date_start and date_end +if (empty($date_start) || empty($date_end)) // We define date_start and date_end +{ $q=GETPOST("q"); - if (empty($q)) { - if (isset($_REQUEST["month"])) { - $date_start=dol_get_first_day($year_start,$_REQUEST["month"],false); - $date_end=dol_get_last_day($year_start,$_REQUEST["month"],false); - } else { - $month_current = strftime("%m",dol_now()); - if ($month_current >= 10) $q=4; - elseif ($month_current >= 7) $q=3; - elseif ($month_current >= 4) $q=2; - else $q=1; + if (empty($q)) + { + if (GETPOST("month")) { $date_start=dol_get_first_day($year_start,GETPOST("month"),false); $date_end=dol_get_last_day($year_start,GETPOST("month"),false); } + else + { + $date_start=dol_get_first_day($year_start,empty($conf->global->SOCIETE_FISCAL_MONTH_START)?1:$conf->global->SOCIETE_FISCAL_MONTH_START,false); + if (empty($conf->global->MAIN_INFO_VAT_RETURN) || $conf->global->MAIN_INFO_VAT_RETURN == 2) $date_end=dol_time_plus_duree($date_start, 3, 'm') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 3) $date_end=dol_time_plus_duree($date_start, 1, 'y') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 1) $date_end=dol_time_plus_duree($date_start, 1, 'm') - 1; } } - if ($q==1) { - $date_start=dol_get_first_day($year_start,1,false); - $date_end=dol_get_last_day($year_start,3,false); - } - if ($q==2) { - $date_start=dol_get_first_day($year_start,4,false); - $date_end=dol_get_last_day($year_start,6,false); - } - if ($q==3) { - $date_start=dol_get_first_day($year_start,7,false); - $date_end=dol_get_last_day($year_start,9,false); - } - if ($q==4) { - $date_start=dol_get_first_day($year_start,10,false); - $date_end=dol_get_last_day($year_start,12,false); + else + { + if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } + if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } + if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } + if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } } } -$min = price2num(GETPOST("min")); +$min = price2num(GETPOST("min","alpha")); if (empty($min)) $min = 0; // Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit +// 0=normal, 1=option vat for services is on debit, 2=option on payments for products $modetax = $conf->global->TAX_MODE; -if (isset($_REQUEST["modetax"])) $modetax=$_REQUEST["modetax"]; +if (GETPOSTISSET("modetax")) $modetax=GETPOST("modetax",'int'); if (empty($modetax)) $modetax=0; // Security check $socid = GETPOST('socid','int'); -if ($user->societe_id) { - $socid=$user->societe_id; -} +if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); // Define modecompta ('CREANCES-DETTES' or 'RECETTES-DEPENSES') @@ -132,93 +118,51 @@ $fsearch.=' '; $fsearch.=' '.$langs->trans("SalesTurnoverMinimum").': '; $fsearch.=' '; -// Affiche en-tete du rapport -if ($modetax==1) { // Calculate on invoice for goods and services - $name=$langs->trans("VATReportByCustomersInDueDebtMode"); - $calcmode=$langs->trans("CalcModeVATDebt"); - $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; - //$name.='
('.$langs->trans("SeeVATReportInInputOutputMode",'','').')'; - $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); - //$periodlink=($year_start?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesVATDueServices"); - $description.='
'; - $description.=$langs->trans("RulesVATDueProducts"); - //if ($conf->global->MAIN_MODULE_COMPTABILITE || $conf->global->MAIN_MODULE_ACCOUNTING) $description.='
'.img_warning().' '.$langs->trans('OptionVatInfoModuleComptabilite'); - //if (! empty($conf->global->MAIN_MODULE_COMPTABILITE)) $description.='
'.$langs->trans("WarningDepositsNotIncluded"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { - $description.='
'.$langs->trans("DepositsAreNotIncluded"); - } else { - $description.='
'.$langs->trans("DepositsAreIncluded"); - } - $description.=$fsearch; - $description.='
' - . ' ' - . $langs->trans('SimpleReport') - . '' - . '
' - . ' ' - . $langs->trans('AddExtraReport') - . '' - . '
'; - $builddate=dol_now(); - //$exportlink=$langs->trans("NotYetAvailable"); +$description=''; - $elementcust=$langs->trans("CustomersInvoices"); - $productcust=$langs->trans("Description"); - $amountcust=$langs->trans("AmountHT"); - if ($mysoc->tva_assuj) { - $vatcust.=' ('.$langs->trans("ToPay").')'; - } - $elementsup=$langs->trans("SuppliersInvoices"); - $productsup=$langs->trans("Description"); - $amountsup=$langs->trans("AmountHT"); - if ($mysoc->tva_assuj) { - $vatsup.=' ('.$langs->trans("ToGetBack").')'; - } +// Show report header +$name=$langs->trans("VATReportByCustomers"); +$calcmode=''; +if ($modetax == 0) $calcmode=$langs->trans('OptionVATDefault'); +if ($modetax == 1) $calcmode=$langs->trans('OptionVATDebitOption'); +if ($modetax == 2) $calcmode=$langs->trans('OptionPaymentForProductAndServices'); +$calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; + +if ($conf->global->TAX_MODE_SELL_PRODUCT == 'invoice') $description.=$langs->trans("RulesVATDueProducts"); +if ($conf->global->TAX_MODE_SELL_PRODUCT == 'payment') $description.=$langs->trans("RulesVATInProducts"); +if ($conf->global->TAX_MODE_SELL_SERVICE == 'invoice') $description.='
'.$langs->trans("RulesVATDueServices"); +if ($conf->global->TAX_MODE_SELL_SERVICE == 'payment') $description.='
'.$langs->trans("RulesVATInServices"); +if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $description.='
'.$langs->trans("DepositsAreNotIncluded"); } -if ($modetax==0) { // Invoice for goods, payment for services - $name=$langs->trans("VATReportByCustomersInInputOutputMode"); - $calcmode=$langs->trans("CalcModeVATEngagement"); - $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; - //$name.='
('.$langs->trans("SeeVATReportInDueDebtMode",'','').')'; - $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); - //$periodlink=($year_start?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesVATInServices"); - $description.=' '.$langs->trans("DepositsAreIncluded"); - $description.='
'; - $description.=$langs->trans("RulesVATInProducts"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { - $description .= ' ' . $langs->trans("DepositsAreNotIncluded"); - } else { - $description .= ' ' . $langs->trans("DepositsAreIncluded"); - } - //if ($conf->global->MAIN_MODULE_COMPTABILITE || $conf->global->MAIN_MODULE_ACCOUNTING) $description.='
'.img_warning().' '.$langs->trans('OptionVatInfoModuleComptabilite'); - //if (! empty($conf->global->MAIN_MODULE_COMPTABILITE)) $description.='
'.$langs->trans("WarningDepositsNotIncluded"); - $description.=$fsearch; - $description.='
' - . ' ' - . $langs->trans('SimpleReport') - . '' - . '
' - . ' ' - . $langs->trans('AddExtraReport') - . '' - . '
'; - $builddate=dol_now(); - //$exportlink=$langs->trans("NotYetAvailable"); +if (! empty($conf->global->MAIN_MODULE_ACCOUNTING)) $description.='
'.$langs->trans("ThisIsAnEstimatedValue"); - $elementcust=$langs->trans("CustomersInvoices"); - $productcust=$langs->trans("Description"); - $amountcust=$langs->trans("AmountHT"); - if ($mysoc->tva_assuj) { - $vatcust.=' ('.$langs->trans("ToPay").')'; - } - $elementsup=$langs->trans("SuppliersInvoices"); - $productsup=$langs->trans("Description"); - $amountsup=$langs->trans("AmountHT"); - if ($mysoc->tva_assuj) { - $vatsup.=' ('.$langs->trans("ToGetBack").')'; - } +$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); +//$periodlink=($year_start?"".img_previous()." ".img_next()."":""); +$description.=$fsearch; +$description.='
' + . ' ' + . $langs->trans('SimpleReport') + . '' + . '
' + . ' ' + . $langs->trans('AddExtraReport') + . '' + . '
'; +$builddate=dol_now(); +//$exportlink=$langs->trans("NotYetAvailable"); + +$elementcust=$langs->trans("CustomersInvoices"); +$productcust=$langs->trans("Description"); +$amountcust=$langs->trans("AmountHT"); +if ($mysoc->tva_assuj) { + $vatcust.=' ('.$langs->trans("ToPay").')'; +} +$elementsup=$langs->trans("SuppliersInvoices"); +$productsup=$langs->trans("Description"); +$amountsup=$langs->trans("AmountHT"); +if ($mysoc->tva_assuj) { + $vatsup.=' ('.$langs->trans("ToGetBack").')'; } report_header($name,'',$period,$periodlink,$description,$builddate,$exportlink,array(),$calcmode); diff --git a/htdocs/compta/tva/index.php b/htdocs/compta/tva/index.php index 89accf57492..1c9632fac61 100644 --- a/htdocs/compta/tva/index.php +++ b/htdocs/compta/tva/index.php @@ -1,7 +1,7 @@ * Copyright (C) 2004 Eric Seigne - * Copyright (C) 2004-2012 Laurent Destailleur + * Copyright (C) 2004-2018 Laurent Destailleur * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2014 Ferran Marcet * @@ -25,35 +25,58 @@ * \brief Index page of VAT reports */ require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/report.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/tax.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; -$langs->load("other"); -$langs->load("compta"); -$langs->load("banks"); -$langs->load("bills"); +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); +// Date range $year=GETPOST("year","int"); -if ($year == 0) +if (empty($year)) { - $year_current = strftime("%Y",time()); - $year_start = $year_current; + $year_current = strftime("%Y",dol_now()); + $year_start = $year_current; } else { - $year_current = $year; - $year_start = $year; + $year_current = $year; + $year_start = $year; +} +$date_start=dol_mktime(0,0,0,GETPOST("date_startmonth"),GETPOST("date_startday"),GETPOST("date_startyear")); +$date_end=dol_mktime(23,59,59,GETPOST("date_endmonth"),GETPOST("date_endday"),GETPOST("date_endyear")); +// Quarter +if (empty($date_start) || empty($date_end)) // We define date_start and date_end +{ + $q=GETPOST("q"); + if (empty($q)) + { + if (GETPOST("month")) { $date_start=dol_get_first_day($year_start,GETPOST("month"),false); $date_end=dol_get_last_day($year_start,GETPOST("month"),false); } + else + { + $date_start=dol_get_first_day($year_start, $conf->global->SOCIETE_FISCAL_MONTH_START,false); + $date_end=dol_time_plus_duree($date_start, 1, 'y') - 1; + } + } + else + { + if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } + if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } + if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } + if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + } } +// Define modetax (0 or 1) +// 0=normal, 1=option vat for services is on debit, 2=option on payments for products +$modetax = $conf->global->TAX_MODE; +if (GETPOSTISSET("modetax")) $modetax=GETPOST("modetax",'int'); +if (empty($modetax)) $modetax=0; + // Security check -$socid = isset($_GET["socid"])?$_GET["socid"]:''; +$socid = GETPOST('socid','int'); if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); -// Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit -$modetax = $conf->global->TAX_MODE; -if (isset($_GET["modetax"])) $modetax=$_GET["modetax"]; - /** * print function @@ -68,8 +91,7 @@ function pt ($db, $sql, $date) global $conf, $bc,$langs; $result = $db->query($sql); - if ($result) - { + if ($result) { $num = $db->num_rows($result); $i = 0; $total = 0; @@ -79,9 +101,8 @@ function pt ($db, $sql, $date) print '
'; print ''."\n"; print "\n"; - $var=True; - while ($i < $num) - { + + while ($i < $num) { $obj = $db->fetch_object($result); print ''; @@ -108,19 +129,45 @@ function pt ($db, $sql, $date) * View */ -llxHeader(); - +$form=new Form($db); +$company_static=new Societe($db); $tva = new Tva($db); +$description = ''; -$textprevyear="".img_previous($langs->trans("Previous"), 'class="valignbottom"').""; -$textnextyear=" ".img_next($langs->trans("Next"), 'class="valignbottom"').""; +// Show report header +$name = $langs->trans("ReportByMonth"); +$calcmode=''; +if ($modetax == 0) $calcmode=$langs->trans('OptionVATDefault'); +if ($modetax == 1) $calcmode=$langs->trans('OptionVATDebitOption'); +if ($modetax == 2) $calcmode=$langs->trans('OptionPaymentForProductAndServices'); +$calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; + +$description = $langs->trans("VATSummary").'
'; +if ($conf->global->TAX_MODE_SELL_PRODUCT == 'invoice') $description.=$langs->trans("RulesVATDueProducts"); +if ($conf->global->TAX_MODE_SELL_PRODUCT == 'payment') $description.=$langs->trans("RulesVATInProducts"); +if ($conf->global->TAX_MODE_SELL_SERVICE == 'invoice') $description.='
'.$langs->trans("RulesVATDueServices"); +if ($conf->global->TAX_MODE_SELL_SERVICE == 'payment') $description.='
'.$langs->trans("RulesVATInServices"); +if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $description.='
'.$langs->trans("DepositsAreNotIncluded"); +} +if (! empty($conf->global->MAIN_MODULE_ACCOUNTING)) $description.='
'.$langs->trans("ThisIsAnEstimatedValue"); + +$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); + +$builddate=dol_now(); + + +llxHeader('', $name); + +//$textprevyear="".img_previous($langs->trans("Previous"), 'class="valignbottom"').""; +//$textnextyear=" ".img_next($langs->trans("Next"), 'class="valignbottom"').""; +//print load_fiche_titre($langs->transcountry("VAT", $mysoc->country_code), $textprevyear." ".$langs->trans("Year")." ".$year_start." ".$textnextyear, 'title_accountancy.png'); + +report_header($name,'',$period,$periodlink,$description,$builddate,$exportlink,array(),$calcmode); +//report_header($name,'',$textprevyear.$langs->trans("Year")." ".$year_start.$textnextyear,'',$description,$builddate,$exportlink,array(),$calcmode); -print $conf->dol_optimize_smallscreen; -print load_fiche_titre($langs->transcountry("VAT", $mysoc->country_code), $textprevyear." ".$langs->trans("Year")." ".$year_start." ".$textnextyear, 'title_accountancy.png'); -print $langs->trans("VATReportBuildWithOptionDefinedInModule").'
'; -print '('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')
'; print '
'; print '
'; @@ -132,21 +179,28 @@ print '
'; print ''; print ''; print ''; -print ''; +print ''; print ''."\n"; print ''."\n"; +$tmp=dol_getdate($date_start); +$y = $tmp['year']; +$m = $tmp['mon']; +$tmp=dol_getdate($date_end); +$yend = $tmp['year']; +$mend = $tmp['mon']; -$y = $year_current ; - - -$var=True; $total=0; $subtotalcoll=0; $subtotalpaye=0; $subtotal=0; -$i=0; -for ($m = 1 ; $m < 13 ; $m++ ) +$i=0; $mcursor=0; +while ((($y < $yend) || ($y == $yend && $m < $mend)) && $mcursor < 1000) // $mcursor is to avoid too large loop { - $coll_listsell = vat_by_date($db, $y, 0, 0, 0, $modetax, 'sell', $m); - $coll_listbuy = vat_by_date($db, $y, 0, 0, 0, $modetax, 'buy', $m); + $m = $conf->global->SOCIETE_FISCAL_MONTH_START + ($mcursor % 12); + if ($m == 13) $y++; + if ($m > 12) $m -= 12; + $mcursor++; + + $coll_listsell = tax_by_date('vat', $db, $y, 0, 0, 0, $modetax, 'sell', $m); + $coll_listbuy = tax_by_date('vat', $db, $y, 0, 0, 0, $modetax, 'buy', $m); $action = "tva"; $object = array(&$coll_listsell, &$coll_listbuy); @@ -200,7 +254,8 @@ for ($m = 1 ; $m < 13 ; $m++ ) print "\n"; $i++; - if ($i > 2) { + if ($i > 2) + { print ''; print ''; print ''; @@ -230,8 +285,8 @@ print load_fiche_titre($langs->trans("VATPaid"), '', ''); $sql = "SELECT SUM(amount) as mm, date_format(f.datep,'%Y-%m') as dm"; $sql.= " FROM ".MAIN_DB_PREFIX."tva as f"; $sql.= " WHERE f.entity = ".$conf->entity; -$sql.= " AND f.datep >= '".$db->idate(dol_get_first_day($y,1,false))."'"; -$sql.= " AND f.datep <= '".$db->idate(dol_get_last_day($y,12,false))."'"; +$sql.= " AND f.datep >= '".$db->idate($date_start)."'"; +$sql.= " AND f.datep <= '".$db->idate($date_end)."'"; $sql.= " GROUP BY dm ORDER BY dm ASC"; pt($db, $sql,$langs->trans("Year")." $y"); @@ -240,7 +295,6 @@ pt($db, $sql,$langs->trans("Year")." $y"); print '
'; - if (! empty($conf->global->MAIN_FEATURES_LEVEL)) { /* @@ -252,8 +306,8 @@ if (! empty($conf->global->MAIN_FEATURES_LEVEL)) $sql1 = "SELECT SUM(amount) as mm, date_format(f.datev,'%Y') as dm"; $sql1 .= " FROM " . MAIN_DB_PREFIX . "tva as f"; $sql1 .= " WHERE f.entity = " . $conf->entity; - $sql1 .= " AND f.datev >= '" . $db->idate(dol_get_first_day($y, 1, false)) . "'"; - $sql1 .= " AND f.datev <= '" . $db->idate(dol_get_last_day($y, 12, false)) . "'"; + $sql1 .= " AND f.datev >= '" . $db->idate($date_start) . "'"; + $sql1 .= " AND f.datev <= '" . $db->idate($date_end) . "'"; $sql1 .= " GROUP BY dm ORDER BY dm ASC"; $result = $db->query($sql1); diff --git a/htdocs/compta/tva/info.php b/htdocs/compta/tva/info.php index f6026c8f8ea..6a3955403e7 100644 --- a/htdocs/compta/tva/info.php +++ b/htdocs/compta/tva/info.php @@ -53,7 +53,7 @@ $head = vat_prepare_head($object); dol_fiche_head($head, 'info', $langs->trans("VATPayment"), -1, 'payment'); -$linkback = ''.$langs->trans("BackToList").''; +$linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref', $morehtmlref, '', 0, '', ''); diff --git a/htdocs/compta/tva/reglement.php b/htdocs/compta/tva/list.php similarity index 95% rename from htdocs/compta/tva/reglement.php rename to htdocs/compta/tva/list.php index 66099cd63db..e796ddacac7 100644 --- a/htdocs/compta/tva/reglement.php +++ b/htdocs/compta/tva/list.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2016 Laurent Destailleur + * Copyright (C) 2004-2018 Laurent Destailleur * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2011-2017 Alexandre Spangaro * @@ -19,7 +19,7 @@ */ /** - * \file htdocs/compta/tva/reglement.php + * \file htdocs/compta/tva/list.php * \ingroup tax * \brief List of VAT payments */ @@ -35,7 +35,7 @@ $langs->load("compta"); $langs->load("bills"); // Security check -$socid = isset($_GET["socid"])?$_GET["socid"]:''; +$socid = GETPOST('socid','int'); if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); @@ -100,7 +100,7 @@ $bankstatic = new Account($db); $sql = "SELECT t.rowid, t.amount, t.label, t.datev, t.datep, t.fk_typepayment as type, t.num_payment, t.fk_bank, pst.code as payment_code,"; $sql.= " ba.rowid as bid, ba.ref as bref, ba.number as bnumber, ba.account_number, ba.fk_accountancy_journal, ba.label as blabel"; $sql.= " FROM ".MAIN_DB_PREFIX."tva as t"; -$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON t.fk_typepayment = pst.id AND pst.entity IN (".getEntity('c_paiement').")"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON t.fk_typepayment = pst.id"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON t.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid"; $sql.= " WHERE t.entity IN (".getEntity('tax').")"; @@ -148,6 +148,11 @@ if ($result) if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; if ($typeid) $param.='&typeid='.$typeid; + $newcardbutton=''; + if ($user->rights->tax->charges->creer) + { + $newcardbutton=''.$langs->trans('NewVATPayment').''; + } print ''; if ($optioncss != '') print ''; @@ -157,7 +162,7 @@ if ($result) print ''; print ''; - print_barre_liste($langs->trans("VATPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$totalnboflines, 'title_accountancy', 0, '', '', $limit); + print_barre_liste($langs->trans("VATPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$totalnboflines, 'title_accountancy', 0, $newcardbutton, '', $limit); print '
'; print '
 '; - if (isset($decaiss_ttc[$case]) && $decaiss_ttc[$case] != 0) + if ($modecompta == 'BOOKKEEPING') { - print ''.price(price2num($decaiss_ttc[$case],'MT')).''; - if (! isset($totsorties[$annee])) $totsorties[$annee]=0; - $totsorties[$annee]+=$decaiss_ttc[$case]; + if (isset($decaiss[$case]) && $decaiss[$case] != 0) + { + print ''.price(price2num($decaiss[$case],'MT')).''; + if (! isset($totsorties[$annee])) $totsorties[$annee]=0; + $totsorties[$annee]+=$decaiss[$case]; + } + } + else + { + if (isset($decaiss_ttc[$case]) && $decaiss_ttc[$case] != 0) + { + print ''.price(price2num($decaiss_ttc[$case],'MT')).''; + if (! isset($totsorties[$annee])) $totsorties[$annee]=0; + $totsorties[$annee]+=$decaiss_ttc[$case]; + } } print " '; - //if (isset($encaiss_ttc[$case]) && $encaiss_ttc[$case] != 0) - if (isset($encaiss_ttc[$case])) + if ($modecompta == 'BOOKKEEPING') { - print ''.price(price2num($encaiss_ttc[$case],'MT')).''; - if (! isset($totentrees[$annee])) $totentrees[$annee]=0; - $totentrees[$annee]+=$encaiss_ttc[$case]; + if (isset($encaiss[$case])) + { + print ''.price(price2num($encaiss[$case],'MT')).''; + if (! isset($totentrees[$annee])) $totentrees[$annee]=0; + $totentrees[$annee]+=$encaiss[$case]; + } + } + else + { + if (isset($encaiss_ttc[$case])) + { + print ''.price(price2num($encaiss_ttc[$case],'MT')).''; + if (! isset($totentrees[$annee])) $totentrees[$annee]=0; + $totentrees[$annee]+=$encaiss_ttc[$case]; + } } print "
'.$langs->trans("TotalTTC").'
'; +if ($modecompta == 'BOOKKEEPING') print $langs->trans("Total"); +else print $langs->trans("TotalTTC"); +print '
'; print $cat['code']; - print ''; + print ''; print $cat['label']; if (count($cpts) > 0) // Show example of 5 first accounting accounts { diff --git a/htdocs/compta/salaries/card.php b/htdocs/compta/salaries/card.php index 608b5a737b0..4f28e2c76c9 100644 --- a/htdocs/compta/salaries/card.php +++ b/htdocs/compta/salaries/card.php @@ -73,7 +73,7 @@ if ($action == 'add' && $_POST["cancel"] <> $langs->trans("Cancel")) $dateep=dol_mktime(12,0,0, $_POST["dateepmonth"], $_POST["dateepday"], $_POST["dateepyear"]); if (empty($datev)) $datev=$datep; - $type_payment = dol_getIdFromCode($db, GETPOST("paymenttype", 'alpha'), 'c_paiement'); + $type_payment = dol_getIdFromCode($db, GETPOST("paymenttype", 'alpha'), 'c_paiement', 'code', 'id', 1); $object->accountid=GETPOST("accountid") > 0 ? GETPOST("accountid","int") : 0; $object->fk_user=GETPOST("fk_user") > 0 ? GETPOST("fk_user","int") : 0; diff --git a/htdocs/compta/salaries/class/paymentsalary.class.php b/htdocs/compta/salaries/class/paymentsalary.class.php index eab661c6614..71cfe997fe0 100644 --- a/htdocs/compta/salaries/class/paymentsalary.class.php +++ b/htdocs/compta/salaries/class/paymentsalary.class.php @@ -31,9 +31,9 @@ require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php'; */ class PaymentSalary extends CommonObject { - //public $element='payment_salary'; //!< Id that identify managed objects - //public $table_element='payment_salary'; //!< Name of table without prefix where object is stored - public $picto='payment'; + public $element='payment_salary'; //!< Id that identify managed objects + public $table_element='payment_salary'; //!< Name of table without prefix where object is stored + public $picto='payment'; public $tms; public $fk_user; diff --git a/htdocs/compta/salaries/class/salariesstats.class.php b/htdocs/compta/salaries/class/salariesstats.class.php new file mode 100644 index 00000000000..647dc27f4a5 --- /dev/null +++ b/htdocs/compta/salaries/class/salariesstats.class.php @@ -0,0 +1,164 @@ + + * Copyright (c) 2018 Fidesio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/compta/salaries/class/salariesstats.class.php + * \ingroup salaries + * \brief Fichier de la classe de gestion des stats des salaires + */ +include_once DOL_DOCUMENT_ROOT . '/core/class/stats.class.php'; +include_once DOL_DOCUMENT_ROOT . '/compta/salaries/class/paymentsalary.class.php'; + +/** + * Classe permettant la gestion des stats des salaires + */ +class SalariesStats extends Stats +{ + public $table_element; + + var $socid; + var $userid; + + var $from; + var $field; + var $where; + + /** + * Constructor + * + * @param DoliDB $db Database handler + * @param int $socid Id third party + * @param mixed $userid Id user for filter or array of user ids + * @return void + */ + function __construct($db, $socid=0, $userid=0) + { + global $conf; + + $this->db = $db; + $this->socid = $socid; + $this->userid = $userid; + + $object=new PaymentSalary($this->db); + $this->from = MAIN_DB_PREFIX.$object->table_element; + $this->field='amount'; + + $this->where.= " entity = ".$conf->entity; + if ($this->socid) + { + $this->where.=" AND fk_soc = ".$this->socid; + } + if (is_array($this->userid) && count($this->userid) > 0) $this->where.=' AND fk_user IN ('.join(',',$this->userid).')'; + else if ($this->userid > 0) $this->where.=' AND fk_user = '.$this->userid; + } + + + /** + * Return the number of salary by year + * + * @return array Array of values + */ + function getNbByYear() + { + $sql = "SELECT YEAR(datep) as dm, count(*)"; + $sql.= " FROM ".$this->from; + $sql.= " GROUP BY dm DESC"; + $sql.= " WHERE ".$this->where; + + return $this->_getNbByYear($sql); + } + + + /** + * Return the number of salary by month, for a given year + * + * @param string $year Year to scan + * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is month number, 2=Label of absiss is first letter of month + * @return array Array of values + */ + function getNbByMonth($year, $format=0) + { + $sql = "SELECT MONTH(datep) as dm, count(*)"; + $sql.= " FROM ".$this->from; + $sql.= " WHERE YEAR(datep) = ".$year; + $sql.= " AND ".$this->where; + $sql.= " GROUP BY dm"; + $sql.= $this->db->order('dm','DESC'); + + $res=$this->_getNbByMonth($year, $sql, $format); + //var_dump($res);print '
'; + return $res; + } + + + /** + * Return amount of salaries by month for a given year + * + * @param int $year Year to scan + * @param int $format 0=Label of absiss is a translated text, 1=Label of absiss is month number, 2=Label of absiss is first letter of month + * @return array Array of values + */ + function getAmountByMonth($year, $format=0) + { + $sql = "SELECT date_format(datep,'%m') as dm, sum(".$this->field.")"; + $sql.= " FROM ".$this->from; + $sql.= " WHERE date_format(datep,'%Y') = '".$year."'"; + $sql.= " AND ".$this->where; + $sql.= " GROUP BY dm"; + $sql.= $this->db->order('dm','DESC'); + + $res=$this->_getAmountByMonth($year, $sql, $format); + //var_dump($res);print '
'; + return $res; + } + + /** + * Return average amount + * + * @param int $year Year to scan + * @return array Array of values + */ + function getAverageByMonth($year) + { + $sql = "SELECT date_format(datep,'%m') as dm, avg(".$this->field.")"; + $sql.= " FROM ".$this->from; + $sql.= " WHERE date_format(datep,'%Y') = '".$year."'"; + $sql.= " AND ".$this->where; + $sql.= " GROUP BY dm"; + $sql.= $this->db->order('dm','DESC'); + + return $this->_getAverageByMonth($year, $sql); + } + + /** + * Return nb, total and average + * + * @return array Array of values + */ + function getAllByYear() + { + $sql = "SELECT date_format(datep,'%Y') as year, count(*) as nb, sum(".$this->field.") as total, avg(".$this->field.") as avg"; + $sql.= " FROM ".$this->from; + $sql.= " WHERE ".$this->where; + $sql.= " GROUP BY year"; + $sql.= $this->db->order('year','DESC'); + + return $this->_getAllByYear($sql); + } +} + diff --git a/htdocs/compta/salaries/index.php b/htdocs/compta/salaries/index.php index 47cafad0f0a..f9b9adf616c 100644 --- a/htdocs/compta/salaries/index.php +++ b/htdocs/compta/salaries/index.php @@ -105,7 +105,7 @@ $sql.= " s.rowid, s.fk_user, s.amount, s.salary, s.label, s.datep as datep, s.da $sql.= " ba.rowid as bid, ba.ref as bref, ba.number as bnumber, ba.account_number, ba.fk_accountancy_journal, ba.label as blabel,"; $sql.= " pst.code as payment_code"; $sql.= " FROM ".MAIN_DB_PREFIX."payment_salary as s"; -$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON s.fk_typepayment = pst.id AND pst.entity IN (".getEntity('c_paiement').")"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pst ON s.fk_typepayment = pst.id"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank as b ON s.fk_bank = b.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_account as ba ON b.fk_account = ba.rowid,"; $sql.= " ".MAIN_DB_PREFIX."user as u"; @@ -150,6 +150,8 @@ if ($result) if ($typeid) $param.='&typeid='.$typeid; if ($optioncss != '') $param.='&optioncss='.$optioncss; + $newcardbutton=''.$langs->trans('NewSalaryPayment').''; + print ''; if ($optioncss != '') print ''; print ''; @@ -159,7 +161,7 @@ if ($result) print ''; print ''; - print_barre_liste($langs->trans("SalariesPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num, $totalnboflines, 'title_accountancy.png', 0, '', '', $limit); + print_barre_liste($langs->trans("SalariesPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num, $totalnboflines, 'title_accountancy.png', 0, $newcardbutton, '', $limit); print '
'; print ''."\n"; @@ -179,7 +181,7 @@ if ($result) print ''; // Type print ''; // Account if (! empty($conf->banque->enabled)) diff --git a/htdocs/compta/salaries/stats/index.php b/htdocs/compta/salaries/stats/index.php new file mode 100644 index 00000000000..0ba0c29ea6f --- /dev/null +++ b/htdocs/compta/salaries/stats/index.php @@ -0,0 +1,293 @@ + + * Copyright (C) 2018 Fidesio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/compta/salaries/stats/index.php + * \ingroup salaries + * \brief Page for statistics of module salaries + */ + +require '../../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/salaries/class/salariesstats.class.php'; + +$langs->load("salaries"); +$langs->load("companies"); + +$WIDTH=DolGraph::getDefaultGraphSizeForStats('width'); +$HEIGHT=DolGraph::getDefaultGraphSizeForStats('height'); + +$userid=GETPOST('userid','int'); if ($userid < 0) $userid=0; +$socid=GETPOST('socid','int'); if ($socid < 0) $socid=0; +$id = GETPOST('id','int'); + +// Security check +$socid = GETPOST("socid","int"); +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'salaries', '', '', ''); + +// Other security check +$childids = $user->getAllChildIds(); +$childids[]=$user->id; +if ($userid > 0) +{ + if (empty($user->rights->salaries->payment->readall) && ! in_array($userid, $childids)) + { + accessforbidden(); + exit; + } +} + +$nowyear=strftime("%Y", dol_now()); +$year = GETPOST('year')>0?GETPOST('year'):$nowyear; +//$startyear=$year-2; +$startyear=$year-1; +$endyear=$year; + +/* + * View + */ + +$form=new Form($db); + + +llxHeader(); + +$title=$langs->trans("SalariesStatistics"); +$dir=$conf->salaries->dir_temp; + +print load_fiche_titre($title, $mesg); + +dol_mkdir($dir); + +$useridtofilter=$userid; // Filter from parameters +if (empty($useridtofilter)) +{ + $useridtofilter=$childids; + if (! empty($user->rights->salaries->payment->readall)) $useridtofilter=0; +} + +$stats = new SalariesStats($db, $socid, $useridtofilter); + + +// Build graphic number of object +// $data = array(array('Lib',val1,val2,val3),...) +//print "$endyear, $startyear"; +$data = $stats->getNbByMonthWithPrevYear($endyear,$startyear); +//var_dump($data); + +$filenamenb = $dir."/salariesnbinyear-".$year.".png"; +$fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=salariesstats&file=salariesnbinyear-'.$year.'.png'; + +$px1 = new DolGraph(); +$mesg = $px1->isGraphKo(); +if (! $mesg) +{ + $px1->SetData($data); + $px1->SetPrecisionY(0); + $i=$startyear;$legend=array(); + while ($i <= $endyear) + { + $legend[]=$i; + $i++; + } + $px1->SetLegend($legend); + $px1->SetMaxValue($px1->GetCeilMaxValue()); + $px1->SetWidth($WIDTH); + $px1->SetHeight($HEIGHT); + $px1->SetYLabel($langs->trans("Number")); + $px1->SetShading(3); + $px1->SetHorizTickIncrement(1); + $px1->SetPrecisionY(0); + $px1->mode='depth'; + $px1->SetTitle($langs->trans("NumberByMonth")); + + $px1->draw($filenamenb,$fileurlnb); +} + +// Build graphic amount of object +$data = $stats->getAmountByMonthWithPrevYear($endyear,$startyear); +//var_dump($data); +// $data = array(array('Lib',val1,val2,val3),...) + +$filenameamount = $dir."/salariesamountinyear-".$year.".png"; +$fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=salariesstats&file=salariesamountinyear-'.$year.'.png'; + +$px2 = new DolGraph(); +$mesg = $px2->isGraphKo(); +if (! $mesg) +{ + $px2->SetData($data); + $i=$startyear;$legend=array(); + while ($i <= $endyear) + { + $legend[]=$i; + $i++; + } + $px2->SetLegend($legend); + $px2->SetMaxValue($px2->GetCeilMaxValue()); + $px2->SetMinValue(min(0,$px2->GetFloorMinValue())); + $px2->SetWidth($WIDTH); + $px2->SetHeight($HEIGHT); + $px2->SetYLabel($langs->trans("Amount")); + $px2->SetShading(3); + $px2->SetHorizTickIncrement(1); + $px2->SetPrecisionY(0); + $px2->mode='depth'; + $px2->SetTitle($langs->trans("AmountTotal")); + + $px2->draw($filenameamount,$fileurlamount); +} + + +$data = $stats->getAverageByMonthWithPrevYear($endyear, $startyear); + +$filename_avg = $dir."/salariesaverageinyear-".$year.".png"; +$fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=salariesstats&file=salariesaverageinyear-'.$year.'.png'; + +$px3 = new DolGraph(); +$mesg = $px3->isGraphKo(); +if (! $mesg) +{ + $px3->SetData($data); + $i = $startyear;$legend=array(); + while ($i <= $endyear) + { + $legend[]=$i; + $i++; + } + $px3->SetLegend($legend); + $px3->SetYLabel($langs->trans("AmountAverage")); + $px3->SetMaxValue($px3->GetCeilMaxValue()); + $px3->SetMinValue($px3->GetFloorMinValue()); + $px3->SetWidth($WIDTH); + $px3->SetHeight($HEIGHT); + $px3->SetShading(3); + $px3->SetHorizTickIncrement(1); + $px3->SetPrecisionY(0); + $px3->mode='depth'; + $px3->SetTitle($langs->trans("AmountAverage")); + + $px3->draw($filename_avg,$fileurl_avg); +} + + +// Show array +$data = $stats->getAllByYear(); +$arrayyears=array(); +foreach($data as $val) { + $arrayyears[$val['year']]=$val['year']; +} +if (! count($arrayyears)) $arrayyears[$nowyear]=$nowyear; + + +$h=0; +$head = array(); +$head[$h][0] = DOL_URL_ROOT . '/compta/salaries/stats/index.php'; +$head[$h][1] = $langs->trans("ByMonthYear"); +$head[$h][2] = 'byyear'; +$h++; + +complete_head_from_modules($conf,$langs,null,$head,$h,'trip_stats'); + +dol_fiche_head($head, 'byyear', $langs->trans("Statistics"), -1); + + +print '
'; + + +// Show filter box +print ''; +print '
 '; - $form->select_types_paiements($typeid,'typeid','',0,0,1,16); + $form->select_types_paiements($typeid,'typeid','',0,1,1,16); print '
'; +print ''; +// User +print ''; +// Year +print ''; +print ''; +print '
'.$langs->trans("Filter").'
'.$langs->trans("User").''; +print $form->select_dolusers($userid, 'userid', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth300'); +print '
'.$langs->trans("Year").''; +if (! in_array($year,$arrayyears)) $arrayyears[$year]=$year; +arsort($arrayyears); +print $form->selectarray('year',$arrayyears,$year,0); +print '
'; +print ''; +print '

'; + +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +$oldyear=0; +foreach ($data as $val) +{ + $year = $val['year']; + while ($year && $oldyear > $year+1) + { + // If we have empty year + $oldyear--; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + $oldyear=$year; +} + +print '
'.$langs->trans("Year").''.$langs->trans("Number").''.$langs->trans("AmountTotal").''.$langs->trans("AmountAverage").'
'.$oldyear.'000
'.$year.''.$val['nb'].''.price(price2num($val['total'],'MT'),1).''.price(price2num($val['avg'],'MT'),1).'
'; + + +print '
'; + + +// Show graphs +print '
'; +if ($mesg) { print $mesg; } +else { + print $px1->show(); + print "
\n"; + print $px2->show(); + print "
\n"; + print $px3->show(); +} +print '
'; + + +print '
'; +print '
'; + + +dol_fiche_end(); + + +llxFooter(); + +$db->close(); diff --git a/htdocs/compta/sociales/card.php b/htdocs/compta/sociales/card.php index 23e3494a25d..cdcabc5617b 100644 --- a/htdocs/compta/sociales/card.php +++ b/htdocs/compta/sociales/card.php @@ -586,7 +586,7 @@ if ($id > 0) $sql.= " FROM ".MAIN_DB_PREFIX."paiementcharge as p"; $sql.= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'bank as b ON p.fk_bank = b.rowid'; $sql.= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'bank_account as ba ON b.fk_account = ba.rowid'; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_typepaiement = c.id AND c.entity IN (" . getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_typepaiement = c.id"; $sql.= ", ".MAIN_DB_PREFIX."chargesociales as cs"; $sql.= " WHERE p.fk_charge = ".$id; $sql.= " AND p.fk_charge = cs.rowid"; diff --git a/htdocs/compta/sociales/class/chargesociales.class.php b/htdocs/compta/sociales/class/chargesociales.class.php index 2d75584cbda..fa4127c3af6 100644 --- a/htdocs/compta/sociales/class/chargesociales.class.php +++ b/htdocs/compta/sociales/class/chargesociales.class.php @@ -83,7 +83,7 @@ class ChargeSociales extends CommonObject $sql.= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle'; $sql.= " FROM ".MAIN_DB_PREFIX."chargesociales as cs"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_chargesociales as c ON cs.fk_type = c.id"; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON cs.fk_mode_reglement = p.id AND p.entity IN ('.getEntity('c_paiement').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON cs.fk_mode_reglement = p.id'; $sql.= ' WHERE cs.entity IN ('.getEntity('tax').')'; if ($ref) $sql.= " AND cs.rowid = ".$ref; else $sql.= " AND cs.rowid = ".$id; diff --git a/htdocs/compta/sociales/class/paymentsocialcontribution.class.php b/htdocs/compta/sociales/class/paymentsocialcontribution.class.php index 60a1a647661..4cd0dfd2b59 100644 --- a/htdocs/compta/sociales/class/paymentsocialcontribution.class.php +++ b/htdocs/compta/sociales/class/paymentsocialcontribution.class.php @@ -203,7 +203,7 @@ class PaymentSocialContribution extends CommonObject $sql.= " t.fk_user_modif,"; $sql.= " pt.code as type_code, pt.libelle as type_libelle,"; $sql.= ' b.fk_account'; - $sql.= " FROM ".MAIN_DB_PREFIX."paiementcharge as t LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepaiement = pt.id AND pt.entity IN (" . getEntity('c_paiement') . ")"; + $sql.= " FROM ".MAIN_DB_PREFIX."paiementcharge as t LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepaiement = pt.id"; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid'; $sql.= " WHERE t.rowid = ".$id; // TODO link on entity of tax; diff --git a/htdocs/compta/sociales/index.php b/htdocs/compta/sociales/index.php index 281a0afb377..3850ddfe5c5 100644 --- a/htdocs/compta/sociales/index.php +++ b/htdocs/compta/sociales/index.php @@ -146,6 +146,12 @@ if ($resql) if ($year) $param.='&year='.$year; if ($typeid) $param.='&typeid='.$typeid; + $newcardbutton=''; + if($user->rights->tax->charges->creer) + { + $newcardbutton=''.$langs->trans('MenuNewSocialContribution').''; + } + print '
'; if ($optioncss != '') print ''; print ''; @@ -158,11 +164,11 @@ if ($resql) if ($year) { $center=($year?"".img_previous()." ".$langs->trans("Year")." $year ".img_next()."":""); - print_barre_liste($langs->trans("SocialContributions"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $center, $num, $totalnboflines, 'title_accountancy.png', 0, '', '', $limit); + print_barre_liste($langs->trans("SocialContributions"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $center, $num, $totalnboflines, 'title_accountancy.png', 0, $newcardbutton, '', $limit); } else { - print_barre_liste($langs->trans("SocialContributions"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $totalnboflines, 'title_accountancy.png', 0, '', '', $limit); + print_barre_liste($langs->trans("SocialContributions"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $totalnboflines, 'title_accountancy.png', 0, $newcardbutton, '', $limit); } if (empty($mysoc->country_id) && empty($mysoc->country_code)) diff --git a/htdocs/compta/sociales/payments.php b/htdocs/compta/sociales/payments.php index a7c388a26e6..879e9b69ee7 100644 --- a/htdocs/compta/sociales/payments.php +++ b/htdocs/compta/sociales/payments.php @@ -133,7 +133,7 @@ if (! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) $sql.= " FROM ".MAIN_DB_PREFIX."c_chargesociales as c,"; $sql.= " ".MAIN_DB_PREFIX."chargesociales as cs,"; $sql.= " ".MAIN_DB_PREFIX."paiementcharge as pc"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON pc.fk_typepaiement = pct.id AND pct.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pct ON pc.fk_typepaiement = pct.id"; $sql.= " WHERE cs.fk_type = c.id AND pc.fk_charge = cs.rowid"; $sql.= " AND cs.entity = ".$conf->entity; if ($year > 0) diff --git a/htdocs/compta/stats/casoc.php b/htdocs/compta/stats/casoc.php index f59b6fc454f..b7b37d0837c 100644 --- a/htdocs/compta/stats/casoc.php +++ b/htdocs/compta/stats/casoc.php @@ -63,18 +63,18 @@ if (! empty($conf->comptabilite->enabled)) $result=restrictedArea($user,'compta' if (! empty($conf->accounting->enabled)) $result=restrictedArea($user,'accounting','','','comptarapport'); // Date range -$year=GETPOST("year"); -$month=GETPOST("month"); -$search_societe = GETPOST("search_societe"); -$search_zip = GETPOST("search_zip"); -$search_town = GETPOST("search_town"); -$search_country = GETPOST("search_country"); -$date_startyear = GETPOST("date_startyear"); -$date_startmonth = GETPOST("date_startmonth"); -$date_startday = GETPOST("date_startday"); -$date_endyear = GETPOST("date_endyear"); -$date_endmonth = GETPOST("date_endmonth"); -$date_endday = GETPOST("date_endday"); +$year=GETPOST("year",'int'); +$month=GETPOST("month",'int'); +$search_societe = GETPOST("search_societe",'alpha'); +$search_zip = GETPOST("search_zip",'alpha'); +$search_town = GETPOST("search_town",'alpha'); +$search_country = GETPOST("search_country",'alpha'); +$date_startyear = GETPOST("date_startyear",'alpha'); +$date_startmonth = GETPOST("date_startmonth",'alpha'); +$date_startday = GETPOST("date_startday",'alpha'); +$date_endyear = GETPOST("date_endyear",'alpha'); +$date_endmonth = GETPOST("date_endmonth",'alpha'); +$date_endday = GETPOST("date_endday",'alpha'); if (empty($year)) { $year_current = strftime("%Y",dol_now()); @@ -85,8 +85,8 @@ if (empty($year)) $month_current = strftime("%m",dol_now()); $year_start = $year; } -$date_start=dol_mktime(0,0,0,$_REQUEST["date_startmonth"],$_REQUEST["date_startday"],$_REQUEST["date_startyear"]); -$date_end=dol_mktime(23,59,59,$_REQUEST["date_endmonth"],$_REQUEST["date_endday"],$_REQUEST["date_endyear"]); +$date_start=dol_mktime(0,0,0,GETPOST("date_startmonth"),GETPOST("date_startday"),GETPOST("date_startyear")); +$date_end=dol_mktime(23,59,59,GETPOST("date_endmonth"),GETPOST("date_endday"),GETPOST("date_endyear")); // Quarter if (empty($date_start) || empty($date_end)) // We define date_start and date_end { diff --git a/htdocs/compta/stats/index.php b/htdocs/compta/stats/index.php index 335cf581740..598de2ba490 100644 --- a/htdocs/compta/stats/index.php +++ b/htdocs/compta/stats/index.php @@ -142,9 +142,7 @@ else if ($modecompta=="BOOKKEEPING") $calcmode.='
('.$langs->trans("SeeReportInInputOutputMode",'','').')'; $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); $periodlink=($year_start?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesCADue"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $description.= $langs->trans("DepositsAreNotIncluded"); - else $description.= $langs->trans("DepositsAreIncluded"); + $description=$langs->trans("RulesCATotalSaleJournal"); $builddate=dol_now(); //$exportlink=$langs->trans("NotYetAvailable"); } @@ -187,15 +185,14 @@ if ($socid) $sql.= " AND f.fk_soc = ".$socid; else if ($modecompta=="BOOKKEEPING") { $sql = "SELECT date_format(b.doc_date,'%Y-%m') as dm, sum(b.credit) as amount_ttc"; - $sql.= " FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as b"; - $sql.= " WHERE b.numero_compte IN (SELECT a.account_number" ; - $sql.= " FROM ".MAIN_DB_PREFIX."accounting_account as a"; - $sql.= " WHERE a.fk_accounting_category = 1 ) " ; // todo sql with accounting category, but we need to define category in turnover + $sql.= " FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as b, ".MAIN_DB_PREFIX."accounting_journal as aj"; + $sql.= " WHERE b.entity = ".$conf->entity; + $sql.= " AND b.code_journal = aj.code AND aj.nature = 2" ; // @TODO currently count amount in sale journal, but we need to define a category group for turnover } - $sql.= " GROUP BY dm"; $sql.= " ORDER BY dm"; +//print $sql; $result = $db->query($sql); if ($result) @@ -221,7 +218,7 @@ else { } // On ajoute les paiements anciennes version, non lies par paiement_facture (very old versions) -if ($modecompta != 'CREANCES-DETTES') +if ($modecompta == 'RECETTES-DEPENSES') { $sql = "SELECT date_format(p.datep,'%Y-%m') as dm, sum(p.amount) as amount_ttc"; $sql.= " FROM ".MAIN_DB_PREFIX."bank as b"; @@ -269,10 +266,11 @@ for ($annee = $year_start ; $annee <= $year_end ; $annee++) { if ($modecompta == 'CREANCES-DETTES') print '
'; else print ''; - print ''; + if ($modecompta != 'BOOKKEEPING') print ''; print $annee; if ($conf->global->SOCIETE_FISCAL_MONTH_START > 1) print '-'.($annee+1); - print ' 
'.$langs->trans("Amount").' 
'.$langs->trans("Year")." ".$y.''.$langs->trans("VATToPay").''.$langs->trans("VATToCollect").''.$langs->trans("TotalToPay").''.$langs->trans("Balance").' 
'.$langs->trans("SubTotal").':'.price($subtotalcoll).'
'; @@ -173,7 +178,7 @@ if ($result) print ''; // Type print ''; // Account if (! empty($conf->banque->enabled)) diff --git a/htdocs/compta/tva/quadri.php b/htdocs/compta/tva/quadri.php index 6d33c358af2..e572eb7e006 100644 --- a/htdocs/compta/tva/quadri.php +++ b/htdocs/compta/tva/quadri.php @@ -29,6 +29,8 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); + $year = GETPOST('year', 'int'); if ($year == 0 ) { @@ -40,7 +42,7 @@ if ($year == 0 ) } // Security check -$socid = isset($_GET["socid"])?$_GET["socid"]:''; +$socid = GETPOST('socid','int'); if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'tax', '', '', 'charges'); @@ -259,7 +261,7 @@ if ($conf->global->ACCOUNTING_MODE == "CREANCES-DETTES") $x_paye_sum = 0; $x_paye_ht = 0; foreach($x_both as $rate => $both){ - + print ''; print ""; print ""; @@ -282,7 +284,7 @@ if ($conf->global->ACCOUNTING_MODE == "CREANCES-DETTES") $total = $total + $diff; $subtotal = $subtotal + $diff; - + print ''; print ''; print "\n"; diff --git a/htdocs/compta/tva/quadri_detail.php b/htdocs/compta/tva/quadri_detail.php index c622881d273..713cab1dc13 100644 --- a/htdocs/compta/tva/quadri_detail.php +++ b/htdocs/compta/tva/quadri_detail.php @@ -1,10 +1,9 @@ - * Copyright (C) 2004 Eric Seigne - * Copyright (C) 2004-2013 Laurent Destailleur - * Copyright (C) 2006-2007 Yannick Warnier - * Copyright (C) 2014 Ferran Marcet - * Copyright (C) 2016 Alexandre Spangaro +/* Copyright (C) 2001-2003 Rodolphe Quiedeville + * Copyright (C) 2004 Eric Seigne + * Copyright (C) 2004-2013 Laurent Destailleur + * Copyright (C) 2006-2007, 2015 Yannick Warnier + * Copyright (C) 2014 Ferran Marcet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,10 +20,9 @@ */ /** - * \file htdocs/compta/tva/quadri_detail.php - * \ingroup tax + * \file htdocs/compta/tva/quadri_detail.php + * \ingroup tax * \brief Trimestrial page - detailed version - * TODO Deal with recurrent invoices as well */ require '../../main.inc.php'; @@ -40,12 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php'; require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php'; require_once DOL_DOCUMENT_ROOT.'/expensereport/class/paymentexpensereport.class.php'; -$langs->load("bills"); -$langs->load("compta"); -$langs->load("companies"); -$langs->load("products"); -$langs->load("trips"); -$langs->load("other"); +$langs->loadLangs(array("other","compta","banks","bills","companies","product","trips","admin")); // Date range $year=GETPOST("year","int"); @@ -68,26 +61,28 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end if (GETPOST("month")) { $date_start=dol_get_first_day($year_start,GETPOST("month"),false); $date_end=dol_get_last_day($year_start,GETPOST("month"),false); } else { - $month_current = strftime("%m",dol_now()); - if ($month_current >= 10) $q=4; - elseif ($month_current >= 7) $q=3; - elseif ($month_current >= 4) $q=2; - else $q=1; + $date_start=dol_get_first_day($year_start,empty($conf->global->SOCIETE_FISCAL_MONTH_START)?1:$conf->global->SOCIETE_FISCAL_MONTH_START,false); + if (empty($conf->global->MAIN_INFO_VAT_RETURN) || $conf->global->MAIN_INFO_VAT_RETURN == 2) $date_end=dol_time_plus_duree($date_start, 3, 'm') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 3) $date_end=dol_time_plus_duree($date_start, 1, 'y') - 1; + else if ($conf->global->MAIN_INFO_VAT_RETURN == 1) $date_end=dol_time_plus_duree($date_start, 1, 'm') - 1; } } - if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } - if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } - if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } - if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + else + { + if ($q==1) { $date_start=dol_get_first_day($year_start,1,false); $date_end=dol_get_last_day($year_start,3,false); } + if ($q==2) { $date_start=dol_get_first_day($year_start,4,false); $date_end=dol_get_last_day($year_start,6,false); } + if ($q==3) { $date_start=dol_get_first_day($year_start,7,false); $date_end=dol_get_last_day($year_start,9,false); } + if ($q==4) { $date_start=dol_get_first_day($year_start,10,false); $date_end=dol_get_last_day($year_start,12,false); } + } } -$min = GETPOST("min"); +$min = price2num(GETPOST("min","alpha")); if (empty($min)) $min = 0; // Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit +// 0=normal, 1=option vat for services is on debit, 2=option on payments for products $modetax = $conf->global->TAX_MODE; -if (isset($_REQUEST["modetax"])) $modetax=$_REQUEST["modetax"]; +if (GETPOSTISSET("modetax")) $modetax=GETPOST("modetax",'int'); if (empty($modetax)) $modetax=0; // Security check @@ -103,7 +98,7 @@ $result = restrictedArea($user, 'tax', '', '', 'charges'); $morequerystring=''; $listofparams=array('date_startmonth','date_startyear','date_startday','date_endmonth','date_endyear','date_endday'); -foreach($listofparams as $param) +foreach ($listofparams as $param) { if (GETPOST($param)!='') $morequerystring.=($morequerystring?'&':'').$param.'='.GETPOST($param); } @@ -130,86 +125,60 @@ $fsearch.=' '; //$fsearch.=' '; -// Affiche en-tete du rapport -if ($modetax==1) // Calculate on invoice for goods and services -{ - $name=$langs->trans("VATReportByQuartersInDueDebtMode"); - $calcmode=$langs->trans("CalcModeVATDebt"); - $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; - $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); - $prevyear=$year_start; $prevquarter=$q; - if ($prevquarter > 1) $prevquarter--; - else { $prevquarter=4; $prevyear--; } - $nextyear=$year_start; $nextquarter=$q; - if ($nextquarter < 4) $nextquarter++; - else { $nextquarter=1; $nextyear++; } - //$periodlink=($prevyear?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesVATDueServices"); - $description.='
'; - $description.=$langs->trans("RulesVATDueProducts"); - //if ($conf->global->MAIN_MODULE_COMPTABILITE || $conf->global->MAIN_MODULE_ACCOUNTING) $description.='
'.img_warning().' '.$langs->trans('OptionVatInfoModuleComptabilite'); - //if (! empty($conf->global->MAIN_MODULE_COMPTABILITE)) $description.='
'.$langs->trans("WarningDepositsNotIncluded"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $description.='
'.$langs->trans("DepositsAreNotIncluded"); - else $description.='
'.$langs->trans("DepositsAreIncluded"); - $description.=$fsearch; - $builddate=dol_now(); - //$exportlink=$langs->trans("NotYetAvailable"); - - // Customers invoices - $elementcust=$langs->trans("CustomersInvoices"); - $productcust=$langs->trans("ProductOrService"); - $amountcust=$langs->trans("AmountHT"); - $vatcust=$langs->trans("VATReceived"); - if ($mysoc->tva_assuj) $vatcust.=' ('.$langs->trans("ToPay").')'; - - // Suppliers invoices - $elementsup=$langs->trans("SuppliersInvoices"); - $productsup=$langs->trans("ProductOrService"); - $amountsup=$langs->trans("AmountHT"); - $vatsup=$langs->trans("VATPaid"); - if ($mysoc->tva_assuj) $vatsup.=' ('.$langs->trans("ToGetBack").')'; - +// Show report header +$name=$langs->trans("VATReportByPeriods"); +$calcmode=''; +if ($modetax == 0) $calcmode=$langs->trans('OptionVATDefault'); +if ($modetax == 1) $calcmode=$langs->trans('OptionVATDebitOption'); +if ($modetax == 2) $calcmode=$langs->trans('OptionPaymentForProductAndServices'); +$calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; +// Set period +$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); +$prevyear=$year_start; $prevquarter=$q; +if ($prevquarter > 1) { + $prevquarter--; +} else { + $prevquarter=4; $prevyear--; } -if ($modetax==0) // Invoice for goods, payment for services -{ - $name=$langs->trans("VATReportByQuartersInInputOutputMode"); - $calcmode=$langs->trans("CalcModeVATEngagement"); - $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; - $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); - $prevyear=$year_start; $prevquarter=$q; - if ($prevquarter > 1) $prevquarter--; - else { $prevquarter=4; $prevyear--; } - $nextyear=$year_start; $nextquarter=$q; - if ($nextquarter < 4) $nextquarter++; - else { $nextquarter=1; $nextyear++; } - //$periodlink=($prevyear?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesVATInServices"); - $description.=' '.$langs->trans("DepositsAreIncluded"); - $description.='
'; - $description.=$langs->trans("RulesVATInProducts"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $description.=' '.$langs->trans("DepositsAreNotIncluded"); - else $description.=' '.$langs->trans("DepositsAreIncluded"); - //if ($conf->global->MAIN_MODULE_COMPTABILITE || $conf->global->MAIN_MODULE_ACCOUNTING) $description.='
'.img_warning().' '.$langs->trans('OptionVatInfoModuleComptabilite'); - //if (! empty($conf->global->MAIN_MODULE_COMPTABILITE)) $description.='
'.$langs->trans("WarningDepositsNotIncluded"); - $description.=$fsearch; - $builddate=dol_now(); - //$exportlink=$langs->trans("NotYetAvailable"); - - // Customers invoices - $elementcust=$langs->trans("CustomersInvoices"); - $productcust=$langs->trans("ProductOrService"); - $amountcust=$langs->trans("AmountHT"); - $vatcust=$langs->trans("VATReceived"); - if ($mysoc->tva_assuj) $vatcust.=' ('.$langs->trans("ToPay").')'; - - // Suppliers invoices - $elementsup=$langs->trans("SuppliersInvoices"); - $productsup=$langs->trans("ProductOrService"); - $amountsup=$langs->trans("AmountHT"); - $vatsup=$langs->trans("VATPaid"); - if ($mysoc->tva_assuj) $vatsup.=' ('.$langs->trans("ToGetBack").')'; - +$nextyear=$year_start; $nextquarter=$q; +if ($nextquarter < 4) { + $nextquarter++; +} else { + $nextquarter=1; $nextyear++; } +$description.=$fsearch; +$builddate=dol_now(); + +if ($conf->global->TAX_MODE_SELL_PRODUCT == 'invoice') $description.=$langs->trans("RulesVATDueProducts"); +if ($conf->global->TAX_MODE_SELL_PRODUCT == 'payment') $description.=$langs->trans("RulesVATInProducts"); +if ($conf->global->TAX_MODE_SELL_SERVICE == 'invoice') $description.='
'.$langs->trans("RulesVATDueServices"); +if ($conf->global->TAX_MODE_SELL_SERVICE == 'payment') $description.='
'.$langs->trans("RulesVATInServices"); +if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $description.='
'.$langs->trans("DepositsAreNotIncluded"); +} +if (! empty($conf->global->MAIN_MODULE_ACCOUNTING)) $description.='
'.$langs->trans("ThisIsAnEstimatedValue"); + +// Customers invoices +$elementcust=$langs->trans("CustomersInvoices"); +$productcust=$langs->trans("ProductOrService"); +$amountcust=$langs->trans("AmountHT"); +$vatcust=$langs->trans("VATReceived"); +$namecust=$langs->trans("Name"); +if ($mysoc->tva_assuj) { + $vatcust.=' ('.$langs->trans("ToPay").')'; +} + +// Suppliers invoices +$elementsup=$langs->trans("SuppliersInvoices"); +$productsup=$productcust; +$amountsup=$amountcust; +$vatsup=$langs->trans("VATPaid"); +$namesup=$namecust; +if ($mysoc->tva_assuj) { + $vatsup.=' ('.$langs->trans("ToGetBack").')'; +} + + report_header($name,'',$period,$periodlink,$description,$builddate,$exportlink,array(),$calcmode); $vatcust=$langs->trans("VATReceived"); @@ -222,68 +191,72 @@ print '
'; - $form->select_types_paiements($typeid,'typeid','',0,0,1,16); + $form->select_types_paiements($typeid,'typeid','',0,1,1,16); print '
$rate%".price($both['coll']['totalht'])."
".price($diff)."
'; $y = $year_current; $total = 0; $i=0; +$columns = 5; // Load arrays of datas -$x_coll = vat_by_date($db, 0, 0, $date_start, $date_end, $modetax, 'sell'); -$x_paye = vat_by_date($db, 0, 0, $date_start, $date_end, $modetax, 'buy'); +$x_coll = tax_by_date('vat', $db, 0, 0, $date_start, $date_end, $modetax, 'sell'); +$x_paye = tax_by_date('vat', $db, 0, 0, $date_start, $date_end, $modetax, 'buy'); if (! is_array($x_coll) || ! is_array($x_paye)) { $langs->load("errors"); - if ($x_coll == -1) - print ''; - else if ($x_coll == -2) - print ''; - else - print ''; -} -else -{ + if ($x_coll == -1) { + print ''; + } else if ($x_coll == -2) { + print ''; + } else { + print ''; + } +} else { $x_both = array(); //now, from these two arrays, get another array with one rate per line foreach(array_keys($x_coll) as $my_coll_rate) { $x_both[$my_coll_rate]['coll']['totalht'] = $x_coll[$my_coll_rate]['totalht']; - $x_both[$my_coll_rate]['coll']['vat'] = $x_coll[$my_coll_rate]['vat']; + $x_both[$my_coll_rate]['coll']['vat'] = $x_coll[$my_coll_rate]['vat']; $x_both[$my_coll_rate]['paye']['totalht'] = 0; $x_both[$my_coll_rate]['paye']['vat'] = 0; $x_both[$my_coll_rate]['coll']['links'] = ''; $x_both[$my_coll_rate]['coll']['detail'] = array(); - foreach($x_coll[$my_coll_rate]['facid'] as $id=>$dummy) - { + foreach($x_coll[$my_coll_rate]['facid'] as $id=>$dummy) { $invoice_customer->id=$x_coll[$my_coll_rate]['facid'][$id]; $invoice_customer->ref=$x_coll[$my_coll_rate]['facnum'][$id]; $invoice_customer->type=$x_coll[$my_coll_rate]['type'][$id]; + $company_static->fetch($x_coll[$my_coll_rate]['company_id'][$id]); $x_both[$my_coll_rate]['coll']['detail'][] = array( - 'id' =>$x_coll[$my_coll_rate]['facid'][$id], - 'descr' =>$x_coll[$my_coll_rate]['descr'][$id], - 'pid' =>$x_coll[$my_coll_rate]['pid'][$id], - 'pref' =>$x_coll[$my_coll_rate]['pref'][$id], - 'ptype' =>$x_coll[$my_coll_rate]['ptype'][$id], - 'payment_id' =>$x_coll[$my_coll_rate]['payment_id'][$id], - 'payment_amount' =>$x_coll[$my_coll_rate]['payment_amount'][$id], - 'ftotal_ttc' =>$x_coll[$my_coll_rate]['ftotal_ttc'][$id], - 'dtotal_ttc' =>$x_coll[$my_coll_rate]['dtotal_ttc'][$id], - 'dtype' =>$x_coll[$my_coll_rate]['dtype'][$id], - 'ddate_start' =>$x_coll[$my_coll_rate]['ddate_start'][$id], - 'ddate_end' =>$x_coll[$my_coll_rate]['ddate_end'][$id], - 'totalht' =>$x_coll[$my_coll_rate]['totalht_list'][$id], - 'vat' =>$x_coll[$my_coll_rate]['vat_list'][$id], - 'link' =>$invoice_customer->getNomUrl(1,'',12)); + 'id' =>$x_coll[$my_coll_rate]['facid'][$id], + 'descr' =>$x_coll[$my_coll_rate]['descr'][$id], + 'pid' =>$x_coll[$my_coll_rate]['pid'][$id], + 'pref' =>$x_coll[$my_coll_rate]['pref'][$id], + 'ptype' =>$x_coll[$my_coll_rate]['ptype'][$id], + 'payment_id'=>$x_coll[$my_coll_rate]['payment_id'][$id], + 'payment_amount'=>$x_coll[$my_coll_rate]['payment_amount'][$id], + 'ftotal_ttc'=>$x_coll[$my_coll_rate]['ftotal_ttc'][$id], + 'dtotal_ttc'=>$x_coll[$my_coll_rate]['dtotal_ttc'][$id], + 'dtype' =>$x_coll[$my_coll_rate]['dtype'][$id], + 'datef' =>$x_coll[$my_coll_rate]['datef'][$id], + 'datep' =>$x_coll[$my_coll_rate]['datep'][$id], + 'company_link'=>$company_static->getNomUrl(1,'',20), + 'ddate_start'=>$x_coll[$my_coll_rate]['ddate_start'][$id], + 'ddate_end' =>$x_coll[$my_coll_rate]['ddate_end'][$id], + 'totalht' =>$x_coll[$my_coll_rate]['totalht_list'][$id], + 'vat' =>$x_coll[$my_coll_rate]['vat_list'][$id], + 'link' =>$invoice_customer->getNomUrl(1,'',12) + ); } } // tva paid - foreach(array_keys($x_paye) as $my_paye_rate){ + foreach (array_keys($x_paye) as $my_paye_rate) { $x_both[$my_paye_rate]['paye']['totalht'] = $x_paye[$my_paye_rate]['totalht']; $x_both[$my_paye_rate]['paye']['vat'] = $x_paye[$my_paye_rate]['vat']; - if(!isset($x_both[$my_paye_rate]['coll']['totalht'])){ + if (!isset($x_both[$my_paye_rate]['coll']['totalht'])) { $x_both[$my_paye_rate]['coll']['totalht'] = 0; $x_both[$my_paye_rate]['coll']['vat'] = 0; } $x_both[$my_paye_rate]['paye']['links'] = ''; $x_both[$my_paye_rate]['paye']['detail'] = array(); - foreach($x_paye[$my_paye_rate]['facid'] as $id=>$dummy) + foreach ($x_paye[$my_paye_rate]['facid'] as $id=>$dummy) { // ExpenseReport if ($x_paye[$my_paye_rate]['ptype'][$id] == 'ExpenseReportPayment') @@ -293,21 +266,21 @@ else $expensereport->type=$x_paye[$my_paye_rate]['type'][$id]; $x_both[$my_paye_rate]['paye']['detail'][] = array( - 'id' =>$x_paye[$my_paye_rate]['facid'][$id], - 'descr' =>$x_paye[$my_paye_rate]['descr'][$id], - 'pid' =>$x_paye[$my_paye_rate]['pid'][$id], - 'pref' =>$x_paye[$my_paye_rate]['pref'][$id], - 'ptype' =>$x_paye[$my_paye_rate]['ptype'][$id], - 'payment_id' =>$x_paye[$my_paye_rate]['payment_id'][$id], - 'payment_amount' =>$x_paye[$my_paye_rate]['payment_amount'][$id], - 'ftotal_ttc' =>price2num($x_paye[$my_paye_rate]['ftotal_ttc'][$id]), - 'dtotal_ttc' =>price2num($x_paye[$my_paye_rate]['dtotal_ttc'][$id]), - 'dtype' =>$x_paye[$my_paye_rate]['dtype'][$id], - 'ddate_start' =>$x_paye[$my_paye_rate]['ddate_start'][$id], - 'ddate_end' =>$x_paye[$my_paye_rate]['ddate_end'][$id], - 'totalht' =>price2num($x_paye[$my_paye_rate]['totalht_list'][$id]), - 'vat' =>$x_paye[$my_paye_rate]['vat_list'][$id], - 'link' =>$expensereport->getNomUrl(1) + 'id' =>$x_paye[$my_paye_rate]['facid'][$id], + 'descr' =>$x_paye[$my_paye_rate]['descr'][$id], + 'pid' =>$x_paye[$my_paye_rate]['pid'][$id], + 'pref' =>$x_paye[$my_paye_rate]['pref'][$id], + 'ptype' =>$x_paye[$my_paye_rate]['ptype'][$id], + 'payment_id' =>$x_paye[$my_paye_rate]['payment_id'][$id], + 'payment_amount' =>$x_paye[$my_paye_rate]['payment_amount'][$id], + 'ftotal_ttc' =>price2num($x_paye[$my_paye_rate]['ftotal_ttc'][$id]), + 'dtotal_ttc' =>price2num($x_paye[$my_paye_rate]['dtotal_ttc'][$id]), + 'dtype' =>$x_paye[$my_paye_rate]['dtype'][$id], + 'ddate_start' =>$x_paye[$my_paye_rate]['ddate_start'][$id], + 'ddate_end' =>$x_paye[$my_paye_rate]['ddate_end'][$id], + 'totalht' =>price2num($x_paye[$my_paye_rate]['totalht_list'][$id]), + 'vat' =>$x_paye[$my_paye_rate]['vat_list'][$id], + 'link' =>$expensereport->getNomUrl(1) ); } else @@ -315,22 +288,26 @@ else $invoice_supplier->id=$x_paye[$my_paye_rate]['facid'][$id]; $invoice_supplier->ref=$x_paye[$my_paye_rate]['facnum'][$id]; $invoice_supplier->type=$x_paye[$my_paye_rate]['type'][$id]; + $company_static->fetch($x_paye[$my_paye_rate]['company_id'][$id]); $x_both[$my_paye_rate]['paye']['detail'][] = array( - 'id' =>$x_paye[$my_paye_rate]['facid'][$id], - 'descr' =>$x_paye[$my_paye_rate]['descr'][$id], - 'pid' =>$x_paye[$my_paye_rate]['pid'][$id], - 'pref' =>$x_paye[$my_paye_rate]['pref'][$id], - 'ptype' =>$x_paye[$my_paye_rate]['ptype'][$id], - 'payment_id' =>$x_paye[$my_paye_rate]['payment_id'][$id], - 'payment_amount' =>$x_paye[$my_paye_rate]['payment_amount'][$id], - 'ftotal_ttc' =>price2num($x_paye[$my_paye_rate]['ftotal_ttc'][$id]), - 'dtotal_ttc' =>price2num($x_paye[$my_paye_rate]['dtotal_ttc'][$id]), - 'dtype' =>$x_paye[$my_paye_rate]['dtype'][$id], - 'ddate_start' =>$x_paye[$my_paye_rate]['ddate_start'][$id], - 'ddate_end' =>$x_paye[$my_paye_rate]['ddate_end'][$id], - 'totalht' =>price2num($x_paye[$my_paye_rate]['totalht_list'][$id]), - 'vat' =>$x_paye[$my_paye_rate]['vat_list'][$id], - 'link' =>$invoice_supplier->getNomUrl(1,'',12) + 'id' =>$x_paye[$my_paye_rate]['facid'][$id], + 'descr' =>$x_paye[$my_paye_rate]['descr'][$id], + 'pid' =>$x_paye[$my_paye_rate]['pid'][$id], + 'pref' =>$x_paye[$my_paye_rate]['pref'][$id], + 'ptype' =>$x_paye[$my_paye_rate]['ptype'][$id], + 'payment_id'=>$x_paye[$my_paye_rate]['payment_id'][$id], + 'payment_amount'=>$x_paye[$my_paye_rate]['payment_amount'][$id], + 'ftotal_ttc'=>price2num($x_paye[$my_paye_rate]['ftotal_ttc'][$id]), + 'dtotal_ttc'=>price2num($x_paye[$my_paye_rate]['dtotal_ttc'][$id]), + 'dtype' =>$x_paye[$my_paye_rate]['dtype'][$id], + 'datef' =>$x_paye[$my_paye_rate]['datef'][$id], + 'datep' =>$x_paye[$my_paye_rate]['datep'][$id], + 'company_link'=>$company_static->getNomUrl(1,'',20), + 'ddate_start'=>$x_paye[$my_paye_rate]['ddate_start'][$id], + 'ddate_end' =>$x_paye[$my_paye_rate]['ddate_end'][$id], + 'totalht' =>price2num($x_paye[$my_paye_rate]['totalht_list'][$id]), + 'vat' =>$x_paye[$my_paye_rate]['vat_list'][$id], + 'link' =>$invoice_supplier->getNomUrl(1,'',12) ); } } @@ -345,16 +322,20 @@ else $x_paye_sum = 0; $x_paye_ht = 0; - $span=3; - if ($modetax == 0) $span+=2; + $span=$columns; + if ($modetax != 1) $span+=2; //print ''; // Customers invoices print ''; print ''; + print ''; + if ($conf->global->TAX_MODE_SELL_PRODUCT == 'payment' || $conf->global->TAX_MODE_SELL_SERVICE == 'payment') print ''; + else print ''; + print ''; print ''; - if ($modetax == 0) + if ($modetax != 1) { print ''; print ''; @@ -374,8 +355,7 @@ else $hookmanager->initHooks(array('externalbalance')); $reshook=$hookmanager->executeHooks('addVatLine',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks - foreach(array_keys($x_coll) as $rate) - { + foreach (array_keys($x_coll) as $rate) { $subtot_coll_total_ht = 0; $subtot_coll_vat = 0; @@ -384,17 +364,20 @@ else // VAT Rate $var=true; print ""; - print ''; + print ''; print ''."\n"; - foreach($x_both[$rate]['coll']['detail'] as $index => $fields) - { + foreach ($x_both[$rate]['coll']['detail'] as $index => $fields) { // Define type $type=($fields['dtype']?$fields['dtype']:$fields['ptype']); // Try to enhance type detection using date_start and date_end for free lines where type // was not saved. - if (! empty($fields['ddate_start'])) $type=1; - if (! empty($fields['ddate_end'])) $type=1; + if (!empty($fields['ddate_start'])) { + $type=1; + } + if (!empty($fields['ddate_end'])) { + $type=1; + } print ''; @@ -402,6 +385,16 @@ else // Ref print ''; + // Invoice date + print ''; + + // Payment date + if ($conf->global->TAX_MODE_SELL_PRODUCT == 'payment' || $conf->global->TAX_MODE_SELL_SERVICE == 'payment') print ''; + else print ''; + + // Company name + print ''; + // Description print ''; // Total HT - if ($modetax == 0) + if ($modetax != 1) { print ''; } // Total collected print ''; // VAT print ''; @@ -487,45 +490,45 @@ else $x_coll_sum += $temp_vat; } } - // Total customers for this vat rate - print ''; - print ''; - print ''; - if ($modetax == 0) - { - print ''; - print ''; - } - print ''; - print ''; - print ''; + // Total customers for this vat rate + print ''; + print ''; + print ''; + if ($modetax != 1) { + print ''; + print ''; + } + print ''; + print ''; + print ''; } - if (count($x_coll) == 0) // Show a total ine if nothing shown - { - print ''; - print ''; - print ''; - if ($modetax == 0) - { - print ''; - print ''; - } - print ''; - print ''; - print ''; - } + if (count($x_coll) == 0) // Show a total ine if nothing shown + { + print ''; + print ''; + print ''; + if ($modetax != 1) { + print ''; + print ''; + } + print ''; + print ''; + print ''; + } - // Blank line + // Blank line print ''; - //print table headers for this quadri - expenses now - //imprime les en-tete de tables pour ce quadri - maintenant les d�penses + // Print table headers for this quadri - expenses now print ''; print ''; + print ''; + if ($conf->global->TAX_MODE_BUY_PRODUCT == 'payment' || $conf->global->TAX_MODE_BUY_SERVICE == 'payment') print ''; + else print ''; + print ''; print ''; - if ($modetax == 0) - { + if ($modetax != 1) { print ''; print ''; } @@ -533,25 +536,29 @@ else print ''; print ''."\n"; - foreach(array_keys($x_paye) as $rate) + foreach (array_keys($x_paye) as $rate) { $subtot_paye_total_ht = 0; $subtot_paye_vat = 0; - if(is_array($x_both[$rate]['paye']['detail'])) + if (is_array($x_both[$rate]['paye']['detail'])) { $var=true; print ""; - print ''; + print ''; print ''."\n"; - foreach($x_both[$rate]['paye']['detail'] as $index=>$fields) - { + + foreach ($x_both[$rate]['paye']['detail'] as $index=>$fields) { // Define type $type=($fields['dtype']?$fields['dtype']:$fields['ptype']); // Try to enhance type detection using date_start and date_end for free lines where type // was not saved. - if (! empty($fields['ddate_start'])) $type=1; - if (! empty($fields['ddate_end'])) $type=1; + if (!empty($fields['ddate_start'])) { + $type=1; + } + if (!empty($fields['ddate_end'])) { + $type=1; + } print ''; @@ -559,6 +566,16 @@ else // Ref print ''; + // Invoice date + print ''; + + // Payment date + if ($conf->global->TAX_MODE_BUY_PRODUCT == 'payment' || $conf->global->TAX_MODE_BUY_SERVICE == 'payment') print ''; + else print ''; + + // Company name + print ''; + // Description print ''; // Total HT - if ($modetax == 0) + if ($modetax != 1) { print ''; } // VAT paid print ''; // VAT print ''; print ''; $subtot_paye_total_ht += $temp_ht; - $subtot_paye_vat += $temp_vat; - $x_paye_sum += $temp_vat; + $subtot_paye_vat += $temp_vat; + $x_paye_sum += $temp_vat; } } - // Total suppliers for this vat rate - print ''; - print ''; - print ''; - if ($modetax == 0) - { - print ''; - print ''; - } - print ''; - print ''; - print ''; + // Total suppliers for this vat rate + print ''; + print ''; + print ''; + if ($modetax != 1) { + print ''; + print ''; + } + print ''; + print ''; + print ''; } - if (count($x_paye) == 0) // Show a total line if nothing shown - { - print ''; - print ''; - print ''; - if ($modetax == 0) - { - print ''; - print ''; - } - print ''; - print ''; - print ''; + if (count($x_paye) == 0) { // Show a total line if nothing shown + print ''; + print ''; + print ''; + if ($modetax != 1) { + print ''; + print ''; + } + print ''; + print ''; + print ''; } - print '
'.$langs->trans("ErrorNoAccountancyModuleLoaded").'
'.$langs->trans("FeatureNotYetAvailable").'
'.$langs->trans("Error").'
' . $langs->trans("ErrorNoAccountancyModuleLoaded") . '
' . $langs->trans("FeatureNotYetAvailable") . '
' . $langs->trans("Error") . '
'..')
'.$elementcust.''.$langs->trans("DateInvoice").''.$langs->trans("DatePayment").''.$namecust.''.$productcust.''.$amountcust.''.$langs->trans("Payment").' ('.$langs->trans("PercentOfInvoice").')
'.$langs->trans("Rate").': '.vatrate($rate).'%'.$langs->trans("Rate").': '.vatrate($rate).'%
'.$fields['link'].'' . dol_print_date($fields['datef'], 'day') . '' . dol_print_date($fields['datep'], 'day') . '' . $fields['company_link'] . ''; if ($fields['pid']) @@ -410,17 +403,25 @@ else $product_static->ref=$fields['pref']; $product_static->type=$fields['ptype']; print $product_static->getNomUrl(1); - if (dol_string_nohtmltag($fields['descr'])) print ' - '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); + if (dol_string_nohtmltag($fields['descr'])) { + print ' - '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); + } } else { - if ($type) $text = img_object($langs->trans('Service'),'service'); - else $text = img_object($langs->trans('Product'),'product'); - if (preg_match('/^\((.*)\)$/',$fields['descr'],$reg)) - { - if ($reg[1]=='DEPOSIT') $fields['descr']=$langs->transnoentitiesnoconv('Deposit'); - elseif ($reg[1]=='CREDIT_NOTE') $fields['descr']=$langs->transnoentitiesnoconv('CreditNote'); - else $fields['descr']=$langs->transnoentitiesnoconv($reg[1]); + if ($type) { + $text = img_object($langs->trans('Service'),'service'); + } else { + $text = img_object($langs->trans('Product'),'product'); + } + if (preg_match('/^\((.*)\)$/',$fields['descr'],$reg)) { + if ($reg[1]=='DEPOSIT') { + $fields['descr']=$langs->transnoentitiesnoconv('Deposit'); + } elseif ($reg[1]=='CREDIT_NOTE') { + $fields['descr']=$langs->transnoentitiesnoconv('CreditNote'); + } else { + $fields['descr']=$langs->transnoentitiesnoconv($reg[1]); + } } print $text.' '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); @@ -430,7 +431,7 @@ else print ''; print price($fields['totalht']); @@ -445,9 +446,8 @@ else // Payment $ratiopaymentinvoice=1; - if ($modetax == 0) + if ($modetax != 1) { - if (isset($fields['payment_amount']) && $fields['ftotal_ttc']) $ratiopaymentinvoice=($fields['payment_amount']/$fields['ftotal_ttc']); print ''; //print $fields['totalht']."-".$fields['payment_amount']."-".$fields['ftotal_ttc']; if ($fields['payment_amount'] && $fields['ftotal_ttc']) @@ -455,28 +455,31 @@ else $payment_static->id=$fields['payment_id']; print $payment_static->getNomUrl(2); } - if ($type == 0) + if (($type == 0 && $conf->global->TAX_MODE_SELL_PRODUCT == 'invoice') + || ($type == 1 && $conf->global->TAX_MODE_SELL_SERVICE == 'invoice')) { - print $langs->trans("NotUsedForGoods"); - } - else { - print price($fields['payment_amount']); - if (isset($fields['payment_amount'])) print ' ('.round($ratiopaymentinvoice*100,2).'%)'; + print $langs->trans("NA"); + } else { + if (isset($fields['payment_amount']) && price2num($fields['ftotal_ttc'])) { + $ratiopaymentinvoice=($fields['payment_amount']/$fields['ftotal_ttc']); + } + print price(price2num($fields['payment_amount'],'MT')); + if (isset($fields['payment_amount'])) { + print ' ('.round($ratiopaymentinvoice*100,2).'%)'; + } } print ''; - $temp_ht=$fields['totalht']; - if ($type == 1) $temp_ht=$fields['totalht']*$ratiopaymentinvoice; + $temp_ht=$fields['totalht']*$ratiopaymentinvoice; print price(price2num($temp_ht,'MT'),1); print ''; - $temp_vat=$fields['vat']; - if ($type == 1) $temp_vat=$fields['vat']*$ratiopaymentinvoice; + $temp_vat=$fields['vat']*$ratiopaymentinvoice; print price(price2num($temp_vat,'MT'),1); //print price($fields['vat']); print '
'.$langs->trans("Total").':  '.price(price2num($subtot_coll_total_ht,'MT')).''.price(price2num($subtot_coll_vat,'MT')).'
'.$langs->trans("Total").':  '.price(price2num($subtot_coll_total_ht,'MT')).''.price(price2num($subtot_coll_vat,'MT')).'
 '.$langs->trans("Total").':  '.price(price2num(0,'MT')).''.price(price2num(0,'MT')).'
'.$langs->trans("Total").':  '.price(price2num(0,'MT')).''.price(price2num(0,'MT')).'
 
'.$elementsup.''.$langs->trans("DateInvoice").''.$langs->trans("DatePayment").''.$namesup.''.$productsup.''.$amountsup.''.$langs->trans("Payment").' ('.$langs->trans("PercentOfInvoice").')'.$vatsup.'
'.$langs->trans("Rate").': '.vatrate($rate).'%'.$langs->trans("Rate").': '.vatrate($rate).'%
'.$fields['link'].'' . dol_print_date($fields['datef'], 'day') . '' . dol_print_date($fields['datep'], 'day') . '' . $fields['company_link'] . ''; if ($fields['pid']) @@ -567,12 +584,17 @@ else $product_static->ref=$fields['pref']; $product_static->type=$fields['ptype']; print $product_static->getNomUrl(1); - if (dol_string_nohtmltag($fields['descr'])) print ' - '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); + if (dol_string_nohtmltag($fields['descr'])) { + print ' - '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); + } } else { - if ($type) $text = img_object($langs->trans('Service'),'service'); - else $text = img_object($langs->trans('Product'),'product'); + if ($type) { + $text = img_object($langs->trans('Service'),'service'); + } else { + $text = img_object($langs->trans('Product'),'product'); + } print $text.' '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); // Show range @@ -581,7 +603,7 @@ else print ''; print price($fields['totalht']); @@ -596,83 +618,84 @@ else // Payment $ratiopaymentinvoice=1; - if ($modetax == 0) + if ($modetax != 1) { - if (isset($fields['payment_amount']) && $fields['ftotal_ttc']) $ratiopaymentinvoice=($fields['payment_amount']/$fields['ftotal_ttc']); print ''; if ($fields['payment_amount'] && $fields['ftotal_ttc']) { $paymentfourn_static->id=$fields['payment_id']; print $paymentfourn_static->getNomUrl(2); } - if ($type == 0) + + if (($type == 0 && $conf->global->TAX_MODE_BUY_PRODUCT == 'invoice') + || ($type == 1 && $conf->global->TAX_MODE_BUY_SERVICE == 'invoice')) { - print $langs->trans("NotUsedForGoods"); + print $langs->trans("NA"); } else { - print price($fields['payment_amount']); - if (isset($fields['payment_amount'])) print ' ('.round($ratiopaymentinvoice*100,2).'%)'; + if (isset($fields['payment_amount']) && $fields['ftotal_ttc']) { + $ratiopaymentinvoice=($fields['payment_amount']/$fields['ftotal_ttc']); + } + print price(price2num($fields['payment_amount'],'MT')); + if (isset($fields['payment_amount'])) { + print ' ('.round($ratiopaymentinvoice*100,2).'%)'; + } } print ''; - $temp_ht=$fields['totalht']; - if ($type == 1) $temp_ht=$fields['totalht']*$ratiopaymentinvoice; + $temp_ht=$fields['totalht']*$ratiopaymentinvoice; print price(price2num($temp_ht,'MT'),1); print ''; - $temp_vat=$fields['vat']; - if ($type == 1) $temp_vat=$fields['vat']*$ratiopaymentinvoice; + $temp_vat=$fields['vat']*$ratiopaymentinvoice; print price(price2num($temp_vat,'MT'),1); //print price($fields['vat']); print '
 '.$langs->trans("Total").':  '.price(price2num($subtot_paye_total_ht,'MT')).''.price(price2num($subtot_paye_vat,'MT')).'
'.$langs->trans("Total").':  '.price(price2num($subtot_paye_total_ht,'MT')).''.price(price2num($subtot_paye_vat,'MT')).'
 '.$langs->trans("Total").':  '.price(price2num(0,'MT')).''.price(price2num(0,'MT')).'
'.$langs->trans("Total").':  '.price(price2num(0,'MT')).''.price(price2num(0,'MT')).'
'; + print ''; // Total to pay - print '

'; - print ''; - $diff = $x_coll_sum - $x_paye_sum; + print '

'; + print '
'; + $diff = $x_coll_sum - $x_paye_sum; print ''; print ''; print '\n"; @@ -680,7 +703,7 @@ else $i++; } -echo '
'.$langs->trans("TotalToPay").($q?', '.$langs->trans("Quadri").' '.$q:'').''.price(price2num($diff,'MT'))."
'; +print ''; llxFooter(); $db->close(); diff --git a/htdocs/compta/tva/quarter_report.php b/htdocs/compta/tva/quarter_report.php deleted file mode 100644 index 4c054923837..00000000000 --- a/htdocs/compta/tva/quarter_report.php +++ /dev/null @@ -1,713 +0,0 @@ - - * Copyright (C) 2004 Eric Seigne - * Copyright (C) 2004-2013 Laurent Destailleur - * Copyright (C) 2006-2007, 2015 Yannick Warnier - * Copyright (C) 2014 Ferran Marcet - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * \file htdocs/compta/tva/quadri_detail.php - * \ingroup tax - * \brief Trimestrial page - detailed version - * TODO Deal with recurrent invoices as well - */ - -require '../../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/report.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/tax.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/tva/class/tva.class.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; -require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; -require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; -require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php'; - -$langs->load("main"); -$langs->load("bills"); -$langs->load("compta"); -$langs->load("companies"); -$langs->load("products"); -$langs->load("other"); - -// Date range -$year=GETPOST('year', 'int'); -if (empty($year)) { - $year_current = strftime("%Y",dol_now()); - $year_start = $year_current; -} else { - $year_current = $year; - $year_start = $year; -} -$date_start=dol_mktime(0,0,0,$_REQUEST["date_startmonth"],$_REQUEST["date_startday"],$_REQUEST["date_startyear"]); -$date_end=dol_mktime(23,59,59,$_REQUEST["date_endmonth"],$_REQUEST["date_endday"],$_REQUEST["date_endyear"]); -// Quarter -if (empty($date_start) || empty($date_end)) { // We define date_start and date_end - $q=GETPOST('q', 'int'); - if (empty($q)) { - if (isset($_REQUEST["month"])) { - $date_start=dol_get_first_day($year_start,$_REQUEST["month"],false); - $date_end=dol_get_last_day($year_start,$_REQUEST["month"],false); - } else { - $month_current = strftime("%m",dol_now()); - if ($month_current >= 10) $q=4; - elseif ($month_current >= 7) $q=3; - elseif ($month_current >= 4) $q=2; - else $q=1; - } - } - if ($q==1) { - $date_start=dol_get_first_day($year_start,1,false); - $date_end=dol_get_last_day($year_start,3,false); - } - if ($q==2) { - $date_start=dol_get_first_day($year_start,4,false); - $date_end=dol_get_last_day($year_start,6,false); - } - if ($q==3) { - $date_start=dol_get_first_day($year_start,7,false); - $date_end=dol_get_last_day($year_start,9,false); - } - if ($q==4) { - $date_start=dol_get_first_day($year_start,10,false); - $date_end=dol_get_last_day($year_start,12,false); - } -} - -$min = GETPOST("min"); -if (empty($min)) { - $min = 0; -} - -// Define modetax (0 or 1) -// 0=normal, 1=option vat for services is on debit -$modetax = $conf->global->TAX_MODE; -if (isset($_REQUEST["modetax"])) { - $modetax=$_REQUEST["modetax"]; -} -if (empty($modetax)) { - $modetax=0; -} - -// Security check -$socid = GETPOST('socid','int'); -if ($user->societe_id) { - $socid=$user->societe_id; -} -$result = restrictedArea($user, 'tax', '', '', 'charges'); - - - -/* - * View - */ - -$morequerystring=''; -$listofparams=array('date_startmonth','date_startyear','date_startday','date_endmonth','date_endyear','date_endday'); -foreach ($listofparams as $param) { - if (GETPOST($param)!='') { - $morequerystring.=($morequerystring?'&':'').$param.'='.GETPOST($param); - } -} - -llxHeader('','','','',0,0,'','',$morequerystring); - -$form=new Form($db); - -$company_static=new Societe($db); -$invoice_customer=new Facture($db); -$invoice_supplier=new FactureFournisseur($db); -$product_static=new Product($db); -$payment_static=new Paiement($db); -$paymentfourn_static=new PaiementFourn($db); - -//print load_fiche_titre($langs->trans("VAT"),""); - -//$fsearch.='
'; -$fsearch.=' '; -$fsearch.=' '; -//$fsearch.=' '.$langs->trans("SalesTurnoverMinimum").': '; -//$fsearch.=' '; - - -// Affiche en-tete du rapport -if ($modetax==1) { // Calculate on invoice for goods and services - $name=$langs->trans("VATReportByQuartersInDueDebtMode"); - $calcmode=$langs->trans("CalcModeVATDebt"); - $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; - $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); - $prevyear=$year_start; $prevquarter=$q; - if ($prevquarter > 1) { - $prevquarter--; - } else { - $prevquarter=4; $prevyear--; - } - $nextyear=$year_start; $nextquarter=$q; - if ($nextquarter < 4) { - $nextquarter++; - } else { - $nextquarter=1; $nextyear++; - } - //$periodlink=($prevyear?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesVATDueServices"); - $description.='
'; - $description.=$langs->trans("RulesVATDueProducts"); - //if ($conf->global->MAIN_MODULE_COMPTABILITE || $conf->global->MAIN_MODULE_ACCOUNTING) $description.='
'.img_warning().' '.$langs->trans('OptionVatInfoModuleComptabilite'); - //if (! empty($conf->global->MAIN_MODULE_COMPTABILITE)) $description.='
'.$langs->trans("WarningDepositsNotIncluded"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { - $description.='
'.$langs->trans("DepositsAreNotIncluded"); - } else { - $description.='
'.$langs->trans("DepositsAreIncluded"); - } - $description.=$fsearch; - $builddate=dol_now(); - //$exportlink=$langs->trans("NotYetAvailable"); - - $elementcust=$langs->trans("CustomersInvoices"); - $productcust=$langs->trans("ProductOrService"); - $amountcust=$langs->trans("AmountHT"); - $vatcust=$langs->trans("VATReceived"); - $namecust=$langs->trans("Name"); - if ($mysoc->tva_assuj) { - $vatcust.=' ('.$langs->trans("ToPay").')'; - } - $elementsup=$langs->trans("SuppliersInvoices"); - $productsup=$langs->trans("ProductOrService"); - $amountsup=$langs->trans("AmountHT"); - $vatsup=$langs->trans("VATPaid"); - $namesup=$namecust; - if ($mysoc->tva_assuj) { - $vatsup.=' ('.$langs->trans("ToGetBack").')'; - } -} -if ($modetax==0) { // Invoice for goods, payment for services - $name=$langs->trans("VATReportByQuartersInInputOutputMode"); - $calcmode=$langs->trans("CalcModeVATEngagement"); - $calcmode.='
('.$langs->trans("TaxModuleSetupToModifyRules",DOL_URL_ROOT.'/admin/taxes.php').')'; - $period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1); - $prevyear=$year_start; $prevquarter=$q; - if ($prevquarter > 1) { - $prevquarter--; - } else { - $prevquarter=4; $prevyear--; - } - $nextyear=$year_start; $nextquarter=$q; - if ($nextquarter < 4) { - $nextquarter++; - } else { - $nextquarter=1; $nextyear++; - } - //$periodlink=($prevyear?"".img_previous()." ".img_next()."":""); - $description=$langs->trans("RulesVATInServices"); - $description.=' '.$langs->trans("DepositsAreIncluded"); - $description.='
'; - $description.=$langs->trans("RulesVATInProducts"); - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { - $description.=' '.$langs->trans("DepositsAreNotIncluded"); - } else { - $description.=' '.$langs->trans("DepositsAreIncluded"); - } - //if ($conf->global->MAIN_MODULE_COMPTABILITE || $conf->global->MAIN_MODULE_ACCOUNTING) $description.='
'.img_warning().' '.$langs->trans('OptionVatInfoModuleComptabilite'); - //if (! empty($conf->global->MAIN_MODULE_COMPTABILITE)) $description.='
'.$langs->trans("WarningDepositsNotIncluded"); - $description.=$fsearch; - $builddate=dol_now(); - //$exportlink=$langs->trans("NotYetAvailable"); - - $elementcust=$langs->trans("CustomersInvoices"); - $productcust=$langs->trans("ProductOrService"); - $amountcust=$langs->trans("AmountHT"); - $vatcust=$langs->trans("VATReceived"); - $namecust=$langs->trans("Name"); - if ($mysoc->tva_assuj) { - $vatcust.=' ('.$langs->trans("ToPay").')'; - } - $elementsup=$langs->trans("SuppliersInvoices"); - $productsup=$productcust; - $amountsup=$amountcust; - $vatsup=$langs->trans("VATPaid"); - $namesup=$namecust; - if ($mysoc->tva_assuj) { - $vatsup.=' ('.$langs->trans("ToGetBack").')'; - } -} -report_header($name,'',$period,$periodlink,$description,$builddate,$exportlink,array(),$calcmode); - -$vatcust=$langs->trans("VATReceived"); -$vatsup=$langs->trans("VATPaid"); - - -// VAT Received and paid - -print ''; - -$y = $year_current; -$total = 0; -$i=0; -$columns = 6; - -// Load arrays of datas -$x_coll = vat_by_date($db, 0, 0, $date_start, $date_end, $modetax, 'sell'); -$x_paye = vat_by_date($db, 0, 0, $date_start, $date_end, $modetax, 'buy'); - -if (!is_array($x_coll) || !is_array($x_paye)) { - $langs->load("errors"); - if ($x_coll == -1) { - print ''; - } else if ($x_coll == -2) { - print ''; - } else { - print ''; - } -} else { - $x_both = array(); - //now, from these two arrays, get another array with one rate per line - foreach(array_keys($x_coll) as $my_coll_rate) { - $x_both[$my_coll_rate]['coll']['totalht'] = $x_coll[$my_coll_rate]['totalht']; - $x_both[$my_coll_rate]['coll']['vat'] = $x_coll[$my_coll_rate]['vat']; - $x_both[$my_coll_rate]['paye']['totalht'] = 0; - $x_both[$my_coll_rate]['paye']['vat'] = 0; - $x_both[$my_coll_rate]['coll']['links'] = ''; - $x_both[$my_coll_rate]['coll']['detail'] = array(); - foreach($x_coll[$my_coll_rate]['facid'] as $id=>$dummy) { - $invoice_customer->id=$x_coll[$my_coll_rate]['facid'][$id]; - $invoice_customer->ref=$x_coll[$my_coll_rate]['facnum'][$id]; - $invoice_customer->type=$x_coll[$my_coll_rate]['type'][$id]; - $company_static->fetch($x_coll[$my_coll_rate]['company_id'][$id]); - $x_both[$my_coll_rate]['coll']['detail'][] = array( - 'id' =>$x_coll[$my_coll_rate]['facid'][$id], - 'descr' =>$x_coll[$my_coll_rate]['descr'][$id], - 'pid' =>$x_coll[$my_coll_rate]['pid'][$id], - 'pref' =>$x_coll[$my_coll_rate]['pref'][$id], - 'ptype' =>$x_coll[$my_coll_rate]['ptype'][$id], - 'payment_id'=>$x_coll[$my_coll_rate]['payment_id'][$id], - 'payment_amount'=>$x_coll[$my_coll_rate]['payment_amount'][$id], - 'ftotal_ttc'=>$x_coll[$my_coll_rate]['ftotal_ttc'][$id], - 'dtotal_ttc'=>$x_coll[$my_coll_rate]['dtotal_ttc'][$id], - 'dtype' =>$x_coll[$my_coll_rate]['dtype'][$id], - 'datef' =>$x_coll[$my_coll_rate]['datef'][$id], - 'company_link'=>$company_static->getNomUrl(1,'',20), - 'ddate_start'=>$x_coll[$my_coll_rate]['ddate_start'][$id], - 'ddate_end' =>$x_coll[$my_coll_rate]['ddate_end'][$id], - 'totalht' =>$x_coll[$my_coll_rate]['totalht_list'][$id], - 'vat' =>$x_coll[$my_coll_rate]['vat_list'][$id], - 'link' =>$invoice_customer->getNomUrl(1,'',12) - ); - } - } - // tva paid - foreach (array_keys($x_paye) as $my_paye_rate) { - $x_both[$my_paye_rate]['paye']['totalht'] = $x_paye[$my_paye_rate]['totalht']; - $x_both[$my_paye_rate]['paye']['vat'] = $x_paye[$my_paye_rate]['vat']; - if (!isset($x_both[$my_paye_rate]['coll']['totalht'])) { - $x_both[$my_paye_rate]['coll']['totalht'] = 0; - $x_both[$my_paye_rate]['coll']['vat'] = 0; - } - $x_both[$my_paye_rate]['paye']['links'] = ''; - $x_both[$my_paye_rate]['paye']['detail'] = array(); - - foreach ($x_paye[$my_paye_rate]['facid'] as $id=>$dummy) { - $invoice_supplier->id=$x_paye[$my_paye_rate]['facid'][$id]; - $invoice_supplier->ref=$x_paye[$my_paye_rate]['facnum'][$id]; - $invoice_supplier->type=$x_paye[$my_paye_rate]['type'][$id]; - $company_static->fetch($x_paye[$my_paye_rate]['company_id'][$id]); - $x_both[$my_paye_rate]['paye']['detail'][] = array( - 'id' =>$x_paye[$my_paye_rate]['facid'][$id], - 'descr' =>$x_paye[$my_paye_rate]['descr'][$id], - 'pid' =>$x_paye[$my_paye_rate]['pid'][$id], - 'pref' =>$x_paye[$my_paye_rate]['pref'][$id], - 'ptype' =>$x_paye[$my_paye_rate]['ptype'][$id], - 'payment_id'=>$x_paye[$my_paye_rate]['payment_id'][$id], - 'payment_amount'=>$x_paye[$my_paye_rate]['payment_amount'][$id], - 'ftotal_ttc'=>price2num($x_paye[$my_paye_rate]['ftotal_ttc'][$id]), - 'dtotal_ttc'=>price2num($x_paye[$my_paye_rate]['dtotal_ttc'][$id]), - 'dtype' =>$x_paye[$my_paye_rate]['dtype'][$id], - 'datef' =>$x_paye[$my_paye_rate]['datef'][$id], - 'company_link'=>$company_static->getNomUrl(1,'',20), - 'ddate_start'=>$x_paye[$my_paye_rate]['ddate_start'][$id], - 'ddate_end' =>$x_paye[$my_paye_rate]['ddate_end'][$id], - 'totalht' =>price2num($x_paye[$my_paye_rate]['totalht_list'][$id]), - 'vat' =>$x_paye[$my_paye_rate]['vat_list'][$id], - 'link' =>$invoice_supplier->getNomUrl(1,'',12) - ); - } - } - //now we have an array (x_both) indexed by rates for coll and paye - - - //print table headers for this quadri - incomes first - - $x_coll_sum = 0; - $x_coll_ht = 0; - $x_paye_sum = 0; - $x_paye_ht = 0; - - $span=$columns-3; - if ($modetax == 0) $span+=2; - - //print ''; - - // Customers invoices - print ''; - print ''; - print ''; - print ''; - print ''; - if ($modetax == 0) { - print ''; - print ''; - } - print ''; - print ''; - print ''; - - $action = "tvadetail"; - $parameters["mode"] = $modetax; - $parameters["start"] = $date_start; - $parameters["end"] = $date_end; - $parameters["type"] = 'vat'; - - $object = array(&$x_coll, &$x_paye, &$x_both); - // Initialize technical object to manage hooks of expenses. Note that conf->hooks_modules contains array array - $hookmanager->initHooks(array('externalbalance')); - $reshook=$hookmanager->executeHooks('addVatLine',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks - - foreach (array_keys($x_coll) as $rate) { - $subtot_coll_total_ht = 0; - $subtot_coll_vat = 0; - - if (is_array($x_both[$rate]['coll']['detail'])) { - // VAT Rate - $var=true; - print ""; - print ''; - print ''."\n"; - - foreach ($x_both[$rate]['coll']['detail'] as $index => $fields) { - // Define type - $type=($fields['dtype']?$fields['dtype']:$fields['ptype']); - // Try to enhance type detection using date_start and date_end for free lines where type - // was not saved. - if (!empty($fields['ddate_start'])) { - $type=1; - } - if (!empty($fields['ddate_end'])) { - $type=1; - } - - - print ''; - - // Ref - print ''; - - // Invoice date - print ''; - // Company name - print ''; - - // Description - print ''; - - // Total HT - if ($modetax == 0) { - print ''; - } - - // Payment - $ratiopaymentinvoice=1; - if ($modetax == 0) { - if (isset($fields['payment_amount']) && $fields['ftotal_ttc']) { - $ratiopaymentinvoice=($fields['payment_amount']/$fields['ftotal_ttc']); - } - print ''; - } - - // Total collected - print ''; - - // VAT - print ''; - print ''; - - $subtot_coll_total_ht += $temp_ht; - $subtot_coll_vat += $temp_vat; - $x_coll_sum += $temp_vat; - } - } - // Total customers for this vat rate - print ''; - print ''; - print ''; - if ($modetax == 0) { - print ''; - print ''; - } - print ''; - print ''; - print ''; - } - - if (count($x_coll) == 0) { // Show a total ine if nothing shown - print ''; - print ''; - print ''; - if ($modetax == 0) { - print ''; - print ''; - } - print ''; - print ''; - print ''; - } - - // Blank line - print ''; - - // Print table headers for this quadri - expenses now - print ''; - print ''; - print ''; - print ''; - print ''; - if ($modetax == 0) { - print ''; - print ''; - } - print ''; - print ''; - print ''."\n"; - - foreach (array_keys($x_paye) as $rate) { - $subtot_paye_total_ht = 0; - $subtot_paye_vat = 0; - - if (is_array($x_both[$rate]['paye']['detail'])) { - $var=true; - print ""; - print ''; - print ''."\n"; - - foreach ($x_both[$rate]['paye']['detail'] as $index=>$fields) { - // Define type - $type=($fields['dtype']?$fields['dtype']:$fields['ptype']); - // Try to enhance type detection using date_start and date_end for free lines where type - // was not saved. - if (!empty($fields['ddate_start'])) { - $type=1; - } - if (!empty($fields['ddate_end'])) { - $type=1; - } - - - print ''; - - // Ref - print ''; - // Invoice date - print ''; - // Company name - print ''; - - // Description - print ''; - - // Total HT - if ($modetax == 0) { - print ''; - } - - // Payment - $ratiopaymentinvoice=1; - if ($modetax == 0) { - if (isset($fields['payment_amount']) && $fields['ftotal_ttc']) { - $ratiopaymentinvoice=($fields['payment_amount']/$fields['ftotal_ttc']); - } - print ''; - } - - // VAT paid - print ''; - - // VAT - print ''; - print ''; - - $subtot_paye_total_ht += $temp_ht; - $subtot_paye_vat += $temp_vat; - $x_paye_sum += $temp_vat; - } - } - // Total suppliers for this vat rate - print ''; - print ''; - print ''; - if ($modetax == 0) { - print ''; - print ''; - } - print ''; - print ''; - print ''; - } - - if (count($x_paye) == 0) { // Show a total line if nothing shown - print ''; - print ''; - print ''; - if ($modetax == 0) { - print ''; - print ''; - } - print ''; - print ''; - print ''; - } - - print '
' . $langs->trans("ErrorNoAccountancyModuleLoaded") . '
' . $langs->trans("FeatureNotYetAvailable") . '
' . $langs->trans("Error") . '
'..')
'.$elementcust.''.$langs->trans("Date").''.$namecust.''.$productcust.''.$amountcust.''.$langs->trans("Payment").' ('.$langs->trans("PercentOfInvoice").')'.$langs->trans("AmountHTVATRealReceived").''.$vatcust.'
'.$langs->trans("Rate").': '.vatrate($rate).'%
'.$fields['link'].'' . $fields['datef'] . '' . $fields['company_link'] . ''; - if ($fields['pid']) { - $product_static->id=$fields['pid']; - $product_static->ref=$fields['pref']; - $product_static->type=$fields['ptype']; - print $product_static->getNomUrl(1); - if (dol_string_nohtmltag($fields['descr'])) { - print ' - '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); - } - } else { - if ($type) { - $text = img_object($langs->trans('Service'),'service'); - } else { - $text = img_object($langs->trans('Product'),'product'); - } - if (preg_match('/^\((.*)\)$/',$fields['descr'],$reg)) { - if ($reg[1]=='DEPOSIT') { - $fields['descr']=$langs->transnoentitiesnoconv('Deposit'); - } elseif ($reg[1]=='CREDIT_NOTE') { - $fields['descr']=$langs->transnoentitiesnoconv('CreditNote'); - } else { - $fields['descr']=$langs->transnoentitiesnoconv($reg[1]); - } - } - print $text.' '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); - - // Show range - print_date_range($fields['ddate_start'],$fields['ddate_end']); - } - print ''; - print price($fields['totalht']); - if (price2num($fields['ftotal_ttc'])) { - //print $fields['dtotal_ttc']."/".$fields['ftotal_ttc']." - "; - $ratiolineinvoice=($fields['dtotal_ttc']/$fields['ftotal_ttc']); - //print ' ('.round($ratiolineinvoice*100,2).'%)'; - } - print ''; - //print $fields['totalht']."-".$fields['payment_amount']."-".$fields['ftotal_ttc']; - if ($fields['payment_amount'] && $fields['ftotal_ttc']) { - $payment_static->id=$fields['payment_id']; - print $payment_static->getNomUrl(2); - } - if ($type == 0) { - print $langs->trans("NotUsedForGoods"); - } else { - print $fields['payment_amount']; - if (isset($fields['payment_amount'])) { - print ' ('.round($ratiopaymentinvoice*100,2).'%)'; - } - } - print ''; - $temp_ht=$fields['totalht']; - if ($type == 1) { - $temp_ht=$fields['totalht']*$ratiopaymentinvoice; - } - print price(price2num($temp_ht,'MT'),1); - print ''; - $temp_vat=$fields['vat']; - if ($type == 1) { - $temp_vat=$fields['vat']*$ratiopaymentinvoice; - } - print price(price2num($temp_vat,'MT'),1); - //print price($fields['vat']); - print '
'.$langs->trans("Total").':  '.price(price2num($subtot_coll_total_ht,'MT')).''.price(price2num($subtot_coll_vat,'MT')).'
'.$langs->trans("Total").':  '.price(price2num(0,'MT')).''.price(price2num(0,'MT')).'
 
'.$elementsup.''.$langs->trans("Date").''.$namesup.''.$productsup.''.$amountsup.''.$langs->trans("Payment").' ('.$langs->trans("PercentOfInvoice").')'.$langs->trans("AmountHTVATRealPaid").''.$vatsup.'
'.$langs->trans("Rate").': '.vatrate($rate).'%
'.$fields['link'].'' . $fields['datef'] . '' . $fields['company_link'] . ''; - if ($fields['pid']) { - $product_static->id=$fields['pid']; - $product_static->ref=$fields['pref']; - $product_static->type=$fields['ptype']; - print $product_static->getNomUrl(1); - if (dol_string_nohtmltag($fields['descr'])) { - print ' - '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); - } - } else { - if ($type) { - $text = img_object($langs->trans('Service'),'service'); - } else { - $text = img_object($langs->trans('Product'),'product'); - } - print $text.' '.dol_trunc(dol_string_nohtmltag($fields['descr']),16); - - // Show range - print_date_range($fields['ddate_start'],$fields['ddate_end']); - } - print ''; - print price($fields['totalht']); - if (price2num($fields['ftotal_ttc'])) { - //print $fields['dtotal_ttc']."/".$fields['ftotal_ttc']." - "; - $ratiolineinvoice=($fields['dtotal_ttc']/$fields['ftotal_ttc']); - //print ' ('.round($ratiolineinvoice*100,2).'%)'; - } - print ''; - if ($fields['payment_amount'] && $fields['ftotal_ttc']) { - $paymentfourn_static->id=$fields['payment_id']; - print $paymentfourn_static->getNomUrl(2); - } - if ($type == 0) { - print $langs->trans("NotUsedForGoods"); - } else { - print $fields['payment_amount']; - if (isset($fields['payment_amount'])) { - print ' ('.round($ratiopaymentinvoice*100,2).'%)'; - } - } - print ''; - $temp_ht=$fields['totalht']; - if ($type == 1) { - $temp_ht=$fields['totalht']*$ratiopaymentinvoice; - } - print price(price2num($temp_ht,'MT'),1); - print ''; - $temp_vat=$fields['vat']; - if ($type == 1) { - $temp_vat=$fields['vat']*$ratiopaymentinvoice; - } - print price(price2num($temp_vat,'MT'),1); - //print price($fields['vat']); - print '
'.$langs->trans("Total").':  '.price(price2num($subtot_paye_total_ht,'MT')).''.price(price2num($subtot_paye_vat,'MT')).'
'.$langs->trans("Total").':  '.price(price2num(0,'MT')).''.price(price2num(0,'MT')).'
'; - - // Total to pay - print '

'; - print ''; - $diff = $x_coll_sum - $x_paye_sum; - print ''; - print ''; - print '\n"; - print "\n"; - - $i++; -} -print '
'.$langs->trans("TotalToPay").($q?', '.$langs->trans("Quadri").' '.$q:'').''.price(price2num($diff,'MT'))."
'; - -llxFooter(); -$db->close(); diff --git a/htdocs/contact/agenda.php b/htdocs/contact/agenda.php index c513db1b49f..d443956a9fe 100644 --- a/htdocs/contact/agenda.php +++ b/htdocs/contact/agenda.php @@ -101,7 +101,7 @@ if (! $sortfield) $sortfield='a.datep, a.id'; if (! $sortorder) $sortorder='DESC'; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('contactcard','globalcard')); +$hookmanager->initHooks(array('contactagenda','globalcard')); /* diff --git a/htdocs/contact/canvas/actions_contactcard_common.class.php b/htdocs/contact/canvas/actions_contactcard_common.class.php index b5df91ce210..ba96c865d1a 100644 --- a/htdocs/contact/canvas/actions_contactcard_common.class.php +++ b/htdocs/contact/canvas/actions_contactcard_common.class.php @@ -43,34 +43,6 @@ abstract class ActionsContactCardCommon var $errors=array(); - /** - * Instantiation of DAO class - * - * @return int 0 - * @deprecated Using getInstanceDao should not be used. - */ - private function getInstanceDao() - { - dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING); - - if (! is_object($this->object)) - { - $modelclassfile = dol_buildpath('/'.$this->dirmodule.'/canvas/'.$this->canvas.'/dao_'.$this->targetmodule.'_'.$this->canvas.'.class.php'); - if (file_exists($modelclassfile)) - { - // Include dataservice class (model) - $ret = require_once $modelclassfile; - if ($ret) - { - // Instantiate dataservice class (model) - $modelclassname = 'Dao'.ucfirst($this->targetmodule).ucfirst($this->canvas); - $this->object = new $modelclassname($this->db); - } - } - } - return 0; - } - /** * Get object * @@ -93,141 +65,6 @@ abstract class ActionsContactCardCommon //} } - /** - * doActions of a canvas is not the doActions of the hook - * @deprecated Use the doActions of hooks instead of this. - * - * @param string $action Type of action - * @param int $id Id of object - * @return void - */ - function doActions(&$action, $id) - { - global $conf, $user, $langs; - - // Creation utilisateur depuis contact - if ($action == 'confirm_create_user' && GETPOST("confirm") == 'yes') - { - // Recuperation contact actuel - $result = $this->object->fetch($id); - - if ($result > 0) - { - $this->db->begin(); - - // Creation user - $nuser = new User($this->db); - $result=$nuser->create_from_contact($this->object,$_POST["login"]); - - if ($result > 0) - { - $result2=$nuser->setPassword($user,$_POST["password"],0,1,1); - if ($result2) - { - $this->db->commit(); - } - else - { - $this->db->rollback(); - } - } - else - { - $this->errors=$nuser->error; - - $this->db->rollback(); - } - } - else - { - $this->errors=$this->object->errors; - } - } - - // Creation contact - if ($action == 'add') - { - $this->assign_post(); - - if (! $_POST["name"]) - { - array_push($this->errors,$langs->trans("ErrorFieldRequired",$langs->transnoentities("Lastname").' / '.$langs->transnoentities("Label"))); - $action = 'create'; - } - - if ($_POST["name"]) - { - $id = $this->object->create($user); - if ($id > 0) - { - header("Location: ".$_SERVER["PHP_SELF"]."?id=".$id); - exit; - } - else - { - $this->errors=$this->object->errors; - $action = 'create'; - } - } - } - - if ($action == 'confirm_delete' && GETPOST("confirm") == 'yes') - { - $result=$this->object->fetch($id); - - $this->object->old_name = $_POST["old_name"]; - $this->object->old_firstname = $_POST["old_firstname"]; - - $result = $this->object->delete(); - if ($result > 0) - { - header("Location: list.php"); - exit; - } - else - { - $this->errors=$this->object->errors; - } - } - - if ($action == 'update') - { - if ($_POST["cancel"]) - { - header("Location: ".$_SERVER["PHP_SELF"]."?id=".$this->object->id); - exit; - } - - if (empty($_POST["name"])) - { - $this->error=array($langs->trans("ErrorFieldRequired",$langs->transnoentities("Name").' / '.$langs->transnoentities("Label"))); - $action = 'edit'; - } - - if (empty($this->error)) - { - $this->object->fetch($_POST["contactid"]); - - $this->object->oldcopy = clone $this->object; - - $this->assign_post(); - - $result = $this->object->update($_POST["contactid"], $user); - - if ($result > 0) - { - header("Location: ".$_SERVER["PHP_SELF"]."?id=".$this->object->id); - exit; - } - else - { - $this->errors=$this->object->errors; - $action = 'edit'; - } - } - } - } - /** * Set content of ->tpl array, to use into template * diff --git a/htdocs/contact/card.php b/htdocs/contact/card.php index 7d024e6f096..560acabaee9 100644 --- a/htdocs/contact/card.php +++ b/htdocs/contact/card.php @@ -176,26 +176,26 @@ if (empty($reshook)) $object->entity = (GETPOSTISSET('entity')?GETPOST('entity', 'int'):$conf->entity); $object->socid = GETPOST("socid",'int'); - $object->lastname = GETPOST("lastname"); - $object->firstname = GETPOST("firstname"); - $object->civility_id = GETPOST("civility_id",'alpha'); - $object->poste = GETPOST("poste"); - $object->address = GETPOST("address"); - $object->zip = GETPOST("zipcode"); - $object->town = GETPOST("town"); + $object->lastname = GETPOST("lastname",'alpha'); + $object->firstname = GETPOST("firstname",'alpha'); + $object->civility_id = GETPOST("civility_id",'alpha'); + $object->poste = GETPOST("poste",'alpha'); + $object->address = GETPOST("address",'alpha'); + $object->zip = GETPOST("zipcode",'alpha'); + $object->town = GETPOST("town",'alpha'); $object->country_id = GETPOST("country_id",'int'); $object->state_id = GETPOST("state_id",'int'); - $object->skype = GETPOST("skype"); + $object->skype = GETPOST("skype",'alpha'); $object->email = GETPOST("email",'alpha'); - $object->phone_pro = GETPOST("phone_pro"); - $object->phone_perso = GETPOST("phone_perso"); - $object->phone_mobile = GETPOST("phone_mobile"); - $object->fax = GETPOST("fax"); + $object->phone_pro = GETPOST("phone_pro",'alpha'); + $object->phone_perso = GETPOST("phone_perso",'alpha'); + $object->phone_mobile = GETPOST("phone_mobile",'alpha'); + $object->fax = GETPOST("fax",'alpha'); $object->jabberid = GETPOST("jabberid",'alpha'); $object->no_email = GETPOST("no_email",'int'); $object->priv = GETPOST("priv",'int'); - $object->note_public = GETPOST("note_public"); - $object->note_private = GETPOST("note_private"); + $object->note_public = GETPOST("note_public",'none'); + $object->note_private = GETPOST("note_private",'none'); $object->statut = 1; //Defult status to Actif // Note: Correct date should be completed with location to have exact GM time of birth. @@ -340,33 +340,33 @@ if (empty($reshook)) $object->oldcopy = clone $object; - $object->old_lastname = GETPOST("old_lastname"); - $object->old_firstname = GETPOST("old_firstname"); + $object->old_lastname = GETPOST("old_lastname",'alpha'); + $object->old_firstname = GETPOST("old_firstname",'alpha'); $object->socid = GETPOST("socid",'int'); - $object->lastname = GETPOST("lastname"); - $object->firstname = GETPOST("firstname"); - $object->civility_id = GETPOST("civility_id",'alpha'); - $object->poste = GETPOST("poste"); + $object->lastname = GETPOST("lastname",'alpha'); + $object->firstname = GETPOST("firstname",'alpha'); + $object->civility_id = GETPOST("civility_id",'alpha'); + $object->poste = GETPOST("poste",'alpha'); - $object->address = GETPOST("address"); - $object->zip = GETPOST("zipcode"); - $object->town = GETPOST("town"); - $object->state_id = GETPOST("state_id",'int'); + $object->address = GETPOST("address",'alpha'); + $object->zip = GETPOST("zipcode",'alpha'); + $object->town = GETPOST("town",'alpha'); + $object->state_id = GETPOST("state_id",'int'); $object->fk_departement = GETPOST("state_id",'int'); // For backward compatibility $object->country_id = GETPOST("country_id",'int'); $object->email = GETPOST("email",'alpha'); $object->skype = GETPOST("skype",'alpha'); - $object->phone_pro = GETPOST("phone_pro"); - $object->phone_perso = GETPOST("phone_perso"); - $object->phone_mobile = GETPOST("phone_mobile"); - $object->fax = GETPOST("fax"); + $object->phone_pro = GETPOST("phone_pro",'alpha'); + $object->phone_perso = GETPOST("phone_perso",'alpha'); + $object->phone_mobile = GETPOST("phone_mobile",'alpha'); + $object->fax = GETPOST("fax",'alpha'); $object->jabberid = GETPOST("jabberid",'alpha'); $object->no_email = GETPOST("no_email",'int'); $object->priv = GETPOST("priv",'int'); - $object->note_public = GETPOST("note_public"); - $object->note_private = GETPOST("note_private"); + $object->note_public = GETPOST("note_public",'none'); + $object->note_private = GETPOST("note_private",'none'); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost($extralabels,$object); @@ -541,9 +541,9 @@ else // Name print ''; - print 'lastname).'" autofocus="autofocus">'; + print 'lastname).'" autofocus="autofocus">'; print ''; - print 'firstname).'">'; + print 'firstname).'">'; // Company if (empty($conf->global->SOCIETE_DISABLE_CONTACTS)) @@ -595,8 +595,8 @@ else if (($objsoc->typent_code == 'TE_PRIVATE' || ! empty($conf->global->CONTACT_USE_COMPANY_ADDRESS)) && dol_strlen(trim($object->zip)) == 0) $object->zip = $objsoc->zip; // Predefined with third party if (($objsoc->typent_code == 'TE_PRIVATE' || ! empty($conf->global->CONTACT_USE_COMPANY_ADDRESS)) && dol_strlen(trim($object->town)) == 0) $object->town = $objsoc->town; // Predefined with third party print ' / '; - print $formcompany->select_ziptown((GETPOST("zipcode")?GETPOST("zipcode"):$object->zip),'zipcode',array('town','selectcountry_id','state_id'),6).' '; - print $formcompany->select_ziptown((GETPOST("town")?GETPOST("town"):$object->town),'town',array('zipcode','selectcountry_id','state_id')); + print $formcompany->select_ziptown((GETPOST("zipcode",'alpha')?GETPOST("zipcode",'alpha'):$object->zip),'zipcode',array('town','selectcountry_id','state_id'),6).' '; + print $formcompany->select_ziptown((GETPOST("town",'alpha')?GETPOST("town",'alpha'):$object->town),'town',array('zipcode','selectcountry_id','state_id')); print ''; // Country @@ -644,7 +644,7 @@ else // EMail if (($objsoc->typent_code == 'TE_PRIVATE' || ! empty($conf->global->CONTACT_USE_COMPANY_ADDRESS)) && dol_strlen(trim($object->email)) == 0) $object->email = $objsoc->email; // Predefined with third party print ''; - print 'email).'">'; + print 'email).'">'; if (! empty($conf->mailing->enabled)) { print ''; @@ -658,13 +658,13 @@ else // Instant message and no email print ''; - print 'jabberid).'">'; + print 'jabberid).'">'; // Skype if (! empty($conf->skype->enabled)) { print ''; - print 'skype).'">'; + print 'skype).'">'; } // Visibility diff --git a/htdocs/contact/class/contact.class.php b/htdocs/contact/class/contact.class.php index eca1174bb59..83ad411b4b6 100644 --- a/htdocs/contact/class/contact.class.php +++ b/htdocs/contact/class/contact.class.php @@ -220,15 +220,15 @@ class Contact extends CommonObject $sql.= ", import_key"; $sql.= ") VALUES ("; $sql.= "'".$this->db->idate($now)."',"; - if ($this->socid > 0) $sql.= " ".$this->socid.","; + if ($this->socid > 0) $sql.= " ".$this->db->escape($this->socid).","; else $sql.= "null,"; $sql.= "'".$this->db->escape($this->lastname)."',"; $sql.= "'".$this->db->escape($this->firstname)."',"; - $sql.= " ".($user->id > 0 ? "'".$user->id."'":"null").","; - $sql.= " ".$this->priv.","; - $sql.= " ".$this->statut.","; + $sql.= " ".($user->id > 0 ? "'".$this->db->escape($user->id)."'":"null").","; + $sql.= " ".$this->db->escape($this->priv).","; + $sql.= " ".$this->db->escape($this->statut).","; $sql.= " ".(! empty($this->canvas)?"'".$this->db->escape($this->canvas)."'":"null").","; - $sql.= " ".$entity.","; + $sql.= " ".$this->db->escape($entity).","; $sql.= "'".$this->db->escape($this->ref_ext)."',"; $sql.= " ".(! empty($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null"); $sql.= ")"; @@ -349,7 +349,7 @@ class Contact extends CommonObject $sql .= ", phone_mobile = ".(isset($this->phone_mobile)?"'".$this->db->escape($this->phone_mobile)."'":"null"); $sql .= ", jabberid = ".(isset($this->jabberid)?"'".$this->db->escape($this->jabberid)."'":"null"); $sql .= ", priv = '".$this->db->escape($this->priv)."'"; - $sql .= ", statut = ".$this->statut; + $sql .= ", statut = ".$this->db->escape($this->statut); $sql .= ", fk_user_modif=".($user->id > 0 ? "'".$this->db->escape($user->id)."'":"NULL"); $sql .= ", default_lang=".($this->default_lang?"'".$this->db->escape($this->default_lang)."'":"NULL"); $sql .= ", no_email=".($this->no_email?"'".$this->db->escape($this->no_email)."'":"0"); @@ -530,7 +530,7 @@ class Contact extends CommonObject if ($this->phone_mobile && ! empty($conf->global->LDAP_CONTACT_FIELD_MOBILE)) $info[$conf->global->LDAP_CONTACT_FIELD_MOBILE] = $this->phone_mobile; if ($this->fax && ! empty($conf->global->LDAP_CONTACT_FIELD_FAX)) $info[$conf->global->LDAP_CONTACT_FIELD_FAX] = $this->fax; if ($this->skype && ! empty($conf->global->LDAP_CONTACT_FIELD_SKYPE)) $info[$conf->global->LDAP_CONTACT_FIELD_SKYPE] = $this->skype; - if ($this->note_private && ! empty($conf->global->LDAP_CONTACT_FIELD_DESCRIPTION)) $info[$conf->global->LDAP_CONTACT_FIELD_DESCRIPTION] = $this->note_private; + if ($this->note_private && ! empty($conf->global->LDAP_CONTACT_FIELD_DESCRIPTION)) $info[$conf->global->LDAP_CONTACT_FIELD_DESCRIPTION] = dol_string_nohtmltag($this->note_private, 2); if ($this->email && ! empty($conf->global->LDAP_CONTACT_FIELD_MAIL)) $info[$conf->global->LDAP_CONTACT_FIELD_MAIL] = $this->email; if ($conf->global->LDAP_SERVER_TYPE == 'egroupware') @@ -1078,9 +1078,10 @@ class Contact extends CommonObject * @param int $maxlen Max length of * @param string $moreparam Add more param into URL * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @param int $notooltip 1=Disable tooltip * @return string String with URL */ - function getNomUrl($withpicto=0, $option='', $maxlen=0, $moreparam='', $save_lastsearch_value=-1) + function getNomUrl($withpicto=0, $option='', $maxlen=0, $moreparam='', $save_lastsearch_value=-1, $notooltip=0) { global $conf, $langs, $hookmanager; @@ -1112,15 +1113,18 @@ class Contact extends CommonObject $linkstart = 'global->MAIN_OPTIMIZEFORTEXTBROWSER)) - { - $label=$langs->trans("ShowContact"); - $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; - } - $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"'; - $linkclose.= ' class="classfortooltip">'; + if (empty($notooltip)) { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowContact"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.= ' class="classfortooltip"'; + } + $linkclose.='>'; - if (! is_object($hookmanager)) + /*if (! is_object($hookmanager)) { include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $hookmanager=new HookManager($this->db); @@ -1128,7 +1132,7 @@ class Contact extends CommonObject $hookmanager->initHooks(array('contactdao')); $parameters=array('id'=>$this->id); $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - if ($reshook > 0) $linkclose = $hookmanager->resPrint; + if ($reshook > 0) $linkclose = $hookmanager->resPrint;*/ $linkstart.=$linkclose; $linkend=''; @@ -1139,12 +1143,23 @@ class Contact extends CommonObject $linkend=''; } - $result.=$linkstart; if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip valigntextbottom"'), 0, 0, $notooltip?0:1); if ($withpicto != 2) $result.=($maxlen?dol_trunc($this->getFullName($langs),$maxlen):$this->getFullName($langs)); $result.=$linkend; + global $action; + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('contactdao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + return $result; } diff --git a/htdocs/contact/list.php b/htdocs/contact/list.php index 6e62cef1eb0..5c101401dac 100644 --- a/htdocs/contact/list.php +++ b/htdocs/contact/list.php @@ -7,6 +7,7 @@ * Copyright (C) 2013 Cédric Salvador * Copyright (C) 2013 Alexandre Spangaro * Copyright (C) 2015 Jean-François Ferry + * Copyright (C) 2018 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -120,7 +121,8 @@ else if ($type == "o") } // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Contact($db); +$hookmanager->initHooks(array('contactlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -133,6 +135,7 @@ $fieldstosearchall = array( 'p.firstname'=>'Firstname', 'p.email'=>'EMail', 's.nom'=>"ThirdParty", + 'p.phone'=>"Phone", ); // Definition of fields for list @@ -409,6 +412,12 @@ if ($user->rights->societe->supprimer) $arrayofmassactions['predelete']=$langs-> if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); +$newcardbutton=''; +if ($user->rights->societe->contact->creer) +{ + $newcardbutton=''.$langs->trans('NewContactAddress').''; +} + print ''; if ($optioncss != '') print ''; print ''; @@ -418,7 +427,7 @@ print ''; print ''; print ''; -print_barre_liste($titre, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies.png', 0, '', '', $limit); +print_barre_liste($titre, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies.png', 0, $newcardbutton, '', $limit); $topicmail="Information"; $modelmail="contact"; diff --git a/htdocs/contrat/admin/contract_extrafields.php b/htdocs/contrat/admin/contract_extrafields.php index 8a0476fb674..3fb55acc2ca 100644 --- a/htdocs/contrat/admin/contract_extrafields.php +++ b/htdocs/contrat/admin/contract_extrafields.php @@ -66,7 +66,7 @@ $textobject = $langs->transnoentitiesnoconv('Contracts'); llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ContractsSetup"),$linkback,'title_setup'); $head=contract_admin_prepare_head(); diff --git a/htdocs/contrat/admin/contractdet_extrafields.php b/htdocs/contrat/admin/contractdet_extrafields.php index e2eb2bacc6c..e3047466e1b 100644 --- a/htdocs/contrat/admin/contractdet_extrafields.php +++ b/htdocs/contrat/admin/contractdet_extrafields.php @@ -66,7 +66,7 @@ $textobject = $langs->transnoentitiesnoconv('Contracts'); llxHeader(); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ContractsSetup"),$linkback,'title_setup'); $head=contract_admin_prepare_head(); diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index d74c677f018..41cf0c12f3e 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -6,7 +6,7 @@ * Copyright (C) 2010-2017 Juanjo Menent * Copyright (C) 2013 Christophe Battarel * Copyright (C) 2013-2014 Florian Henry - * Copyright (C) 2014-2016 Ferran Marcet + * Copyright (C) 2014-2018 Ferran Marcet * Copyright (C) 2014-2016 Marcos García * Copyright (C) 2015 Jean-François Ferry * @@ -842,7 +842,7 @@ if (empty($reshook)) $result=$object->delete($user); if ($result >= 0) { - header("Location: index.php"); + header("Location: list.php?restore_lastsearch_values=1"); return; } else @@ -880,7 +880,7 @@ if (empty($reshook)) // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) { @@ -955,19 +955,35 @@ if (empty($reshook)) setEventMessages($object->error, $object->errors, 'errors'); } - $result = $object->setValueFrom('ref', GETPOST('ref','alpha'), '', null, 'text', '', $user, 'CONTRACT_MODIFY'); - if ($result < 0) { - setEventMessages($object->error, $object->errors, 'errors'); - $action = 'editref'; - } else { - header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id); - exit; - } - } - else { - header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $id); - exit; - } + $old_ref = $object->ref; + + $result = $object->setValueFrom('ref', GETPOST('ref','alpha'), '', null, 'text', '', $user, 'CONTRACT_MODIFY'); + if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); + $action = 'editref'; + } else { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + $old_filedir = $conf->contrat->dir_output . '/' . dol_sanitizeFileName($old_ref); + $new_filedir = $conf->contrat->dir_output . '/' . dol_sanitizeFileName($object->ref); + + $files = dol_dir_list($old_filedir); + if (!empty($files)) + { + if (!is_dir($new_filedir)) dol_mkdir($new_filedir); + foreach ($files as $file) + { + dol_move($file['fullname'], $new_filedir.'/'.$file['name']); + } + } + + header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id); + exit; + } + } + else { + header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $id); + exit; + } } elseif ($action=='setdate_contrat') { @@ -1527,10 +1543,10 @@ else // Title line for service $cursorline=1; print '
'; - while ($cursorline <= $nbofservices) + while ($cursorline <= $nbofservices) { print '
'; - print ''; + print ''; print ''; print ''; print ''; @@ -1640,13 +1656,14 @@ else print ''; if ($user->rights->contrat->creer && count($arrayothercontracts) && ($object->statut >= 0)) { - print ''; + print ''; + print ''; print img_picto($langs->trans("MoveToAnotherContract"),'uparrow'); print ''; } if ($user->rights->contrat->creer && ($object->statut >= 0)) { - print ''; + print ''; print img_edit(); print ''; } @@ -1704,14 +1721,11 @@ else print ''; } - // Display lines extrafields if (is_array($extralabelslines) && count($extralabelslines)>0) { - print ''; $line = new ContratLigne($db); $line->fetch_optionals($objp->rowid); - print $line->showOptionals($extrafieldsline, 'view', array('style'=>$bcnd[$var], 'colspan'=>$colspan)); - print ''; + print $line->showOptionals($extrafieldsline, 'view', array('style'=>$bcnd[$var], 'colspan'=>$colspan), '', '', empty($conf->global->MAIN_EXTRAFIELDS_IN_ONE_TD)?0:1); } } // Ligne en mode update @@ -1766,7 +1780,8 @@ else print ''; print '
'; print ''; - + print ''; + $colspan=6; if (! empty($conf->margin->enabled) && ! empty($conf->global->MARGIN_SHOW_ON_CONTRACT)) $colspan++; if($conf->global->PRODUCT_USE_UNITS) $colspan++; @@ -1779,16 +1794,13 @@ else print '   '.$langs->trans("DateEndPlanned").' '; $form->select_date($db->jdate($objp->date_fin),"date_end_update",$usehm,$usehm,($db->jdate($objp->date_fin)>0?0:1),"update"); print ''; + print ''; if (is_array($extralabelslines) && count($extralabelslines)>0) { - print ''; $line = new ContratLigne($db); $line->fetch_optionals($objp->rowid); - print $line->showOptionals($extrafieldsline, 'edit', array('style'=>$bcnd[$var], 'colspan'=>$colspan)); - print ''; + print $line->showOptionals($extrafieldsline, 'edit', array('style'=>$bcnd[$var], 'colspan'=>$colspan), '', '', empty($conf->global->MAIN_EXTRAFIELDS_IN_ONE_TD)?0:1); } - - print ''; } $db->free($result); @@ -1875,7 +1887,7 @@ else // Area with status and activation info of line if ($object->statut > 0) { - print ''; + print '
'; print ''; print ''; @@ -1895,7 +1907,7 @@ else } if (($tmpaction=='activateline' && $user->rights->contrat->activer) || ($tmpaction=='unactivateline' && $user->rights->contrat->desactiver)) { - print ''; + print ''; print img_picto($tmpactiontext, $tmpactionpicto); print ''; } @@ -1926,7 +1938,7 @@ else print $langs->trans("DateEndReal").': '; print dol_print_date($objp->date_fin_reelle, 'day'); } - if (! empty($objp->comment)) print "
".$objp->comment; + if (! empty($objp->comment)) print "  -  ".$objp->comment; print ''; print ''; @@ -1996,7 +2008,7 @@ else print ''; print ''; - print '
'.$langs->trans("ServiceStatus").': '.$object->lines[$cursorline-1]->getLibStatut(4).' 
'; + print '
'; // Definie date debut et fin par defaut $dateactstart = $objp->date_debut_reelle; @@ -2108,9 +2120,9 @@ else // Send if ($object->statut == 1) { if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->commande->order_advance->send)) { - print ''; + print ''; } else - print ''; + print ''; } if ($object->statut == 0 && $nbofservices) @@ -2143,7 +2155,7 @@ else print ''; } - if ($object->nbofservicesclosed > 0) + if ($object->nbofservicesclosed > 0 || $object->nbofserviceswait > 0) { print ''; } diff --git a/htdocs/contrat/class/contrat.class.php b/htdocs/contrat/class/contrat.class.php index 0b84b05b6fd..215dc185ac4 100644 --- a/htdocs/contrat/class/contrat.class.php +++ b/htdocs/contrat/class/contrat.class.php @@ -262,9 +262,11 @@ class Contrat extends CommonObject * @param User $user Object User making action * @param int|string $date_start Date start (now if empty) * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers + * @param string $comment Comment * @return int <0 if KO, >0 if OK + * @see closeAll */ - function activateAll($user, $date_start='', $notrigger=0) + function activateAll($user, $date_start='', $notrigger=0, $comment='') { if (empty($date_start)) $date_start = dol_now(); @@ -278,11 +280,11 @@ class Contrat extends CommonObject foreach($this->lines as $contratline) { // Open lines not already open - if ($contratline->statut != 4) + if ($contratline->statut != ContratLigne::STATUS_OPEN) { $contratline->context = $this->context; - $result = $contratline->active_line($user, $date_start, -1); + $result = $contratline->active_line($user, $date_start, -1, $comment); if ($result < 0) { $error++; @@ -316,9 +318,11 @@ class Contrat extends CommonObject * * @param User $user Object User making action * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers + * @param string $comment Comment * @return int <0 if KO, >0 if OK + * @see activateAll */ - function closeAll(User $user, $notrigger=0) + function closeAll(User $user, $notrigger=0, $comment='') { $this->db->begin(); @@ -332,12 +336,12 @@ class Contrat extends CommonObject foreach($this->lines as $contratline) { // Close lines not already closed - if ($contratline->statut != 5) + if ($contratline->statut != ContratLigne::STATUS_CLOSED) { $contratline->date_cloture=$now; $contratline->fk_user_cloture=$user->id; - $contratline->statut='5'; - $result=$contratline->close_line($user, $now); + $contratline->statut=ContratLigne::STATUS_CLOSED; + $result=$contratline->close_line($user, $now, $comment, $notrigger); if ($result < 0) { $error++; @@ -673,7 +677,8 @@ class Contrat extends CommonObject } /** - * Load lines array into this->lines + * Load lines array into this->lines. + * This set also nbofserviceswait, nbofservicesopened, nbofservicesexpired and nbofservicesclosed * * @return ContratLigne[] Return array of contract lines */ @@ -803,10 +808,10 @@ class Contrat extends CommonObject //dol_syslog("1 ".$line->desc); //dol_syslog("2 ".$line->product_desc); - if ($line->statut == 0) $this->nbofserviceswait++; - if ($line->statut == 4 && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) $this->nbofservicesopened++; - if ($line->statut == 4 && (! empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) $this->nbofservicesexpired++; - if ($line->statut == 5) $this->nbofservicesclosed++; + if ($line->statut == ContratLigne::STATUS_INITIAL) $this->nbofserviceswait++; + if ($line->statut == ContratLigne::STATUS_OPEN && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) $this->nbofservicesopened++; + if ($line->statut == ContratLigne::STATUS_OPEN && (! empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) $this->nbofservicesexpired++; + if ($line->statut == ContratLigne::STATUS_CLOSED) $this->nbofservicesclosed++; $total_ttc+=$objp->total_ttc; // TODO Not saved into database $total_vat+=$objp->total_tva; @@ -897,17 +902,20 @@ class Contrat extends CommonObject if ($result > 0) { $modCodeContract = new $module(); - } - if (!empty($modCodeContract->code_auto)) { - // Mise a jour ref - $sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".$this->id; - if ($this->db->query($sql)) - { - if ($this->id) + if (!empty($modCodeContract->code_auto)) { + // Mise a jour ref + $sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".$this->id; + if ($this->db->query($sql)) { - $this->ref="(PROV".$this->id.")"; + if ($this->id) + { + $this->ref="(PROV".$this->id.")"; + } } + } else { + $error++; + $this->error='Failed to get PROV number'; } } @@ -1334,13 +1342,14 @@ class Contrat extends CommonObject function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type='HT', $pu_ttc=0.0, $info_bits=0, $fk_fournprice=null, $pa_ht = 0,$array_options=0, $fk_unit = null, $rang=0) { global $user, $langs, $conf, $mysoc; + $error=0; dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type, $pu_ttc, $info_bits, $rang"); // Check parameters if ($fk_product <= 0 && empty($desc)) { - $this->error="DescRequiredForFreeProductLines"; + $this->error="ErrorDescRequiredForFreeProductLines"; return -1; } @@ -1467,49 +1476,39 @@ class Contrat extends CommonObject { $contractlineid = $this->db->last_insert_id(MAIN_DB_PREFIX."contratdet"); - $result=$this->update_statut($user); - if ($result > 0) + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used { - - if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($array_options) && count($array_options)>0) // For avoid conflicts if trigger used + $contractline = new ContratLigne($this->db); + $contractline->array_options=$array_options; + $contractline->id=$contractlineid; + $result=$contractline->insertExtraFields(); + if ($result < 0) { - $contractline = new ContratLigne($this->db); - $contractline->array_options=$array_options; - $contractline->id=$contractlineid; - $result=$contractline->insertExtraFields(); - if ($result < 0) - { - $this->error[]=$contractline->error; - $error++; - } - } - - if (empty($error)) { - // Call trigger - $result=$this->call_trigger('LINECONTRACT_INSERT',$user); - if ($result < 0) - { - $error++; - } - // End call triggers - } - - if ($error) - { - $this->db->rollback(); - return -1; - } - else - { - $this->db->commit(); - return $contractlineid; + $this->error[]=$contractline->error; + $error++; } } - else + + if (empty($error)) { + // Call trigger + $result=$this->call_trigger('LINECONTRACT_INSERT',$user); + if ($result < 0) + { + $error++; + } + // End call triggers + } + + if ($error) { $this->db->rollback(); return -1; } + else + { + $this->db->commit(); + return $contractlineid; + } } else { @@ -1552,6 +1551,8 @@ class Contrat extends CommonObject { global $user, $conf, $langs, $mysoc; + $error=0; + // Clean parameters $qty=trim($qty); $desc=trim($desc); @@ -1843,7 +1844,6 @@ class Contrat extends CommonObject } if ($mode == 4 || $mode == 6 || $mode == 7) { - $line=new ContratLigne($this->db); $text=''; if ($mode == 4) { @@ -1854,13 +1854,13 @@ class Contrat extends CommonObject $text.=''; } $text.=($mode == 7?'
':''); - $text.=($mode != 7 || $this->nbofserviceswait > 0) ? ($this->nbofserviceswait.$line->LibStatut(0,3,-1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesopened || $this->nbofservicesexpired || $this->nbofservicesclosed)?'   ':'') : ''; + $text.=($mode != 7 || $this->nbofserviceswait > 0) ? ($this->nbofserviceswait.ContratLigne::LibStatut(0,3,-1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesopened || $this->nbofservicesexpired || $this->nbofservicesclosed)?'   ':'') : ''; $text.=($mode == 7?'
':''); - $text.=($mode != 7 || $this->nbofservicesopened > 0) ? ($this->nbofservicesopened.$line->LibStatut(4,3,0,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesexpired || $this->nbofservicesclosed)?'   ':'') : ''; + $text.=($mode != 7 || $this->nbofservicesopened > 0) ? ($this->nbofservicesopened.ContratLigne::LibStatut(4,3,0,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesexpired || $this->nbofservicesclosed)?'   ':'') : ''; $text.=($mode == 7?'
':''); - $text.=($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.$line->LibStatut(4,3,1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesclosed)?'   ':'') : ''; + $text.=($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.ContratLigne::LibStatut(4,3,1,'class="paddingleft2 inline-block valigntextbottom"')).(($mode != 7 || $this->nbofservicesclosed)?'   ':'') : ''; $text.=($mode == 7?'
':''); - $text.=($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.$line->LibStatut(5,3,-1,'class="paddingleft2 inline-block valigntextbottom"')) : ''; + $text.=($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.ContratLigne::LibStatut(5,3,-1,'class="paddingleft2 inline-block valigntextbottom"')) : ''; $text.=($mode == 7?'
':''); return $text; } @@ -2520,6 +2520,11 @@ class ContratLigne extends CommonObjectLine var $fk_user_cloture; var $commentaire; + const STATUS_INITIAL = 0; + const STATUS_OPEN = 4; + const STATUS_CLOSED = 5; + + /** * Constructor @@ -2552,57 +2557,57 @@ class ContratLigne extends CommonObjectLine * @param string $moreatt More attribute * @return string Libelle */ - function LibStatut($statut,$mode,$expired=-1,$moreatt='') + static function LibStatut($statut,$mode,$expired=-1,$moreatt='') { global $langs; $langs->load("contracts"); if ($mode == 0) { - if ($statut == 0) { return $langs->trans("ServiceStatusInitial"); } - if ($statut == 4 && $expired == -1) { return $langs->trans("ServiceStatusRunning"); } - if ($statut == 4 && $expired == 0) { return $langs->trans("ServiceStatusNotLate"); } - if ($statut == 4 && $expired == 1) { return $langs->trans("ServiceStatusLate"); } - if ($statut == 5) { return $langs->trans("ServiceStatusClosed"); } + if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial"); } + if ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning"); } + if ($statut == self::STATUS_OPEN && $expired == 0) { return $langs->trans("ServiceStatusNotLate"); } + if ($statut == self::STATUS_OPEN && $expired == 1) { return $langs->trans("ServiceStatusLate"); } + if ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed"); } } if ($mode == 1) { - if ($statut == 0) { return $langs->trans("ServiceStatusInitial"); } - if ($statut == 4 && $expired == -1) { return $langs->trans("ServiceStatusRunning"); } - if ($statut == 4 && $expired == 0) { return $langs->trans("ServiceStatusNotLateShort"); } - if ($statut == 4 && $expired == 1) { return $langs->trans("ServiceStatusLateShort"); } - if ($statut == 5) { return $langs->trans("ServiceStatusClosed"); } + if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial"); } + if ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning"); } + if ($statut == self::STATUS_OPEN && $expired == 0) { return $langs->trans("ServiceStatusNotLateShort"); } + if ($statut == self::STATUS_OPEN && $expired == 1) { return $langs->trans("ServiceStatusLateShort"); } + if ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed"); } } if ($mode == 2) { - if ($statut == 0) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); } - if ($statut == 4 && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); } - if ($statut == 4 && $expired == 0) { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLateShort"); } - if ($statut == 4 && $expired == 1) { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLateShort"); } - if ($statut == 5) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); } + if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); } + if ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); } + if ($statut == self::STATUS_OPEN && $expired == 0) { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLateShort"); } + if ($statut == self::STATUS_OPEN && $expired == 1) { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLateShort"); } + if ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); } } if ($mode == 3) { - if ($statut == 0) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0',$moreatt); } - if ($statut == 4 && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4',$moreatt); } - if ($statut == 4 && $expired == 0) { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4',$moreatt); } - if ($statut == 4 && $expired == 1) { return img_picto($langs->trans('ServiceStatusLate'),'statut3',$moreatt); } - if ($statut == 5) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6',$moreatt); } + if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0',$moreatt); } + if ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4',$moreatt); } + if ($statut == self::STATUS_OPEN && $expired == 0) { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4',$moreatt); } + if ($statut == self::STATUS_OPEN && $expired == 1) { return img_picto($langs->trans('ServiceStatusLate'),'statut3',$moreatt); } + if ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6',$moreatt); } } if ($mode == 4) { - if ($statut == 0) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); } - if ($statut == 4 && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); } - if ($statut == 4 && $expired == 0) { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLate"); } - if ($statut == 4 && $expired == 1) { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLate"); } - if ($statut == 5) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); } + if ($statut == self::STATUS_INITIAL) { return img_picto($langs->trans('ServiceStatusInitial'),'statut0').' '.$langs->trans("ServiceStatusInitial"); } + if ($statut == self::STATUS_OPEN && $expired == -1) { return img_picto($langs->trans('ServiceStatusRunning'),'statut4').' '.$langs->trans("ServiceStatusRunning"); } + if ($statut == self::STATUS_OPEN && $expired == 0) { return img_picto($langs->trans('ServiceStatusNotLate'),'statut4').' '.$langs->trans("ServiceStatusNotLate"); } + if ($statut == self::STATUS_OPEN && $expired == 1) { return img_picto($langs->trans('ServiceStatusLate'),'statut3').' '.$langs->trans("ServiceStatusLate"); } + if ($statut == self::STATUS_CLOSED) { return img_picto($langs->trans('ServiceStatusClosed'),'statut6') .' '.$langs->trans("ServiceStatusClosed"); } } if ($mode == 5) { - if ($statut == 0) { return $langs->trans("ServiceStatusInitial").' '.img_picto($langs->trans('ServiceStatusInitial'),'statut0'); } - if ($statut == 4 && $expired == -1) { return $langs->trans("ServiceStatusRunning").' '.img_picto($langs->trans('ServiceStatusRunning'),'statut4'); } - if ($statut == 4 && $expired == 0) { return $langs->trans("ServiceStatusNotLateShort").' '.img_picto($langs->trans('ServiceStatusNotLateShort'),'statut4'); } - if ($statut == 4 && $expired == 1) { return $langs->trans("ServiceStatusLateShort").' '.img_picto($langs->trans('ServiceStatusLate'),'statut3'); } - if ($statut == 5) { return $langs->trans("ServiceStatusClosed").' '.img_picto($langs->trans('ServiceStatusClosed'),'statut6'); } + if ($statut == self::STATUS_INITIAL) { return $langs->trans("ServiceStatusInitial").' '.img_picto($langs->trans('ServiceStatusInitial'),'statut0'); } + if ($statut == self::STATUS_OPEN && $expired == -1) { return $langs->trans("ServiceStatusRunning").' '.img_picto($langs->trans('ServiceStatusRunning'),'statut4'); } + if ($statut == self::STATUS_OPEN && $expired == 0) { return $langs->trans("ServiceStatusNotLateShort").' '.img_picto($langs->trans('ServiceStatusNotLateShort'),'statut4'); } + if ($statut == self::STATUS_OPEN && $expired == 1) { return $langs->trans("ServiceStatusLateShort").' '.img_picto($langs->trans('ServiceStatusLate'),'statut3'); } + if ($statut == self::STATUS_CLOSED) { return $langs->trans("ServiceStatusClosed").' '.img_picto($langs->trans('ServiceStatusClosed'),'statut6'); } } } @@ -3094,39 +3099,43 @@ class ContratLigne extends CommonObjectLine { global $langs, $conf; - // Update object - $this->date_ouverture = $date; - $this->date_fin_validite = $date_end; - $this->fk_user_ouverture = $user->id; - $this->date_cloture = null; - $this->commentaire = $comment; - $error = 0; $this->db->begin(); - $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = 4,"; + $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = ".ContratLigne::STATUS_OPEN.","; $sql .= " date_ouverture = " . (dol_strlen($date) != 0 ? "'" . $this->db->idate($date) . "'" : "null") . ","; if ($date_end >= 0) $sql .= " date_fin_validite = " . (dol_strlen($date_end) != 0 ? "'" . $this->db->idate($date_end) . "'" : "null") . ","; $sql .= " fk_user_ouverture = " . $user->id . ","; $sql .= " date_cloture = null,"; $sql .= " commentaire = '" . $this->db->escape($comment) . "'"; - $sql .= " WHERE rowid = " . $this->id . " AND (statut = 0 OR statut = 3 OR statut = 5)"; + $sql .= " WHERE rowid = " . $this->id . " AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")"; dol_syslog(get_class($this) . "::active_line", LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { // Call trigger $result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user); - if ($result < 0) { - $error++; + if ($result < 0) $error++; + // End call triggers + + if (! $error) + { + $this->statut = ContratLigne::STATUS_OPEN; + $this->date_ouverture = $date; + $this->date_fin_validite = $date_end; + $this->fk_user_ouverture = $user->id; + $this->date_cloture = null; + $this->commentaire = $comment; + + $this->db->commit(); + return 1; + } + else + { $this->db->rollback(); return -1; } - // End call triggers - - $this->db->commit(); - return 1; } else { $this->error = $this->db->lasterror(); $this->db->rollback(); @@ -3140,9 +3149,10 @@ class ContratLigne extends CommonObjectLine * @param User $user Objet User who close contract * @param int $date_end Date end * @param string $comment A comment typed by user + * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers * @return int <0 if KO, >0 if OK */ - function close_line($user, $date_end, $comment = '') + function close_line($user, $date_end, $comment = '', $notrigger=0) { global $langs, $conf; @@ -3157,22 +3167,26 @@ class ContratLigne extends CommonObjectLine $this->db->begin(); - $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = 5,"; + $sql = "UPDATE " . MAIN_DB_PREFIX . "contratdet SET statut = ".ContratLigne::STATUS_CLOSED.","; $sql .= " date_cloture = '" . $this->db->idate($date_end) . "',"; $sql .= " fk_user_cloture = " . $user->id . ","; $sql .= " commentaire = '" . $this->db->escape($comment) . "'"; - $sql .= " WHERE rowid = " . $this->id . " AND statut = 4"; + $sql .= " WHERE rowid = " . $this->id . " AND statut = ".ContratLigne::STATUS_OPEN; $resql = $this->db->query($sql); - if ($resql) { - // Call trigger - $result = $this->call_trigger('LINECONTRACT_CLOSE', $user); - if ($result < 0) { - $error++; - $this->db->rollback(); - return -1; + if ($resql) + { + if (! $notrigger) + { + // Call trigger + $result = $this->call_trigger('LINECONTRACT_CLOSE', $user); + if ($result < 0) { + $error++; + $this->db->rollback(); + return -1; + } + // End call triggers } - // End call triggers $this->db->commit(); return 1; diff --git a/htdocs/contrat/contact.php b/htdocs/contrat/contact.php index 64050c3512d..1b81d9d13f6 100644 --- a/htdocs/contrat/contact.php +++ b/htdocs/contrat/contact.php @@ -49,8 +49,13 @@ $result=restrictedArea($user,'contrat',$id); $object = new Contrat($db); +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('contractcard','globalcard')); -// Add new contact + +/* + * Actions + */ if ($action == 'addcontact' && $user->rights->contrat->creer) { diff --git a/htdocs/contrat/document.php b/htdocs/contrat/document.php index 32fcd5a64bd..4bc2747a829 100644 --- a/htdocs/contrat/document.php +++ b/htdocs/contrat/document.php @@ -77,10 +77,14 @@ if ($object->id > 0) $upload_dir = $conf->contrat->dir_output.'/'.dol_sanitizeFileName($object->ref); $modulepart='contract'; +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('contractcard','globalcard')); + /* * Actions */ + include_once DOL_DOCUMENT_ROOT . '/core/actions_linkedfiles.inc.php'; diff --git a/htdocs/contrat/info.php b/htdocs/contrat/info.php index 10f31ac1612..b6c92eac27e 100644 --- a/htdocs/contrat/info.php +++ b/htdocs/contrat/info.php @@ -41,6 +41,17 @@ $ref = GETPOST('ref','alpha'); if ($user->societe_id) $socid=$user->societe_id; $result = restrictedArea($user, 'contrat', $id, ''); +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('contractcard','globalcard')); + + +/* + * Actions + */ + +// None + + /* * View diff --git a/htdocs/contrat/list.php b/htdocs/contrat/list.php index babc31f7702..b1755b7f7ec 100644 --- a/htdocs/contrat/list.php +++ b/htdocs/contrat/list.php @@ -45,6 +45,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'contractlist'; // To manage different context of search $search_name=GETPOST('search_name'); $search_email=GETPOST('search_email'); @@ -95,10 +96,8 @@ $staticcontratligne=new ContratLigne($db); if ($search_status == '') $search_status=1; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage='contractlist'; - -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Contrat($db); +$hookmanager->initHooks(array('contractlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -205,8 +204,6 @@ $formother = new FormOther($db); $socstatic = new Societe($db); $contracttmp = new Contrat($db); -llxHeader('', $langs->trans("Contracts")); - $sql = 'SELECT'; $sql.= " c.rowid, c.ref, c.datec as date_creation, c.tms as date_update, c.date_contrat, c.statut, c.ref_customer, c.ref_supplier, c.note_private, c.note_public,"; $sql.= ' s.rowid as socid, s.nom as name, s.email, s.town, s.zip, s.fk_pays, s.client, s.code_client,'; @@ -309,489 +306,510 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) $sql.= $db->plimit($limit + 1, $offset); $resql=$db->query($sql); -if ($resql) +if (! $resql) { - $num = $db->num_rows($resql); - $i = 0; + dol_print_error($db); + exit; +} - $arrayofselected=is_array($toselect)?$toselect:array(); +$num = $db->num_rows($resql); - if ($socid > 0) +// Direct jump if only one record found +if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $sall) +{ + $obj = $db->fetch_object($resql); + $id = $obj->rowid; + header("Location: ".DOL_URL_ROOT.'/contrat/card.php?id='.$id); + exit; +} + + +// Output page +// -------------------------------------------------------------------- + +llxHeader('', $langs->trans("Contracts")); + +$i = 0; + +$arrayofselected=is_array($toselect)?$toselect:array(); + +if ($socid > 0) +{ + $soc = new Societe($db); + $soc->fetch($socid); + if (empty($search_name)) $search_name = $soc->name; +} + +$param=''; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; +if ($sall != '') $param.='&sall='.urlencode($sall); +if ($search_contract != '') $param.='&search_contract='.urlencode($search_contract); +if ($search_name != '') $param.='&search_name='.urlencode($search_name); +if ($search_email != '') $param.='&search_email='.urlencode($search_email); +if ($search_ref_customer != '') $param.='&search_ref_customer='.urlencode($search_ref_customer); +if ($search_ref_supplier != '') $param.='&search_ref_supplier='.urlencode($search_ref_supplier); +if ($search_op2df != '') $param.='&search_op2df='.urlencode($search_op2df); +if ($search_dfyear != '') $param.='&search_dfyear='.urlencode($search_dfyear); +if ($search_dfmonth != '') $param.='&search_dfmonth='.urlencode($search_dfmonth); +if ($search_sale != '') $param.='&search_sale=' .urlencode($search_sale); +if ($search_user != '') $param.='&search_user=' .urlencode($search_user); +if ($search_product_category != '') $param.='&search_product_category=' .urlencode($search_product_category); +if ($show_files) $param.='&show_files=' .urlencode($show_files); +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +// Add $param from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; + +// List of mass actions available +$arrayofmassactions = array( + 'presend'=>$langs->trans("SendByMail"), + 'builddoc'=>$langs->trans("PDFMerge"), +); +if ($user->rights->contrat->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); +$massactionbutton=$form->selectMassAction('', $arrayofmassactions); + +$newcardbutton=''; +if ($user->rights->contrat->creer) +{ + $newcardbutton=''.$langs->trans('NewContractSubscription').''; +} + +print ''; +if ($optioncss != '') print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +print_barre_liste($langs->trans("ListOfContracts"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $totalnboflines, 'title_commercial.png', 0, $newcardbutton, '', $limit); + +$topicmail="SendContractRef"; +$modelmail="contract"; +$objecttmp=new Contrat($db); +$trackid='con'.$object->id; +include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; + +if ($sall) +{ + foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); + print $langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall); +} + +$moreforfilter=''; + +// If the user can view prospects other than his' +if ($user->rights->societe->client->voir || $socid) +{ + $langs->load("commercial"); + $moreforfilter.='
'; + $moreforfilter.=$langs->trans('ThirdPartiesOfSaleRepresentative'). ': '; + $moreforfilter.=$formother->select_salesrepresentatives($search_sale,'search_sale',$user,0,1,'maxwidth200'); + $moreforfilter.='
'; +} +// If the user can view other users +if ($user->rights->user->user->lire) +{ + $moreforfilter.='
'; + $moreforfilter.=$langs->trans('LinkedToSpecificUsers'). ': '; + $moreforfilter.=$form->select_dolusers($search_user, 'search_user', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth200'); + $moreforfilter.='
'; +} +// If the user can view categories of products +if ($conf->categorie->enabled && ($user->rights->produit->lire || $user->rights->service->lire)) +{ + include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + $moreforfilter.='
'; + $moreforfilter.=$langs->trans('IncludingProductWithTag'). ': '; + $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, null, 'parent', null, null, 1); + $moreforfilter.=$form->selectarray('search_product_category', $cate_arbo, $search_product_category, 1, 0, 0, '', 0, 0, 0, 0, 'maxwidth300', 1); + $moreforfilter.='
'; +} + +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters); // Note that $action and $object may have been modified by hook +if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint; +else $moreforfilter = $hookmanager->resPrint; + +if (! empty($moreforfilter)) +{ + print '
'; + print $moreforfilter; + print '
'; +} + +$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; +$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields +if ($massactionbutton) $selectedfields.=$form->showCheckAddButtons('checkforselect', 1); + +print '
'; +print '
'."\n"; + +print ''; +if (! empty($arrayfields['c.ref']['checked'])) +{ + print ''; +} +if (! empty($arrayfields['c.ref_customer']['checked'])) +{ + print ''; +} +if (! empty($arrayfields['c.ref_supplier']['checked'])) +{ + print ''; +} +if (! empty($arrayfields['s.nom']['checked'])) +{ + print ''; +} +if (! empty($arrayfields['s.email']['checked'])) +{ + print ''; +} +// Town +if (! empty($arrayfields['s.town']['checked'])) print ''; +// Zip +if (! empty($arrayfields['s.zip']['checked'])) print ''; +// State +if (! empty($arrayfields['state.nom']['checked'])) +{ + print ''; +} +// Country +if (! empty($arrayfields['country.code_iso']['checked'])) +{ + print ''; +} +// Company type +if (! empty($arrayfields['typent.code']['checked'])) +{ + print ''; +} +if (! empty($arrayfields['sale_representative']['checked'])) +{ + print ''; +} +if (! empty($arrayfields['c.date_contrat']['checked'])) +{ + // Date contract + print ''; +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; + +// Fields from hook +$parameters=array('arrayfields'=>$arrayfields); +$reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Date creation +if (! empty($arrayfields['c.datec']['checked'])) +{ + print ''; +} +// Date modification +if (! empty($arrayfields['c.tms']['checked'])) +{ + print ''; +} +// First end date +if (! empty($arrayfields['lower_planned_end_date']['checked'])) +{ + print ''; +} +// Status +if (! empty($arrayfields['status']['checked'])) +{ + print ''; +} +print ''; +print "\n"; + +print ''; +if (! empty($arrayfields['c.ref']['checked'])) print_liste_field_titre($arrayfields['c.ref']['label'], $_SERVER["PHP_SELF"], "c.ref","","$param",'',$sortfield,$sortorder); +if (! empty($arrayfields['c.ref_customer']['checked'])) print_liste_field_titre($arrayfields['c.ref_customer']['label'], $_SERVER["PHP_SELF"], "c.ref_customer","","$param",'',$sortfield,$sortorder); +if (! empty($arrayfields['c.ref_supplier']['checked'])) print_liste_field_titre($arrayfields['c.ref_supplier']['label'], $_SERVER["PHP_SELF"], "c.ref_supplier","","$param",'',$sortfield,$sortorder); +if (! empty($arrayfields['s.nom']['checked'])) print_liste_field_titre($arrayfields['s.nom']['label'], $_SERVER["PHP_SELF"], "s.nom","","$param",'',$sortfield,$sortorder); +if (! empty($arrayfields['s.email']['checked'])) print_liste_field_titre($arrayfields['s.email']['label'], $_SERVER["PHP_SELF"], "s.email","","$param",'',$sortfield,$sortorder); +if (! empty($arrayfields['s.town']['checked'])) print_liste_field_titre($arrayfields['s.town']['label'],$_SERVER["PHP_SELF"],'s.town','',$param,'',$sortfield,$sortorder); +if (! empty($arrayfields['s.zip']['checked'])) print_liste_field_titre($arrayfields['s.zip']['label'],$_SERVER["PHP_SELF"],'s.zip','',$param,'',$sortfield,$sortorder); +if (! empty($arrayfields['state.nom']['checked'])) print_liste_field_titre($arrayfields['state.nom']['label'],$_SERVER["PHP_SELF"],"state.nom","",$param,'',$sortfield,$sortorder); +if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($arrayfields['country.code_iso']['label'],$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder); +if (! empty($arrayfields['typent.code']['checked'])) print_liste_field_titre($arrayfields['typent.code']['label'],$_SERVER["PHP_SELF"],"typent.code","",$param,'align="center"',$sortfield,$sortorder); +if (! empty($arrayfields['sale_representative']['checked'])) print_liste_field_titre($arrayfields['sale_representative']['label'], $_SERVER["PHP_SELF"], "","","$param",'',$sortfield,$sortorder); +if (! empty($arrayfields['c.date_contrat']['checked'])) print_liste_field_titre($arrayfields['c.date_contrat']['label'], $_SERVER["PHP_SELF"], "c.date_contrat","","$param",'align="center"',$sortfield,$sortorder); +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; +// Hook fields +$parameters=array('arrayfields'=>$arrayfields); +$reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +if (! empty($arrayfields['c.datec']['checked'])) print_liste_field_titre($arrayfields['c.datec']['label'],$_SERVER["PHP_SELF"],"c.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); +if (! empty($arrayfields['c.tms']['checked'])) print_liste_field_titre($arrayfields['c.tms']['label'],$_SERVER["PHP_SELF"],"c.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); +if (! empty($arrayfields['lower_planned_end_date']['checked'])) print_liste_field_titre($arrayfields['lower_planned_end_date']['label'],$_SERVER["PHP_SELF"],"lower_planned_end_date","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); +if (! empty($arrayfields['status']['checked'])) +{ + print_liste_field_titre($staticcontratligne->LibStatut(0,3), '', '', '', '', 'width="16"'); + print_liste_field_titre($staticcontratligne->LibStatut(4,3,0), '', '', '', '', 'width="16"'); + print_liste_field_titre($staticcontratligne->LibStatut(4,3,1), '', '', '', '', 'width="16"'); + print_liste_field_titre($staticcontratligne->LibStatut(5,3), '', '', '', '', 'width="16"'); +} +print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch '); +print "\n"; + +while ($i < min($num,$limit)) +{ + $obj = $db->fetch_object($resql); + + $contracttmp->ref=$obj->ref; + $contracttmp->id=$obj->rowid; + $contracttmp->ref_customer=$obj->ref_customer; + $contracttmp->ref_supplier=$obj->ref_supplier; + + if ($obj->socid > 0) { - $soc = new Societe($db); - $soc->fetch($socid); - if (empty($search_name)) $search_name = $soc->name; + $result=$socstatic->fetch($obj->socid); } - $param=''; - if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); - if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; - if ($sall != '') $param.='&sall='.urlencode($sall); - if ($search_contract != '') $param.='&search_contract='.urlencode($search_contract); - if ($search_name != '') $param.='&search_name='.urlencode($search_name); - if ($search_email != '') $param.='&search_email='.urlencode($search_email); - if ($search_ref_customer != '') $param.='&search_ref_customer='.urlencode($search_ref_customer); - if ($search_ref_supplier != '') $param.='&search_ref_supplier='.urlencode($search_ref_supplier); - if ($search_op2df != '') $param.='&search_op2df='.urlencode($search_op2df); - if ($search_dfyear != '') $param.='&search_dfyear='.urlencode($search_dfyear); - if ($search_dfmonth != '') $param.='&search_dfmonth='.urlencode($search_dfmonth); - if ($search_sale != '') $param.='&search_sale=' .urlencode($search_sale); - if ($search_user != '') $param.='&search_user=' .urlencode($search_user); - if ($search_product_category != '') $param.='&search_product_category=' .urlencode($search_product_category); - if ($show_files) $param.='&show_files=' .urlencode($show_files); - if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); - // Add $param from extra fields - include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; - - // List of mass actions available - $arrayofmassactions = array( - 'presend'=>$langs->trans("SendByMail"), - 'builddoc'=>$langs->trans("PDFMerge"), - ); - if ($user->rights->contrat->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); - if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); - $massactionbutton=$form->selectMassAction('', $arrayofmassactions); - - print ''; - if ($optioncss != '') print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - - print_barre_liste($langs->trans("ListOfContracts"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $totalnboflines, 'title_commercial.png', 0, '', '', $limit); - - $topicmail="SendContractRef"; - $modelmail="contract"; - $objecttmp=new Contrat($db); - $trackid='con'.$object->id; - include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; - - if ($sall) - { - foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); - print $langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall); - } - - $moreforfilter=''; - - // If the user can view prospects other than his' - if ($user->rights->societe->client->voir || $socid) - { - $langs->load("commercial"); - $moreforfilter.='
'; - $moreforfilter.=$langs->trans('ThirdPartiesOfSaleRepresentative'). ': '; - $moreforfilter.=$formother->select_salesrepresentatives($search_sale,'search_sale',$user,0,1,'maxwidth200'); - $moreforfilter.='
'; - } - // If the user can view other users - if ($user->rights->user->user->lire) - { - $moreforfilter.='
'; - $moreforfilter.=$langs->trans('LinkedToSpecificUsers'). ': '; - $moreforfilter.=$form->select_dolusers($search_user, 'search_user', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth200'); - $moreforfilter.='
'; - } - // If the user can view categories of products - if ($conf->categorie->enabled && ($user->rights->produit->lire || $user->rights->service->lire)) - { - include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - $moreforfilter.='
'; - $moreforfilter.=$langs->trans('IncludingProductWithTag'). ': '; - $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, null, 'parent', null, null, 1); - $moreforfilter.=$form->selectarray('search_product_category', $cate_arbo, $search_product_category, 1, 0, 0, '', 0, 0, 0, 0, 'maxwidth300', 1); - $moreforfilter.='
'; - } - - $parameters=array(); - $reshook=$hookmanager->executeHooks('printFieldPreListTitle',$parameters); // Note that $action and $object may have been modified by hook - if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint; - else $moreforfilter = $hookmanager->resPrint; - - if (! empty($moreforfilter)) - { - print '
'; - print $moreforfilter; - print '
'; - } - - $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; - $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields - if ($massactionbutton) $selectedfields.=$form->showCheckAddButtons('checkforselect', 1); - - print '
'; - print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print $form->select_country($search_country,'search_country','',0,'maxwidth100'); + print ''; + print $form->selectarray("search_type_thirdparty", $formcompany->typent_array(0), $search_type_thirdparty, 0, 0, 0, '', 0, 0, 0, (empty($conf->global->SOCIETE_SORT_ON_TYPEENT)?'ASC':$conf->global->SOCIETE_SORT_ON_TYPEENT)); + print ''; + //print $langs->trans('Month').': '; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + //print ' '.$langs->trans('Year').': '; + $syear = $year; + print $formother->selectyear($syear,'year',1, 20, 5, 0, 0, '', 'widthauto'); + print ''; + print ''; + print ''; + $arrayofoperators=array('0'=>'','='=>'=','<='=>'<=','>='=>'>='); + print $form->selectarray('search_op2df',$arrayofoperators,$search_op2df,0); + print '
'; + print $formother->select_month($search_dfmonth, 'search_dfmonth', 1); + print ' '; + $formother->select_year($search_dfyear, 'search_dfyear', 1, 20, 5); + print '
'; +$searchpicto=$form->showFilterButtons(); +print $searchpicto; +print '
'."\n"; - - print ''; + print ''; if (! empty($arrayfields['c.ref']['checked'])) { - print ''; + print ''; } if (! empty($arrayfields['c.ref_customer']['checked'])) { - print ''; + print ''; } if (! empty($arrayfields['c.ref_supplier']['checked'])) { - print ''; + print ''; } if (! empty($arrayfields['s.nom']['checked'])) { - print ''; } if (! empty($arrayfields['s.email']['checked'])) { - print ''; + print ''; } // Town - if (! empty($arrayfields['s.town']['checked'])) print ''; + if (! empty($arrayfields['s.town']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } // Zip - if (! empty($arrayfields['s.zip']['checked'])) print ''; + if (! empty($arrayfields['s.zip']['checked'])) + { + print ''; + if (! $i) $totalarray['nbfield']++; + } // State if (! empty($arrayfields['state.nom']['checked'])) { - print ''; + print "\n"; + if (! $i) $totalarray['nbfield']++; } // Country if (! empty($arrayfields['country.code_iso']['checked'])) { - print ''; + if (! $i) $totalarray['nbfield']++; } - // Company type + // Type ent if (! empty($arrayfields['typent.code']['checked'])) { - print ''; + if (! $i) $totalarray['nbfield']++; } if (! empty($arrayfields['sale_representative']['checked'])) { - print ''; - } - if (! empty($arrayfields['c.date_contrat']['checked'])) - { - // Date contract - print ''; } + // Date + if (! empty($arrayfields['c.date_contrat']['checked'])) + { + print ''; + } // Extra fields - include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; - + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; // Fields from hook - $parameters=array('arrayfields'=>$arrayfields); - $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook + $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); + $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; // Date creation if (! empty($arrayfields['c.datec']['checked'])) { - print ''; + if (! $i) $totalarray['nbfield']++; } // Date modification if (! empty($arrayfields['c.tms']['checked'])) { - print ''; + if (! $i) $totalarray['nbfield']++; } - // First end date + // Date lower end date if (! empty($arrayfields['lower_planned_end_date']['checked'])) { - print ''; + print ''; + if (! $i) $totalarray['nbfield']++; } // Status if (! empty($arrayfields['status']['checked'])) { - print ''; + print ''; + print ''; + print ''; + print ''; + } + // Action column + print ''; + if (! $i) $totalarray['nbfield']++; + print "\n"; - - print ''; - if (! empty($arrayfields['c.ref']['checked'])) print_liste_field_titre($arrayfields['c.ref']['label'], $_SERVER["PHP_SELF"], "c.ref","","$param",'',$sortfield,$sortorder); - if (! empty($arrayfields['c.ref_customer']['checked'])) print_liste_field_titre($arrayfields['c.ref_customer']['label'], $_SERVER["PHP_SELF"], "c.ref_customer","","$param",'',$sortfield,$sortorder); - if (! empty($arrayfields['c.ref_supplier']['checked'])) print_liste_field_titre($arrayfields['c.ref_supplier']['label'], $_SERVER["PHP_SELF"], "c.ref_supplier","","$param",'',$sortfield,$sortorder); - if (! empty($arrayfields['s.nom']['checked'])) print_liste_field_titre($arrayfields['s.nom']['label'], $_SERVER["PHP_SELF"], "s.nom","","$param",'',$sortfield,$sortorder); - if (! empty($arrayfields['s.email']['checked'])) print_liste_field_titre($arrayfields['s.email']['label'], $_SERVER["PHP_SELF"], "s.email","","$param",'',$sortfield,$sortorder); - if (! empty($arrayfields['s.town']['checked'])) print_liste_field_titre($arrayfields['s.town']['label'],$_SERVER["PHP_SELF"],'s.town','',$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['s.zip']['checked'])) print_liste_field_titre($arrayfields['s.zip']['label'],$_SERVER["PHP_SELF"],'s.zip','',$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['state.nom']['checked'])) print_liste_field_titre($arrayfields['state.nom']['label'],$_SERVER["PHP_SELF"],"state.nom","",$param,'',$sortfield,$sortorder); - if (! empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($arrayfields['country.code_iso']['label'],$_SERVER["PHP_SELF"],"country.code_iso","",$param,'align="center"',$sortfield,$sortorder); - if (! empty($arrayfields['typent.code']['checked'])) print_liste_field_titre($arrayfields['typent.code']['label'],$_SERVER["PHP_SELF"],"typent.code","",$param,'align="center"',$sortfield,$sortorder); - if (! empty($arrayfields['sale_representative']['checked'])) print_liste_field_titre($arrayfields['sale_representative']['label'], $_SERVER["PHP_SELF"], "","","$param",'',$sortfield,$sortorder); - if (! empty($arrayfields['c.date_contrat']['checked'])) print_liste_field_titre($arrayfields['c.date_contrat']['label'], $_SERVER["PHP_SELF"], "c.date_contrat","","$param",'align="center"',$sortfield,$sortorder); - // Extra fields - include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; - // Hook fields - $parameters=array('arrayfields'=>$arrayfields); - $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; - if (! empty($arrayfields['c.datec']['checked'])) print_liste_field_titre($arrayfields['c.datec']['label'],$_SERVER["PHP_SELF"],"c.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); - if (! empty($arrayfields['c.tms']['checked'])) print_liste_field_titre($arrayfields['c.tms']['label'],$_SERVER["PHP_SELF"],"c.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); - if (! empty($arrayfields['lower_planned_end_date']['checked'])) print_liste_field_titre($arrayfields['lower_planned_end_date']['label'],$_SERVER["PHP_SELF"],"lower_planned_end_date","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); - if (! empty($arrayfields['status']['checked'])) - { - print_liste_field_titre($staticcontratligne->LibStatut(0,3), '', '', '', '', 'width="16"'); - print_liste_field_titre($staticcontratligne->LibStatut(4,3,0), '', '', '', '', 'width="16"'); - print_liste_field_titre($staticcontratligne->LibStatut(4,3,1), '', '', '', '', 'width="16"'); - print_liste_field_titre($staticcontratligne->LibStatut(5,3), '', '', '', '', 'width="16"'); - } - print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch '); - print "\n"; - - while ($i < min($num,$limit)) - { - $obj = $db->fetch_object($resql); - - $contracttmp->ref=$obj->ref; - $contracttmp->id=$obj->rowid; - $contracttmp->ref_customer=$obj->ref_customer; - $contracttmp->ref_supplier=$obj->ref_supplier; - - if ($obj->socid > 0) - { - $result=$socstatic->fetch($obj->socid); - } - - print ''; - if (! empty($arrayfields['c.ref']['checked'])) - { - print ''; - - print ''; - } - if (! empty($arrayfields['c.ref_customer']['checked'])) - { - print ''; - } - if (! empty($arrayfields['c.ref_supplier']['checked'])) - { - print ''; - } - if (! empty($arrayfields['s.nom']['checked'])) - { - print ''; - } - if (! empty($arrayfields['s.email']['checked'])) - { - print ''; - } - // Town - if (! empty($arrayfields['s.town']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - // Zip - if (! empty($arrayfields['s.zip']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - // State - if (! empty($arrayfields['state.nom']['checked'])) - { - print "\n"; - if (! $i) $totalarray['nbfield']++; - } - // Country - if (! empty($arrayfields['country.code_iso']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - // Type ent - if (! empty($arrayfields['typent.code']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - if (! empty($arrayfields['sale_representative']['checked'])) - { - // Sales representatives - print ''; - } - // Date - if (! empty($arrayfields['c.date_contrat']['checked'])) - { - print ''; - } - // Extra fields - include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; - // Fields from hook - $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); - $reshook=$hookmanager->executeHooks('printFieldListValue',$parameters); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; - // Date creation - if (! empty($arrayfields['c.datec']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - // Date modification - if (! empty($arrayfields['c.tms']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - // Date lower end date - if (! empty($arrayfields['lower_planned_end_date']['checked'])) - { - print ''; - if (! $i) $totalarray['nbfield']++; - } - // Status - if (! empty($arrayfields['status']['checked'])) - { - print ''; - print ''; - print ''; - print ''; - } - // Action column - print ''; - if (! $i) $totalarray['nbfield']++; - - print "\n"; - $i++; - } - $db->free($resql); - - print '
'; - print ''; + print ''; + print $contracttmp->getNomUrl(1); + if ($obj->nb_late) print img_warning($langs->trans("Late")); + if (!empty($obj->note_private) || !empty($obj->note_public)) + { + print ' '; + print ''.img_picto($langs->trans("ViewPrivateNote"),'object_generic').''; + print ''; + } + + $filename=dol_sanitizeFileName($obj->ref); + $filedir=$conf->contrat->dir_output . '/' . dol_sanitizeFileName($obj->ref); + $urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->rowid; + print $formfile->getDocumentsLink($contracttmp->element, $filename, $filedir); + print ''; - print ''; - print ''.$obj->ref_customer.''; - print ''; - print ''.$obj->ref_supplier.''; - print ''; + print ''; + //print ''.img_object($langs->trans("ShowCompany"),"company").' '.$obj->name.''; + if ($obj->socid > 0) + { + print $socstatic->getNomUrl(1, ''); + } print ''; - print ''; - print ''.$obj->email.''; + print $obj->town; + print ''; + print $obj->zip; + print ''; - print ''; - print '".$obj->state_name."'; - print $form->select_country($search_country,'search_country','',0,'maxwidth100'); + print ''; + $tmparray=getCountry($obj->fk_pays,'all'); + print $tmparray['label']; print ''; - print $form->selectarray("search_type_thirdparty", $formcompany->typent_array(0), $search_type_thirdparty, 0, 0, 0, '', 0, 0, 0, (empty($conf->global->SOCIETE_SORT_ON_TYPEENT)?'ASC':$conf->global->SOCIETE_SORT_ON_TYPEENT)); + print ''; + if (count($typenArray)==0) $typenArray = $formcompany->typent_array(1); + print $typenArray[$obj->typent_code]; print ''; - //print $langs->trans('Month').': '; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - //print ' '.$langs->trans('Year').': '; - $syear = $year; - $formother->select_year($syear,'year',1, 20, 5); + // Sales representatives + print ''; + if ($obj->socid > 0) + { + $listsalesrepresentatives=$socstatic->getSalesRepresentatives($user); + if ($listsalesrepresentatives < 0) dol_print_error($db); + $nbofsalesrepresentative=count($listsalesrepresentatives); + if ($nbofsalesrepresentative > 3) // We print only number + { + print ''; + print $nbofsalesrepresentative; + print ''; + } + else if ($nbofsalesrepresentative > 0) + { + $userstatic=new User($db); + $j=0; + foreach($listsalesrepresentatives as $val) + { + $userstatic->id=$val['id']; + $userstatic->lastname=$val['lastname']; + $userstatic->firstname=$val['firstname']; + $userstatic->email=$val['email']; + $userstatic->statut=$val['statut']; + $userstatic->entity=$val['entity']; + $userstatic->photo=$val['photo']; + + //print '
': + print $userstatic->getNomUrl(-2); + $j++; + if ($j < $nbofsalesrepresentative) print ' '; + //print '
'; + } + } + //else print $langs->trans("NoSalesRepresentativeAffected"); + } + else + { + print ' '; + } print '
'.dol_print_date($db->jdate($obj->date_contrat), 'day', 'tzuser').''; + print ''; + print dol_print_date($db->jdate($obj->date_creation), 'dayhour', 'tzuser'); print ''; + print ''; + print dol_print_date($db->jdate($obj->date_update), 'dayhour', 'tzuser'); print ''; - $arrayofoperators=array('0'=>'','='=>'=','<='=>'<=','>='=>'>='); - print $form->selectarray('search_op2df',$arrayofoperators,$search_op2df,0); - print '
'; - print $formother->select_month($search_dfmonth, 'search_dfmonth', 1); - print ' '; - $formother->select_year($search_dfyear, 'search_dfyear', 1, 20, 5); - print '
'; + print dol_print_date($db->jdate($obj->lower_planned_end_date), 'day', 'tzuser'); + print ''.($obj->nb_initial>0?$obj->nb_initial:'').''.($obj->nb_running>0?$obj->nb_running:'').''.($obj->nb_expired>0?$obj->nb_expired:'').''.($obj->nb_closed>0 ?$obj->nb_closed:'').''; + if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + { + $selected=0; + if (in_array($obj->rowid, $arrayofselected)) $selected=1; + print ''; } - print ''; - $searchpicto=$form->showFilterButtons(); - print $searchpicto; print '
'; - print $contracttmp->getNomUrl(1); - if ($obj->nb_late) print img_warning($langs->trans("Late")); - if (!empty($obj->note_private) || !empty($obj->note_public)) - { - print ' '; - print ''.img_picto($langs->trans("ViewPrivateNote"),'object_generic').''; - print ''; - } - - $filename=dol_sanitizeFileName($obj->ref); - $filedir=$conf->contrat->dir_output . '/' . dol_sanitizeFileName($obj->ref); - $urlsource=$_SERVER['PHP_SELF'].'?id='.$obj->rowid; - print $formfile->getDocumentsLink($contracttmp->element, $filename, $filedir); - print ''.$obj->ref_customer.''.$obj->ref_supplier.''; - //print ''.img_object($langs->trans("ShowCompany"),"company").' '.$obj->name.''; - if ($obj->socid > 0) - { - print $socstatic->getNomUrl(1, ''); - } - print ''.$obj->email.''; - print $obj->town; - print ''; - print $obj->zip; - print '".$obj->state_name."'; - $tmparray=getCountry($obj->fk_pays,'all'); - print $tmparray['label']; - print ''; - if (count($typenArray)==0) $typenArray = $formcompany->typent_array(1); - print $typenArray[$obj->typent_code]; - print ''; - if ($obj->socid > 0) - { - $listsalesrepresentatives=$socstatic->getSalesRepresentatives($user); - if ($listsalesrepresentatives < 0) dol_print_error($db); - $nbofsalesrepresentative=count($listsalesrepresentatives); - if ($nbofsalesrepresentative > 3) // We print only number - { - print ''; - print $nbofsalesrepresentative; - print ''; - } - else if ($nbofsalesrepresentative > 0) - { - $userstatic=new User($db); - $j=0; - foreach($listsalesrepresentatives as $val) - { - $userstatic->id=$val['id']; - $userstatic->lastname=$val['lastname']; - $userstatic->firstname=$val['firstname']; - $userstatic->email=$val['email']; - $userstatic->statut=$val['statut']; - $userstatic->entity=$val['entity']; - $userstatic->photo=$val['photo']; - - //print '
': - print $userstatic->getNomUrl(-2); - $j++; - if ($j < $nbofsalesrepresentative) print ' '; - //print '
'; - } - } - //else print $langs->trans("NoSalesRepresentativeAffected"); - } - else - { - print ' '; - } - print '
'.dol_print_date($db->jdate($obj->date_contrat), 'day', 'tzuser').''; - print dol_print_date($db->jdate($obj->date_creation), 'dayhour', 'tzuser'); - print ''; - print dol_print_date($db->jdate($obj->date_update), 'dayhour', 'tzuser'); - print ''; - print dol_print_date($db->jdate($obj->lower_planned_end_date), 'day', 'tzuser'); - print ''.($obj->nb_initial>0?$obj->nb_initial:'').''.($obj->nb_running>0?$obj->nb_running:'').''.($obj->nb_expired>0?$obj->nb_expired:'').''.($obj->nb_closed>0 ?$obj->nb_closed:'').''; - if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined - { - $selected=0; - if (in_array($obj->rowid, $arrayofselected)) $selected=1; - print ''; - } - print '
'; - print '
'; - - print ''; - - $hidegeneratedfilelistifempty=1; - if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) $hidegeneratedfilelistifempty=0; - - // Show list of available documents - $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder; - $urlsource.=str_replace('&','&',$param); - - $filedir=$diroutputmassaction; - $genallowed=$user->rights->contrat->lire; - $delallowed=$user->rights->contrat->lire; - - print $formfile->showdocuments('massfilesarea_contract','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'','','',null,$hidegeneratedfilelistifempty); -} -else -{ - dol_print_error($db); + $i++; } +$db->free($resql); + +print ''; +print '
'; + +print ''; + +$hidegeneratedfilelistifempty=1; +if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) $hidegeneratedfilelistifempty=0; + +// Show list of available documents +$urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder; +$urlsource.=str_replace('&','&',$param); + +$filedir=$diroutputmassaction; +$genallowed=$user->rights->contrat->lire; +$delallowed=$user->rights->contrat->lire; + +print $formfile->showdocuments('massfilesarea_contract','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'','','',null,$hidegeneratedfilelistifempty); llxFooter(); diff --git a/htdocs/contrat/note.php b/htdocs/contrat/note.php index 9a6d28dd4c9..ab76d9aaec3 100644 --- a/htdocs/contrat/note.php +++ b/htdocs/contrat/note.php @@ -49,6 +49,10 @@ $object->fetch($id,$ref); $permissionnote=$user->rights->contrat->creer; // Used by the include of actions_setnotes.inc.php +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('contractcard','globalcard')); + + /* * Actions diff --git a/htdocs/contrat/services_list.php b/htdocs/contrat/services_list.php index 108fb6d4403..f78ed639804 100644 --- a/htdocs/contrat/services_list.php +++ b/htdocs/contrat/services_list.php @@ -53,6 +53,7 @@ $search_status=GETPOST("search_status","alpha"); $statut=GETPOST('statut')?GETPOST('statut'):1; $search_product_category=GETPOST('search_product_category','int'); $socid=GETPOST('socid','int'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'contractservicelist'.$mode; $opouvertureprevuemonth=GETPOST('opouvertureprevuemonth'); $opouvertureprevueday=GETPOST('opouvertureprevueday'); @@ -74,11 +75,10 @@ $opclotureday=GETPOST('opclotureday'); $opclotureyear=GETPOST('opclotureyear'); $filter_opcloture=GETPOST('filter_opcloture'); -// Initialize context for list -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'contractservicelist'.$mode; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new ContratLigne($db); +$hookmanager->initHooks(array('contractservicelist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels diff --git a/htdocs/contrat/tpl/linkedobjectblock.tpl.php b/htdocs/contrat/tpl/linkedobjectblock.tpl.php index a44f20679b8..ae4a56ef4a9 100644 --- a/htdocs/contrat/tpl/linkedobjectblock.tpl.php +++ b/htdocs/contrat/tpl/linkedobjectblock.tpl.php @@ -52,10 +52,10 @@ foreach($linkedObjectBlock as $key => $objectlink) date_contrat,'day'); ?> rights->contrat->lire && empty($conf->global->CONTRACT_SHOW_TOTAL_OF_PRODUCT_AS_PRICE)) + if ($user->rights->contrat->lire && empty($conf->global->CONTRACT_SHOW_TOTAL_OF_PRODUCT_AS_PRICE)) { $totalcontrat = 0; foreach ($objectlink->lines as $linecontrat) { @@ -65,7 +65,7 @@ foreach($linkedObjectBlock as $key => $objectlink) echo price($totalcontrat); } ?> getLibStatut(7); ?> - ">transnoentitiesnoconv("RemoveLink")); ?> + ">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?> diff --git a/htdocs/core/actions_addupdatedelete.inc.php b/htdocs/core/actions_addupdatedelete.inc.php index 6318e4d5c54..b8ede4d7d14 100644 --- a/htdocs/core/actions_addupdatedelete.inc.php +++ b/htdocs/core/actions_addupdatedelete.inc.php @@ -93,8 +93,16 @@ if ($action == 'update' && ! empty($permissiontoadd)) if (in_array($key, array('rowid', 'entity', 'date_creation', 'tms', 'fk_user_creat', 'fk_user_modif', 'import_key'))) continue; // Ignore special fields // Set value to update - if (in_array($object->fields[$key]['type'], array('text', 'html'))) $value = GETPOST($key,'none'); - else $value = GETPOST($key,'alpha'); + if (in_array($object->fields[$key]['type'], array('text', 'html'))) { + $value = GETPOST($key,'none'); + } + elseif ($object->fields[$key]['type']=='date') { + $value = dol_mktime(12, 0, 0, GETPOST($key.'month'), GETPOST($key.'day'), GETPOST($key.'year')); + } elseif ($object->fields[$key]['type']=='datetime') { + $value = dol_mktime(GETPOST($key.'hour'), GETPOST($key.'min'), 0, GETPOST($key.'month'), GETPOST($key.'day'), GETPOST($key.'year')); + } else { + $value = GETPOST($key,'alpha'); + } if (preg_match('/^integer:/i', $object->fields[$key]['type']) && $value == '-1') $value=''; // This is an implicit foreign key field if (! empty($object->fields[$key]['foreignkey']) && $value == '-1') $value=''; // This is an explicit foreign key field diff --git a/htdocs/core/actions_builddoc.inc.php b/htdocs/core/actions_builddoc.inc.php index ff7a89484fa..9d33def496c 100644 --- a/htdocs/core/actions_builddoc.inc.php +++ b/htdocs/core/actions_builddoc.inc.php @@ -124,5 +124,13 @@ if ($action == 'remove_file' && $permissioncreate) $ret=dol_delete_file($file,0,0,0,$object); if ($ret) setEventMessages($langs->trans("FileWasRemoved", $filetodelete), null, 'mesgs'); else setEventMessages($langs->trans("ErrorFailToDeleteFile", $filetodelete), null, 'errors'); + + // Make a redirect to avoid to keep the remove_file into the url that create side effects + $urltoredirect = $_SERVER['REQUEST_URI']; + $urltoredirect = preg_replace('/#builddoc$/', '', $urltoredirect); + $urltoredirect = preg_replace('/action=remove_file&?/', '', $urltoredirect); + + header('Location: '.$urltoredirect); + exit; } diff --git a/htdocs/core/actions_linkedfiles.inc.php b/htdocs/core/actions_linkedfiles.inc.php index 7f938ba9b6c..297f7821599 100644 --- a/htdocs/core/actions_linkedfiles.inc.php +++ b/htdocs/core/actions_linkedfiles.inc.php @@ -27,7 +27,7 @@ // Submit file/link -if (GETPOST('sendit','none') && ! empty($conf->global->MAIN_UPLOAD_DOC)) +if (GETPOST('sendit','alpha') && ! empty($conf->global->MAIN_UPLOAD_DOC)) { if (! empty($_FILES)) { @@ -176,11 +176,11 @@ elseif ($action == 'confirm_updateline' && GETPOST('save','alpha') && GETPOST('l } elseif ($action == 'renamefile' && GETPOST('renamefilesave','alpha')) { - // For documents pages, upload_dir contains already path to file from module dir, so we clean path into urlfile. - if (! empty($upload_dir)) - { - $filenamefrom=dol_sanitizeFileName(GETPOST('renamefilefrom','alpha'), '_', 0); // Do not remove accents - $filenameto=dol_sanitizeFileName(GETPOST('renamefileto','alpha'), '_', 0); // Do not remove accents + // For documents pages, upload_dir contains already path to file from module dir, so we clean path into urlfile. + if (! empty($upload_dir)) + { + $filenamefrom=dol_sanitizeFileName(GETPOST('renamefilefrom','alpha'), '_', 0); // Do not remove accents + $filenameto=dol_sanitizeFileName(GETPOST('renamefileto','alpha'), '_', 0); // Do not remove accents if ($filenamefrom != $filenameto) { @@ -197,23 +197,38 @@ elseif ($action == 'renamefile' && GETPOST('renamefilesave','alpha')) $srcpath = $upload_dir.'/'.$filenamefrom; $destpath = $upload_dir.'/'.$filenameto; - $result = dol_move($srcpath, $destpath); - if ($result) + $reshook=$hookmanager->initHooks(array('actionlinkedfiles')); + $parameters=array('filenamefrom' => $filenamefrom, 'filenameto' => $filenameto, 'upload_dir' => $upload_dir); + $reshook=$hookmanager->executeHooks('renameUploadedFile', $parameters, $object); + + if (empty($reshook)) { - if ($object->id) + if (! file_exists($destpath)) { - $object->addThumbs($destpath); + $result = dol_move($srcpath, $destpath); + if ($result) + { + if ($object->id) + { + $object->addThumbs($destpath); + } + + // TODO Add revert function of addThumbs to remove for old name + //$object->delThumbs($srcpath); + + setEventMessages($langs->trans("FileRenamed"), null); + } + else + { + $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now. + setEventMessages($langs->trans("ErrorFailToRenameFile", $filenamefrom, $filenameto), null, 'errors'); + } + } + else + { + $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now. + setEventMessages($langs->trans("ErrorDestinationAlreadyExists", $filenameto), null, 'errors'); } - - // TODO Add revert function of addThumbs to remove for old name - //$object->delThumbs($srcpath); - - setEventMessages($langs->trans("FileRenamed"), null); - } - else - { - $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now. - setEventMessages($langs->trans("ErrorFailToRenameFile", $filenamefrom, $filenameto), null, 'errors'); } } } diff --git a/htdocs/core/actions_massactions.inc.php b/htdocs/core/actions_massactions.inc.php index b08993acf8a..9a9298528eb 100644 --- a/htdocs/core/actions_massactions.inc.php +++ b/htdocs/core/actions_massactions.inc.php @@ -1,5 +1,6 @@ + * Copyright (C) 2018 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,7 +47,7 @@ if (! empty($massaction) && count($toselect) < 1) $error++; setEventMessages($langs->trans("NoRecordSelected"), null, "warnings"); } -if (! $error && count($toselect) > $maxformassaction) +if (! $error && is_array($toselect) && count($toselect) > $maxformassaction) { setEventMessages($langs->trans('TooManyRecordForMassAction',$maxformassaction), null, 'errors'); $error++; @@ -686,6 +687,35 @@ if ($massaction == 'confirm_createbills') { $db->commit(); setEventMessage($langs->trans('BillCreated', $nb_bills_created)); + + // Make a redirect to avoid to bill twice if we make a refresh or back + $param=''; + if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); + if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); + if ($sall) $param.='&sall='.urlencode($sall); + if ($socid > 0) $param.='&socid='.urlencode($socid); + if ($viewstatut != '') $param.='&viewstatut='.urlencode($viewstatut); + if ($search_orderday) $param.='&search_orderday='.urlencode($search_orderday); + if ($search_ordermonth) $param.='&search_ordermonth='.urlencode($search_ordermonth); + if ($search_orderyear) $param.='&search_orderyear='.urlencode($search_orderyear); + if ($search_deliveryday) $param.='&search_deliveryday='.urlencode($search_deliveryday); + if ($search_deliverymonth) $param.='&search_deliverymonth='.urlencode($search_deliverymonth); + if ($search_deliveryyear) $param.='&search_deliveryyear='.urlencode($search_deliveryyear); + if ($search_ref) $param.='&search_ref='.urlencode($search_ref); + if ($search_company) $param.='&search_company='.urlencode($search_company); + if ($search_ref_customer) $param.='&search_ref_customer='.urlencode($search_ref_customer); + if ($search_user > 0) $param.='&search_user='.urlencode($search_user); + if ($search_sale > 0) $param.='&search_sale='.urlencode($search_sale); + if ($search_total_ht != '') $param.='&search_total_ht='.urlencode($search_total_ht); + if ($search_total_vat != '') $param.='&search_total_vat='.urlencode($search_total_vat); + if ($search_total_ttc != '') $param.='&search_total_ttc='.urlencode($search_total_ttc); + if ($search_project_ref >= 0) $param.="&search_project_ref=".urlencode($search_project_ref); + if ($show_files) $param.='&show_files=' .urlencode($show_files); + if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); + if ($billed != '') $param.='&billed='.urlencode($billed); + + header("Location: ".$_SERVER['PHP_SELF'].'?'.$param); + exit; } else { @@ -944,7 +974,41 @@ if (! $error && $massaction == 'validate' && $permtocreate) //var_dump($listofobjectthirdparties);exit; } } +// Closed records +if (!$error && $massaction == 'closed' && $objectclass == "Propal" && $permtoclose) { + $db->begin(); + $objecttmp = new $objectclass($db); + $nbok = 0; + foreach ($toselect as $toselectid) { + $result = $objecttmp->fetch($toselectid); + if ($result > 0) { + $result = $objecttmp->cloture($user, 3); + if ($result <= 0) { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } else + $nbok++; + } + else { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } + } + + if (!$error) { + if ($nbok > 1) + setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); + else + setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); + $db->commit(); + } + else { + $db->rollback(); + } +} // Delete record from mass action (massaction = 'delete' for direct delete, action/confirm='delete'/'yes' with a confirmation step before) if (! $error && ($massaction == 'delete' || ($action == 'delete' && $confirm == 'yes')) && $permtodelete) { diff --git a/htdocs/core/actions_sendmails.inc.php b/htdocs/core/actions_sendmails.inc.php index 962d00ee4b8..8f7ee1287fe 100644 --- a/htdocs/core/actions_sendmails.inc.php +++ b/htdocs/core/actions_sendmails.inc.php @@ -318,7 +318,6 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO $filename = $attachedfiles['names']; $mimetype = $attachedfiles['mimes']; - // Feature to push mail sent into Sent folder /* This code must be now included into the hook mail, method sendMailAfter if (! empty($conf->dolimail->enabled)) @@ -386,7 +385,7 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO if ($mailfile->error) { - setEventMessage($mailfile->error, 'errors'); + setEventMessages($mailfile->error, $mailfile->errors, 'errors'); $action='presend'; } else diff --git a/htdocs/core/ajax/row.php b/htdocs/core/ajax/row.php index 71a5a0aac88..4fe31ee7ae8 100644 --- a/htdocs/core/ajax/row.php +++ b/htdocs/core/ajax/row.php @@ -63,7 +63,7 @@ if ((isset($_POST['roworder']) && ! empty($_POST['roworder'])) && (isset($_POST[ $row->table_element_line = $table_element_line; $row->fk_element = $fk_element; $row->id = $element_id; - $row->line_ajaxorder($newrowordertab); // This update field rank or position in table table_element_line + $row->line_ajaxorder($newrowordertab); // This update field rank or position in table row->table_element_line // Reorder line to have position of children lines sharing same counter than parent lines // This should be useless because there is no need to have children sharing same counter than parent, but well, it's cleaner into database. diff --git a/htdocs/core/ajax/selectsearchbox.php b/htdocs/core/ajax/selectsearchbox.php index 4256d14f203..1bc527ff555 100644 --- a/htdocs/core/ajax/selectsearchbox.php +++ b/htdocs/core/ajax/selectsearchbox.php @@ -126,7 +126,7 @@ if (! empty($conf->ficheinter->enabled) && empty($conf->global->MAIN_SEARCHFORM_ // HR if (! empty($conf->user->enabled) && empty($conf->global->MAIN_SEARCHFORM_USER_DISABLED) && $user->rights->user->user->lire) { - $arrayresult['searchintouser']=array('position'=>200, 'shortcut'=>'U', 'img'=>'object_user', 'label'=>$langs->trans("SearchIntoUsers", $search_boxvalue), 'text'=>img_picto('','object_user').' '.$langs->trans("SearchIntoUsers", $search_boxvalue), 'url'=>DOL_URL_ROOT.'/user/index.php'.($search_boxvalue?'?sall='.urlencode($search_boxvalue):'')); + $arrayresult['searchintouser']=array('position'=>200, 'shortcut'=>'U', 'img'=>'object_user', 'label'=>$langs->trans("SearchIntoUsers", $search_boxvalue), 'text'=>img_picto('','object_user').' '.$langs->trans("SearchIntoUsers", $search_boxvalue), 'url'=>DOL_URL_ROOT.'/user/list.php'.($search_boxvalue?'?sall='.urlencode($search_boxvalue):'')); } if (! empty($conf->expensereport->enabled) && empty($conf->global->MAIN_SEARCHFORM_EXPENSEREPORT_DISABLED) && $user->rights->expensereport->lire) { diff --git a/htdocs/core/boxes/box_commandes.php b/htdocs/core/boxes/box_commandes.php index 9e1e89d60fc..9bbde140dbe 100644 --- a/htdocs/core/boxes/box_commandes.php +++ b/htdocs/core/boxes/box_commandes.php @@ -136,7 +136,7 @@ class box_commandes extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="tdoverflowmax100"', + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $societestatic->getNomUrl(1), 'asis' => 1, ); diff --git a/htdocs/core/boxes/box_contacts.php b/htdocs/core/boxes/box_contacts.php index a67d2c3ac02..936d269f82a 100644 --- a/htdocs/core/boxes/box_contacts.php +++ b/htdocs/core/boxes/box_contacts.php @@ -3,6 +3,7 @@ * Copyright (C) 2004-2015 Laurent Destailleur * Copyright (C) 2005-2009 Regis Houssin * Copyright (C) 2015 Frederic France + * Copyright (C) 2018 Josep Lluís Amador * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +21,7 @@ /** * \file htdocs/core/boxes/box_contacts.php - * \ingroup societes + * \ingroup contacts * \brief Module to show box of contacts */ @@ -57,7 +58,7 @@ class box_contacts extends ModeleBoxes $this->db=$db; - $this->hidden=! ($user->rights->societe->lire); + $this->hidden=! ($user->rights->societe->lire && $user->rights->societe->contact->lire); } /** @@ -75,12 +76,12 @@ class box_contacts extends ModeleBoxes $this->info_box_head = array('text' => $langs->trans("BoxTitleLastModifiedContacts",$max)); - if ($user->rights->societe->lire) + if ($user->rights->societe->lire && $user->rights->societe->contact->lire) { $sql = "SELECT sp.rowid as id, sp.lastname, sp.firstname, sp.civility as civility_id, sp.datec, sp.tms, sp.fk_soc, sp.statut as status"; $sql.= ", sp.address, sp.zip, sp.town, sp.phone, sp.phone_perso, sp.phone_mobile"; $sql.= ", s.nom as socname, s.name_alias"; - $sql.= ", s.client, s.fournisseur, s.code_client, s.code_fournisseur"; + $sql.= ", s.client, s.fournisseur, s.code_client, s.code_fournisseur"; $sql.= " FROM ".MAIN_DB_PREFIX."socpeople as sp"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON sp.fk_soc = s.rowid"; if (! $user->rights->societe->client->voir && ! $user->societe_id) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; @@ -91,98 +92,97 @@ class box_contacts extends ModeleBoxes $sql.= $db->plimit($max, 0); $result = $db->query($sql); - if ($result) { + if ($result) { $num = $db->num_rows($result); $contactstatic=new Contact($db); $societestatic=new Societe($db); $line = 0; - while ($line < $num) - { + while ($line < $num) + { $objp = $db->fetch_object($result); $datec=$db->jdate($objp->datec); $datem=$db->jdate($objp->tms); - $contactstatic->id=$objp->id; + $contactstatic->id=$objp->id; $contactstatic->lastname=$objp->lastname; - $contactstatic->firstname=$objp->firstname; - $contactstatic->civility_id=$objp->civility_id; + $contactstatic->firstname=$objp->firstname; + $contactstatic->civility_id=$objp->civility_id; $contactstatic->statut=$objp->status; - $contactstatic->phone_pro = $objp->phone; - $contactstatic->phone_perso = $objp->phone_perso; - $contactstatic->phone_mobile = $objp->phone_mobile; - $contactstatic->address = $objp->address; - $contactstatic->zip = $objp->zip; - $contactstatic->town = $objp->town; + $contactstatic->phone_pro = $objp->phone; + $contactstatic->phone_perso = $objp->phone_perso; + $contactstatic->phone_mobile = $objp->phone_mobile; + $contactstatic->address = $objp->address; + $contactstatic->zip = $objp->zip; + $contactstatic->town = $objp->town; $societestatic->id = $objp->fk_soc; - $societestatic->name = $objp->socname; - $societestatic->name_alias = $objp->name_alias; - $societestatic->code_client = $objp->code_client; - $societestatic->code_fournisseur = $objp->code_fournisseur; - $societestatic->client = $objp->client; - $societestatic->fournisseur = $objp->fournisseur; + $societestatic->name = $objp->socname; + $societestatic->name_alias = $objp->name_alias; + $societestatic->code_client = $objp->code_client; + $societestatic->code_fournisseur = $objp->code_fournisseur; + $societestatic->client = $objp->client; + $societestatic->fournisseur = $objp->fournisseur; - $this->info_box_contents[$line][] = array( - 'td' => '', - 'text' => $contactstatic->getNomUrl(1), - 'asis' => 1, - ); + $this->info_box_contents[$line][] = array( + 'td' => '', + 'text' => $contactstatic->getNomUrl(1), + 'asis' => 1, + ); - $this->info_box_contents[$line][] = array( - 'td' => '', - 'text' => ($objp->fk_soc > 0 ? $societestatic->getNomUrl(1) : ''), - 'asis' => 1, - ); + $this->info_box_contents[$line][] = array( + 'td' => '', + 'text' => ($objp->fk_soc > 0 ? $societestatic->getNomUrl(1) : ''), + 'asis' => 1, + ); - $this->info_box_contents[$line][] = array( - 'td' => 'class="right"', - 'text' => dol_print_date($datem, "day"), - ); + $this->info_box_contents[$line][] = array( + 'td' => 'class="right"', + 'text' => dol_print_date($datem, "day"), + ); - $this->info_box_contents[$line][] = array( - 'td' => 'align="right" class="nowrap" width="18"', - 'text' => $contactstatic->getLibStatut(3), - 'asis'=>1, - ); + $this->info_box_contents[$line][] = array( + 'td' => 'align="right" class="nowrap" width="18"', + 'text' => $contactstatic->getLibStatut(3), + 'asis'=>1, + ); - $line++; - } + $line++; + } - if ($num==0) - $this->info_box_contents[$line][0] = array( - 'td' => 'align="center"', - 'text'=>$langs->trans("NoRecordedContacts"), - ); + if ($num==0) + $this->info_box_contents[$line][0] = array( + 'td' => 'align="center"', + 'text'=>$langs->trans("NoRecordedContacts"), + ); - $db->free($result); - } else { - $this->info_box_contents[0][0] = array( - 'td' => '', - 'maxlength'=>500, - 'text' => ($db->error().' sql='.$sql), - ); - } - } else { - $this->info_box_contents[0][0] = array( - 'td' => 'align="left" class="nohover opacitymedium"', - 'text' => $langs->trans("ReadPermissionNotAllowed") - ); - } - - } + $db->free($result); + } else { + $this->info_box_contents[0][0] = array( + 'td' => '', + 'maxlength'=>500, + 'text' => ($db->error().' sql='.$sql), + ); + } + } else { + $this->info_box_contents[0][0] = array( + 'td' => 'align="left" class="nohover opacitymedium"', + 'text' => $langs->trans("ReadPermissionNotAllowed") + ); + } + } /** * Method to show box * - * @param array $head Array with properties of box title - * @param array $contents Array with properties of box lines - * @param int $nooutput No print, only return string + * @param array $head Array with properties of box title + * @param array $contents Array with properties of box lines + * @param int $nooutput No print, only return string * @return string */ - function showBox($head = null, $contents = null, $nooutput=0) - { + function showBox($head = null, $contents = null, $nooutput=0) + { return parent::showBox($this->info_box_head, $this->info_box_contents, $nooutput); } diff --git a/htdocs/core/boxes/box_contracts.php b/htdocs/core/boxes/box_contracts.php index 59db96b08aa..49d6df67d7f 100644 --- a/htdocs/core/boxes/box_contracts.php +++ b/htdocs/core/boxes/box_contracts.php @@ -128,7 +128,7 @@ class box_contracts extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"', + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $thirdpartytmp->getNomUrl(1), 'asis'=>1 ); diff --git a/htdocs/core/boxes/box_graph_invoices_permonth.php b/htdocs/core/boxes/box_graph_invoices_permonth.php index 8b5bfbc817d..d665d70da56 100644 --- a/htdocs/core/boxes/box_graph_invoices_permonth.php +++ b/htdocs/core/boxes/box_graph_invoices_permonth.php @@ -91,6 +91,8 @@ class box_graph_invoices_permonth extends ModeleBoxes if ($user->rights->facture->lire) { + $mesg = ''; + $param_year='DOLUSERCOOKIE_box_'.$this->boxcode.'_year'; $param_shownb='DOLUSERCOOKIE_box_'.$this->boxcode.'_shownb'; $param_showtot='DOLUSERCOOKIE_box_'.$this->boxcode.'_showtot'; @@ -246,13 +248,11 @@ class box_graph_invoices_permonth extends ModeleBoxes $stringtoshow.='
'; $stringtoshow.='
'; } - $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); } else { - $this->info_box_contents[0][0] = array( 'td' => 'align="left" class="nohover"', - 'maxlength'=>500, - 'text' => $mesg); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="left" class="nohover"', 'maxlength'=>500, 'text' => $mesg); } } diff --git a/htdocs/core/boxes/box_graph_invoices_supplier_permonth.php b/htdocs/core/boxes/box_graph_invoices_supplier_permonth.php index 436343a56dc..efd7de41e5a 100644 --- a/htdocs/core/boxes/box_graph_invoices_supplier_permonth.php +++ b/htdocs/core/boxes/box_graph_invoices_supplier_permonth.php @@ -245,11 +245,11 @@ class box_graph_invoices_supplier_permonth extends ModeleBoxes $stringtoshow.=''; $stringtoshow.=''; } - $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"','td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); } else { - $this->info_box_contents[0][0] = array( 'td' => 'align="left" class="nohover"', + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="left" class="nohover"', 'maxlength'=>500, 'text' => $mesg); } diff --git a/htdocs/core/boxes/box_graph_orders_permonth.php b/htdocs/core/boxes/box_graph_orders_permonth.php index 7e60e00a2fc..699f2db902b 100644 --- a/htdocs/core/boxes/box_graph_orders_permonth.php +++ b/htdocs/core/boxes/box_graph_orders_permonth.php @@ -244,11 +244,11 @@ class box_graph_orders_permonth extends ModeleBoxes $stringtoshow.=''; $stringtoshow.=''; } - $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); } else { - $this->info_box_contents[0][0] = array( 'td' => 'align="left" class="nohover"', + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="left" class="nohover"', 'maxlength'=>500, 'text' => $mesg); } diff --git a/htdocs/core/boxes/box_graph_orders_supplier_permonth.php b/htdocs/core/boxes/box_graph_orders_supplier_permonth.php index 6f8ebf39001..f49f38c1228 100644 --- a/htdocs/core/boxes/box_graph_orders_supplier_permonth.php +++ b/htdocs/core/boxes/box_graph_orders_supplier_permonth.php @@ -243,11 +243,11 @@ class box_graph_orders_supplier_permonth extends ModeleBoxes $stringtoshow.=''; $stringtoshow.=''; } - $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); } else { - $this->info_box_contents[0][0] = array( 'td' => 'align="left" class="nohover"', + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="left" class="nohover"', 'maxlength'=>500, 'text' => $mesg); } diff --git a/htdocs/core/boxes/box_graph_product_distribution.php b/htdocs/core/boxes/box_graph_product_distribution.php index bac56d8006b..69a09e8aad2 100644 --- a/htdocs/core/boxes/box_graph_product_distribution.php +++ b/htdocs/core/boxes/box_graph_product_distribution.php @@ -386,7 +386,7 @@ class box_graph_product_distribution extends ModeleBoxes $stringtoshow.=$px3->show(); $stringtoshow.=''; } - $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); } else { diff --git a/htdocs/core/boxes/box_graph_propales_permonth.php b/htdocs/core/boxes/box_graph_propales_permonth.php index 690df3d41fa..50e3cbe9bf8 100644 --- a/htdocs/core/boxes/box_graph_propales_permonth.php +++ b/htdocs/core/boxes/box_graph_propales_permonth.php @@ -245,11 +245,11 @@ class box_graph_propales_permonth extends ModeleBoxes $stringtoshow.=''; $stringtoshow.=''; } - $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); } else { - $this->info_box_contents[0][0] = array( 'td' => 'align="left" class="nohover"', + $this->info_box_contents[0][0] = array('tr'=>'class="oddeven nohover"', 'td' => 'align="left" class="nohover"', 'maxlength'=>500, 'text' => $mesg); } diff --git a/htdocs/core/boxes/box_last_modified_ticketsup.php b/htdocs/core/boxes/box_last_modified_ticketsup.php new file mode 100644 index 00000000000..c63864b0fe0 --- /dev/null +++ b/htdocs/core/boxes/box_last_modified_ticketsup.php @@ -0,0 +1,189 @@ + + * 2016 Christophe Battarel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file core/boxes/box_last_modified_ticketsup.php + * \ingroup ticketsup + * \brief This box shows latest modified tickets + */ +require_once DOL_DOCUMENT_ROOT . "/core/boxes/modules_boxes.php"; + +/** + * Class to manage the box + */ +class box_last_modified_ticketsup extends ModeleBoxes +{ + + public $boxcode = "box_last_modified_ticketsup"; + public $boximg = "ticketsup"; + public $boxlabel; + public $depends = array("ticketsup"); + public $db; + public $param; + public $info_box_head = array(); + public $info_box_contents = array(); + + /** + * Constructor + */ + public function __construct() + { + global $langs; + $langs->load("boxes"); + + $this->boxlabel = $langs->transnoentitiesnoconv("BoxLastModifiedTicketsup"); + } + + /** + * Load data into info_box_contents array to show array later. + * + * @param int $max Maximum number of records to load + * @return void + */ + public function loadBox($max = 5) + { + global $conf, $user, $langs, $db; + + $this->max = $max; + + dol_include_once("/ticketsup/class/ticketsup.class.php"); + + $text = $langs->trans("BoxLastModifiedTicketsupDescription", $max); + $this->info_box_head = array( + 'text' => $text, + 'limit' => dol_strlen($text) + ); + + $this->info_box_contents[0][0] = array('td' => 'align="left"', + 'text' => $langs->trans("BoxLastModifiedTicketsupContent")); + + if ($user->rights->ticketsup->read) { + $sql = "SELECT t.rowid as id, t.ref, t.track_id, t.fk_soc, t.fk_user_create, t.fk_user_assign, t.subject, t.message, t.fk_statut, t.type_code, t.category_code, t.severity_code, t.datec, t.date_read, t.date_close, t.origin_email "; + $sql.= ", type.label as type_label, category.label as category_label, severity.label as severity_label"; + $sql.= ", s.nom as company_name"; + $sql.= " FROM ".MAIN_DB_PREFIX."ticketsup as t"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticketsup_type as type ON type.code=t.type_code"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticketsup_category as category ON category.code=t.category_code"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticketsup_severity as severity ON severity.code=t.severity_code"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=t.fk_soc"; + + $sql.= " WHERE t.entity = ".$conf->entity; + // $sql.= " AND e.rowid = er.fk_event"; + //if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " WHERE s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id; + if ($user->societe_id) { + $sql.= " AND t.fk_soc= ".$user->societe_id; + } + + $sql.= " ORDER BY t.tms DESC, t.rowid DESC "; + $sql.= $db->plimit($max, 0); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $now=gmmktime(); + + $i = 0; + + while ($i < $num) { + $objp = $db->fetch_object($resql); + $datec=$db->jdate($objp->datec); + $dateterm=$db->jdate($objp->fin_validite); + $dateclose=$db->jdate($objp->date_cloture); + $late = ''; + + $ticketsup = new Ticketsup($this->db); + + + $r=0; + + // Picto + $this->info_box_contents[$i][0] = array( + 'td' => 'align="left" width="16"', + 'logo' => $this->boximg, + 'url' => dol_buildpath("/ticketsup/card.php?track_id=".$objp->track_id, 1)); + $r++; + + // Id + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="left"', + 'text' => $objp->ref, + 'url' => dol_buildpath("/ticketsup/card.php?track_id=".$objp->track_id, 1)); + $r++; + + // Subject + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="left"', + 'text' => $objp->subject, // Some event have no ref + 'url' => dol_buildpath("/ticketsup/card.php?track_id=".$objp->track_id, 1)); + $r++; + + // Customer + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="left"', + 'logo' => ($objp->fk_soc>0?'company':''), + 'text' => ($objp->company_name?$objp->company_name:$objp->origin_email), + 'url' => ($objp->fk_soc>0?DOL_URL_ROOT."/comm/card.php?socid=".$objp->fk_soc:'') + ); + $r++; + + + // Date creation + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="right"', + 'text' => dol_print_date($db->idate($objp->datec), 'dayhour') + ); + $r++; + + // Statut + $ticketstat = new Ticketsup($this->db); + $ticketstat->fk_statut = $objp->fk_statut; + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="right"', + 'text' => $ticketstat->getLibStatut(3) + ); + $r++; + + $i++; + } + + if ($num==0) { + $this->info_box_contents[$i][0] = array('td' => 'align="center"','text'=>$langs->trans("BoxLastModifiedTicketsupNoRecordedTickets")); + } + } else { + dol_print_error($db); + } + } else { + $this->info_box_contents[0][0] = array('td' => 'align="left"', + 'text' => $langs->trans("ReadPermissionNotAllowed")); + } + } + + /** + * Method to show box + * + * @param array $head Array with properties of box title + * @param array $contents Array with properties of box lines + * @param int $nooutput No print, only return string + * @return string + */ + function showBox($head = null, $contents = null, $nooutput=0) + { + parent::showBox($this->info_box_head, $this->info_box_contents, $nooutput); + } +} diff --git a/htdocs/core/boxes/box_last_ticketsup.php b/htdocs/core/boxes/box_last_ticketsup.php new file mode 100644 index 00000000000..5c39229835b --- /dev/null +++ b/htdocs/core/boxes/box_last_ticketsup.php @@ -0,0 +1,189 @@ + + * 2016 Christophe Battarel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file core/boxes/box_ticketsup_latest.php + * \ingroup ticketsup + * \brief This box shows latest created tickets + */ +require_once DOL_DOCUMENT_ROOT . "/core/boxes/modules_boxes.php"; + +/** + * Class to manage the box + */ +class box_last_ticketsup extends ModeleBoxes +{ + + public $boxcode = "box_last_ticketsup"; + public $boximg = "ticketsup"; + public $boxlabel; + public $depends = array("ticketsup"); + public $db; + public $param; + public $info_box_head = array(); + public $info_box_contents = array(); + + /** + * Constructor + */ + public function __construct() + { + global $langs; + $langs->load("boxes"); + + $this->boxlabel = $langs->transnoentitiesnoconv("BoxLastTicketsup"); + } + + /** + * Load data into info_box_contents array to show array later. + * + * @param int $max Maximum number of records to load + * @return void + */ + public function loadBox($max = 5) + { + global $conf, $user, $langs, $db; + + $this->max = $max; + + dol_include_once("/ticketsup/class/ticketsup.class.php"); + + $text = $langs->trans("BoxLastTicketsupDescription", $max); + $this->info_box_head = array( + 'text' => $text, + 'limit' => dol_strlen($text), + ); + + $this->info_box_contents[0][0] = array('td' => 'align="left"', + 'text' => $langs->trans("BoxLastTicketsupContent")); + + if ($user->rights->ticketsup->read) { + $sql = "SELECT t.rowid as id, t.ref, t.track_id, t.fk_soc, t.fk_user_create, t.fk_user_assign, t.subject, t.message, t.fk_statut, t.type_code, t.category_code, t.severity_code, t.datec, t.date_read, t.date_close, t.origin_email "; + $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label"; + $sql .= ", s.nom as company_name"; + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup as t"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_type as type ON type.code=t.type_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_category as category ON category.code=t.category_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_severity as severity ON severity.code=t.severity_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON s.rowid=t.fk_soc"; + + $sql .= " WHERE t.entity = " . $conf->entity; + // $sql.= " AND e.rowid = er.fk_event"; + //if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " WHERE s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id; + if ($user->societe_id) { + $sql .= " AND t.fk_soc= " . $user->societe_id; + } + + //$sql.= " AND t.fk_statut > 9"; + + $sql .= " ORDER BY t.datec DESC, t.rowid DESC "; + $sql .= $db->plimit($max, 0); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $now = gmmktime(); + + $i = 0; + + while ($i < $num) { + $objp = $db->fetch_object($resql); + $datec = $db->jdate($objp->datec); + $dateterm = $db->jdate($objp->fin_validite); + $dateclose = $db->jdate($objp->date_cloture); + $late = ''; + + $ticketsup = new Ticketsup($this->db); + + $r = 0; + + // Picto + $this->info_box_contents[$i][0] = array( + 'td' => 'align="left" width="16"', + 'logo' => $this->boximg, + 'url' => dol_buildpath("/ticketsup/card.php?track_id=" . $objp->track_id, 1)); + $r++; + + // Id + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="left"', + 'text' => $objp->ref, + 'url' => dol_buildpath("/ticketsup/card.php?track_id=" . $objp->track_id, 1)); + $r++; + + // Subject + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="left"', + 'text' => $objp->subject, // Some event have no ref + 'url' => dol_buildpath("/ticketsup/card.php?track_id=" . $objp->track_id, 1)); + $r++; + + // Customer + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="left"', + 'logo' => ($objp->fk_soc > 0 ? 'company' : ''), + 'text' => ($objp->company_name ? $objp->company_name : $objp->origin_email), + 'url' => ($objp->fk_soc > 0 ? DOL_URL_ROOT . "/comm/card.php?socid=" . $objp->fk_soc : ''), + ); + $r++; + + // Date creation + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="right"', + 'text' => dol_print_date($db->idate($objp->datec), 'dayhour'), + ); + $r++; + + // Statut + $ticketstat = new Ticketsup($this->db); + $ticketstat->fk_statut = $objp->fk_statut; + $this->info_box_contents[$i][$r] = array( + 'td' => 'align="right"', + 'text' => $ticketstat->getLibStatut(3), + ); + $r++; + + $i++; + } + + if ($num == 0) { + $this->info_box_contents[$i][0] = array('td' => 'align="center"', 'text' => $langs->trans("BoxLastTicketsupNoRecordedTickets")); + } + } else { + dol_print_error($db); + } + } else { + $this->info_box_contents[0][0] = array('td' => 'align="left"', + 'text' => $langs->trans("ReadPermissionNotAllowed")); + } + } + + /** + * Method to show box + * + * @param array $head Array with properties of box title + * @param array $contents Array with properties of box lines + * @param int $nooutput No print, only return string + * @return string + */ + function showBox($head = null, $contents = null, $nooutput=0) + { + parent::showBox($this->info_box_head, $this->info_box_contents, $nooutput); + } +} diff --git a/htdocs/core/boxes/box_members.php b/htdocs/core/boxes/box_members.php index 25fa257d098..e5819f0f14c 100644 --- a/htdocs/core/boxes/box_members.php +++ b/htdocs/core/boxes/box_members.php @@ -88,7 +88,7 @@ class box_members extends ModeleBoxes $sql.= " a.datec, a.tms, a.statut as status, a.datefin as date_end_subscription,"; $sql.= " t.subscription"; $sql.= " FROM ".MAIN_DB_PREFIX."adherent as a, ".MAIN_DB_PREFIX."adherent_type as t"; - $sql.= " WHERE a.entity = ".$conf->entity; + $sql.= " WHERE a.entity IN (".getEntity('member').")"; $sql.= " AND a.fk_adherent_type = t.rowid"; $sql.= " ORDER BY a.tms DESC"; $sql.= $db->plimit($max, 0); diff --git a/htdocs/core/boxes/box_produits.php b/htdocs/core/boxes/box_produits.php index 3261572460e..53449afceeb 100644 --- a/htdocs/core/boxes/box_produits.php +++ b/htdocs/core/boxes/box_produits.php @@ -131,13 +131,13 @@ class box_produits extends ModeleBoxes $productstatic->entity = $objp->entity; $this->info_box_contents[$line][] = array( - 'td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"', + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $productstatic->getNomUrl(1), 'asis' => 1, ); $this->info_box_contents[$line][] = array( - 'td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"', + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $objp->label, ); diff --git a/htdocs/core/boxes/box_produits_alerte_stock.php b/htdocs/core/boxes/box_produits_alerte_stock.php index 9dc75612773..ee49e9e4534 100644 --- a/htdocs/core/boxes/box_produits_alerte_stock.php +++ b/htdocs/core/boxes/box_produits_alerte_stock.php @@ -144,7 +144,7 @@ class box_produits_alerte_stock extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"', + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $objp->label, ); diff --git a/htdocs/core/boxes/box_propales.php b/htdocs/core/boxes/box_propales.php index a94ee0c1f08..8dad41f64a1 100644 --- a/htdocs/core/boxes/box_propales.php +++ b/htdocs/core/boxes/box_propales.php @@ -131,13 +131,13 @@ class box_propales extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="tdoverflowmax100"', + 'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $societestatic->getNomUrl(1), 'asis' => 1, ); $this->info_box_contents[$line][] = array( - 'td' => 'class="right"', + 'td' => 'class="right nowraponall"', 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/boxes/box_services_contracts.php b/htdocs/core/boxes/box_services_contracts.php index 0cc4e68682b..44a1ca020b5 100644 --- a/htdocs/core/boxes/box_services_contracts.php +++ b/htdocs/core/boxes/box_services_contracts.php @@ -177,7 +177,7 @@ class box_services_contracts extends ModeleBoxes } - $this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"', + $this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $s, 'asis' => 1 ); @@ -187,7 +187,7 @@ class box_services_contracts extends ModeleBoxes 'asis' => 1 ); - $this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"', + $this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"', 'text' => $thirdpartytmp->getNomUrl(1), 'asis' => 1 ); diff --git a/htdocs/core/boxes/box_services_expired.php b/htdocs/core/boxes/box_services_expired.php index f123872b85b..72d4390a2e2 100644 --- a/htdocs/core/boxes/box_services_expired.php +++ b/htdocs/core/boxes/box_services_expired.php @@ -126,7 +126,7 @@ class box_services_expired extends ModeleBoxes 'asis' => 1 ); - $this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax100 maxwidth100onsmartphone" align="left"', + $this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax150 maxwidth150onsmartphone" align="left"', 'text' => $thirdpartytmp->getNomUrl(1, 'customer'), 'asis' => 1 ); diff --git a/htdocs/core/boxes/modules_boxes.php b/htdocs/core/boxes/modules_boxes.php index 025cfba71a5..74f0ffc9d9d 100644 --- a/htdocs/core/boxes/modules_boxes.php +++ b/htdocs/core/boxes/modules_boxes.php @@ -248,7 +248,7 @@ class ModeleBoxes // Can't be abtract as it is instantiated to build "empty" $out.= '>'; if ($conf->use_javascript_ajax) { - $out.= ''; + $out = ''; return $out; } diff --git a/htdocs/core/class/hookmanager.class.php b/htdocs/core/class/hookmanager.class.php index 1a19e313e08..8cc2c62bee7 100644 --- a/htdocs/core/class/hookmanager.class.php +++ b/htdocs/core/class/hookmanager.class.php @@ -134,27 +134,29 @@ class HookManager if (in_array( $method, array( - 'addCalendarChoice', - 'addMoreActionsButtons', - 'addMoreMassActions', - 'addSearchEntry', + 'addCalendarChoice', + 'addMoreActionsButtons', + 'addMoreMassActions', + 'addSearchEntry', 'addStatisticLine', - 'createDictionaryFieldList', + 'createDictionaryFieldlist', 'editDictionaryFieldlist', 'getFormMail', - 'deleteFile', + 'deleteFile', 'doActions', - 'doMassActions', + 'doMassActions', + 'formatEvent', 'formCreateThirdpartyOptions', 'formObjectOptions', 'formattachOptions', 'formBuilddocLineOptions', - 'formatNotificationMessage', - 'getFormMail', - 'getIdProfUrl', - 'getDirList', + 'formatNotificationMessage', + 'getFormMail', + 'getIdProfUrl', + 'getDirList', 'moveUploadedFile', - 'pdf_build_address', + 'moreHtmlStatus', + 'pdf_build_address', 'pdf_writelinedesc', 'pdf_getlinenum', 'pdf_getlineref', @@ -175,12 +177,11 @@ class HookManager 'printAddress', 'printSearchForm', 'printTabsHead', - 'formatEvent', - 'printObjectLine', - 'printObjectSubLine', - 'showLinkToObjectBlock', + 'printObjectLine', + 'printObjectSubLine', 'sendMail', - 'sendMailAfter' + 'sendMailAfter', + 'showLinkToObjectBlock' ) )) $hooktype='addreplace'; diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index ba0ea7a37c2..c7fa142daf1 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -81,9 +81,10 @@ class Form * @param string $moreparam More param to add on a href URL. * @param int $fieldrequired 1 if we want to show field as mandatory using the "fieldrequired" CSS. * @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' ' + * @param string $paramid Key of parameter for id ('id', 'socid') * @return string HTML edit field */ - function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0) + function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0, $paramid='id') { global $conf,$langs; @@ -117,7 +118,7 @@ class Form if (! empty($notabletag)) $ret.=' '; if (empty($notabletag) && GETPOST('action','aZ09') != 'edit'.$htmlname && $perm) $ret.=''; if (empty($notabletag) && GETPOST('action','aZ09') != 'edit'.$htmlname && $perm) $ret.=''; @@ -139,12 +140,13 @@ class Form * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of value). Use '' to use same than $value * @param object $extObject External object * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') - * @param string $moreparam More param to add on a href URL + * @param string $moreparam More param to add on the form action href URL * @param int $notabletag Do no output table tags * @param string $formatfunc Call a specific function to output field + * @param string $paramid Key of parameter for id ('id', 'socid') * @return string HTML edit field */ - function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata='string', $editvalue='', $extObject=null, $custommsg=null, $moreparam='', $notabletag=0, $formatfunc='') + function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata='string', $editvalue='', $extObject=null, $custommsg=null, $moreparam='', $notabletag=0, $formatfunc='', $paramid='id') { global $conf,$langs,$db; @@ -166,8 +168,8 @@ class Form $ret.=''; $ret.=''; $ret.=''; - $ret.=''; - if (empty($notabletag)) $ret.='
'; + $out.= ''; + if($action == 'selectlines') + { + print ''; + } + print "\n"; } @@ -3963,6 +4032,12 @@ abstract class CommonObject $discount->fetch($line->fk_remise_except); $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0)); } + elseif ($line->desc == '(EXCESS PAID)') + { + $discount=new DiscountAbsolute($this->db); + $discount->fetch($line->fk_remise_except); + $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid",$discount->getNomUrl(0)); + } else { $this->tpl['description'] = dol_trunc($line->desc,60); @@ -4116,6 +4191,7 @@ abstract class CommonObject * @param int $hideref 1 to hide product reference. 0 by default * @param null|array $moreparams Array to provide more information * @return int >0 if OK, <0 if KO + * @see addFileIntoDatabaseIndex */ protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null) { @@ -4268,8 +4344,10 @@ abstract class CommonObject if ($useonlinesignature) $setsharekey=true; if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true; } - if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true; - if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true; + if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true; + if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true; + if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true; + if ($setsharekey) { if (empty($ecmfile->share)) // Because object not found or share not set yet @@ -4354,6 +4432,7 @@ abstract class CommonObject /** * Build thumb + * @TODO Move this into files.lib.php * * @param string $file Path file in UTF8 to original file to create thumbs from. * @return void @@ -4493,10 +4572,11 @@ abstract class CommonObject { $extrafields->fetch_name_optionals_label($this->table_element); } - $optionsArray = $extrafields->attributes[$this->table_element]['label']; + $optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null); } else { + global $extrafields; dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING); } @@ -4532,7 +4612,7 @@ abstract class CommonObject if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key)) { // we can add this attribute to object - if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime'))) + if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime'))) { //var_dump($extrafields->attributes[$this->table_element]['type'][$key]); $this->array_options["options_".$key]=$this->db->jdate($value); @@ -4629,10 +4709,10 @@ abstract class CommonObject foreach($new_array_options as $key => $value) { $attributeKey = substr($key,8); // Remove 'options_' prefix - $attributeType = $extrafields->attribute_type[$attributeKey]; - $attributeLabel = $extrafields->attribute_label[$attributeKey]; - $attributeParam = $extrafields->attribute_param[$attributeKey]; - $attributeRequired = $extrafields->attribute_required[$attributeKey]; + $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey]; + $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey]; + $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey]; + $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey]; if ($attributeRequired) { @@ -4665,7 +4745,44 @@ abstract class CommonObject $this->array_options[$key] = null; } break;*/ - case 'price': + case 'password': + $algo=''; + if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) + { + // If there is an encryption choice, we use it to crypt data before insert + $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']); + $algo=reset($tmparrays); + if ($algo != '') + { + //global $action; // $action may be 'create', 'update', 'update_extras'... + //var_dump($action); + //var_dump($this->oldcopy);exit; + if (is_object($this->oldcopy)) // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value + { + //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]); + if ($this->array_options[$key] == $this->oldcopy->array_options[$key]) // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update. + { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + else + { + // var_dump($algo); + $newvalue = dol_hash($this->array_options[$key], $algo); + $new_array_options[$key] = $newvalue; + } + } + else + { + $new_array_options[$key] = $this->array_options[$key]; // Value is kept + } + } + } + else // Common usage + { + $new_array_options[$key] = $this->array_options[$key]; + } + break; + case 'price': $new_array_options[$key] = price2num($this->array_options[$key]); break; case 'date': @@ -4723,7 +4840,7 @@ abstract class CommonObject { $attributeKey = substr($key,8); // Remove 'options_' prefix // Add field of attribut - if ($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate + if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator $sql.=",".$attributeKey; } $sql .= ") VALUES (".$this->id; @@ -4731,8 +4848,8 @@ abstract class CommonObject foreach($new_array_options as $key => $value) { $attributeKey = substr($key,8); // Remove 'options_' prefix - // Add field o fattribut - if($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate) + // Add field of attribute + if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator) { if ($new_array_options[$key] != '') { @@ -4781,7 +4898,7 @@ abstract class CommonObject * Update an exta field value for the current object. * Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...) * - * @param string $key Key of the extrafield + * @param string $key Key of the extrafield (without starting 'options_') * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY) * @param User $userused Object user * @return int -1=error, O=did nothing, 1=OK @@ -4806,9 +4923,12 @@ abstract class CommonObject $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element); $value=$this->array_options["options_".$key]; - $attributeType = $extrafields->attribute_type[$key]; - $attributeLabel = $extrafields->attribute_label[$key]; - $attributeParam = $extrafields->attribute_param[$key]; + + $attributeType = $extrafields->attributes[$this->table_element]['type'][$key]; + $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key]; + $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key]; + $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key]; + switch ($attributeType) { case 'int': @@ -4838,7 +4958,7 @@ abstract class CommonObject $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]); break; case 'link': - $param_list=array_keys($attributeParam ['options']); + $param_list=array_keys($attributeParam['options']); // 0 : ObjectName // 1 : classPath $InfoFieldList = explode(":", $param_list[0]); @@ -4873,6 +4993,7 @@ abstract class CommonObject if ($error) { + dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR); $this->db->rollback(); return -1; } @@ -5794,15 +5915,15 @@ abstract class CommonObject /** * Function to show lines of extrafields with output datas * - * @param Extrafields $extrafields Extrafield Object - * @param string $mode Show output (view) or input (edit) for extrafield - * @param array $params Optional parameters - * @param string $keysuffix Suffix string to add after name and id of field (can be used to avoid duplicate names) - * @param string $keyprefix Prefix string to add before name and id of field (can be used to avoid duplicate names) - * - * @return string + * @param Extrafields $extrafields Extrafield Object + * @param string $mode Show output (view) or input (edit) for extrafield + * @param array $params Optional parameters + * @param string $keysuffix Suffix string to add after name and id of field (can be used to avoid duplicate names) + * @param string $keyprefix Prefix string to add before name and id of field (can be used to avoid duplicate names) + * @param string $onetrtd All fields in same tr td + * @return string */ - function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='') + function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0) { global $_POST, $conf, $langs, $action; @@ -5860,21 +5981,29 @@ abstract class CommonObject else { $csstyle=''; - $class=(!empty($extrafields->attribute_hidden[$key]) ? 'class="hideobject" ' : ''); + $class=(!empty($extrafields->attribute_hidden[$key]) ? 'hideobject ' : ''); if (is_array($params) && count($params)>0) { if (array_key_exists('style',$params)) { $csstyle=$params['style']; } } + + // add html5 elements + $domData = ' data-element="extrafield"'; + $domData .= ' data-targetelement="'.$this->element.'"'; + $domData .= ' data-targetid="'.$this->id.'"'; + + $html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : ''; + + $out .= ''; + if ( !empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { - $out .= ''; - $colspan='0'; - } - else - { - $out .= ''; + if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; } } + + if($action == 'selectlines'){ $colspan++; } + // Convert date into timestamp format (value in memory must be a timestamp) if (in_array($extrafields->attribute_type[$key],array('date','datetime'))) { @@ -5892,6 +6021,7 @@ abstract class CommonObject { $labeltoshow = ''.$labeltoshow.''; } + $out .= ''; $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : ''; @@ -6076,6 +6206,243 @@ abstract class CommonObject return $buyPrice; } + /** + * Show photos of an object (nbmax maximum), into several columns + * + * @param string $modulepart 'product', 'ticketsup', ... + * @param string $sdir Directory to scan (full absolute path) + * @param int $size 0=original size, 1='small' use thumbnail if possible + * @param int $nbmax Nombre maximum de photos (0=pas de max) + * @param int $nbbyrow Number of image per line or -1 to use div. Used only if size=1. + * @param int $showfilename 1=Show filename + * @param int $showaction 1=Show icon with action links (resize, delete) + * @param int $maxHeight Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image. + * @param int $maxWidth Max width of original image when size='small' + * @param int $nolink Do not add a href link to view enlarged imaged into a new tab + * @param int $notitle Do not add title tag on image + * @param int $usesharelink Use the public shared link of image (if not available, the 'nophoto' image will be shown instead) + * @return string Html code to show photo. Number of photos shown is saved in this->nbphoto + */ + function show_photos($modulepart, $sdir, $size=0, $nbmax=0, $nbbyrow=5, $showfilename=0, $showaction=0, $maxHeight=120, $maxWidth=160, $nolink=0, $notitle=0, $usesharelink=0) + { + global $conf,$user,$langs; + + include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php'; + + $sortfield='position_name'; + $sortorder='asc'; + + $dir = $sdir . '/'; + $pdir = '/'; + if ($modulepart == 'ticketsup') + { + $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/'; + $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/'; + } + else + { + $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/'; + $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/'; + } + + // For backward compatibility + if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) + { + $dir = $sdir . '/'. get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/"; + $pdir = '/' . get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/"; + } + + // Defined relative dir to DOL_DATA_ROOT + $relativedir = ''; + if ($dir) + { + $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir); + $relativedir = preg_replace('/^[\\/]/','',$relativedir); + $relativedir = preg_replace('/[\\/]$/','',$relativedir); + } + + $dirthumb = $dir.'thumbs/'; + $pdirthumb = $pdir.'thumbs/'; + + $return =''."\n"; + $nbphoto=0; + + $filearray=dol_dir_list($dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1); + + /*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) // For backward compatiblity, we scan also old dirs + { + $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1); + $filearray=array_merge($filearray, $filearrayold); + }*/ + + completeFileArrayWithDatabaseInfo($filearray, $relativedir); + + if (count($filearray)) + { + if ($sortfield && $sortorder) + { + $filearray=dol_sort_array($filearray, $sortfield, $sortorder); + } + + foreach($filearray as $key => $val) + { + $photo=''; + $file = $val['name']; + + //if (! utf8_check($file)) $file=utf8_encode($file); // To be sure file is stored in UTF8 in memory + + //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0) + if (image_format_supported($file) >= 0) + { + $nbphoto++; + $photo = $file; + $viewfilename = $file; + + if ($size == 1 || $size == 'small') { // Format vignette + + // Find name of thumb file + $photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small')); + if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette=''; + + // Get filesize of original file + $imgarray=dol_getImageSize($dir.$photo); + + if ($nbbyrow > 0) + { + if ($nbphoto == 1) $return.= '
'; } if (! empty($head['text'])) { diff --git a/htdocs/core/class/CMailFile.class.php b/htdocs/core/class/CMailFile.class.php index e19892e9a58..af3dce343f4 100644 --- a/htdocs/core/class/CMailFile.class.php +++ b/htdocs/core/class/CMailFile.class.php @@ -80,14 +80,16 @@ class CMailFile var $atleastoneimage=0; // at least one image file with file=xxx.ext into content (TODO Debug this. How can this case be tested. Remove if not used). var $html_images=array(); var $images_encoded=array(); - var $image_types = array('gif' => 'image/gif', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'bmp' => 'image/bmp', - 'png' => 'image/png', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff'); + var $image_types = array( + 'gif' => 'image/gif', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'bmp' => 'image/bmp', + 'png' => 'image/png', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + ); /** @@ -109,13 +111,16 @@ class CMailFile * @param string $trackid Tracking string (contains type and id of related element) * @param string $moreinheader More in header. $moreinheader must contains the "\r\n" (TODO not supported for other MAIL_SEND_MODE different than 'phpmail' and 'smtps' for the moment) * @param string $sendcontext 'standard', 'emailing', ... (used to define with sending mode and parameters to use) + * @param string $replyto Reply-to email (will be set to same value than From by default if not provided) */ - function __construct($subject,$to,$from,$msg,$filename_list=array(),$mimetype_list=array(),$mimefilename_list=array(),$addr_cc="",$addr_bcc="",$deliveryreceipt=0,$msgishtml=0,$errors_to='',$css='',$trackid='',$moreinheader='',$sendcontext='standard') + function __construct($subject,$to,$from,$msg,$filename_list=array(),$mimetype_list=array(),$mimefilename_list=array(),$addr_cc="",$addr_bcc="",$deliveryreceipt=0,$msgishtml=0,$errors_to='',$css='',$trackid='',$moreinheader='',$sendcontext='standard',$replyto='') { global $conf, $dolibarr_main_data_root; $this->sendcontext = $sendcontext; + if (empty($replyto)) $replyto=$from; + // Define this->sendmode $this->sendmode = ''; if ($this->sendcontext == 'emailing' && !empty($conf->global->MAIN_MAIL_SENDMODE_EMAILING) && $conf->global->MAIN_MAIL_SENDMODE_EMAILING != 'default') @@ -234,7 +239,7 @@ class CMailFile // Define smtp_headers $this->subject = $subject; $this->addr_from = $from; - $this->reply_to = $from; // Set this property after constructor if you want to use another value + $this->reply_to = $replyto; $this->errors_to = $errors_to; $this->addr_to = $to; $this->addr_cc = $addr_cc; @@ -293,7 +298,7 @@ class CMailFile $smtps->setTO($this->getValidAddress($to,0,1)); $smtps->setFrom($this->getValidAddress($from,0,1)); $smtps->setTrackId($trackid); - $smtps->setReplyTo($this->getValidAddress($from,0,1)); // Set property with this->smtps->setReplyTo after constructor if you want to use another value than the From + $smtps->setReplyTo($this->getValidAddress($replyto,0,1)); if (! empty($moreinheader)) $smtps->setMoreInHeader($moreinheader); @@ -335,71 +340,39 @@ class CMailFile $this->smtps=$smtps; } - // TODO not stable, in progress - else if ($this->sendmode == 'phpmailer') - { - // Use PHPMailer library - // ------------------------------------------ - - require_once DOL_DOCUMENT_ROOT.'/includes/phpmailer/class.phpmailer.php'; - $this->phpmailer = new PHPMailer(); - $this->phpmailer->CharSet = $conf->file->character_set_client; - - $this->phpmailer->Subject($this->encodetorfc2822($subject)); - $this->phpmailer->setTO($this->getValidAddress($to,0,1)); - $this->phpmailer->SetFrom($this->getValidAddress($from,0,1)); - $this->phpmailer->SetReplyTo($this->getValidAddress($from,0,1)); // Set property with this->phpmailer->setReplyTo after constructor if you want to use another value than the From - // TODO Add trackid into smtp header - // TODO if (! empty($moreinheader)) ... - - if (! empty($this->html)) - { - if (!empty($css)) - { - $this->css = $css; - $this->buildCSS(); - } - $msg = $this->html; - $msg = $this->checkIfHTML($msg); - } - - if ($this->msgishtml) $smtps->setBodyContent($msg,'html'); - else $smtps->setBodyContent($msg,'plain'); - - if ($this->atleastoneimage) - { - foreach ($this->images_encoded as $img) - { - $smtps->setImageInline($img['image_encoded'],$img['name'],$img['content_type'],$img['cid']); - } - } - - if ($this->atleastonefile) - { - foreach ($filename_list as $i => $val) - { - $content=file_get_contents($filename_list[$i]); - $smtps->setAttachment($content,$mimefilename_list[$i],$mimetype_list[$i]); - } - } - - $this->phpmailer->setCC($addr_cc); - $this->phpmailer->setBCC($addr_bcc); - $this->phpmailer->setErrorsTo($errors_to); - $this->phpmailer->setDeliveryReceipt($deliveryreceipt); - } else if ($this->sendmode == 'swiftmailer') { // Use Swift Mailer library // ------------------------------------------ - $host = dol_getprefix('email'); + $host = dol_getprefix('email'); + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php'; + + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Exception/InvalidEmail.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Exception/NoDomainPart.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailParser.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailLexer.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailValidator.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Warning.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LocalTooLong.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/Parser.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/DomainPart.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/LocalPart.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/EmailValidation.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/RFCValidation.php'; + + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/classes/Swift/InputByteStream.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/classes/Swift/Signer.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php'; + require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php'; + //require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/classes/Swift/SignedMessage.php'; require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php'; // Create the message - $this->message = Swift_Message::newInstance(); - - // Adding a trackid header to a message + //$this->message = Swift_Message::newInstance(); + $this->message = new Swift_Message(); + //$this->message = new Swift_SignedMessage(); + // Adding a trackid header to a message $headers = $this->message->getHeaders(); $headers->addTextHeader('X-Dolibarr-TRACKID', $trackid); $headerID = time() . '.swiftmailer-dolibarr-' . $trackid . '@' . $host; @@ -413,12 +386,30 @@ class CMailFile // Set the From address with an associative array //$this->message->setFrom(array('john@doe.com' => 'John Doe')); - if (! empty($from)) $this->message->setFrom($this->getArrayAddress($from)); + if (! empty($from)) { + try { + $this->message->setFrom($this->getArrayAddress($from)); + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + } + } // Set the To addresses with an associative array - if (! empty($to)) $this->message->setTo($this->getArrayAddress($to)); + if (! empty($to)) { + try { + $this->message->setTo($this->getArrayAddress($to)); + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + } + } - if (! empty($from)) $this->message->SetReplyTo($this->getArrayAddress($from)); + if (! empty($replyto)) { + try { + $this->message->SetReplyTo($this->getArrayAddress($replyto)); + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + } + } $this->message->setCharSet($conf->file->character_set_client); @@ -449,11 +440,11 @@ class CMailFile if ($this->msgishtml) { $this->message->setBody($msg,'text/html'); // And optionally an alternative body - //$this->message->addPart('Here is the message itself', 'text/plain'); + $this->message->addPart(html_entity_decode(strip_tags($msg)), 'text/plain'); } else { $this->message->setBody($msg,'text/plain'); // And optionally an alternative body - //$this->message->addPart('Here is the message itself', 'text/html'); + $this->message->addPart($msg, 'text/html'); } if ($this->atleastonefile) @@ -491,7 +482,7 @@ class CMailFile global $conf,$db,$langs; $errorlevel=error_reporting(); - error_reporting($errorlevel ^ E_WARNING); // Desactive warnings + //error_reporting($errorlevel ^ E_WARNING); // Desactive warnings $res=false; @@ -563,7 +554,7 @@ class CMailFile $keyfortls ='MAIN_MAIL_EMAIL_TLS_EMAILING'; $keyforstarttls ='MAIN_MAIL_EMAIL_STARTTLS_EMAILING'; } - + if(!empty($conf->global->MAIN_MAIL_FORCE_SENDTO)) { $this->addr_to = $conf->global->MAIN_MAIL_FORCE_SENDTO; $this->addr_cc = ''; @@ -590,13 +581,22 @@ class CMailFile if (! empty($conf->global->$keyforsmtpserver)) ini_set('SMTP',$conf->global->$keyforsmtpserver); if (! empty($conf->global->$keyforsmtpport)) ini_set('smtp_port',$conf->global->$keyforsmtpport); + $res=true; + if ($res && ! $this->subject) + { + $this->error="Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."
Subject is empty"; + dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); + $res=false; + } $dest=$this->getValidAddress($this->addr_to,2); - if (! $dest) + if ($res && ! $dest) { $this->error="Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."
Recipient address '$dest' invalid"; dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); + $res=false; } - else + + if ($res) { $additionnalparam = ''; // By default if (! empty($conf->global->MAIN_MAIL_ALLOW_SENDMAIL_F)) @@ -689,14 +689,14 @@ class CMailFile $res=true; $from=$this->smtps->getFrom('org'); - if (! $from) + if ($res && ! $from) { $this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."
Sender address '$from' invalid"; dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); $res=false; } $dest=$this->smtps->getTo(); - if (! $dest) + if ($res && ! $dest) { $this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."
Recipient address '$dest' invalid"; dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); @@ -733,21 +733,30 @@ class CMailFile if (empty($conf->global->$keyforsmtpport)) $conf->global->$keyforsmtpport=ini_get('smtp_port'); // If we use SSL/TLS - $server=$conf->global->$keyforsmtpserver; - $secure=''; + $server = $conf->global->$keyforsmtpserver; + $secure = ''; if (! empty($conf->global->$keyfortls) && function_exists('openssl_open')) $secure='ssl'; if (! empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) $secure='tls'; - $this->transport = Swift_SmtpTransport::newInstance($server, $conf->global->$keyforsmtpport, $secure); + $this->transport = new Swift_SmtpTransport($server, $conf->global->$keyforsmtpport, $secure); if (! empty($conf->global->$keyforsmtpid)) $this->transport->setUsername($conf->global->$keyforsmtpid); if (! empty($conf->global->$keyforsmtppw)) $this->transport->setPassword($conf->global->$keyforsmtppw); //$smtps->_msgReplyTo = 'reply@web.com'; // Create the Mailer using your created Transport - $this->mailer = Swift_Mailer::newInstance($this->transport); + $this->mailer = new Swift_Mailer($this->transport); - if (! empty($conf->global->MAIN_MAIL_DEBUG)) { + // DKIM SIGN + if ($conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED) { + $privateKey = $conf->global->MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY; + $domainName = $conf->global->MAIN_MAIL_EMAIL_DKIM_DOMAIN; + $selector = $conf->global->MAIN_MAIL_EMAIL_DKIM_SELECTOR; + $signer = new Swift_Signers_DKIMSigner($privateKey, $domainName, $selector); + $this->message->attachSigner($signer->ignoreHeader('Return-Path')); + } + + if (! empty($conf->global->MAIN_MAIL_DEBUG)) { // To use the ArrayLogger $this->logger = new Swift_Plugins_Loggers_ArrayLogger(); // Or to use the Echo Logger @@ -763,11 +772,12 @@ class CMailFile if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->dump_mail(); $res = true; - if (! empty($this->error) && ! $result) { + if (! empty($this->error) || ! $result) { dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); $res=false; } else { - $this->error = sprintf ("Sent %d messages\n", $result); + $this->error = $langs->trans("SentXXXmessages", $result); + $this->errors[] = $langs->trans("SentXXXmessages", $result); } } else diff --git a/htdocs/core/class/comment.class.php b/htdocs/core/class/comment.class.php index a21057662f2..9aeebd575bf 100644 --- a/htdocs/core/class/comment.class.php +++ b/htdocs/core/class/comment.class.php @@ -7,20 +7,22 @@ class Comment extends CommonObject public $element='comment'; //!< Id that identify managed objects public $table_element='comment'; //!< Name of table without prefix where object is stored - var $fk_element; - var $element_type; + public $fk_element; + public $element_type; - var $description; + public $description; - var $tms; + public $tms; - var $datec; + public $datec; - var $fk_user_author; + public $fk_user_author; - var $entity; + public $entity; - var $import_key; + public $import_key; + + public $comments = array(); public $oldcopy; @@ -289,10 +291,10 @@ class Comment extends CommonObject * @param int $fk_element Id of element * @return array Comment array */ - public static function fetchAllFor($element_type, $fk_element) + public function fetchAllFor($element_type, $fk_element) { global $db,$conf; - $TComments = array(); + $this->comments = array(); if(!empty($element_type) && !empty($fk_element)) { $sql = "SELECT"; $sql.= " c.rowid"; @@ -302,7 +304,7 @@ class Comment extends CommonObject $sql.= " AND c.entity = ".$conf->entity; $sql.= " ORDER BY c.tms DESC"; - dol_syslog("Comment::fetchAllFor", LOG_DEBUG); + dol_syslog(get_class($this).'::'.__METHOD__, LOG_DEBUG); $resql=$db->query($sql); if ($resql) { @@ -313,12 +315,17 @@ class Comment extends CommonObject { $comment = new self($db); $comment->fetch($obj->rowid); - $TComments[] = $comment; + $this->comments[] = $comment; } } $db->free($resql); + } else { + $error++; $this->errors[]="Error ".$this->db->lasterror(); + return -1; } + } - return $TComments; + + return count($this->comments); } } \ No newline at end of file diff --git a/htdocs/core/class/commondocgenerator.class.php b/htdocs/core/class/commondocgenerator.class.php index c9c250ee1fa..4d45f5aabbb 100644 --- a/htdocs/core/class/commondocgenerator.class.php +++ b/htdocs/core/class/commondocgenerator.class.php @@ -44,7 +44,6 @@ abstract class CommonDocGenerator */ public function __construct($db) { $this->db = $db; - return 1; } @@ -316,6 +315,14 @@ abstract class CommonDocGenerator 'current_server_datehour_locale'=>dol_print_date($now,'dayhour','tzserver',$outputlangs), ); + + foreach($conf->global as $key => $val) + { + if (preg_match('/(_pass|password|secret|_key|key$)/i', $key)) $newval = '*****forbidden*****'; + else $newval = $val; + $array_other['__['.$key.']__'] = $newval; + } + return $array_other; } @@ -345,17 +352,19 @@ abstract class CommonDocGenerator $sumcreditnote = $object->getSumCreditNotesUsed(); } + $date = ($object->element == 'contrat' ? $object->date_contrat : $object->date); + $resarray=array( $array_key.'_id'=>$object->id, $array_key.'_ref'=>$object->ref, $array_key.'_ref_ext'=>$object->ref_ext, - $array_key.'_ref_customer'=>$object->ref_client, - $array_key.'_ref_supplier'=>(! empty($object->ref_fournisseur)?$object->ref_fournisseur:''), + $array_key.'_ref_customer'=>(! empty($object->ref_client) ? $object->ref_client : (empty($object->ref_customer) ? '' : $object->ref_customer)), + $array_key.'_ref_supplier'=>(! empty($object->ref_fournisseur) ? $object->ref_fournisseur : (empty($object->ref_supplier) ? '' : $object->ref_supplier)), $array_key.'_source_invoice_ref'=>$invoice_source->ref, // Dates - $array_key.'_hour'=>dol_print_date($object->date,'hour'), - $array_key.'_date'=>dol_print_date($object->date,'day'), - $array_key.'_date_rfc'=>dol_print_date($object->date,'dayrfc'), + $array_key.'_hour'=>dol_print_date($date,'hour'), + $array_key.'_date'=>dol_print_date($date,'day'), + $array_key.'_date_rfc'=>dol_print_date($date,'dayrfc'), $array_key.'_date_limit'=>(! empty($object->date_lim_reglement)?dol_print_date($object->date_lim_reglement,'day'):''), $array_key.'_date_end'=>(! empty($object->fin_validite)?dol_print_date($object->fin_validite,'day'):''), $array_key.'_date_creation'=>dol_print_date($object->date_creation,'day'), diff --git a/htdocs/core/class/commoninvoice.class.php b/htdocs/core/class/commoninvoice.class.php index 9d7ae9d0998..9ce569edb35 100644 --- a/htdocs/core/class/commoninvoice.class.php +++ b/htdocs/core/class/commoninvoice.class.php @@ -180,12 +180,6 @@ abstract class CommonInvoice extends CommonObject */ function getSumCreditNotesUsed($multicurrency=0) { - if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') - { - // TODO - return 0; - } - require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php'; $discountstatic=new DiscountAbsolute($this->db); @@ -274,6 +268,63 @@ abstract class CommonInvoice extends CommonObject } } + /** + * Return list of payments + * + * @param string $filtertype 1 to filter on type of payment == 'PRE' + * @return array Array with list of payments + */ + function getListOfPayments($filtertype='') + { + $retarray=array(); + + $table='paiement_facture'; + $table2='paiement'; + $field='fk_facture'; + $field2='fk_paiement'; + $sharedentity='facture'; + if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') + { + $table='paiementfourn_facturefourn'; + $table2='paiementfourn'; + $field='fk_facturefourn'; + $field2='fk_paiementfourn'; + $sharedentity='facture_fourn'; + } + + $sql = 'SELECT p.ref, pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code'; + $sql.= ' FROM '.MAIN_DB_PREFIX.$table.' as pf, '.MAIN_DB_PREFIX.$table2.' as p, '.MAIN_DB_PREFIX.'c_paiement as t'; + $sql.= ' WHERE pf.'.$field.' = '.$this->id; + //$sql.= ' WHERE pf.'.$field.' = 1'; + $sql.= ' AND pf.'.$field2.' = p.rowid'; + $sql.= ' AND p.fk_paiement = t.id'; + $sql.= ' AND p.entity IN (' . getEntity($sharedentity).')'; + if ($filtertype) $sql.=" AND t.code='PRE'"; + + dol_syslog(get_class($this)."::getListOfPayments", LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i=0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + $retarray[]=array('amount'=>$obj->amount,'type'=>$obj->code, 'date'=>$obj->datep, 'num'=>$obj->num, 'ref'=>$obj->ref); + $i++; + } + $this->db->free($resql); + return $retarray; + } + else + { + $this->error=$this->db->lasterror(); + dol_print_error($this->db); + return array(); + } + } + + /** * Return if an invoice can be deleted * Rule is: @@ -457,7 +508,7 @@ abstract class CommonInvoice extends CommonObject { if ($status == 0) return img_picto($langs->trans('BillStatusDraft'),'statut0').' '.$langs->trans('Bill'.$prefix.'StatusDraft'); if (($status == 3 || $status == 2) && $alreadypaid <= 0) return img_picto($langs->trans('StatusCanceled'),'statut5').' '.$langs->trans('Bill'.$prefix.'StatusCanceled'); - if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7').' '.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially'); + if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut9').' '.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially'); if ($alreadypaid <= 0) return img_picto($langs->trans('BillStatusNotPaid'),'statut1').' '.$langs->trans('Bill'.$prefix.'StatusNotPaid'); return img_picto($langs->trans('BillStatusStarted'),'statut3').' '.$langs->trans('Bill'.$prefix.'StatusStarted'); } @@ -475,7 +526,7 @@ abstract class CommonInvoice extends CommonObject { if ($status == 0) return img_picto($langs->trans('BillStatusDraft'),'statut0'); if (($status == 3 || $status == 2) && $alreadypaid <= 0) return img_picto($langs->trans('BillStatusCanceled'),'statut5'); - if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7'); + if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut9'); if ($alreadypaid <= 0) return img_picto($langs->trans('BillStatusNotPaid'),'statut1'); return img_picto($langs->trans('BillStatusStarted'),'statut3'); } @@ -493,7 +544,7 @@ abstract class CommonInvoice extends CommonObject { if ($status == 0) return img_picto($langs->trans('BillStatusDraft'),'statut0').' '.$langs->trans('BillStatusDraft'); if (($status == 3 || $status == 2) && $alreadypaid <= 0) return img_picto($langs->trans('BillStatusCanceled'),'statut5').' '.$langs->trans('Bill'.$prefix.'StatusCanceled'); - if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7').' '.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially'); + if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut9').' '.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially'); if ($alreadypaid <= 0) return img_picto($langs->trans('BillStatusNotPaid'),'statut1').' '.$langs->trans('BillStatusNotPaid'); return img_picto($langs->trans('BillStatusStarted'),'statut3').' '.$langs->trans('BillStatusStarted'); } @@ -512,7 +563,7 @@ abstract class CommonInvoice extends CommonObject { if ($status == 0) return ''.$langs->trans('Bill'.$prefix.'StatusDraft').' '.img_picto($langs->trans('BillStatusDraft'),'statut0'); if (($status == 3 || $status == 2) && $alreadypaid <= 0) return ''.$langs->trans('Bill'.$prefix.'StatusCanceled').' '.img_picto($langs->trans('BillStatusCanceled'),'statut5'); - if (($status == 3 || $status == 2) && $alreadypaid > 0) return ''.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially').' '.img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7'); + if (($status == 3 || $status == 2) && $alreadypaid > 0) return ''.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially').' '.img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut9'); if ($alreadypaid <= 0) { if ($type == self::TYPE_CREDIT_NOTE) return ''.$langs->trans('Bill'.$prefix.'StatusNotRefunded').' '.img_picto($langs->trans('StatusNotRefunded'),'statut1'); @@ -545,9 +596,11 @@ abstract class CommonInvoice extends CommonObject $sqltemp = 'SELECT c.type_cdr,c.nbjour,c.decalage'; $sqltemp.= ' FROM '.MAIN_DB_PREFIX.'c_payment_term as c'; - $sqltemp.= " WHERE c.entity IN (" . getEntity('c_payment_term').")"; - if (is_numeric($cond_reglement)) $sqltemp.= " AND c.rowid=".$cond_reglement; - else $sqltemp.= " AND c.code='".$this->db->escape($cond_reglement)."'"; + if (is_numeric($cond_reglement)) $sqltemp.= " WHERE c.rowid=".$cond_reglement; + else { + $sqltemp.= " WHERE c.entity IN (".getEntity('c_payment_term').")"; + $sqltemp.= " AND c.code='".$this->db->escape($cond_reglement)."'"; + } dol_syslog(get_class($this).'::calculate_date_lim_reglement', LOG_DEBUG); $resqltemp=$this->db->query($sqltemp); @@ -623,7 +676,7 @@ abstract class CommonInvoiceLine extends CommonObjectLine { /** * Quantity - * @var int + * @var double */ public $qty; diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index f69e842ec81..88ef341cb04 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -13,6 +13,7 @@ * Copyright (C) 2016 Bahfir abbes * Copyright (C) 2017 ATM Consulting * Copyright (C) 2017 Nicolas ZABOURI + * Copyright (C) 2017 Rui Strecht * Copyright (C) 2018 Frederic France * * This program is free software; you can redistribute it and/or modify @@ -197,7 +198,32 @@ abstract class CommonObject * @var string * @see getFullAddress(), isInEEC(), country */ - public $country_code; + public $country_code; + /** + * @var string + * @see getFullAddress() + */ + public $state; + /** + * @var int + * @see getFullAddress(), state + */ + public $state_id; + /** + * @var string + * @see getFullAddress(), state + */ + public $state_code; + /** + * @var string + * @see getFullAddress(), region + */ + public $region; + /** + * @var string + * @see getFullAddress(), region + */ + public $region_code; /** * @var int @@ -422,9 +448,10 @@ abstract class CommonObject * * @param int $withcountry 1=Add country into address string * @param string $sep Separator to use to build string + * @param int $withregion 1=Add region into address string * @return string Full address string */ - function getFullAddress($withcountry=0,$sep="\n") + function getFullAddress($withcountry=0,$sep="\n",$withregion=0) { if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) { @@ -434,6 +461,16 @@ abstract class CommonObject $this->country =$tmparray['label']; } + if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde))) + { + require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php'; + $tmparray=getState($this->state_id,'all',0,1); + $this->state_code =$tmparray['code']; + $this->state =$tmparray['label']; + $this->region_code =$tmparray['region_code']; + $this->region =$tmparray['region']; + } + return dol_format_address($this, $withcountry, $sep); } @@ -471,7 +508,7 @@ abstract class CommonObject $out=''; $outdone=0; - $coords = $this->getFullAddress(1,', '); + $coords = $this->getFullAddress(1,', ',$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT); if ($coords) { if (! empty($conf->use_javascript_ajax)) @@ -489,7 +526,12 @@ abstract class CommonObject if (! in_array($this->country_code,$countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS) // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state) { - $out.=($outdone?' - ':'').$this->state; + if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) { + $out.=($outdone?' - ':'').$this->region.' - '.$this->state; + } + else { + $out.=($outdone?' - ':'').$this->state; + } $outdone++; } @@ -1231,7 +1273,7 @@ abstract class CommonObject } /** - * Charge le projet d'id $this->fk_project dans this->projet + * Load the project with id $this->fk_project into this->project * * @return int <0 if KO, >=0 if OK */ @@ -1251,7 +1293,25 @@ abstract class CommonObject } /** - * Charge le user d'id userid dans this->user + * Load the product with id $this->fk_product into this->product + * + * @return int <0 if KO, >=0 if OK + */ + function fetch_product() + { + include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + + if (empty($this->fk_product)) return 0; + + $product = new Product($this->db); + $result = $product->fetch($this->fk_product); + + $this->product = $product; + return $result; + } + + /** + * Load the user with id $userid into this->user * * @param int $userid Id du contact * @return int <0 if KO, >0 if OK @@ -1368,19 +1428,24 @@ abstract class CommonObject if (empty($format)) $format='text'; if (empty($id_field)) $id_field='rowid'; + $fk_user_field = 'fk_user_modif'; + $error=0; $this->db->begin(); // Special case if ($table == 'product' && $field == 'note_private') $field='note'; + if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) { + $fk_user_field = 'fk_user_mod'; + } $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET "; if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'"; else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value); else if ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null"); - if (! empty($fuser) && is_object($fuser)) $sql.=", fk_user_modif = ".$fuser->id; - elseif (empty($fuser) || $fuser != 'none') $sql.=", fk_user_modif = ".$user->id; + if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id; + elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id; $sql.= " WHERE ".$id_field." = ".$id; dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG); @@ -2377,11 +2442,13 @@ abstract class CommonObject if (! $this->table_element) { + $this->error='update_note was called on objet with property table_element not defined'; dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR); return -1; } if (! in_array($suffix,array('','_public','_private'))) { + $this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\''; dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR); return -2; } @@ -2721,7 +2788,7 @@ abstract class CommonObject * @param string $targettype Object target type (if not defined, elemennt name of object) * @param string $clause 'OR' or 'AND' clause used when both source id and target id are provided * @param int $alsosametype 0=Return only links to object that differs from source. 1=Include also link to objects of same type. - * @return void + * @return int <0 if KO, >0 if OK * @see add_object_linked, updateObjectLinked, deleteObjectLinked */ function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1) @@ -2781,7 +2848,6 @@ abstract class CommonObject $sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')"; } $sql .= ' ORDER BY sourcetype'; - //print $sql; dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG); $resql = $this->db->query($sql); @@ -2902,10 +2968,12 @@ abstract class CommonObject } } } + return 1; } else { dol_print_error($this->db); + return -1; } } @@ -3024,9 +3092,10 @@ abstract class CommonObject * @param int $status Status to set * @param int $elementId Id of element to force (use this->id by default) * @param string $elementType Type of element to force (use this->table_element by default) + * @param string $trigkey Trigger key to use for trigger * @return int <0 if KO, >0 if OK */ - function setStatut($status,$elementId=null,$elementType='') + function setStatut($status, $elementId=null, $elementType='', $trigkey='') { global $user,$langs,$conf; @@ -3038,7 +3107,9 @@ abstract class CommonObject $this->db->begin(); $fieldstatus="fk_statut"; + if ($elementTable == 'facture_rec') $fieldstatus="suspended"; if ($elementTable == 'mailing') $fieldstatus="statut"; + if ($elementTable == 'cronjob') $fieldstatus="status"; if ($elementTable == 'user') $fieldstatus="statut"; if ($elementTable == 'expensereport') $fieldstatus="fk_statut"; if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status"; @@ -3054,13 +3125,16 @@ abstract class CommonObject { $error = 0; - $trigkey=''; - if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class - if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class - if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class - if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE'; - if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED'; - if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED'; + // Try autoset of trigkey + if (empty($trigkey)) + { + if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class + if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class + if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class + if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE'; + if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED'; + if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED'; + } if ($trigkey) { @@ -3502,19 +3576,6 @@ abstract class CommonObject } - /** - * Return if a country is inside the EEC (European Economic Community) - * @deprecated Use function isInEEC function instead - * - * @return boolean true = country inside EEC, false = country outside EEC - */ - function isInEEC() - { - require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; - return isInEEC($this); - } - - // -------------------- // TODO: All functions here must be redesigned and moved as they are not business functions but output functions // -------------------- @@ -3563,7 +3624,7 @@ abstract class CommonObject * Return HTML table for object lines * TODO Move this into an output class file (htmlline.class.php) * If lines are into a template, title must also be into a template - * But for the moment we don't know if it'st possible as we keep a method available on overloaded objects. + * But for the moment we don't know if it's possible as we keep a method available on overloaded objects. * * @param string $action Action code * @param string $seller Object of seller third party @@ -3661,6 +3722,14 @@ abstract class CommonObject print '
'; + print ''; + print ''; + print '
'.$labeltoshow.'
'; + + if ($nbphoto % $nbbyrow == 1) $return.= ''; + $return.= ''; + if (($nbphoto % $nbbyrow) == 0) $return.= ''; + } + else if ($nbbyrow < 0) $return.=''; + } + + if (empty($size)) { // Format origine + $return.= ''; + + if ($showfilename) $return.= '
'.$viewfilename; + if ($showaction) + { + // Special case for product + if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer)) + { + // Link to resize + $return.= ''.img_picto($langs->trans("Resize"), 'resize', '').'   '; + + // Link to delete + $return.= ''; + $return.= img_delete().''; + } + } + } + + // On continue ou on arrete de boucler ? + if ($nbmax && $nbphoto >= $nbmax) break; + } + } + + if ($size==1 || $size=='small') + { + if ($nbbyrow > 0) + { + // Ferme tableau + while ($nbphoto % $nbbyrow) + { + $return.= ''; + $nbphoto++; + } + + if ($nbphoto) $return.= '
'; + } + else if ($nbbyrow < 0) $return .= '
'; + + $return.= "\n"; + + $relativefile=preg_replace('/^\//', '', $pdir.$photo); + if (empty($nolink)) + { + $urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity); + if ($urladvanced) $return.=''; + else $return.= ''; + } + + // Show image (width height=$maxHeight) + // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine + $alt=$langs->transnoentitiesnoconv('File').': '.$relativefile; + $alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height']; + if ($notitle) $alt=''; + + if ($usesharelink) + { + if ($val['share']) + { + if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight) + { + $return.= ''; + $return.= ''; + } + else { + $return.= ''; + $return.= ''; + } + } + else + { + $return.= ''; + $return.= ''; + } + } + else + { + if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight) + { + $return.= ''; + $return.= ''; + } + else { + $return.= ''; + $return.= ''; + } + } + + if (empty($nolink)) $return.= ''; + $return.="\n"; + + if ($showfilename) $return.= '
'.$viewfilename; + if ($showaction) + { + $return.= '
'; + // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites + if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight)) + { + $return.= ''.img_picto($langs->trans('GenerateThumb'),'refresh').'  '; + } + // Special cas for product + if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer)) + { + // Link to resize + $return.= ''.img_picto($langs->trans("Resize"), 'resize', '').'   '; + + // Link to delete + $return.= ''; + $return.= img_delete().''; + } + } + $return.= "\n"; + + if ($nbbyrow > 0) + { + $return.= '
 
'; + } + } + } + + $this->nbphoto = $nbphoto; + + return $return; + } /** @@ -6110,7 +6477,6 @@ abstract class CommonObject else return false; } - /** * Function test if type is date * @@ -6133,7 +6499,7 @@ abstract class CommonObject { if(is_array($info)) { - if(isset($info['type']) && ($info['type']=='int' || $info['type']=='integer' )) return true; + if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i',$info['type']) ) ) return true; else return false; } else return false; @@ -6193,7 +6559,7 @@ abstract class CommonObject * * @return array */ - private function set_save_query() + protected function setSaveQuery() { global $conf; @@ -6247,7 +6613,7 @@ abstract class CommonObject * * @param stdClass $obj Contain data of object from database */ - private function setVarsFromFetchObj(&$obj) + protected function setVarsFromFetchObj(&$obj) { foreach ($this->fields as $field => $info) { @@ -6292,7 +6658,7 @@ abstract class CommonObject * * @return string */ - private function get_field_list() + protected function getFieldList() { $keys = array_keys($this->fields); return implode(',', $keys); @@ -6327,7 +6693,7 @@ abstract class CommonObject $now=dol_now(); - $fieldvalues = $this->set_save_query(); + $fieldvalues = $this->setSaveQuery(); if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now); if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id; unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert. @@ -6411,19 +6777,21 @@ abstract class CommonObject /** * Load object in memory from the database * - * @param int $id Id object - * @param string $ref Ref - * @return int <0 if KO, 0 if not found, >0 if OK + * @param int $id Id object + * @param string $ref Ref + * @param string $morewhere More SQL filters (' AND ...') + * @return int <0 if KO, 0 if not found, >0 if OK */ - public function fetchCommon($id, $ref = null) + public function fetchCommon($id, $ref = null, $morewhere = '') { if (empty($id) && empty($ref)) return false; - $sql = 'SELECT '.$this->get_field_list(); + $sql = 'SELECT '.$this->getFieldList(); $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element; - if(!empty($id)) $sql.= ' WHERE rowid = '.$id; + if (!empty($id)) $sql.= ' WHERE rowid = '.$id; else $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']); + if ($morewhere) $sql.=$morewhere; $res = $this->db->query($sql); if ($res) @@ -6462,7 +6830,7 @@ abstract class CommonObject $now=dol_now(); - $fieldvalues = $this->set_save_query(); + $fieldvalues = $this->setSaveQuery(); if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now); if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id; unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update. @@ -6605,6 +6973,9 @@ abstract class CommonObject // TODO... } + + /* Part for comments */ + /** * Load comments linked with current task * @return boolean 1 if ok @@ -6614,8 +6985,14 @@ abstract class CommonObject require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php'; $comment = new Comment($this->db); - $this->comments = Comment::fetchAllFor($this->element, $this->id); - return 1; + $result=$comment->fetchAllFor($this->element, $this->id); + if ($result<0) { + $this->errors=array_merge($this->errors,$comment->errors); + return -1; + } else { + $this->comments = $comment->comments; + } + return count($this->comments); } /** diff --git a/htdocs/core/class/commonorder.class.php b/htdocs/core/class/commonorder.class.php index 8cacf7065b1..b3e5033330d 100644 --- a/htdocs/core/class/commonorder.class.php +++ b/htdocs/core/class/commonorder.class.php @@ -73,7 +73,7 @@ abstract class CommonOrderLine extends CommonObjectLine /** * Quantity - * @var int + * @var float */ public $qty; diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index a1d051440c3..c4ccd6fe982 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -310,7 +310,7 @@ class Conf // For user storage $this->user->multidir_output = array($this->entity => $rootfordata."/users"); - $this->user->multidir_temp = array($this->entity => $rootfordata."/users/temp"); + $this->user->multidir_temp = array($this->entity => $rootfordata."/users/temp"); // For backward compatibility $this->user->dir_output=$rootforuser."/users"; $this->user->dir_temp=$rootforuser."/users/temp"; @@ -320,6 +320,9 @@ class Conf $this->usergroup->dir_temp=$rootforuser."/usergroups/temp"; // For propal storage + $this->propal->multidir_output = array($this->entity => $rootfordata."/propale"); + $this->propal->multidir_temp = array($this->entity => $rootfordata."/propale/temp"); + // For backward compatibility $this->propal->dir_output=$rootfordata."/propale"; $this->propal->dir_temp=$rootfordata."/propale/temp"; @@ -422,7 +425,7 @@ class Conf if (empty($this->global->MAIN_MONNAIE)) $this->global->MAIN_MONNAIE='EUR'; $this->currency=$this->global->MAIN_MONNAIE; - if (empty($conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY)) $conf->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY = 30; // Less than 1 minutes to be sure + if (empty($this->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY)) $this->global->MAIN_BROWSER_NOTIFICATION_FREQUENCY = 30; // Less than 1 minutes to be sure // conf->global->ACCOUNTING_MODE = Option des modules Comptabilites (simple ou expert). Defini le mode de calcul des etats comptables (CA,...) if (empty($this->global->ACCOUNTING_MODE)) $this->global->ACCOUNTING_MODE='RECETTES-DEPENSES'; // By default. Can be 'RECETTES-DEPENSES' ou 'CREANCES-DETTES' @@ -482,7 +485,7 @@ class Conf // Default pdf option if (! isset($this->global->MAIN_PDF_DASH_BETWEEN_LINES)) $this->global->MAIN_PDF_DASH_BETWEEN_LINES=1; // use dash between lines - if (! isset($this->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) $this->global->PDF_ALLOW_HTML_FOR_FREE_TEXT=1; // allow html content into free footer text + if (! isset($this->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) $this->global->PDF_ALLOW_HTML_FOR_FREE_TEXT=1; // allow html content into free footer text // Set default value to MAIN_SHOW_LOGO if (! isset($this->global->MAIN_SHOW_LOGO)) $this->global->MAIN_SHOW_LOGO=1; @@ -502,6 +505,10 @@ class Conf // Define list of limited modules (value must be key found for "name" property of module, so for example 'supplierproposal' for Module "Supplier Proposal" if (! isset($this->global->MAIN_MODULES_FOR_EXTERNAL)) $this->global->MAIN_MODULES_FOR_EXTERNAL='user,societe,propal,commande,facture,categorie,supplierproposal,fournisseur,contact,projet,contrat,ficheinter,expedition,agenda,resource,adherent,blockedlog'; // '' means 'all'. Note that contact is added here as it should be a module later. + // Module part to include an external module into the MAIN_MODULES_FOR_EXTERNAL list + if (! empty($this->modules_parts['moduleforexternal'])) + foreach($this->modules_parts['moduleforexternal'] as $key=>$value) $this->global->MAIN_MODULES_FOR_EXTERNAL.=",$key"; + // Enable select2 if (empty($this->global->MAIN_USE_JQUERY_MULTISELECT) || $this->global->MAIN_USE_JQUERY_MULTISELECT == '1') $this->global->MAIN_USE_JQUERY_MULTISELECT='select2'; @@ -581,13 +588,15 @@ class Conf if (! isset($this->global->THEME_HIDE_BORDER_ON_INPUT)) $this->global->THEME_HIDE_BORDER_ON_INPUT=0; // Save inconsistent option - if (empty($conf->global->AGENDA_USE_EVENT_TYPE) && (! isset($conf->global->AGENDA_DEFAULT_FILTER_TYPE) || $conf->global->AGENDA_DEFAULT_FILTER_TYPE == 'AC_NON_AUTO')) + if (empty($this->global->AGENDA_USE_EVENT_TYPE) && (! isset($this->global->AGENDA_DEFAULT_FILTER_TYPE) || $this->global->AGENDA_DEFAULT_FILTER_TYPE == 'AC_NON_AUTO')) { - $conf->global->AGENDA_DEFAULT_FILTER_TYPE='0'; // 'AC_NON_AUTO' does not exists when AGENDA_DEFAULT_FILTER_TYPE is not on. + $this->global->AGENDA_DEFAULT_FILTER_TYPE='0'; // 'AC_NON_AUTO' does not exists when AGENDA_DEFAULT_FILTER_TYPE is not on. } - $conf->global->MAIN_MODULE_DOLISTORE_API_SRV='https://www.dolistore.com'; - $conf->global->MAIN_MODULE_DOLISTORE_API_KEY='dolistorecatalogpublickey1234567'; + if (! isset($this->global->MAIN_EXTRAFIELDS_IN_ONE_TD)) $this->global->MAIN_EXTRAFIELDS_IN_ONE_TD = 1; + + $this->global->MAIN_MODULE_DOLISTORE_API_SRV='https://www.dolistore.com'; + $this->global->MAIN_MODULE_DOLISTORE_API_KEY='dolistorecatalogpublickey1234567'; // For backward compatibility if (isset($this->product)) $this->produit=$this->product; diff --git a/htdocs/core/class/discount.class.php b/htdocs/core/class/discount.class.php index 37dedea8b6b..5cb7d3e6882 100644 --- a/htdocs/core/class/discount.class.php +++ b/htdocs/core/class/discount.class.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2009 Laurent Destailleur + * Copyright (C) 2004-2018 Laurent Destailleur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +24,7 @@ /** - * \class DiscountAbsolute - * \brief Class to manage absolute discounts + * Class to manage absolute discounts */ class DiscountAbsolute { @@ -34,6 +33,7 @@ class DiscountAbsolute public $id; // Id discount public $fk_soc; + public $discount_type; // 0 => customer discount, 1 => supplier discount public $amount_ht; // public $amount_tva; // public $amount_ttc; // @@ -45,6 +45,7 @@ class DiscountAbsolute public $fk_facture; // Id invoice when a discount line is used into an invoice (for credit note) public $fk_facture_source; // Id facture avoir a l'origine de la remise public $ref_facture_source; // Ref facture avoir a l'origine de la remise + public $ref_invoice_supplier_source; /** * Constructor @@ -60,33 +61,36 @@ class DiscountAbsolute /** * Load object from database into memory * - * @param int $rowid id discount to load - * @param int $fk_facture_source fk_facture_source - * @return int <0 if KO, =0 if not found, >0 if OK + * @param int $rowid id discount to load + * @param int $fk_facture_source fk_facture_source + * @param int $fk_invoice_supplier_source fk_invoice_supplier_source + * @return int <0 if KO, =0 if not found, >0 if OK */ - function fetch($rowid, $fk_facture_source=0) + function fetch($rowid, $fk_facture_source=0, $fk_invoice_supplier_source=0) { global $conf; // Check parameters - if (! $rowid && ! $fk_facture_source) + if (! $rowid && ! $fk_facture_source && ! $fk_invoice_supplier_source) { $this->error='ErrorBadParameters'; return -1; } - $sql = "SELECT sr.rowid, sr.fk_soc,"; + $sql = "SELECT sr.rowid, sr.fk_soc, sr.discount_type,"; $sql.= " sr.fk_user,"; $sql.= " sr.amount_ht, sr.amount_tva, sr.amount_ttc, sr.tva_tx,"; $sql.= " sr.multicurrency_amount_ht, sr.multicurrency_amount_tva, sr.multicurrency_amount_ttc,"; - $sql.= " sr.fk_facture_line, sr.fk_facture, sr.fk_facture_source, sr.description,"; + $sql.= " sr.fk_facture_line, sr.fk_facture, sr.fk_facture_source, sr.fk_invoice_supplier_line, sr.fk_invoice_supplier, sr.fk_invoice_supplier_source, sr.description,"; $sql.= " sr.datec,"; - $sql.= " f.facnumber as ref_facture_source"; + $sql.= " f.facnumber as ref_facture_source, fsup.facnumber as ref_invoice_supplier_source"; $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise_except as sr"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON sr.fk_facture_source = f.rowid"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as fsup ON sr.fk_invoice_supplier_source = fsup.rowid"; $sql.= " WHERE sr.entity = " . $conf->entity; if ($rowid) $sql.= " AND sr.rowid=".$rowid; if ($fk_facture_source) $sql.= " AND sr.fk_facture_source=".$fk_facture_source; + if ($fk_invoice_supplier_source) $sql.= " AND sr.fk_invoice_supplier_source=".$fk_invoice_supplier_source; dol_syslog(get_class($this)."::fetch", LOG_DEBUG); $resql = $this->db->query($sql); @@ -98,6 +102,7 @@ class DiscountAbsolute $this->id = $obj->rowid; $this->fk_soc = $obj->fk_soc; + $this->discount_type = $obj->discount_type; $this->amount_ht = $obj->amount_ht; $this->amount_tva = $obj->amount_tva; @@ -113,6 +118,10 @@ class DiscountAbsolute $this->fk_facture = $obj->fk_facture; $this->fk_facture_source = $obj->fk_facture_source; // Id avoir source $this->ref_facture_source = $obj->ref_facture_source; // Ref avoir source + $this->fk_invoice_supplier_line = $obj->fk_invoice_supplier_line; + $this->fk_invoice_supplier = $obj->fk_invoice_supplier; + $this->fk_invoice_supplier_source = $obj->fk_invoice_supplier_source; // Id avoir source + $this->ref_invoice_supplier_source = $obj->ref_invoice_supplier_source; // Ref avoir source $this->description = $obj->description; $this->datec = $this->db->jdate($obj->datec); @@ -159,13 +168,14 @@ class DiscountAbsolute // Insert request $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_remise_except"; - $sql.= " (entity, datec, fk_soc, fk_user, description,"; + $sql.= " (entity, datec, fk_soc, discount_type, fk_user, description,"; $sql.= " amount_ht, amount_tva, amount_ttc, tva_tx,"; - $sql.= " fk_facture_source"; + $sql.= " fk_facture_source, fk_invoice_supplier_source"; $sql.= ")"; - $sql.= " VALUES (".$conf->entity.", '".$this->db->idate($this->datec!=''?$this->datec:dol_now())."', ".$this->fk_soc.", ".$user->id.", '".$this->db->escape($this->description)."',"; + $sql.= " VALUES (".$conf->entity.", '".$this->db->idate($this->datec!=''?$this->datec:dol_now())."', ".$this->fk_soc.", ".(empty($this->discount_type)?0:intval($this->discount_type)).", ".$user->id.", '".$this->db->escape($this->description)."',"; $sql.= " ".$this->amount_ht.", ".$this->amount_tva.", ".$this->amount_ttc.", ".$this->tva_tx.","; - $sql.= " ".($this->fk_facture_source ? "'".$this->db->escape($this->fk_facture_source)."'":"null"); + $sql.= " ".($this->fk_facture_source ? "'".$this->db->escape($this->fk_facture_source)."'":"null").","; + $sql.= " ".($this->fk_invoice_supplier_source ? "'".$this->db->escape($this->fk_invoice_supplier_source)."'":"null"); $sql.= ")"; dol_syslog(get_class($this)."::create", LOG_DEBUG); @@ -221,14 +231,45 @@ class DiscountAbsolute } } + // Check if we can remove the discount + if ($this->fk_invoice_supplier_source) + { + $sql="SELECT COUNT(rowid) as nb"; + $sql.=" FROM ".MAIN_DB_PREFIX."societe_remise_except"; + $sql.=" WHERE (fk_invoice_supplier_line IS NOT NULL"; // Not used as absolute simple discount + $sql.=" OR fk_invoice_supplier IS NOT NULL)"; // Not used as credit note and not used as deposit + $sql.=" AND fk_invoice_supplier_source = ".$this->fk_invoice_supplier_source; + //$sql.=" AND rowid != ".$this->id; + + dol_syslog(get_class($this)."::delete Check if we can remove discount", LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + $obj = $this->db->fetch_object($resql); + if ($obj->nb > 0) + { + $this->error='ErrorThisPartOrAnotherIsAlreadyUsedSoDiscountSerieCantBeRemoved'; + return -2; + } + } + else + { + dol_print_error($this->db); + return -1; + } + } + $this->db->begin(); // Delete but only if not used $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_remise_except "; if ($this->fk_facture_source) $sql.= " WHERE fk_facture_source = ".$this->fk_facture_source; // Delete all lines of same serie + elseif ($this->fk_invoice_supplier_source) $sql.= " WHERE fk_invoice_supplier_source = ".$this->fk_invoice_supplier_source; // Delete all lines of same serie else $sql.= " WHERE rowid = ".$this->id; // Delete only line $sql.= " AND (fk_facture_line IS NULL"; // Not used as absolute simple discount $sql.= " AND fk_facture IS NULL)"; // Not used as credit note and not used as deposit + $sql.= " AND (fk_invoice_supplier_line IS NULL"; // Not used as absolute simple discount + $sql.= " AND fk_invoice_supplier IS NULL)"; // Not used as credit note and not used as deposit dol_syslog(get_class($this)."::delete Delete discount", LOG_DEBUG); $result=$this->db->query($sql); @@ -255,6 +296,26 @@ class DiscountAbsolute return -1; } } + elseif($this->fk_invoice_supplier_source) { + + $sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn"; + $sql.=" set paye=0, fk_statut=1"; + $sql.=" WHERE (type = 2 or type = 3) AND rowid=".$this->fk_invoice_supplier_source; + + dol_syslog(get_class($this)."::delete Update credit note or deposit invoice statut", LOG_DEBUG); + $result=$this->db->query($sql); + if ($result) + { + $this->db->commit(); + return 1; + } + else + { + $this->error=$this->db->lasterror(); + $this->db->rollback(); + return -1; + } + } else { $this->db->commit(); @@ -295,16 +356,26 @@ class DiscountAbsolute } $sql ="UPDATE ".MAIN_DB_PREFIX."societe_remise_except"; - if ($rowidline) $sql.=" SET fk_facture_line = ".$rowidline; - if ($rowidinvoice) $sql.=" SET fk_facture = ".$rowidinvoice; + if(! empty($this->discount_type)) { + if ($rowidline) $sql.=" SET fk_invoice_supplier_line = ".$rowidline; + if ($rowidinvoice) $sql.=" SET fk_invoice_supplier = ".$rowidinvoice; + } else { + if ($rowidline) $sql.=" SET fk_facture_line = ".$rowidline; + if ($rowidinvoice) $sql.=" SET fk_facture = ".$rowidinvoice; + } $sql.=" WHERE rowid = ".$this->id; dol_syslog(get_class($this)."::link_to_invoice", LOG_DEBUG); $resql = $this->db->query($sql); if ($resql) { - $this->fk_facture_line=$rowidline; - $this->fk_facture=$rowidinvoice; + if(! empty($this->discount_type)) { + $this->fk_invoice_supplier_line=$rowidline; + $this->fk_invoice_supplier=$rowidinvoice; + } else { + $this->fk_facture_line=$rowidline; + $this->fk_facture=$rowidinvoice; + } return 1; } else @@ -324,7 +395,11 @@ class DiscountAbsolute function unlink_invoice() { $sql ="UPDATE ".MAIN_DB_PREFIX."societe_remise_except"; - $sql.=" SET fk_facture_line = NULL, fk_facture = NULL"; + if(! empty($this->discount_type)) { + $sql.=" SET fk_invoice_supplier_line = NULL, fk_invoice_supplier = NULL"; + } else { + $sql.=" SET fk_facture_line = NULL, fk_facture = NULL"; + } $sql.=" WHERE rowid = ".$this->id; dol_syslog(get_class($this)."::unlink_invoice", LOG_DEBUG); @@ -344,14 +419,14 @@ class DiscountAbsolute /** * Return amount (with tax) of discounts currently available for a company, user or other criteria * - * @param Societe $company Object third party for filter - * @param User $user Filtre sur un user auteur des remises - * @param string $filter Filtre autre - * @param int $maxvalue Filter on max value for discount - * @param string $mode 'customer' = discounts the customer has, 'supplier' = discount i have at this supplier + * @param Societe $company Object third party for filter + * @param User $user Filtre sur un user auteur des remises + * @param string $filter Filtre autre + * @param int $maxvalue Filter on max value for discount + * @param int $discount_type 0 => customer discount, 1 => supplier discount * @return int <0 if KO, amount otherwise */ - function getAvailableDiscounts($company='', $user='',$filter='', $maxvalue=0, $mode='customer') + function getAvailableDiscounts($company='', $user='',$filter='', $maxvalue=0, $discount_type=0) { global $conf; @@ -359,8 +434,12 @@ class DiscountAbsolute //$sql = "SELECT rc.amount_ttc as amount"; $sql.= " FROM ".MAIN_DB_PREFIX."societe_remise_except as rc"; $sql.= " WHERE rc.entity = " . $conf->entity; - if ($mode != 'supplier') $sql.= " AND (rc.fk_facture IS NULL AND rc.fk_facture_line IS NULL)"; // Available - else $sql.= " AND (rc.fk_suppler_invoice IS NULL AND rc.fk_supplier_invoice IS NULL)"; // Available + $sql.= " AND rc.discount_type=".intval($discount_type); + if (! empty($discount_type)) { + $sql.= " AND (rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_line IS NULL)"; // Available from supplier + } else { + $sql.= " AND (rc.fk_facture IS NULL AND rc.fk_facture_line IS NULL)"; // Available to customer + } if (is_object($company)) $sql.= " AND rc.fk_soc = ".$company->id; if (is_object($user)) $sql.= " AND rc.fk_user = ".$user->id; if ($filter) $sql.=' AND ('.$filter.')'; @@ -452,7 +531,7 @@ class DiscountAbsolute $sql = 'SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount'; $sql.= ' FROM '.MAIN_DB_PREFIX.'societe_remise_except as rc, '.MAIN_DB_PREFIX.'facture_fourn as f'; $sql.= ' WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = '.$invoice->id; - $sql.= ' AND (f.type = 2 OR f.type = 0)'; // Find discount coming from credit note or excess received + $sql.= ' AND (f.type = 2 OR f.type = 0)'; // Find discount coming from credit note or excess paid } else { @@ -489,10 +568,12 @@ class DiscountAbsolute $result=''; if ($option == 'invoice') { + $facid=! empty($this->discount_type)?$this->fk_invoice_supplier_source:$this->fk_facture_source; + $link=! empty($this->discount_type)?'/fourn/facture/card.php':'/compta/facture/card.php'; $label=$langs->trans("ShowDiscount").': '.$this->ref_facture_source; - $link = ''; + $link = ''; $linkend=''; - $ref=$this->ref_facture_source; + $ref=! empty($this->discount_type)?$this->ref_invoice_supplier_source:$this->ref_facture_source; $picto='bill'; } if ($option == 'discount') { diff --git a/htdocs/core/class/extrafields.class.php b/htdocs/core/class/extrafields.class.php index ee165ac6808..bf9bcf587f8 100644 --- a/htdocs/core/class/extrafields.class.php +++ b/htdocs/core/class/extrafields.class.php @@ -82,6 +82,7 @@ class ExtraFields public static $type2label=array( 'varchar'=>'String', 'text'=>'TextLong', + 'html'=>'HtmlText', 'int'=>'Int', 'double'=>'Float', 'date'=>'Date', @@ -133,7 +134,7 @@ class ExtraFields * * @param string $attrname Code of attribute * @param string $label label of attribute - * @param int $type Type of attribute ('boolean', 'int', 'text', 'varchar', 'date', 'datehour','price','phone','mail','password','url','select','checkbox', ...) + * @param int $type Type of attribute ('boolean','int','varchar','text','html','date','datehour','price','phone','mail','password','url','select','checkbox','separate',...) * @param int $pos Position of attribute * @param string $size Size/length of attribute * @param string $elementtype Element type ('member', 'product', 'thirdparty', ...) @@ -189,7 +190,7 @@ class ExtraFields * This is a private method. For public method, use addExtraField. * * @param string $attrname code of attribute - * @param int $type Type of attribute ('boolean', 'int', 'text', 'varchar', 'date', 'datehour','price','phone','mail','password','url','select','checkbox', ...) + * @param int $type Type of attribute ('boolean', 'int', 'varchar', 'text', 'html', 'date', 'datehour','price','phone','mail','password','url','select','checkbox', ...) * @param string $length Size/length of attribute ('5', '24,8', ...) * @param string $elementtype Element type ('member', 'product', 'thirdparty', 'contact', ...) * @param int $unique Is field unique or not @@ -232,9 +233,12 @@ class ExtraFields } elseif ($type=='link') { $typedb='int'; $lengthdb='11'; + } elseif ($type=='html') { + $typedb='text'; + $lengthdb=$length; } elseif($type=='password') { $typedb='varchar'; - $lengthdb='50'; + $lengthdb='128'; } else { $typedb=$type; $lengthdb=$length; @@ -275,7 +279,7 @@ class ExtraFields * * @param string $attrname code of attribute * @param string $label label of attribute - * @param int $type Type of attribute ('int', 'text', 'varchar', 'date', 'datehour', 'float') + * @param int $type Type of attribute ('int', 'varchar', 'text', 'html', 'date', 'datehour', 'float') * @param int $pos Position of attribute * @param string $size Size/length of attribute ('5', '24,8', ...) * @param string $elementtype Element type ('member', 'product', 'thirdparty', ...) @@ -483,7 +487,7 @@ class ExtraFields * * @param string $attrname Name of attribute * @param string $label Label of attribute - * @param string $type Type of attribute ('boolean', 'int', 'text', 'varchar', 'date', 'datehour','price','phone','mail','password','url','select','checkbox', ...) + * @param string $type Type of attribute ('boolean', 'int', 'varchar', 'text', 'html', 'date', 'datehour','price','phone','mail','password','url','select','checkbox', ...) * @param int $length Length of attribute * @param string $elementtype Element type ('member', 'product', 'thirdparty', 'contact', ...) * @param int $unique Is field unique or not @@ -529,6 +533,8 @@ class ExtraFields } elseif (($type=='select') || ($type=='sellist') || ($type=='radio') || ($type=='checkbox') || ($type=='chkbxlst')) { $typedb='text'; $lengthdb=''; + } elseif ($type == 'html') { + $typedb='text'; } elseif ($type=='link') { $typedb='int'; $lengthdb='11'; @@ -733,11 +739,26 @@ class ExtraFields // To avoid conflicts with external modules. TODO Remove this. if (!$forceload && !empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return $array_name_label; + // Set array of label of entity + // TODO Remove completely loading of label. This should be done by presentation. + $labelmulticompany=array(); + if (!empty($conf->multicompany->enabled)) + { + $sql_entity_name='SELECT rowid, label FROM '.MAIN_DB_PREFIX.'entity WHERE rowid in (0,'.$conf->entity.')'; + $resql_entity_name=$this->db->query($sql_entity_name); + if ($resql_entity_name) + { + while ($obj = $this->db->fetch_object($resql_entity_name)) + { + $labelmulticompany[$obj->rowid]=$obj->label; + } + } + } + // We should not have several time this log. If we have, there is some optimization to do by calling a simple $object->fetch_optionals() that include cache management. dol_syslog("fetch_name_optionals_label elementtype=".$elementtype); - $sql = "SELECT rowid,name,label,type,size,elementtype,fieldunique,fieldrequired,param,pos,alwayseditable,perms,langs,list,fielddefault,fieldcomputed"; - $sql .= ",entity"; + $sql = "SELECT rowid,name,label,type,size,elementtype,fieldunique,fieldrequired,param,pos,alwayseditable,perms,langs,list,fielddefault,fieldcomputed,entity"; $sql.= " FROM ".MAIN_DB_PREFIX."extrafields"; $sql.= " WHERE entity IN (0,".$conf->entity.")"; if ($elementtype) $sql.= " AND elementtype = '".$elementtype."'"; @@ -772,6 +793,7 @@ class ExtraFields $this->attribute_langfile[$tab->name]=$tab->langs; $this->attribute_list[$tab->name]=$tab->list; $this->attribute_entityid[$tab->name]=$tab->entity; + $this->attribute_entitylabel[$tab->name]=(empty($labelmulticompany[$tab->entity])?'Entity'.$tab->entity:$labelmulticompany[$tab->entity]); // New usage $this->attributes[$tab->elementtype]['type'][$tab->name]=$tab->type; @@ -789,26 +811,12 @@ class ExtraFields $this->attributes[$tab->elementtype]['langfile'][$tab->name]=$tab->langs; $this->attributes[$tab->elementtype]['list'][$tab->name]=$tab->list; $this->attributes[$tab->elementtype]['entityid'][$tab->name]=$tab->entity; + $this->attributes[$tab->elementtype]['entitylabel'][$tab->name]=(empty($labelmulticompany[$tab->entity])?'Entity'.$tab->entity:$labelmulticompany[$tab->entity]); - if (!empty($conf->multicompany->enabled)) - { - $sql_entity_name='SELECT label FROM '.MAIN_DB_PREFIX.'entity WHERE rowid='.$tab->entity; - $resql_entity_name=$this->db->query($sql_entity_name); - if ($resql_entity_name) - { - if ($this->db->num_rows($resql_entity_name)) - { - if ($obj = $this->db->fetch_object($resql_entity_name)) - { - $this->attribute_entitylabel[$tab->name]=$obj->label; - $this->attributes[$tab->elementtype]['entitylabel'][$tab->name]=$obj->label; - } - } - } - } + $this->attributes[$tab->elementtype]['loaded']=1; } } - if ($elementtype) $this->attributes[$elementtype]['loaded']=1; + if ($elementtype) $this->attributes[$elementtype]['loaded']=1; // If nothing found, we also save tag 'loaded' } else { @@ -933,6 +941,19 @@ class ExtraFields $out=''; } elseif ($type == 'text') + { + if (! preg_match('/search_/', $keyprefix)) // If keyprefix is search_ or search_options_, we must just use a simple text field + { + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,false,ROWS_5,'90%'); + $out=$doleditor->Create(1); + } + else + { + $out=''; + } + } + elseif ($type == 'html') { if (! preg_match('/search_/', $keyprefix)) // If keyprefix is search_ or search_options_, we must just use a simple text field { @@ -1373,7 +1394,7 @@ class ExtraFields $list=$this->attributes[$extrafieldsobjectkey]['list'][$key]; $hidden=(($list == 0) ? 1 : 0); // If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller) } - else + else // Old usage { $elementtype=$this->attribute_elementtype[$key]; // seems not used $label=$this->attribute_label[$key]; @@ -1646,6 +1667,10 @@ class ExtraFields { $value=dol_htmlentitiesbr($value); } + elseif ($type == 'html') + { + $value=dol_htmlentitiesbr($value); + } elseif ($type == 'password') { $value=preg_replace('/./i','*',$value); @@ -1718,7 +1743,7 @@ class ExtraFields */ function showSeparator($key) { - $out = '
'.$this->attribute_label[$key].'
'.$this->attribute_label[$key].'
'; - if ($htmlname && GETPOST('action','aZ09') != 'edit'.$htmlname && $perm) $ret.='id.$moreparam.'">'.img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)).''; + if ($htmlname && GETPOST('action','aZ09') != 'edit'.$htmlname && $perm) $ret.='id.$moreparam.'">'.img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)).''; if (! empty($notabletag) && $notabletag == 1) $ret.=' : '; if (! empty($notabletag) && $notabletag == 3) $ret.=' '; if (empty($notabletag) && GETPOST('action','aZ09') != 'edit'.$htmlname && $perm) $ret.='
'; + $ret.=''; + if (empty($notabletag)) $ret.='
'; if (empty($notabletag)) $ret.=''; $addcolumforpicto=($delallowed || $printer || $morepicto); - $out.= ''; } else $out = $hookmanager->resPrint; // Replace line @@ -807,7 +811,7 @@ class FormFile if (count($file_list) == 0 && count($link_list) == 0 && $headershown) { - $out.=''."\n"; + $out.=''."\n"; } } @@ -847,9 +851,28 @@ class FormFile $out=''; $this->infofiles=array('nboffiles'=>0,'extensions'=>array(),'files'=>array()); - $filterforfilesearch = preg_quote(basename($modulesubdir),'/').'[^\-]+'; + // Get object entity + if (empty($conf->multicompany->enabled)) + { + $entity = $conf->entity; + } + else + { + preg_match('/\/([0-9]+)\/[^\/]+\/'.preg_quote($modulesubdir).'$/', $filedir, $regs); + $entity = ((! empty($regs[1]) && $regs[1] > 1) ? $regs[1] : $conf->entity); + } - $file_list=dol_dir_list($filedir, 'files', 0, $filterforfilesearch, '\.meta$|\.png$'); // Get list of files starting with name of ref (but not followed by "-" to discard uploaded files) + // Get list of files starting with name of ref (but not followed by "-" to discard uploaded files and get only generated files) + // @TODO Why not showing by default all files by just removing the '[^\-]+' at end of regex ? + if (! empty($conf->global->MAIN_SHOW_ALL_FILES_ON_DOCUMENT_TOOLTIP)) + { + $filterforfilesearch = preg_quote(basename($modulesubdir),'/'); + } + else + { + $filterforfilesearch = preg_quote(basename($modulesubdir),'/').'[^\-]+'; + } + $file_list=dol_dir_list($filedir, 'files', 0, $filterforfilesearch, '\.meta$|\.png$'); // We also discard .meta and .png preview //var_dump($file_list); // For ajax treatment @@ -900,7 +923,7 @@ class FormFile } // Download - $tmpout.= '
  • 0) { if ($permtoeditline) { // Link to resize - print ''.img_picto($langs->trans("Resize"),DOL_URL_ROOT.'/theme/common/transform-crop-and-resize','class="paddingrightonly"',1).''; + print ''.img_picto($langs->trans("ResizeOrCrop"),'resize','class="paddingrightonly"').''; } } diff --git a/htdocs/core/class/html.formmail.class.php b/htdocs/core/class/html.formmail.class.php index 6e25c1ca730..b9043626c51 100644 --- a/htdocs/core/class/html.formmail.class.php +++ b/htdocs/core/class/html.formmail.class.php @@ -2,7 +2,7 @@ /* Copyright (C) 2005-2012 Laurent Destailleur * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2010-2011 Juanjo Menent - * Copyright (C) 2015 Marcos García + * Copyright (C) 2015-2017 Marcos García * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -148,16 +148,19 @@ class FormMail extends Form * Add a file into the list of attached files (stored in SECTION array) * * @param string $path Full absolute path on filesystem of file, including file name - * @param string $file Only filename - * @param string $type Mime type + * @param string $file Only filename (can be basename($path)) + * @param string $type Mime type (can be dol_mimetype($file)) * @return void */ - function add_attached_files($path,$file,$type) + function add_attached_files($path, $file='', $type='') { $listofpaths=array(); $listofnames=array(); $listofmimes=array(); + if (empty($file)) $file=basename($path); + if (empty($type)) $type=dol_mimetype($file); + $keytoavoidconflict = empty($this->trackid)?'':'-'.$this->trackid; // this->trackid must be defined if (! empty($_SESSION["listofpaths".$keytoavoidconflict])) $listofpaths=explode(';',$_SESSION["listofpaths".$keytoavoidconflict]); if (! empty($_SESSION["listofnames".$keytoavoidconflict])) $listofnames=explode(';',$_SESSION["listofnames".$keytoavoidconflict]); @@ -237,13 +240,13 @@ class FormMail extends Form * Get the form to input an email * this->withfile: 0=No attaches files, 1=Show attached files, 2=Can add new attached files * this->withfile - * this->param: Contains more parameteres like email templates info + * this->param: Contains more parameters like email templates info * * @param string $addfileaction Name of action when posting file attachments * @param string $removefileaction Name of action when removing file attachments * @return string Form to show */ - function get_form($addfileaction='addfile',$removefileaction='removefile') + function get_form($addfileaction='addfile', $removefileaction='removefile') { global $conf, $langs, $user, $hookmanager, $form; @@ -252,6 +255,14 @@ class FormMail extends Form $langs->load("other"); $langs->load("mails"); + + // Clear temp files. Must be done at beginning, before call of triggers + if (GETPOST('mode','alpha') == 'init' || (GETPOST('modelmailselected','alpha') && GETPOST('modelmailselected','alpha') != '-1')) + { + $this->clear_attached_files(); + } + + // Call hook getFormMail $hookmanager->initHooks(array('formmail')); $parameters=array( @@ -283,7 +294,7 @@ class FormMail extends Form } // Get message template for $this->param["models"] into c_email_templates - $arraydefaultmessage=array(); + $arraydefaultmessage = -1; if ($this->param['models'] != 'none') { $model_id=0; @@ -291,12 +302,10 @@ class FormMail extends Form { $model_id=$this->param["models_id"]; } - $arraydefaultmessage=$this->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, ($model_id ? $model_id : -1)); // we set -1 if model_id empty - } - //var_dump($this->param["models"]); - //var_dump($model_id); - //var_dump($arraydefaultmessage); + // we set -1 if model_id empty + $arraydefaultmessage = $this->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, ($model_id ? $model_id : -1)); + } // Define list of attached files $listofpaths=array(); @@ -306,8 +315,7 @@ class FormMail extends Form if (GETPOST('mode','alpha') == 'init' || (GETPOST('modelmailselected','alpha') && GETPOST('modelmailselected','alpha') != '-1')) { - $this->clear_attached_files(); - if (! empty($arraydefaultmessage['joinfiles']) && is_array($this->param['fileinit'])) + if (! empty($arraydefaultmessage->joinfiles) && is_array($this->param['fileinit'])) { foreach($this->param['fileinit'] as $file) { @@ -399,20 +407,21 @@ class FormMail extends Form - $out.= '
  • '; if (preg_match('/^(string|email)/',$typeofdata)) { @@ -1070,11 +1072,11 @@ class Form // On recherche les societes $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.client, s.fournisseur, s.code_client, s.code_fournisseur"; $sql.= " FROM ".MAIN_DB_PREFIX ."societe as s"; - if (!$user->rights->societe->client->voir && !$user->societe_id) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; $sql.= " WHERE s.entity IN (".getEntity('societe').")"; - if (! empty($user->societe_id)) $sql.= " AND s.rowid = ".$user->societe_id; + if (! empty($user->socid)) $sql.= " AND s.rowid = ".$user->socid; if ($filter) $sql.= " AND (".$filter.")"; - if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id; + if (!$user->rights->societe->client->voir && !$user->socid) $sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id; if (! empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) $sql.= " AND s.status <> 0"; // Add criteria if ($filterkey && $filterkey != '') @@ -1236,6 +1238,7 @@ class Form if (preg_match('/\(CREDIT_NOTE\)/', $desc)) $desc=preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc); if (preg_match('/\(DEPOSIT\)/', $desc)) $desc=preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc); if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) $desc=preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc); + if (preg_match('/\(EXCESS PAID\)/', $desc)) $desc=preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc); $selectstring=''; if ($selected > 0 && $selected == $obj->rowid) $selectstring=' selected'; @@ -1444,7 +1447,7 @@ class Form /** * Return select list of users * - * @param string $selected User id or user object of user preselected. If -1, we use id of current user. + * @param string $selected User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed) * @param string $htmlname Field name in form * @param int $show_empty 0=list with no empty value, 1=add also an empty value into list * @param array $exclude Array list of users id to exclude @@ -1454,15 +1457,16 @@ class Form * @param int $force_entity 0 or Id of environment to force * @param int $maxlength Maximum length of string into list (0=no limit) * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status - * @param string $morefilter Add more filters into sql request + * @param string $morefilter Add more filters into sql request (Example: 'employee = 1') * @param integer $show_every 0=default list, 1=add also a value "Everybody" at beginning of list * @param string $enableonlytext If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty. * @param string $morecss More css * @param int $noactive Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on). + * @param int $outputmode 0=HTML select string, 1=Array * @return string HTML select string * @see select_dolgroups */ - function select_dolusers($selected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity=0, $maxlength=0, $showstatus=0, $morefilter='', $show_every=0, $enableonlytext='', $morecss='', $noactive=0) + function select_dolusers($selected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity=0, $maxlength=0, $showstatus=0, $morefilter='', $show_every=0, $enableonlytext='', $morecss='', $noactive=0, $outputmode=0) { global $conf,$user,$langs; @@ -1488,6 +1492,7 @@ class Form } $out=''; + $outarray = array(); // Forge request to select users $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut, u.login, u.admin, u.entity"; @@ -1527,7 +1532,6 @@ class Form $sql.= " ORDER BY u.lastname ASC"; } - dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) @@ -1616,6 +1620,7 @@ class Form $out.=' - '.$disableline; // This is text from $enableonlytext parameter } $out.= ''; + $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength); $i++; } @@ -1632,6 +1637,7 @@ class Form dol_print_error($this->db); } + if ($outputmode) return $outarray; return $out; } @@ -1697,7 +1703,10 @@ class Form $userstatic->fetch($value['id']); $out.= $userstatic->getNomUrl(-1); if ($i == 0) { $ownerid = $value['id']; $out.=' ('.$langs->trans("Owner").')'; } - if ($nbassignetouser > 1 && $action != 'view') $out.=' '; + if ($nbassignetouser > 1 && $action != 'view') + { + $out.=' '; + } // Show my availability if ($showproperties) { @@ -2481,6 +2490,10 @@ class Form $opt = '
    '; + $out.= ''; // Model if (! empty($modellist)) @@ -699,7 +702,8 @@ class FormFile $out.= ''."\n"; // Show title of array if not already shown - if ((! empty($file_list) || ! empty($link_list) || preg_match('/^massfilesarea/', $modulepart)) && ! $headershown) + if ((! empty($file_list) || ! empty($link_list) || preg_match('/^massfilesarea/', $modulepart)) + && ! $headershown) { $headershown=1; $out.= '
    '.$titletoshow.'
    '."\n"; @@ -744,14 +748,14 @@ class FormFile if ($delallowed || $printer || $morepicto) { - $out.= '
    '; + $out.= ''; if ($delallowed) { $out.= ''.img_picto($langs->trans("Delete"), 'delete.png').''; + $out.= '">'.img_picto($langs->trans("Delete"), 'delete').''; } if ($printer) { @@ -774,7 +778,7 @@ class FormFile $res = $hookmanager->executeHooks('formBuilddocLineOptions',$parameters,$file); if (empty($res)) { - $out .= $hookmanager->resPrint; // Complete line + $out.= $hookmanager->resPrint; // Complete line $out.= '
    '.$langs->trans("None").'
    '.$langs->trans("None").'
    '."\n"; + $out.= '
    '."\n"; - // Substitution array + // Substitution array/string + $helpforsubstitution=''; + if (is_array($this->substit) && count($this->substit)) $helpforsubstitution.=$langs->trans('AvailableVariables').' :
    '."\n"; + foreach($this->substit as $key => $val) + { + $helpforsubstitution.=$key.' -> '.$langs->trans(dol_string_nohtmltag($val)).'
    '; + } if (! empty($this->withsubstit)) // Unset or set ->withsubstit=0 to disable this. { $out.= '\n"; //$out.=''; } @@ -755,14 +764,19 @@ class FormMail extends Form $defaulttopic=GETPOST('subject','none'); if (! GETPOST('modelselected','alpha') || GETPOST('modelmailselected') != '-1') { - if (is_array($arraydefaultmessage) && count($arraydefaultmessage) > 0 && $arraydefaultmessage['topic']) $defaulttopic=$arraydefaultmessage['topic']; - elseif (! is_numeric($this->withtopic)) $defaulttopic=$this->withtopic; + if ($arraydefaultmessage && $arraydefaultmessage->topic) { + $defaulttopic = $arraydefaultmessage->topic; + } elseif (! is_numeric($this->withtopic)) { + $defaulttopic = $this->withtopic; + } } $defaulttopic=make_substitutions($defaulttopic,$this->substit); $out.= ''; - $out.= ''; + $out.= ''; $out.= ''; - $out.= ''; + $out.= ''; $out.= ''; if (empty($strictw3c)) print ''; print "\n"; - $var=true; $listofparam=array(); - foreach($tableau as $const) // Loop on each param + foreach($tableau as $key => $const) // Loop on each param { + // $const is a const key like 'MYMODULE_ABC' + if (is_numeric($key)) { + $type = 'string'; + } + else + { + $type = $const; + $const = $key; + } + $sql = "SELECT "; $sql.= "rowid"; $sql.= ", ".$db->decrypt('name')." as name"; @@ -1353,7 +1365,7 @@ function form_constantes($tableau, $strictw3c=0, $helptext='') $sql.= ", type"; $sql.= ", note"; $sql.= " FROM ".MAIN_DB_PREFIX."const"; - $sql.= " WHERE ".$db->decrypt('name')." = '".$const."'"; + $sql.= " WHERE ".$db->decrypt('name')." = '".$db->escape($const)."'"; $sql.= " AND entity IN (0, ".$conf->entity.")"; $sql.= " ORDER BY name ASC, entity DESC"; $result = $db->query($sql); @@ -1363,23 +1375,26 @@ function form_constantes($tableau, $strictw3c=0, $helptext='') { $obj = $db->fetch_object($result); // Take first result of select - - // For avoid warning in strict mode - if (empty($obj)) { - $obj = (object) array('rowid'=>'','name'=>'','value'=>'','type'=>'','note'=>''); + if (empty($obj)) // If not yet into table + { + $obj = (object) array('rowid'=>'','name'=>$const,'value'=>'','type'=>$type,'note'=>''); } - if (empty($strictw3c)) print "\n".''; + if (empty($strictw3c)) + { + print "\n".''; + print ''; + } print ''; // Show constant print ''; } else { print ''; } @@ -1500,7 +1538,7 @@ function showModulesExludedForExternal($modules) //if (empty($conf->global->$moduleconst)) continue; if (! in_array($modulename,$listofmodules)) continue; - //var_dump($modulename.'eee'.$langs->trans('Module'.$module->numero.'Name')); + //var_dump($modulename.' - '.$langs->trans('Module'.$module->numero.'Name')); if ($i > 0) $text.=', '; else $text.=' '; @@ -1610,6 +1648,33 @@ function phpinfo_array() return $info_arr; } +/** + * Return array head with list of tabs to view object informations. + * + * @return array head array with tabs + */ +function company_admin_prepare_head() +{ + global $langs, $conf, $user; + + $h = 0; + $head = array(); + + $head[$h][0] = DOL_URL_ROOT."/admin/company.php"; + $head[$h][1] = $langs->trans("Company"); + $head[$h][2] = 'company'; + $h++; + + $head[$h][0] = DOL_URL_ROOT."/admin/accountant.php"; + $head[$h][1] = $langs->trans("Accountant"); + $head[$h][2] = 'accountant'; + $h++; + + complete_head_from_modules($conf,$langs,null,$head,$h,'company_admin','remove'); + + return $head; +} + /** * Return array head with list of tabs to view object informations. * diff --git a/htdocs/core/lib/assets.lib.php b/htdocs/core/lib/assets.lib.php new file mode 100644 index 00000000000..cc9ea922854 --- /dev/null +++ b/htdocs/core/lib/assets.lib.php @@ -0,0 +1,120 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file core/lib/assets.lib.php + * \ingroup assets + * \brief Library files with common functions for Assets + */ + +/** + * Prepare admin pages header + * + * @return array + */ +function AssetsAdminPrepareHead() +{ + global $langs, $conf; + + $langs->load("assets"); + + $h = 0; + $head = array(); + + $head[$h][0] = DOL_URL_ROOT . '/assets/admin/setup.php'; + $head[$h][1] = $langs->trans("Settings"); + $head[$h][2] = 'settings'; + $h++; + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + //$this->tabs = array( + // 'entity:+tabname:Title:@assets:/assets/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@assets:/assets/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'assets_admin'); + + $head[$h][0] = DOL_URL_ROOT . '/assets/admin/assets_extrafields.php'; + $head[$h][1] = $langs->trans("ExtraFields"); + $head[$h][2] = 'attributes'; + $h++; + + $head[$h][0] = DOL_URL_ROOT . '/assets/admin/assets_type_extrafields.php'; + $head[$h][1] = $langs->trans("ExtraFieldsAssetsType"); + $head[$h][2] = 'attributes_type'; + $h++; + + complete_head_from_modules($conf, $langs, $object, $head, $h, 'assets_admin', 'remove'); + + return $head; +} + +function AssetsPrepareHead() +{ + global $langs, $conf; + + $langs->load("assets"); + + $h = 0; + $head = array(); + + $head[$h][0] = DOL_URL_ROOT . '/assets/card.php'; + $head[$h][1] = $langs->trans("Card"); + $head[$h][2] = 'card'; + $h++; + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + //$this->tabs = array( + // 'entity:+tabname:Title:@assets:/assets/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@assets:/assets/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'assets'); + + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; + $upload_dir = $conf->assets->dir_output . '/' . get_exdir($filename,2,0,1,$object,'assets'). '/'. dol_sanitizeFileName($object->ref); + $nbFiles = count(dol_dir_list($upload_dir,'files',0,'','(\.meta|_preview.*\.png)$')); + $nbLinks=Link::count($db, $object->element, $object->id); + $head[$h][0] = DOL_URL_ROOT.'/assets/document.php?id='.$object->id; + $head[$h][1] = $langs->trans('Documents'); + if (($nbFiles+$nbLinks) > 0) $head[$h][1].= ' '.($nbFiles+$nbLinks).''; + $head[$h][2] = 'documents'; + $h++; + + $nbNote = 0; + if(!empty($object->note_private)) $nbNote++; + if(!empty($object->note_public)) $nbNote++; + $head[$h][0] = DOL_URL_ROOT.'/assets/note.php?id='.$object->id; + $head[$h][1] = $langs->trans("Notes"); + if ($nbNote > 0) $head[$h][1].= ' '.$nbNote.''; + $head[$h][2] = 'note'; + $h++; + + $head[$h][0] = DOL_URL_ROOT . '/assets/info.php?id=' . $object->id; + $head[$h][1] = $langs->trans("Info"); + $head[$h][2] = 'info'; + $h++; + + complete_head_from_modules($conf, $langs, $object, $head, $h, 'assets', 'remove'); + + return $head; +} diff --git a/htdocs/core/lib/company.lib.php b/htdocs/core/lib/company.lib.php index 3f7c45dda52..320b8cc1b63 100644 --- a/htdocs/core/lib/company.lib.php +++ b/htdocs/core/lib/company.lib.php @@ -9,6 +9,7 @@ * Copyright (C) 2013 Alexandre Spangaro * Copyright (C) 2015 Frederic France * Copyright (C) 2015 Raphaël Doursenaud + * Copyright (C) 2017 Rui Strecht * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -167,7 +168,7 @@ function societe_prepare_head(Societe $object) } // Related items - if (! empty($conf->commande->enabled) || ! empty($conf->propal->enabled) || ! empty($conf->facture->enabled) || ! empty($conf->fichinter->enabled) || ! empty($conf->fournisseur->enabled)) + if (! empty($conf->commande->enabled) || ! empty($conf->propal->enabled) || ! empty($conf->facture->enabled) || ! empty($conf->ficheinter->enabled) || ! empty($conf->fournisseur->enabled)) { $head[$h][0] = DOL_URL_ROOT.'/societe/consumption.php?socid='.$object->id; $head[$h][1] = $langs->trans("Referers"); @@ -175,14 +176,28 @@ function societe_prepare_head(Societe $object) $h++; } - // Bank accounrs + // Bank accounts if (empty($conf->global->SOCIETE_DISABLE_BANKACCOUNT)) { - $langs->load("banks"); + $nbBankAccount=0; + $foundonexternalonlinesystem=0; + $langs->load("banks"); + + $title = $langs->trans("BankAccounts"); + if (! empty($conf->stripe->enabled)) + { + $langs->load("stripe"); + $title = $langs->trans("BankAccountsAndGateways"); + + $servicestatus = 0; + if (! empty($conf->global->STRIPE_LIVE) && ! GETPOST('forcesandbox','alpha')) $servicestatus = 1; + + include_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php'; + $societeaccount = new SocieteAccount($db); + $stripecu = $societeaccount->getCustomerAccount($object->id, 'stripe', $servicestatus); // Get thirdparty cu_... + if ($stripecu) $foundonexternalonlinesystem++; + } - $nbBankAccount=0; - $head[$h][0] = DOL_URL_ROOT .'/societe/rib.php?socid='.$object->id; - $head[$h][1] = $langs->trans("BankAccounts"); $sql = "SELECT COUNT(n.rowid) as nb"; $sql.= " FROM ".MAIN_DB_PREFIX."societe_rib as n"; $sql.= " WHERE fk_soc = ".$object->id; @@ -201,7 +216,13 @@ function societe_prepare_head(Societe $object) else { dol_print_error($db); } - if ($nbBankAccount > 0) $head[$h][1].= ' '.$nbBankAccount.''; + + //if (! empty($conf->stripe->enabled) && $nbBankAccount > 0) $nbBankAccount = '...'; // No way to know exact number + + $head[$h][0] = DOL_URL_ROOT .'/societe/paymentmodes.php?socid='.$object->id; + $head[$h][1] = $title; + if ($foundonexternalonlinesystem) $head[$h][1].= ' ...'; + elseif ($nbBankAccount > 0) $head[$h][1].= ' '.$nbBankAccount.''; $head[$h][2] = 'rib'; $h++; } @@ -453,22 +474,29 @@ function getCountry($searchkey, $withcode='', $dbtouse=0, $outputlangs='', $entc /** * Return state translated from an id. Return value is always utf8 encoded and without entities. * - * @param int $id id of state (province/departement) + * @param int $id id of state (province/departement) * @param int $withcode '0'=Return label, * '1'=Return string code + label, * '2'=Return code, * 'all'=return array('id'=>,'code'=>,'label'=>) * @param DoliDB $dbtouse Database handler (using in global way may fail because of conflicts with some autoload features) - * @return string String with state code or state name (Return value is always utf8 encoded and without entities) + * @param int $withregion '0'=Ignores region, + * '1'=Add region name/code/id as needed to output, + * @param Translate $outputlangs Langs object for output translation, not fully implemented yet + * @param int $entconv 0=Return value without entities and not converted to output charset, 1=Ready for html output + * @return mixed String with state code or state name or Array('id','code','label')/Array('id','code','label','region_code','region') */ -function getState($id,$withcode='',$dbtouse=0) +function getState($id,$withcode='',$dbtouse=0,$withregion=0,$outputlangs='',$entconv=1) { global $db,$langs; if (! is_object($dbtouse)) $dbtouse=$db; - $sql = "SELECT rowid, code_departement as code, nom as label FROM ".MAIN_DB_PREFIX."c_departements"; - $sql.= " WHERE rowid=".$id; + $sql = "SELECT d.rowid as id, d.code_departement as code, d.nom as name, d.active, c.label as country, c.code as country_code, r.code_region as region_code, r.nom as region_name FROM"; + $sql .= " ".MAIN_DB_PREFIX ."c_departements as d, ".MAIN_DB_PREFIX."c_regions as r,".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE d.fk_region=r.code_region and r.fk_pays=c.rowid and d.rowid=".$id; + $sql .= " AND d.active = 1 AND r.active = 1 AND c.active = 1"; + $sql .= " ORDER BY c.code, d.code_departement"; dol_syslog("Company.lib::getState", LOG_DEBUG); $resql=$dbtouse->query($sql); @@ -477,11 +505,46 @@ function getState($id,$withcode='',$dbtouse=0) $obj = $dbtouse->fetch_object($resql); if ($obj) { - $label=$obj->label; - if ($withcode == '1') return $label=$obj->code?"$obj->code":"$obj->code - $label"; - else if ($withcode == '2') return $label=$obj->code; - else if ($withcode == 'all') return array('id'=>$obj->rowid,'code'=>$obj->code,'label'=>$label); - else return $label; + $label=((! empty($obj->name) && $obj->name!='-')?$obj->name:''); + if (is_object($outputlangs)) + { + $outputlangs->load("dict"); + if ($entconv) $label=($obj->code && ($outputlangs->trans("State".$obj->code)!="State".$obj->code))?$outputlangs->trans("State".$obj->code):$label; + else $label=($obj->code && ($outputlangs->transnoentitiesnoconv("State".$obj->code)!="State".$obj->code))?$outputlangs->transnoentitiesnoconv("State".$obj->code):$label; + } + + if ($withcode == 1) { + if ($withregion == 1) { + return $label = $obj->region_name . ' - ' . $obj->code . ' - ' . ($langs->trans($obj->code)!=$obj->code?$langs->trans($obj->code):($obj->name!='-'?$obj->name:'')); + } + else { + return $label = $obj->code . ' - ' . ($langs->trans($obj->code)!=$obj->code?$langs->trans($obj->code):($obj->name!='-'?$obj->name:'')); + } + } + else if ($withcode == 2) { + if ($withregion == 1) { + return $label = $obj->region_name . ' - ' . ($langs->trans($obj->code)!=$obj->code?$langs->trans($obj->code):($obj->name!='-'?$obj->name:'')); + } + else { + return $label = ($langs->trans($obj->code)!=$obj->code?$langs->trans($obj->code):($obj->name!='-'?$obj->name:'')); + } + } + else if ($withcode === 'all') { + if ($withregion == 1) { + return array('id'=>$obj->id,'code'=>$obj->code,'label'=>$label,'region_code'=>$obj->region_code,'region'=>$obj->region_name); + } + else { + return array('id'=>$obj->id,'code'=>$obj->code,'label'=>$label); + } + } + else { + if ($withregion == 1) { + return $label = $obj->region_name . ' - ' . $label; + } + else { + return $label; + } + } } else { @@ -572,9 +635,58 @@ function getFormeJuridiqueLabel($code) } } + +/** + * Return list of countries that are inside the EEC (European Economic Community) + * TODO Add a field into country dictionary. + * + * @return array Array of countries code in EEC + */ +function getCountriesInEEC() +{ + // List of all country codes that are in europe for european vat rules + // List found on http://ec.europa.eu/taxation_customs/common/faq/faq_1179_en.htm#9 + $country_code_in_EEC=array( + 'AT', // Austria + 'BE', // Belgium + 'BG', // Bulgaria + 'CY', // Cyprus + 'CZ', // Czech republic + 'DE', // Germany + 'DK', // Danemark + 'EE', // Estonia + 'ES', // Spain + 'FI', // Finland + 'FR', // France + 'GB', // United Kingdom + 'GR', // Greece + 'HR', // Croatia + 'NL', // Holland + 'HU', // Hungary + 'IE', // Ireland + 'IM', // Isle of Man - Included in UK + 'IT', // Italy + 'LT', // Lithuania + 'LU', // Luxembourg + 'LV', // Latvia + 'MC', // Monaco - Included in France + 'MT', // Malta + //'NO', // Norway + 'PL', // Poland + 'PT', // Portugal + 'RO', // Romania + 'SE', // Sweden + 'SK', // Slovakia + 'SI', // Slovenia + 'UK', // United Kingdom + //'CH', // Switzerland - No. Swizerland in not in EEC + ); + + return $country_code_in_EEC; +} + /** * Return if a country of an object is inside the EEC (European Economic Community) - * TODO Add a field into country dictionary. * * @param Object $object Object * @return boolean true = country inside EEC, false = country outside EEC @@ -583,43 +695,8 @@ function isInEEC($object) { if (empty($object->country_code)) return false; - // List of all country codes that are in europe for european vat rules - // List found on http://ec.europa.eu/taxation_customs/common/faq/faq_1179_en.htm#9 - $country_code_in_EEC=array( - 'AT', // Austria - 'BE', // Belgium - 'BG', // Bulgaria - 'CY', // Cyprus - 'CZ', // Czech republic - 'DE', // Germany - 'DK', // Danemark - 'EE', // Estonia - 'ES', // Spain - 'FI', // Finland - 'FR', // France - 'GB', // United Kingdom - 'GR', // Greece - 'HR', // Croatia - 'NL', // Holland - 'HU', // Hungary - 'IE', // Ireland - 'IM', // Isle of Man - Included in UK - 'IT', // Italy - 'LT', // Lithuania - 'LU', // Luxembourg - 'LV', // Latvia - 'MC', // Monaco - Included in France - 'MT', // Malta - //'NO', // Norway - 'PL', // Poland - 'PT', // Portugal - 'RO', // Romania - 'SE', // Sweden - 'SK', // Slovakia - 'SI', // Slovenia - 'UK', // United Kingdom - //'CH', // Switzerland - No. Swizerland in not in EEC - ); + $country_code_in_EEC = getCountriesInEEC(); + //print "dd".$this->country_code; return in_array($object->country_code, $country_code_in_EEC); } @@ -772,6 +849,7 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='') $form = new Form($db); + $optioncss = GETPOST('optioncss', 'alpha'); $sortfield = GETPOST("sortfield",'alpha'); $sortorder = GETPOST("sortorder",'alpha'); $page = GETPOST('page','int'); @@ -823,7 +901,7 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='') { if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); } - + $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); // Purge search criteria if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers @@ -870,9 +948,12 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='') print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table print "\n".'
    '; //$out.='
    '; - $help=""; - foreach($this->substit as $key => $val) - { - $help.=$key.' -> '.$langs->trans(dol_string_nohtmltag($val)).'
    '; - } - if (is_numeric($this->withsubstit)) $out.= $form->textwithpicto($langs->trans("EMailTestSubstitutionReplacedByGenericValues"), $help, 1, 'help', '', 0, 2, 'substittooltip'); // Old usage - else $out.= $form->textwithpicto($langs->trans('AvailableVariables'), $help, 1, 'help', '', 0, 2, 'substittooltip'); // New usage + if (is_numeric($this->withsubstit)) $out.= $form->textwithpicto($langs->trans("EMailTestSubstitutionReplacedByGenericValues"), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltip'); // Old usage + else $out.= $form->textwithpicto($langs->trans('AvailableVariables'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltip'); // New usage $out.= "
    '.$langs->trans("MailTopic").''; + $out.=$form->textwithpicto($langs->trans('MailTopic'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfromtopic'); + $out.=''; if ($this->withtopicreadonly) { @@ -847,11 +861,14 @@ class FormMail extends Form $defaultmessage=GETPOST('message','none'); if (! GETPOST('modelselected','alpha') || GETPOST('modelmailselected') != '-1') { - if (count($arraydefaultmessage) > 0 && $arraydefaultmessage['content']) $defaultmessage=$arraydefaultmessage['content']; - elseif (! is_numeric($this->withbody)) $defaultmessage=$this->withbody; + if ($arraydefaultmessage && $arraydefaultmessage->content) { + $defaultmessage = $arraydefaultmessage->content; + } elseif (! is_numeric($this->withbody)) { + $defaultmessage = $this->withbody; + } } - // Complete substitution array + // Complete substitution array with the url to make online payment $paymenturl=''; if (empty($this->substit['__REF__'])) { @@ -863,18 +880,18 @@ class FormMail extends Form require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; $langs->load('paypal'); $typeforonlinepayment='free'; - if ($this->param["models"]=='order_send') $typeforonlinepayment='order'; // TODO use detection on something else than template - if ($this->param["models"]=='facture_send') $typeforonlinepayment='invoice'; // TODO use detection on something else than template - if ($this->param["models"]=='member_send') $typeforonlinepayment='member'; // TODO use detection on something else than template + if ($this->param["models"]=='order' || $this->param["models"]=='order_send') $typeforonlinepayment='order'; // TODO use detection on something else than template + if ($this->param["models"]=='invoice' || $this->param["models"]=='facture_send') $typeforonlinepayment='invoice'; // TODO use detection on something else than template + if ($this->param["models"]=='member') $typeforonlinepayment='member'; // TODO use detection on something else than template $url=getOnlinePaymentUrl(0, $typeforonlinepayment, $this->substit['__REF__']); - $paymenturl=$url; + $paymenturl=$url; } - + $this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__']=($paymenturl?$langs->trans("PredefinedMailContentLink", $paymenturl):''); $this->substit['__ONLINE_PAYMENT_URL__']=$paymenturl; //Add lines substitution key from each line $lines = ''; - $defaultlines = $arraydefaultmessage['content_lines']; + $defaultlines = $arraydefaultmessage->content_lines; if (isset($defaultlines)) { foreach ($this->substit_lines as $substit_line) @@ -903,7 +920,9 @@ class FormMail extends Form } $out.= '
    '.$langs->trans("MailText").''; + $out.=$form->textwithpicto($langs->trans('MailText'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody'); + $out.=''; if ($this->withbodyreadonly) { @@ -980,44 +999,58 @@ class FormMail extends Form * * @param DoliDB $db Database handler * @param string $type_template Get message for type=$type_template, type='all' also included. - * @param string $user Use template public or limited to this user + * @param string $user Get template public or limited to this user * @param Translate $outputlangs Output lang object - * @param int $id Id of template to find, or -1 for first found with position = 0, or 0 for all + * @param int $id Id of template to find, or -1 for first found with position 0, or 0 for first found whatever is position (priority order depends on lang provided or not) or -2 for exact match with label (no answer if not found) * @param int $active 1=Only active template, 0=Only disabled, -1=All - * @return array array('topic'=>,'content'=>,..) + * @param string $label Label of template + * @return ModelMail One instance of ModelMail */ - public function getEMailTemplate($db, $type_template, $user, $outputlangs, $id=0, $active=1) + public function getEMailTemplate($db, $type_template, $user, $outputlangs, $id=0, $active=1, $label='') { - $ret=array(); + $ret = new ModelMail(); - $sql = "SELECT label, topic, joinfiles, content, content_lines, lang"; + if ($id == -2 && empty($label)) + { + $this->error = 'LabelIsMandatoryWhenIdIs-2'; + return -1; + } + + $sql = "SELECT rowid, label, topic, joinfiles, content, content_lines, lang"; $sql.= " FROM ".MAIN_DB_PREFIX.'c_email_templates'; $sql.= " WHERE (type_template='".$db->escape($type_template)."' OR type_template='all')"; $sql.= " AND entity IN (".getEntity('c_email_templates').")"; $sql.= " AND (private = 0 OR fk_user = ".$user->id.")"; // Get all public or private owned if ($active >= 0) $sql.=" AND active = ".$active; - if (is_object($outputlangs)) $sql.= " AND (lang = '".$outputlangs->defaultlang."' OR lang IS NULL OR lang = '')"; + if ($label) $sql.=" AND label ='".$db->escape($label)."'"; + if (is_object($outputlangs)) $sql.= " AND (lang = '".$db->escape($outputlangs->defaultlang)."' OR lang IS NULL OR lang = '')"; if ($id > 0) $sql.= " AND rowid=".$id; if ($id == -1) $sql.= " AND position=0"; - $sql.= $db->order("position,lang,label","ASC"); - if ($id == -1) $sql.= $db->plimit(1); + if (is_object($outputlangs)) $sql.= $db->order("position,lang,label","ASC,DESC,ASC"); // We want line with lang set first, then with lang null or '' + else $sql.= $db->order("position,lang,label","ASC,ASC,ASC"); // If no language provided, we give priority to lang not defined + $sql.= $db->plimit(1); //print $sql; $resql = $db->query($sql); if ($resql) { - $obj = $db->fetch_object($resql); // Get first found - if ($obj) - { - $ret['label']=$obj->label; - $ret['lang']=$obj->lang; - $ret['topic']=$obj->topic; - $ret['joinfiles']=$obj->joinfiles; - $ret['content']=$obj->content; - $ret['content_lines']=$obj->content_lines; + // Get first found + $obj = $db->fetch_object($resql); + + if ($obj) { + $ret->id = $obj->rowid; + $ret->label = $obj->label; + $ret->lang = $obj->lang; + $ret->topic = $obj->topic; + $ret->content = $obj->content; + $ret->content_lines = $obj->content_lines; + $ret->joinfiles = $obj->joinfiles; } - else // If there is no template at all - { + elseif($id == -2) { + // Not found with the provided label + return -1; + } + else { // If there is no template at all $defaultmessage=''; if ($type_template=='facture_send') { $defaultmessage=$outputlangs->transnoentities("PredefinedMailContentSendInvoice"); } elseif ($type_template=='facture_relance') { $defaultmessage=$outputlangs->transnoentities("PredefinedMailContentSendInvoiceReminder"); } @@ -1031,12 +1064,12 @@ class FormMail extends Form elseif ($type_template=='thirdparty') { $defaultmessage=$outputlangs->transnoentities("PredefinedMailContentThirdparty"); } elseif ($type_template=='user') { $defaultmessage=$outputlangs->transnoentities("PredefinedMailContentUser"); } - $ret['label']='default'; - $ret['lang']=$outputlangs->defaultlang; - $ret['topic']=''; - $ret['joinfiles']=1; - $ret['content']=$defaultmessage; - $ret['content_lines']=''; + $ret->label = 'default'; + $ret->lang = $outputlangs->defaultlang; + $ret->topic = ''; + $ret->joinfiles = 1; + $ret->content = $defaultmessage; + $ret->content_lines =''; } $db->free($resql); @@ -1126,6 +1159,7 @@ class FormMail extends Form $line->topic=$obj->topic; $line->content=$obj->content; $line->content_lines=$obj->content_lines; + $this->lines_model[]=$line; } $this->db->free($resql); @@ -1297,4 +1331,5 @@ class ModelMail public $content; public $content_lines; public $lang; + public $joinfiles; } diff --git a/htdocs/core/class/html.formmargin.class.php b/htdocs/core/class/html.formmargin.class.php index 5f78fa0e283..da8acce83d4 100644 --- a/htdocs/core/class/html.formmargin.class.php +++ b/htdocs/core/class/html.formmargin.class.php @@ -85,8 +85,6 @@ class FormMargin $product = new ProductFournisseur($db); if ($product->fetch_product_fournisseur_price($line->fk_fournprice)) $line->pa_ht = $product->fourn_unitprice * (1 - $product->fourn_remise_percent / 100); - if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == "2" && $product->fourn_unitcharges > 0) - $line->pa_ht += $product->fourn_unitcharges; } // si prix d'achat non renseigné et devrait l'être, alors prix achat = prix vente if ((!isset($line->pa_ht) || $line->pa_ht == 0) && $line->subprice > 0 && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) { @@ -96,7 +94,7 @@ class FormMargin $pv = $line->qty * $line->subprice * (1 - $line->remise_percent / 100); $pa_ht = ($pv < 0 ? - $line->pa_ht : $line->pa_ht); // We choosed to have line->pa_ht always positive in database, so we guess the correct sign $pa = $line->qty * $pa_ht; - + // calcul des marges if (isset($line->fk_remise_except) && isset($conf->global->MARGIN_METHODE_FOR_DISCOUNT)) { // remise if ($conf->global->MARGIN_METHODE_FOR_DISCOUNT == '1') { // remise globale considérée comme produit @@ -214,7 +212,9 @@ class FormMargin if (!empty($hidemargininfos)) print ''; } + print '
    '; print ''."\n"; + print ''; print ''; print ''; @@ -273,6 +273,7 @@ class FormMargin print ''; } print '
    '.$langs->trans('Margins').'
    '; + print '
    '; } } diff --git a/htdocs/core/class/html.formother.class.php b/htdocs/core/class/html.formother.class.php index 3339b2b054e..3f07066bf2e 100644 --- a/htdocs/core/class/html.formother.class.php +++ b/htdocs/core/class/html.formother.class.php @@ -815,13 +815,14 @@ class FormOther /** * Return HTML combo list of month * - * @param string $selected Preselected value - * @param string $htmlname Name of HTML select object - * @param int $useempty Show empty in list - * @param int $longlabel Show long label + * @param string $selected Preselected value + * @param string $htmlname Name of HTML select object + * @param int $useempty Show empty in list + * @param int $longlabel Show long label + * @param string $morecss More Css * @return string */ - function select_month($selected='',$htmlname='monthid',$useempty=0,$longlabel=0) + function select_month($selected='', $htmlname='monthid', $useempty=0, $longlabel=0, $morecss='') { global $langs; @@ -830,7 +831,7 @@ class FormOther if ($longlabel) $montharray = monthArray($langs, 0); // Get array else $montharray = monthArray($langs, 1); - $select_month = ''; if ($useempty) { $select_month .= ''; @@ -863,11 +864,12 @@ class FormOther * @param int $offset Offset * @param int $invert Invert * @param string $option Option + * @param string $morecss More CSS * @return string */ - function select_year($selected='',$htmlname='yearid',$useempty=0, $min_year=10, $max_year=5, $offset=0, $invert=0, $option='') + function select_year($selected='',$htmlname='yearid',$useempty=0, $min_year=10, $max_year=5, $offset=0, $invert=0, $option='', $morecss='') { - print $this->selectyear($selected,$htmlname,$useempty,$min_year,$max_year,$offset,$invert,$option); + print $this->selectyear($selected,$htmlname,$useempty,$min_year,$max_year,$offset,$invert,$option,$morecss); } /** @@ -881,9 +883,10 @@ class FormOther * @param int $offset Offset * @param int $invert Invert * @param string $option Option + * @param string $morecss More css * @return string */ - function selectyear($selected='',$htmlname='yearid',$useempty=0, $min_year=10, $max_year=5, $offset=0, $invert=0, $option='') + function selectyear($selected='',$htmlname='yearid',$useempty=0, $min_year=10, $max_year=5, $offset=0, $invert=0, $option='', $morecss='') { $out=''; @@ -892,7 +895,7 @@ class FormOther $min_year = $currentyear-$min_year; if(empty($selected) && empty($useempty)) $selected = $currentyear; - $out.= ''; if($useempty) { $selected_html=''; diff --git a/htdocs/core/class/html.formprojet.class.php b/htdocs/core/class/html.formprojet.class.php index f1d407d3bba..a572d546ca7 100644 --- a/htdocs/core/class/html.formprojet.class.php +++ b/htdocs/core/class/html.formprojet.class.php @@ -91,7 +91,7 @@ class FormProjets } else { - $out.=$this->select_projects_list($socid, $selected, $htmlname, $maxlength, $option_only, $show_empty, $discard_closed, $forcefocus, $disabled, 0, $filterkey, 1, $forceaddid, $htmlid); + $out.=$this->select_projects_list($socid, $selected, $htmlname, $maxlength, $option_only, $show_empty, $discard_closed, $forcefocus, $disabled, 0, $filterkey, 1, $forceaddid, $htmlid, $morecss); } if ($discard_closed) { @@ -177,7 +177,7 @@ class FormProjets include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php'; $comboenhancement = ajax_combobox($htmlid, array(), 0, $forcefocus); $out.=$comboenhancement; - $morecss='minwidth100 maxwidth500'; + $morecss.=' minwidth100'; } if (empty($option_only)) { @@ -292,7 +292,7 @@ class FormProjets } /** - * Output a combo list with projects qualified for a third party + * Output a combo list with tasks qualified for a third party * * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) * @param int $selected Id task preselected @@ -304,9 +304,11 @@ class FormProjets * @param int $forcefocus Force focus on field (works with javascript only) * @param int $disabled Disabled * @param string $morecss More css added to the select component + * @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids. + * @param string $showproject 'all' = Show project info, ''=Hide project info * @return int Nbr of project if OK, <0 if KO */ - function selectTasks($socid=-1, $selected='', $htmlname='taskid', $maxlength=24, $option_only=0, $show_empty='1', $discard_closed=0, $forcefocus=0, $disabled=0, $morecss='maxwidth500') + function selectTasks($socid=-1, $selected='', $htmlname='taskid', $maxlength=24, $option_only=0, $show_empty='1', $discard_closed=0, $forcefocus=0, $disabled=0, $morecss='maxwidth500', $projectsListId='', $showproject='all') { global $user,$conf,$langs; @@ -317,11 +319,13 @@ class FormProjets $hideunselectables = false; if (! empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true; - $projectsListId = false; - if (empty($user->rights->projet->all->lire)) + if (empty($projectsListId)) { - $projectstatic=new Project($this->db); - $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user,0,1); + if (empty($user->rights->projet->all->lire)) + { + $projectstatic=new Project($this->db); + $projectsListId = $projectstatic->getProjectsAuthorizedForUser($user,0,1); + } } // Search all projects @@ -332,7 +336,7 @@ class FormProjets $sql.= ', '.MAIN_DB_PREFIX.'projet_task as t'; $sql.= " WHERE p.entity IN (".getEntity('project').")"; $sql.= " AND t.fk_projet = p.rowid"; - if ($projectsListId !== false) $sql.= " AND p.rowid IN (".$projectsListId.")"; + if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")"; if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)"; if ($socid > 0) $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)"; $sql.= " ORDER BY p.ref, t.ref ASC"; @@ -378,31 +382,38 @@ class FormProjets continue; } - $labeltoshow=dol_trunc($obj->ref,18); // Project ref - //if ($obj->public) $labeltoshow.=' ('.$langs->trans("SharedProject").')'; - //else $labeltoshow.=' ('.$langs->trans("Private").')'; - $labeltoshow.=' '.dol_trunc($obj->title,$maxlength); + $labeltoshow = ''; - if ($obj->name) $labeltoshow.=' ('.$obj->name.')'; + if ($showproject == 'all') + { + $labeltoshow.=dol_trunc($obj->ref,18); // Project ref + //if ($obj->public) $labeltoshow.=' ('.$langs->trans("SharedProject").')'; + //else $labeltoshow.=' ('.$langs->trans("Private").')'; + $labeltoshow.=' '.dol_trunc($obj->title,$maxlength); - $disabled=0; - if ($obj->fk_statut == 0) - { - $disabled=1; - $labeltoshow.=' - '.$langs->trans("Draft"); - } - else if ($obj->fk_statut == 2) - { - if ($discard_closed == 2) $disabled=1; - $labeltoshow.=' - '.$langs->trans("Closed"); - } - else if ($socid > 0 && (! empty($obj->fk_soc) && $obj->fk_soc != $socid)) - { - $disabled=1; - $labeltoshow.=' - '.$langs->trans("LinkedToAnotherCompany"); + if ($obj->name) $labeltoshow.=' ('.$obj->name.')'; + + $disabled=0; + if ($obj->fk_statut == 0) + { + $disabled=1; + $labeltoshow.=' - '.$langs->trans("Draft"); + } + else if ($obj->fk_statut == 2) + { + if ($discard_closed == 2) $disabled=1; + $labeltoshow.=' - '.$langs->trans("Closed"); + } + else if ($socid > 0 && (! empty($obj->fk_soc) && $obj->fk_soc != $socid)) + { + $disabled=1; + $labeltoshow.=' - '.$langs->trans("LinkedToAnotherCompany"); + } + $labeltoshow.=' - '; } + // Label for task - $labeltoshow.=' - '.$obj->tref.' '.dol_trunc($obj->tlabel,$maxlength); + $labeltoshow.=$obj->tref.' '.dol_trunc($obj->tlabel,$maxlength); if (!empty($selected) && $selected == $obj->rowid) { diff --git a/htdocs/core/class/html.formsms.class.php b/htdocs/core/class/html.formsms.class.php index 94e60ba8082..103b40e33df 100644 --- a/htdocs/core/class/html.formsms.class.php +++ b/htdocs/core/class/html.formsms.class.php @@ -205,8 +205,16 @@ function limitChars(textarea, limit, infodiv) try { $classname=ucfirst($classfile); - $sms = new $classname($this->db); - $resultsender = $sms->SmsSenderList(); + if (class_exists($classname)) + { + $sms = new $classname($this->db); + $resultsender = $sms->SmsSenderList(); + } + else + { + $sms = new stdClass(); + $sms->error='The SMS manager '.$classfile.' defined into SMS setup MAIN_SMS_SENDMODE is not found'; + } } catch(Exception $e) { diff --git a/htdocs/core/class/html.formticketsup.class.php b/htdocs/core/class/html.formticketsup.class.php new file mode 100644 index 00000000000..b86ebc95442 --- /dev/null +++ b/htdocs/core/class/html.formticketsup.class.php @@ -0,0 +1,1020 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/class/html.ticketsup.class.php + * \ingroup core + * \brief Fichier de la classe permettant la generation du formulaire html d'envoi de mail unitaire + */ +require_once DOL_DOCUMENT_ROOT . "/core/class/html.form.class.php"; +require_once DOL_DOCUMENT_ROOT . "/core/class/html.formmail.class.php"; + +if (!class_exists('FormCompany')) { + include DOL_DOCUMENT_ROOT . '/core/class/html.formcompany.class.php'; +} + +/** + * Classe permettant la generation du formulaire d'un nouveau ticket. + * + * @package Ticketsup + + * \remarks Utilisation: $formticketsup = new FormTicketsup($db) + * \remarks $formticketsup->proprietes=1 ou chaine ou tableau de valeurs + * \remarks $formticketsup->show_form() affiche le formulaire + */ +class FormTicketsup +{ + public $db; + + public $track_id; + public $fk_user_create; + + public $message; + public $topic_title; + + public $action; + + public $withtopic; + public $withemail; + /** + * + * @var int $withsubstit Show substitution array + */ + public $withsubstit; + + public $withfile; + + public $ispublic; // To show information or not into public form + + public $withtitletopic; + public $withcompany; // affiche liste déroulante company + public $withfromsocid; + public $withfromcontactid; + public $withnotnotifytiersatcreate; + public $withusercreate; // Show name of creating user in form + public $withcreatereadonly; + + public $withref; // Show ref field + + public $withcancel; + + /** + * + * @var array $substit Substitutions + */ + public $substit = array(); + public $param = array(); + + public $error; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + + $this->action = 'add_ticket'; + + $this->withcompany = 1; + $this->withfromsocid = 0; + $this->withfromcontactid = 0; + //$this->withthreadid=0; + //$this->withtitletopic=''; + $this->withnotnotifytiersatcreate = 0; + $this->withusercreate = 1; + $this->withcreatereadonly = 1; + $this->withemail = 0; + $this->withref = 0; + $this->withextrafields = 0; // Show extrafields or not + //$this->withtopicreadonly=0; + + return 1; + } + + /** + * Show the form to input ticket + * + * @param string $width Width of form + * @return void + */ + public function showForm($width = '100%') + { + global $conf, $langs, $user, $hookmanager; + + $langs->load("other"); + $langs->load("mails"); + $langs->load("ticketsup"); + + $form = new Form($this->db); + $formcompany = new FormCompany($this->db); + $ticketstatic = new Ticketsup($this->db); + + $soc = new Societe($this->db); + if (!empty($this->withfromsocid) && $this->withfromsocid > 0) { + $soc->fetch($this->withfromsocid); + } + + $ticketstat = new TicketSup($this->db); + + $extrafields = new ExtraFields($this->db); + $extralabels = $extrafields->fetch_name_optionals_label($ticketstat->table_element); + + print "\n\n"; + + print ''; + print ''; + print ''; + foreach ($this->param as $key => $value) { + print ''; + } + print ''; + + print '
    '; + print ''; + + + if ($this->withref) { + // Ref + $defaultref = $ticketstat->getDefaultRef(); + print ''; + } + + // FK_USER_CREATE + if ($this->withusercreate > 0 && $this->fk_user_create) { + print '\n"; + } + + // Customer or supplier + if ($this->withcompany) { + // altairis: force company and contact id for external user + if (empty($user->socid)) { + // Company + print ''; + if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) { + $htmlname = 'socid'; + print ''; + } + + // Contact and type + print ''; + } else { + print ''; + print ''; + print ''; + } + } + + // TITLE + if ($this->withemail) { + print ''; + } + + // Si origin du ticket + if (isset($this->param['origin']) && $this->param['originid'] > 0) { + // Parse element/subelement (ex: project_task) + $element = $subelement = $this->param['origin']; + if (preg_match('/^([^_]+)_([^_]+)/i', $this->param['origin'], $regs)) { + $element = $regs[1]; + $subelement = $regs[2]; + } + + dol_include_once('/' . $element . '/class/' . $subelement . '.class.php'); + $classname = ucfirst($subelement); + $objectsrc = new $classname($this->db); + $objectsrc->fetch(GETPOST('originid')); + + if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) { + $objectsrc->fetch_lines(); + } + + $objectsrc->fetch_thirdparty(); + $newclassname = $classname; + print ''; + } + + // Type + print ''; + + // Category + print ''; + + // Severity + print ''; + + // Notify thirdparty at creation + print ''; + + // TITLE + if ($this->withtitletopic) { + print ''; + } else { + if ($this->withthreadid > 0) { + $subject = $langs->trans('SubjectAnswerToTicket') . ' ' . $this->withthreadid . ' : ' . $this->topic_title . ''; + } + print ''; + print ''; + } + } + + // MESSAGE + $msg = GETPOST('message', 'alpha') ? GETPOST('message', 'alpha') : ''; + print ''; + + // Attached files + if (!empty($this->withfile)) { + // Define list of attached files + $listofpaths = array(); + $listofnames = array(); + $listofmimes = array(); + if (!empty($_SESSION["listofpaths"])) { + $listofpaths = explode(';', $_SESSION["listofpaths"]); + } + + if (!empty($_SESSION["listofnames"])) { + $listofnames = explode(';', $_SESSION["listofnames"]); + } + + if (!empty($_SESSION["listofmimes"])) { + $listofmimes = explode(';', $_SESSION["listofmimes"]); + } + + $out .= ''; + $out .= ''; + $out .= '\n"; + + print $out; + } + + // Other attributes + if ($this->withextrafields == 1) { + $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $ticketstat, $action); // Note that $action and $object may have been modified by hook + if (empty($reshook) && !empty($extrafields->attribute_label)) { + print $ticketstat->showOptionals($extrafields, 'edit'); + } + } + + print '
    ' . $langs->trans("Ref") . '
    ' . $langs->trans("CreatedBy") . ''; + $langs->load("users"); + $fuser = new User($this->db); + + if ($this->withcreatereadonly) { + if ($res = $fuser->fetch($this->fk_user_create)) { + print $fuser->getNomUrl(1); + } + } + print '   '; + print "
    ' . $langs->trans("ThirdParty") . ''; + $events = array(); + $events[] = array('method' => 'getContacts', 'url' => dol_buildpath('/core/ajax/contacts.php', 1), 'htmlname' => 'contactid', 'params' => array('add-customer-contact' => 'disabled')); + print $form->select_company($this->withfromsocid, 'socid', '', 1, 1, '', $events); + print '
    ' . $langs->trans("Contact") . ''; + // If no socid, set to first one (id=1) to avoid full contacts list + $selectedCompany = $this->withfromsocid > 0 ? $this->withfromsocid : 1; + $nbofcontacts = $form->select_contacts($selectedCompany, $this->withfromcontactid, 'contactid', 0, '', '', 0, 'minwidth200'); + $formcompany->selectTypeContact($ticketstatic, '', 'type', 'external', '', 0, 'maginleftonly'); + print '
    '; + print ''; + print '
    ' . $langs->trans($newclassname) . '' . $objectsrc->getNomUrl(1) . '
    '; + print $this->selectTypesTickets((GETPOST('type_code') ? GETPOST('type_code') : $this->type_code), 'type_code', '', '2'); + print '
    '; + print $this->selectCategoriesTickets((GETPOST('category_code') ? GETPOST('category_code') : $this->category_code), 'category_code', '', '2'); + print '
    '; + print $this->selectSeveritiesTickets((GETPOST('severity_code') ? GETPOST('severity_code') : $this->severity_code), 'severity_code', '', '2'); + print '
    '; + print 'withnotifytiersatcreate?' checked="checked"':'').'>'; + print '
    '; + + // Réponse à un ticket : affichage du titre du thread en readonly + if ($this->withtopicreadonly) { + print $langs->trans('SubjectAnswerToTicket') . ' ' . $this->topic_title; + print '
    '; + + // If public form, display more information + if ($this->ispublic) { + print '
    ' . ($conf->global->TICKETS_PUBLIC_TEXT_HELP_MESSAGE ? $conf->global->TICKETS_PUBLIC_TEXT_HELP_MESSAGE : $langs->trans('TicketPublicPleaseBeAccuratelyDescribe')) . '
    '; + } + include_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + $uselocalbrowser = true; + $doleditor = new DolEditor('message', GETPOST('message', 'alpha'), '100%', 250, 'dolibarr_details', 'In', true, $uselocalbrowser); + $doleditor->Create(); + print '
    ' . $langs->trans("MailFile") . ''; + // TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript + $out .= '' . "\n"; + $out .= '' . "\n"; + if (count($listofpaths)) { + foreach ($listofpaths as $key => $val) { + $out .= '
    '; + $out .= img_mime($listofnames[$key]) . ' ' . $listofnames[$key]; + if (!$this->withfilereadonly) { + $out .= ' '; + } + $out .= '
    '; + } + } else { + $out .= $langs->trans("NoAttachedFiles") . '
    '; + } + if ($this->withfile == 2) { // Can add other files + $out .= ''; + $out .= ' '; + $out .= ''; + } + $out .= "
    '; + print '
    '; + + print '
    '; + print ''; + + if ($this->withcancel) { + print "     "; + print "trans("Cancel") . "\">"; + } + print "
    \n"; + + print "\n"; + print "\n"; + } + + /** + * Return html list of tickets type + * + * @param string $selected Id du type pre-selectionne + * @param string $htmlname Nom de la zone select + * @param string $filtertype To filter on field type in llx_c_ticketsup_type (array('code'=>xx,'label'=>zz)) + * @param int $format 0=id+libelle, 1=code+code, 2=code+libelle, 3=id+code + * @param int $empty 1=peut etre vide, 0 sinon + * @param int $noadmininfo 0=Add admin info, 1=Disable admin info + * @param int $maxlength Max length of label + * @param string $morecss More CSS + * @return void + */ + public function selectTypesTickets($selected = '', $htmlname = 'tickettype', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss='') + { + global $langs, $user; + + $ticketstat = new Ticketsup($this->db); + + dol_syslog(get_class($this) . "::select_types_tickets " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG); + + $filterarray = array(); + + if ($filtertype != '' && $filtertype != '-1') { + $filterarray = explode(',', $filtertype); + } + + $ticketstat->loadCacheTypesTickets(); + + print ''; + if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + print ajax_combobox('select'.$htmlname); + } + + /** + * Return html list of ticket categories + * + * @param string $selected Id categorie pre-selectionnée + * @param string $htmlname Nom de la zone select + * @param string $filtertype To filter on field type in llx_c_ticketsup_category (array('code'=>xx,'label'=>zz)) + * @param int $format 0=id+libelle, 1=code+code, 2=code+libelle, 3=id+code + * @param int $empty 1=peut etre vide, 0 sinon + * @param int $noadmininfo 0=Add admin info, 1=Disable admin info + * @param int $maxlength Max length of label + * @param string $morecss More CSS + * @return void + */ + public function selectCategoriesTickets($selected = '', $htmlname = 'ticketcategory', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss='') + { + global $langs, $user; + + $ticketstat = new Ticketsup($this->db); + + dol_syslog(get_class($this) . "::selectCategoryTickets " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG); + + $filterarray = array(); + + if ($filtertype != '' && $filtertype != '-1') { + $filterarray = explode(',', $filtertype); + } + + $ticketstat->loadCacheCategoriesTickets(); + + print ''; + if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + print ajax_combobox('select'.$htmlname); + } + + /** + * Return html list of ticket severitys + * + * @param string $selected Id severity pre-selectionnée + * @param string $htmlname Nom de la zone select + * @param string $filtertype To filter on field type in llx_c_ticketsup_severity (array('code'=>xx,'label'=>zz)) + * @param int $format 0=id+libelle, 1=code+code, 2=code+libelle, 3=id+code + * @param int $empty 1=peut etre vide, 0 sinon + * @param int $noadmininfo 0=Add admin info, 1=Disable admin info + * @param int $maxlength Max length of label + * @param string $morecss More CSS + * @return void + */ + public function selectSeveritiesTickets($selected = '', $htmlname = 'ticketseverity', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss='') + { + global $langs, $user; + + $ticketstat = new Ticketsup($this->db); + + dol_syslog(get_class($this) . "::selectSeveritiesTickets " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG); + + $filterarray = array(); + + if ($filtertype != '' && $filtertype != '-1') { + $filterarray = explode(',', $filtertype); + } + + $ticketstat->loadCacheSeveritiesTickets(); + + print ''; + if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + print ajax_combobox('select'.$htmlname); + } + + /** + * Show the form to add message on ticket + * + * @param string $width Width of form + * @return void + */ + public function showMessageForm($width = '40%') + { + global $conf, $langs, $user, $mysoc; + + $langs->load("other"); + $langs->load("mails"); + + $addfileaction = 'addfile'; + + $form = new Form($this->db); + $formmail = new FormMail($this->db); + + + // Define list of attached files + $listofpaths = array(); + $listofnames = array(); + $listofmimes = array(); + if (!empty($_SESSION["listofpaths"])) { + $listofpaths = explode(';', $_SESSION["listofpaths"]); + } + + if (!empty($_SESSION["listofnames"])) { + $listofnames = explode(';', $_SESSION["listofnames"]); + } + + if (!empty($_SESSION["listofmimes"])) { + $listofmimes = explode(';', $_SESSION["listofmimes"]); + } + + // Define output language + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) { + $newlang = $this->param['langsmodels']; + } + if (! empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outputlangs->load('other'); + } + + print "\n\n"; + + $send_email = GETPOST('send_email', 'int') ? GETPOST('send_email', 'int') : 0; + + // Example 1 : Adding jquery code + print ''; + + print '
    '; + print ''; + print ''; + foreach ($this->param as $key => $value) { + print ''; + } + + // Get message template + $model_id=0; + if (array_key_exists('models_id', $this->param)) { + $model_id=$this->param["models_id"]; + $arraydefaultmessage=$formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id); + } + + $result = $formmail->fetchAllEMailTemplate($this->param["models"], $user, $outputlangs); + if ($result<0) { + setEventMessages($this->error, $this->errors, 'errors'); + } + $modelmail_array=array(); + foreach ($formmail->lines_model as $line) { + $modelmail_array[$line->id]=$line->label; + } + + print ''; + + + // External users can't send message email + if ($user->rights->ticketsup->write && !$user->socid) { + print ''; + + // Zone to select its email template + if (count($modelmail_array)>0) { + print ''; + } + + // Substitution array + if ($this->withsubstit) { + print '"; + } + + if (! $user->socid) { + print ''; + } + + + print ''; + $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; + print ''; + + // Destinataires + print ''; + } + + // Intro + // External users can't send message email + if ($user->rights->ticketsup->write && !$user->socid) { + $mail_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKETS_MESSAGE_MAIL_INTRO; + print ''; + } + + // MESSAGE + $defaultmessage=""; + if (is_array($arraydefaultmessage) && count($arraydefaultmessage) > 0 && $arraydefaultmessage->content) { + $defaultmessage=$arraydefaultmessage->content; + } + $defaultmessage=str_replace('\n', "\n", $defaultmessage); + + // Deal with format differences between message and signature (text / HTML) + if (dol_textishtml($defaultmessage) && !dol_textishtml($this->substit['__SIGNATURE__'])) { + $this->substit['__SIGNATURE__'] = dol_nl2br($this->substit['__SIGNATURE__']); + } elseif (!dol_textishtml($defaultmessage) && dol_textishtml($this->substit['__SIGNATURE__'])) { + $defaultmessage = dol_nl2br($defaultmessage); + } + if (isset($_POST["message"]) && ! $_POST['modelselected']) { + $defaultmessage=GETPOST('message'); + } else { + $defaultmessage=make_substitutions($defaultmessage, $this->substit); + // Clean first \n and br (to avoid empty line when CONTACTCIVNAME is empty) + $defaultmessage=preg_replace("/^(
    )+/", "", $defaultmessage); + $defaultmessage=preg_replace("/^\n+/", "", $defaultmessage); + } + + print ''; + + // Signature + // External users can't send message email + if ($user->rights->ticketsup->write && !$user->socid) { + $mail_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE; + print ''; + } + + // Attached files + if (!empty($this->withfile)) { + $out .= ''; + $out .= ''; + $out .= '\n"; + + print $out; + } + + print ''; + print '
    '; + $checkbox_selected = ( GETPOST('send_email') == "1" ? ' checked' : ''); + print ' '; + print ''; + print '
    '; + $checkbox_selected = ( GETPOST('private_message') == "1" ? ' checked' : ''); + print ' '; + print ''; + print ''; + print $form->textwithpicto('', $langs->trans("TicketMessagePrivateHelp"), 1, 'help'); + print '
    '; + include_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + $doleditor = new DolEditor('message', $defaultmessage, '100%', 350, 'dolibarr_details', '', false, true, $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_2, 70); + $doleditor->Create(); + print ''; + if ($user->rights->ticketsup->write && !$user->socid) { + print $form->textwithpicto('', $langs->trans("TicketMessageHelp"), 1, 'help'); + } + + print '
    ' . $langs->trans("MailFile") . ''; + // TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript + $out .= '' . "\n"; + $out .= '' . "\n"; + if (count($listofpaths)) { + foreach ($listofpaths as $key => $val) { + $out .= '
    '; + $out .= img_mime($listofnames[$key]) . ' ' . $listofnames[$key]; + if (!$this->withfilereadonly) { + $out .= ' '; + } + $out .= '
    '; + } + } else { + $out .= $langs->trans("NoAttachedFiles") . '
    '; + } + if ($this->withfile == 2) { // Can add other files + $out .= ''; + $out .= ' '; + $out .= ''; + } + $out .= "
    '; + print '
    '; + print ''; + + if ($this->withcancel) { + print "     "; + print "trans("Cancel") . "\">"; + } + print "
    \n"; + print '
    '; + + print "
    \n"; + print "\n"; + } +} diff --git a/htdocs/core/class/html.formwebsite.class.php b/htdocs/core/class/html.formwebsite.class.php index e7cd6d447a0..86e0f48c65f 100644 --- a/htdocs/core/class/html.formwebsite.class.php +++ b/htdocs/core/class/html.formwebsite.class.php @@ -164,7 +164,7 @@ class FormWebsite * * @param string $htmlname Name of select zone * @param string $selected Selected value - * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param int $useempty 1=Add an empty value in list * @param string $moreattrib More attributes on HTML select tag * @return void */ @@ -177,9 +177,9 @@ class FormWebsite $arrayofsamples=array('corporatehome'=>'CorporateHomePage', 'empty'=>'EmptyPage'); $out = ''; - $out .= '"; diff --git a/htdocs/core/class/interfaces.class.php b/htdocs/core/class/interfaces.class.php index a34754b8d4c..655969d73f6 100644 --- a/htdocs/core/class/interfaces.class.php +++ b/htdocs/core/class/interfaces.class.php @@ -169,12 +169,12 @@ class Interfaces if (method_exists($objMod, 'runTrigger')) // New method to implement { - dol_syslog(get_class($this)."::run_triggers action=".$action." Launch runTrigger for file '".$files[$key]."'", LOG_INFO); + //dol_syslog(get_class($this)."::run_triggers action=".$action." Launch runTrigger for file '".$files[$key]."'", LOG_DEBUG); $result=$objMod->runTrigger($action,$object,$user,$langs,$conf); } elseif (method_exists($objMod, 'run_trigger')) // Deprecated method { - dol_syslog(get_class($this)."::run_triggers action=".$action." Launch run_trigger for file '".$files[$key]."'", LOG_INFO); + dol_syslog(get_class($this)."::run_triggers action=".$action." Launch old method run_trigger (rename your trigger into runTrigger) for file '".$files[$key]."'", LOG_WARNING); $result=$objMod->run_trigger($action,$object,$user,$langs,$conf); } else diff --git a/htdocs/core/class/ldap.class.php b/htdocs/core/class/ldap.class.php index 20a9b85cf86..8fcdf53b4a6 100644 --- a/htdocs/core/class/ldap.class.php +++ b/htdocs/core/class/ldap.class.php @@ -177,20 +177,10 @@ class Ldap if ($connected) break; if (empty($host)) continue; - if (preg_match('/^ldap/',$host)) - { - if ($this->serverPing($host) === true) { - $this->connection = ldap_connect($host); - } - else continue; - } - else - { - if ($this->serverPing($host, $this->serverPort) === true) { - $this->connection = ldap_connect($host,$this->serverPort); - } - else continue; + if ($this->serverPing($host, $this->serverPort) === true) { + $this->connection = ldap_connect($host, $this->serverPort); } + else continue; if (is_resource($this->connection)) { @@ -738,6 +728,14 @@ class Ldap */ function serverPing($host, $port=389, $timeout=1) { + // Replace ldaps:// by ssl:// + if (preg_match('/^ldaps:\/\/([^\/]+)\/?$/',$host, $regs)) { + $host = 'ssl://'.$regs[1]; + } + // Remove ldap:// + if (preg_match('/^ldap:\/\/([^\/]+)\/?$/',$host, $regs)) { + $host = $regs[1]; + } $op = @fsockopen($host, $port, $errno, $errstr, $timeout); if (!$op) return false; //DC is N/A else { diff --git a/htdocs/core/class/notify.class.php b/htdocs/core/class/notify.class.php index 7554a82a5fd..698eca5fd21 100644 --- a/htdocs/core/class/notify.class.php +++ b/htdocs/core/class/notify.class.php @@ -392,13 +392,13 @@ class Notify break; case 'PROPAL_VALIDATE': $link='/comm/propal/card.php?id='.$object->id; - $dir_output = $conf->propal->dir_output; + $dir_output = $conf->propal->multidir_output[$object->entity]; $object_type = 'propal'; $mesg = $langs->transnoentitiesnoconv("EMailTextProposalValidated",$newref); break; case 'PROPAL_CLOSE_SIGNED': $link='/comm/propal/card.php?id='.$object->id; - $dir_output = $conf->propal->dir_output; + $dir_output = $conf->propal->multidir_output[$object->entity]; $object_type = 'propal'; $mesg = $langs->transnoentitiesnoconv("EMailTextProposalClosedSigned",$newref); break; @@ -460,7 +460,7 @@ class Notify $message.= $outputlangs->transnoentities("YouReceiveMailBecauseOfNotification2",$application,$mysoc->name)."\n"; $message.= "\n"; $message.= $mesg; - if ($link) $message=dol_concatdesc($message,$urlwithroot.$link); + if ($link) $message.= "\n" . $urlwithroot . $link; $parameters=array('notifcode'=>$notifcode, 'sendto'=>$sendto, 'replyto'=>$replyto, 'file'=>$file, 'mimefile'=>$mimefile, 'filename'=>$filename); $reshook=$hookmanager->executeHooks('formatNotificationMessage',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks @@ -555,77 +555,77 @@ class Notify switch ($notifcode) { case 'BILL_VALIDATE': - $link='/compta/facture/card.php?facid='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->facture->dir_output; $object_type = 'facture'; - $mesg = $langs->transnoentitiesnoconv("EMailTextInvoiceValidated",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextInvoiceValidated",$link); break; case 'BILL_PAYED': - $link='/compta/facture/card.php?facid='.$object->id; + $link ='' . $newref . ''; $dir_output = $conf->facture->dir_output; $object_type = 'facture'; - $mesg = $langs->transnoentitiesnoconv("EMailTextInvoicePayed",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextInvoicePayed",$link); break; case 'ORDER_VALIDATE': - $link='/commande/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->commande->dir_output; $object_type = 'order'; - $mesg = $langs->transnoentitiesnoconv("EMailTextOrderValidated",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextOrderValidated",$link); break; case 'PROPAL_VALIDATE': $link='/comm/propal/card.php?id='.$object->id; - $dir_output = $conf->propal->dir_output; + $dir_output = $conf->propal->multidir_output[$object->entity]; $object_type = 'propal'; - $mesg = $langs->transnoentitiesnoconv("EMailTextProposalValidated",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextProposalValidated",$link); break; case 'PROPAL_CLOSE_SIGNED': $link='/comm/propal/card.php?id='.$object->id; - $dir_output = $conf->propal->dir_output; + $dir_output = $conf->propal->multidir_output[$object->entity]; $object_type = 'propal'; - $mesg = $langs->transnoentitiesnoconv("EMailTextProposalClosedSigned",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextProposalClosedSigned",$link); break; case 'FICHINTER_ADD_CONTACT': - $link='/fichinter/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->facture->dir_output; $object_type = 'ficheinter'; - $mesg = $langs->transnoentitiesnoconv("EMailTextInterventionAddedContact",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextInterventionAddedContact",$link); break; case 'FICHINTER_VALIDATE': - $link='/fichinter/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->facture->dir_output; $object_type = 'ficheinter'; - $mesg = $langs->transnoentitiesnoconv("EMailTextInterventionValidated",$newref); + $mesg = $langs->transnoentitiesnoconv("EMailTextInterventionValidated",$link); break; case 'ORDER_SUPPLIER_VALIDATE': - $link='/fourn/commande/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->fournisseur->commande->dir_output; $object_type = 'order_supplier'; $mesg = $langs->transnoentitiesnoconv("Hello").",\n\n"; - $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderValidatedBy",$newref,$user->getFullName($langs)); + $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderValidatedBy",$link,$user->getFullName($langs)); $mesg.= "\n\n".$langs->transnoentitiesnoconv("Sincerely").".\n\n"; break; case 'ORDER_SUPPLIER_APPROVE': - $link='/fourn/commande/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->fournisseur->commande->dir_output; $object_type = 'order_supplier'; $mesg = $langs->transnoentitiesnoconv("Hello").",\n\n"; - $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderApprovedBy",$newref,$user->getFullName($langs)); + $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderApprovedBy",$link,$user->getFullName($langs)); $mesg.= "\n\n".$langs->transnoentitiesnoconv("Sincerely").".\n\n"; break; case 'ORDER_SUPPLIER_APPROVE2': - $link='/fourn/commande/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->fournisseur->commande->dir_output; $object_type = 'order_supplier'; $mesg = $langs->transnoentitiesnoconv("Hello").",\n\n"; - $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderApprovedBy",$newref,$user->getFullName($langs)); + $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderApprovedBy",$link,$user->getFullName($langs)); $mesg.= "\n\n".$langs->transnoentitiesnoconv("Sincerely").".\n\n"; break; case 'ORDER_SUPPLIER_REFUSE': - $link='/fourn/commande/card.php?id='.$object->id; + $link = '' . $newref . ''; $dir_output = $conf->fournisseur->dir_output.'/commande/'; $object_type = 'order_supplier'; $mesg = $langs->transnoentitiesnoconv("Hello").",\n\n"; - $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderRefusedBy",$newref,$user->getFullName($langs)); + $mesg.= $langs->transnoentitiesnoconv("EMailTextOrderRefusedBy",$link,$user->getFullName($langs)); $mesg.= "\n\n".$langs->transnoentitiesnoconv("Sincerely").".\n\n"; break; case 'SHIPPING_VALIDATE': @@ -650,7 +650,7 @@ class Notify $message.= $langs->transnoentities("YouReceiveMailBecauseOfNotification2",$application,$mysoc->name)."\n"; $message.= "\n"; $message.= $mesg; - if ($link) $message=dol_concatdesc($message,$urlwithroot.$link); + $message = nl2br($message); // Replace keyword __SUPERVISOREMAIL__ if (preg_match('/__SUPERVISOREMAIL__/', $sendto)) @@ -678,7 +678,6 @@ class Notify if (! empty($hookmanager->resArray['subject'])) $subject.=$hookmanager->resArray['subject']; if (! empty($hookmanager->resArray['message'])) $message.=$hookmanager->resArray['message']; } - $mailfile = new CMailFile( $subject, $sendto, @@ -690,7 +689,7 @@ class Notify '', '', 0, - -1 + 1 ); if ($mailfile->sendfile()) diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index 14f017bef55..0df169a3c3e 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -895,7 +895,7 @@ class Translate $sql = "SELECT ".$fieldlabel." as label"; $sql.= " FROM ".MAIN_DB_PREFIX.$tablename; - $sql.= " WHERE ".$fieldkey." = '".($keyforselect?$keyforselect:$key)."'"; + $sql.= " WHERE ".$fieldkey." = '".$db->escape($keyforselect?$keyforselect:$key)."'"; if ($filteronentity) $sql.= " AND entity IN (" . getEntity($tablename). ')'; dol_syslog(get_class($this).'::getLabelFromKey', LOG_DEBUG); $resql = $db->query($sql); @@ -977,7 +977,7 @@ class Translate $sql = "SELECT code_iso, label, unicode"; $sql.= " FROM ".MAIN_DB_PREFIX."c_currencies"; $sql.= " WHERE active = 1"; - if (! empty($currency_code)) $sql.=" AND code_iso = '".$currency_code."'"; + if (! empty($currency_code)) $sql.=" AND code_iso = '".$db->escape($currency_code)."'"; //$sql.= " ORDER BY code_iso ASC"; // Not required, a sort is done later dol_syslog(get_class($this).'::loadCacheCurrencies', LOG_DEBUG); @@ -1027,6 +1027,7 @@ class Translate foreach($this->tab_translate as $code => $label) { $substitutionarray['lang_'.$code] = $label; + $substitutionarray['__('.$code.')__'] = $label; } return $substitutionarray; diff --git a/htdocs/core/class/utils.class.php b/htdocs/core/class/utils.class.php index 4502eb03240..3a7a79122c9 100644 --- a/htdocs/core/class/utils.class.php +++ b/htdocs/core/class/utils.class.php @@ -47,7 +47,7 @@ class Utils * Purge files into directory of data files. * CAN BE A CRON TASK * - * @param string $choice Choice of purge mode ('tempfiles', 'tempfilesold' to purge temp older than 24h, 'allfiles', 'logfile') + * @param string $choice Choice of purge mode ('tempfiles', '' or 'tempfilesold' to purge temp older than 24h, 'allfiles', 'logfile') * @return int 0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK) */ function purgeFiles($choice='tempfilesold') @@ -67,7 +67,7 @@ class Utils // Delete temporary files if ($dolibarr_main_data_root) { - $filesarray=dol_dir_list($dolibarr_main_data_root,"directories",1,'^temp$','','','',2); + $filesarray=dol_dir_list($dolibarr_main_data_root, "directories", 1, '^temp$', '', 'name', SORT_ASC, 2, 0, '', 1); // Do not follow symlinks if ($choice == 'tempfilesold') { $now = dol_now(); @@ -81,10 +81,10 @@ class Utils if ($choice=='allfiles') { - // Delete all files (except install.lock) + // Delete all files (except install.lock, do not follow symbolic links) if ($dolibarr_main_data_root) { - $filesarray=dol_dir_list($dolibarr_main_data_root,"all",0,'','install\.lock$'); + $filesarray=dol_dir_list($dolibarr_main_data_root, "all", 0, '', 'install\.lock$', 'name', SORT_ASC, 0, 0, '', 1); } } @@ -93,7 +93,7 @@ class Utils // Define files log if ($dolibarr_main_data_root) { - $filesarray=dol_dir_list($dolibarr_main_data_root, "files", 0, '.*\.log[\.0-9]*$', 'install\.lock$'); + $filesarray=dol_dir_list($dolibarr_main_data_root, "files", 0, '.*\.log[\.0-9]*(\.gz)?$', 'install\.lock$', 'name', SORT_ASC, 0, 0, '', 1); } $filelog=''; @@ -129,7 +129,7 @@ class Utils } elseif ($filesarray[$key]['type'] == 'file') { - // If (file that is not logfile) or (if logfile with option logfile) + // If (file that is not logfile) or (if mode is logfile) if ($filesarray[$key]['fullname'] != $filelog || $choice=='logfile') { $result=dol_delete_file($filesarray[$key]['fullname'], 1, 1); @@ -138,7 +138,10 @@ class Utils $count++; $countdeleted++; } - else $counterror++; + else + { + $counterror++; + } } } } @@ -446,7 +449,6 @@ class Utils } } - return 0; } @@ -668,4 +670,116 @@ class Utils return -1; } -} + + /** + * This saves syslog files and compresses older ones + * Used from cronjob + * + * @return int 0 if OK, < 0 if KO + */ + function compressSyslogs() { + global $conf; + + if(empty($conf->loghandlers['mod_syslog_file'])) { // File Syslog disabled + return 0; + } + + if(! function_exists('gzopen')) { + $this->error = 'Support for gzopen not available in this PHP'; + return -1; + } + + dol_include_once('/core/lib/files.lib.php'); + + $nbSaves = ! empty($conf->global->SYSLOG_FILE_SAVES) ? intval($conf->global->SYSLOG_FILE_SAVES) : 14; + + if (empty($conf->global->SYSLOG_FILE)) { + $mainlogdir = DOL_DATA_ROOT; + $mainlog = 'dolibarr.log'; + } else { + $mainlogfull = str_replace('DOL_DATA_ROOT', DOL_DATA_ROOT, $conf->global->SYSLOG_FILE); + $mainlogdir = dirname($mainlogfull); + $mainlog = basename($mainlogfull); + } + + $tabfiles = dol_dir_list(DOL_DATA_ROOT, 'files', 0, '^(dolibarr_.+|odt2pdf)\.log$'); // Also handle other log files like dolibarr_install.log + $tabfiles[] = array('name' => $mainlog, 'path' => $mainlogdir); + + foreach($tabfiles as $file) { + + $logname = $file['name']; + $logpath = $file['path']; + + // Handle already compressed files to rename them and add +1 + + $filter = '^'.preg_quote($logname, '/').'\.([0-9]+)\.gz$'; + + $gzfilestmp = dol_dir_list($logpath, 'files', 0, $filter); + $gzfiles = array(); + + foreach($gzfilestmp as $gzfile) { + $tabmatches = array(); + preg_match('/'.$filter.'/i', $gzfile['name'], $tabmatches); + + $numsave = intval($tabmatches[1]); + + $gzfiles[$numsave] = $gzfile; + } + + krsort($gzfiles, SORT_NUMERIC); + + foreach($gzfiles as $numsave => $dummy) { + if (dol_is_file($logpath.'/'.$logname.'.'.($numsave+1).'.gz')) { + return -2; + } + + if($numsave >= $nbSaves) { + dol_delete_file($logpath.'/'.$logname.'.'.$numsave.'.gz'); + } else { + dol_move($logpath.'/'.$logname.'.'.$numsave.'.gz', $logpath.'/'.$logname.'.'.($numsave+1).'.gz', 0, 1, 0, 0); + } + } + + // Compress last save + if (dol_is_file($logpath.'/'.$logname.'.1')) { + if($nbSaves > 1) { + $gzfilehandle = gzopen($logpath.'/'.$logname.'.2.gz', 'wb9'); + + if (empty($gzfilehandle)) { + $this->error = 'Failted to open file '.$logpath.'/'.$logname.'.2.gz'; + return -3; + } + + $sourcehandle = fopen($logpath.'/'.$logname.'.1', 'r'); + + if (empty($sourcehandle)) { + $this->error = 'Failed to open file '.$logpath.'/'.$logname.'.1'; + return -4; + } + + while(! feof($sourcehandle)) { + gzwrite($gzfilehandle, fread($sourcehandle, 512 * 1024)); // Read 512 kB at a time + } + + fclose($sourcehandle); + gzclose($gzfilehandle); + } else { + dol_delete_file($logpath.'/'.$logname.'.1'); + } + } + + // Compress current file et recreate it + + if (dol_is_file($logpath.'/'.$logname)) { + if (dol_move($logpath.'/'.$logname, $logpath.'/'.$logname.'.1', 0, 1, 0, 0)) + { + $newlog = fopen($logpath.'/'.$logname, 'a+'); + fclose($newlog); + } + } + } + + $this->output = 'Archive log files (keeping last SYSLOG_FILE_SAVES='.$nbSaves.' files) done.'; + return 0; + } +} \ No newline at end of file diff --git a/htdocs/core/get_info.php b/htdocs/core/get_info.php new file mode 100644 index 00000000000..7b2c57b42e3 --- /dev/null +++ b/htdocs/core/get_info.php @@ -0,0 +1,209 @@ + + * + * This file is a modified version of datepicker.php from phpBSM to fix some + * bugs, to add new features and to dramatically increase speed. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/get_info.php + * \brief File to return a single page with just logged user info, to be used by other frontend + */ + +//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); // Not disabled cause need to load personalized language +//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1'); // Not disabled cause need to load personalized language +//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); +//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); // Not disabled cause need to do translations +if (! defined('NOCSRFCHECK')) define('NOCSRFCHECK',1); +if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL',1); +//if (! defined('NOLOGIN')) define('NOLOGIN',1); // Not disabled cause need to load personalized language +if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU',1); +//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML',1); + +require_once '../main.inc.php'; + +if (GETPOST('lang', 'aZ09')) $langs->setDefaultLang(GETPOST('lang', 'aZ09')); // If language was forced on URL by the main.inc.php + +$langs->load("main"); + +$right=($langs->trans("DIRECTION")=='rtl'?'left':'right'); +$left=($langs->trans("DIRECTION")=='rtl'?'right':'left'); + + +/* + * View + */ + +$title=$langs->trans("Info"); + +// URL http://mydolibarr/core/search_page?dol_use_jmobile=1 can be used for tests +$head=''."\n"; +$arrayofjs=array(); +$arrayofcss=array(); +top_htmlhead($head, $title, 0, 0, $arrayofjs, $arrayofcss); + + + +print ''."\n"; +print '
    '; +//print '
    '; + +$nbofsearch=0; + +// Define link to login card +$appli=constant('DOL_APPLICATION_TITLE'); +if (! empty($conf->global->MAIN_APPLICATION_TITLE)) +{ + $appli=$conf->global->MAIN_APPLICATION_TITLE; + if (preg_match('/\d\.\d/', $appli)) + { + if (! preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) $appli.=" (".DOL_VERSION.")"; // If new title contains a version that is different than core + } + else $appli.=" ".DOL_VERSION; +} +else $appli.=" ".DOL_VERSION; + +if (! empty($conf->global->MAIN_FEATURES_LEVEL)) $appli.="
    ".$langs->trans("LevelOfFeature").': '.$conf->global->MAIN_FEATURES_LEVEL; + +$logouttext=''; +if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) +{ + //$logouthtmltext=$appli.'
    '; + if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') + { + $logouthtmltext.=$langs->trans("Logout").'
    '; + + $logouttext .=''; + //$logouttext .= img_picto($langs->trans('Logout').":".$langs->trans('Logout'), 'logout_top.png', 'class="login"', 0, 0, 1); + $logouttext .=''; + $logouttext .=''; + } + else + { + $logouthtmltext.=$langs->trans("NoLogoutProcessWithAuthMode",$_SESSION["dol_authmode"]); + $logouttext .= img_picto($langs->trans('Logout').":".$langs->trans('Logout'), 'logout_top.png', 'class="login"', 0, 0, 1); + } +} + +print ''; + +print $toprightmenu; + +print "
    \n"; // end div class="login_block" + +print ''; +print ''."\n"; + +$db->close(); diff --git a/htdocs/core/get_menudiv.php b/htdocs/core/get_menudiv.php index 3ff299fa00c..3b49482a217 100644 --- a/htdocs/core/get_menudiv.php +++ b/htdocs/core/get_menudiv.php @@ -20,7 +20,7 @@ /** * \file htdocs/core/get_menudiv.php - * \brief File to return menu into a div tree + * \brief File to return menu into a div tree, to be used by other frontend */ //if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); // Not disabled cause need to load personalized language @@ -37,8 +37,6 @@ if (! defined('DISABLE_JQUERY_TABLEDND')) define('DISABLE_JQUERY_TABLEDND',1); if (! defined('DISABLE_JQUERY_JNOTIFY')) define('DISABLE_JQUERY_JNOTIFY',1); if (! defined('DISABLE_JQUERY_FLOT')) define('DISABLE_JQUERY_FLOT',1); if (! defined('DISABLE_JQUERY_JEDITABLE')) define('DISABLE_JQUERY_JEDITABLE',1); -if (! defined('DISABLE_JQUERY_JEDITABLE')) define('DISABLE_JQUERY_JEDITABLE',1); -if (! defined('DISABLE_CKEDITOR')) define('DISABLE_CKEDITOR',1); if (! defined('DISABLE_CKEDITOR')) define('DISABLE_CKEDITOR',1); if (! defined('DISABLE_BROWSER_NOTIF')) define('DISABLE_BROWSER_NOTIF',1); if (! defined('DISABLE_DATE_PICKER')) define('DISABLE_DATE_PICKER',1); diff --git a/htdocs/core/js/timesheet.js b/htdocs/core/js/timesheet.js index 20a713ce220..ae7d839919c 100644 --- a/htdocs/core/js/timesheet.js +++ b/htdocs/core/js/timesheet.js @@ -216,6 +216,21 @@ function updateTotal(days,mode) if (total.getHours() || total.getMinutes()) jQuery('.totalDay'+days).addClass("bold"); else jQuery('.totalDay'+days).removeClass("bold"); jQuery('.totalDay'+days).text(pad(total.getHours())+':'+pad(total.getMinutes())); + + var total = new Date(0); + total.setHours(0); + total.setMinutes(0); + for (var i=0; i<7; i++) + { + var taskTime= new Date(0); + result=parseTime(jQuery('.totalDay'+i).text(),taskTime); + if (result >= 0) + { + total.setHours(total.getHours()+taskTime.getHours()); + total.setMinutes(total.getMinutes()+taskTime.getMinutes()); + } + } + jQuery('.totalDayAll').text(pad(total.getHours())+':'+pad(total.getMinutes())); } else { @@ -260,7 +275,6 @@ function updateTotal(days,mode) else jQuery('.totalDay'+days).removeClass("bold"); jQuery('.totalDay'+days).text(total); } - } \ No newline at end of file diff --git a/htdocs/core/lib/admin.lib.php b/htdocs/core/lib/admin.lib.php index 65cc2965bea..85a9be1f0fd 100644 --- a/htdocs/core/lib/admin.lib.php +++ b/htdocs/core/lib/admin.lib.php @@ -513,7 +513,7 @@ function dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $not $sql.= " VALUES ("; $sql.= $db->encrypt($name,1); $sql.= ", ".$db->encrypt($value,1); - $sql.= ",'".$type."',".$visible.",'".$db->escape($note)."',".$entity.")"; + $sql.= ",'".$db->escape($type)."',".$visible.",'".$db->escape($note)."',".$entity.")"; //print "sql".$value."-".pg_escape_string($value)."-".$sql;exit; //print "xx".$db->escape($value); @@ -870,7 +870,7 @@ function activateModule($value,$withdeps=1) // Test if Dolibarr version ok $verdol=versiondolibarrarray(); $vermin=isset($objMod->need_dolibarr_version)?$objMod->need_dolibarr_version:0; - //print 'eee '.versioncompare($verdol,$vermin).' - '.join(',',$verdol).' - '.join(',',$vermin);exit; + //print 'version: '.versioncompare($verdol,$vermin).' - '.join(',',$verdol).' - '.join(',',$vermin);exit; if (is_array($vermin) && versioncompare($verdol, $vermin) < 0) { $ret['errors'][] = $langs->trans("ErrorModuleRequireDolibarrVersion", versiontostring($vermin)); return $ret; @@ -888,6 +888,7 @@ function activateModule($value,$withdeps=1) } $result=$objMod->init(); // Enable module + if ($result <= 0) { $ret['errors'][]=$objMod->error; @@ -1031,7 +1032,8 @@ function unActivateModule($value, $requiredby=1) /** - * Add external modules to list of dictionaries + * Add external modules to list of dictionaries. + * Addition is done into var $taborder, $tabname, etc... that are passed with pointers. * * @param array $taborder Taborder * @param array $tabname Tabname @@ -1097,23 +1099,20 @@ function complete_dictionary_with_modules(&$taborder,&$tabname,&$tablib,&$tabsql if ($modulequalified) { // Load languages files of module - if (isset($objMod->langfiles) && is_array($objMod->langfiles)) - { - foreach($objMod->langfiles as $langfile) - { - $langs->load($langfile); - } - } + if (isset($objMod->langfiles) && is_array($objMod->langfiles)) { + foreach ($objMod->langfiles as $langfile) { + $langs->load($langfile); + } + } - // Complete arrays - //&$tabname,&$tablib,&$tabsql,&$tabsqlsort,&$tabfield,&$tabfieldvalue,&$tabfieldinsert,&$tabrowid,&$tabcond + // Complete the arrays &$tabname,&$tablib,&$tabsql,&$tabsqlsort,&$tabfield,&$tabfieldvalue,&$tabfieldinsert,&$tabrowid,&$tabcond if (empty($objMod->dictionaries) && ! empty($objMod->dictionnaries)) $objMod->dictionaries=$objMod->dictionnaries; // For backward compatibility if (! empty($objMod->dictionaries)) { //var_dump($objMod->dictionaries['tabname']); $nbtabname=$nbtablib=$nbtabsql=$nbtabsqlsort=$nbtabfield=$nbtabfieldvalue=$nbtabfieldinsert=$nbtabrowid=$nbtabcond=$nbtabfieldcheck=$nbtabhelp=0; - foreach($objMod->dictionaries['tabname'] as $val) { $nbtabname++; $taborder[] = max($taborder)+1; $tabname[] = $val; } + foreach($objMod->dictionaries['tabname'] as $val) { $nbtabname++; $taborder[] = max($taborder)+1; $tabname[] = $val; } // Position foreach($objMod->dictionaries['tablib'] as $val) { $nbtablib++; $tablib[] = $val; } foreach($objMod->dictionaries['tabsql'] as $val) { $nbtabsql++; $tabsql[] = $val; } foreach($objMod->dictionaries['tabsqlsort'] as $val) { $nbtabsqlsort++; $tabsqlsort[] = $val; } @@ -1130,6 +1129,10 @@ function complete_dictionary_with_modules(&$taborder,&$tabname,&$tablib,&$tabsql print 'Error in descriptor of module '.$const_name.'. Array ->dictionaries has not same number of record for key "tabname", "tablib", "tabsql" and "tabsqlsort"'; //print "$const_name: $nbtabname=$nbtablib=$nbtabsql=$nbtabsqlsort=$nbtabfield=$nbtabfieldvalue=$nbtabfieldinsert=$nbtabrowid=$nbtabcond=$nbtabfieldcheck=$nbtabhelp\n"; } + else + { + $taborder[] = 0; // Add an empty line + } } $j++; @@ -1151,7 +1154,7 @@ function complete_dictionary_with_modules(&$taborder,&$tabname,&$tablib,&$tabsql } /** - * Activate external modules mandatroy when country is country_code + * Activate external modules mandatory when country is country_code * * @param string $country_code CountryCode * @return int 1 @@ -1288,11 +1291,10 @@ function complete_elementList_with_modules(&$elementList) $modules[$i] = $objMod; $filename[$i]= $modName; - $orders[$i] = $objMod->family."_".$j; // Tri par famille puis numero module + $orders[$i] = $objMod->family."_".$j; // Sort on family then module number + $dirmod[$i] = $dir; //print "x".$modName." ".$orders[$i]."\n
    "; - if (isset($categ[$objMod->special])) $categ[$objMod->special]++; // Array of all different modules categories - else $categ[$objMod->special]=1; - $dirmod[$i] = $dirroot; + if (! empty($objMod->module_parts['contactelement'])) { $elementList[$objMod->name] = $langs->trans($objMod->name); @@ -1319,14 +1321,15 @@ function complete_elementList_with_modules(&$elementList) /** * Show array with constants to edit * - * @param array $tableau Array of constants + * @param array $tableau Array of constants array('key'=>type, ) where type can be 'string', 'text', 'textarea', 'html', 'yesno', 'emailtemplate:xxx', ... * @param int $strictw3c 0=Include form into table (deprecated), 1=Form is outside table to respect W3C (no form into table), 2=No form nor button at all * @param string $helptext Help * @return void */ function form_constantes($tableau, $strictw3c=0, $helptext='') { - global $db,$bc,$langs,$conf,$_Avery_Labels; + global $db,$bc,$langs,$conf,$user; + global $_Avery_Labels; $form = new Form($db); @@ -1341,11 +1344,20 @@ function form_constantes($tableau, $strictw3c=0, $helptext='') print '
    '.$langs->trans("Action").'
    '; - print ''; - print ''; + if (empty($strictw3c)) print ''; print ''; print ''; - print ''; + print ''; + print ''; print $langs->trans('Desc'.$const); @@ -1426,34 +1441,57 @@ function form_constantes($tableau, $strictw3c=0, $helptext='') } print $form->selectarray('constvalue'.(empty($strictw3c)?'':'[]'),$arrayoflabels,($obj->value?$obj->value:'CARD'),1,0,0); print ''; + print ''; print ''; - if (in_array($const,array('ADHERENT_CARD_TEXT','ADHERENT_CARD_TEXT_RIGHT','ADHERENT_ETIQUETTE_TEXT'))) + print ''; + print ''; + if ($obj->type == 'textarea' || in_array($const,array('ADHERENT_CARD_TEXT','ADHERENT_CARD_TEXT_RIGHT','ADHERENT_ETIQUETTE_TEXT'))) { print '\n"; - print ''; } - else if (in_array($const,array('ADHERENT_AUTOREGISTER_NOTIF_MAIL','ADHERENT_AUTOREGISTER_MAIL','ADHERENT_MAIL_VALID','ADHERENT_MAIL_COTIS','ADHERENT_MAIL_RESIL'))) + elseif ($obj->type == 'html') { - require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; - $doleditor=new DolEditor('constvalue_'.$const.(empty($strictw3c)?'':'[]'),$obj->value,'',160,'dolibarr_notes','',false,false,$conf->fckeditor->enabled,ROWS_5,'90%'); - $doleditor->Create(); - print ''; + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor=new DolEditor('constvalue_'.$const.(empty($strictw3c)?'':'[]'),$obj->value,'',160,'dolibarr_notes','',false,false,$conf->fckeditor->enabled,ROWS_5,'90%'); + $doleditor->Create(); } - else if ($obj->type == 'yesno') + elseif ($obj->type == 'yesno') { - print $form->selectyesno('constvalue'.(empty($strictw3c)?'':'[]'),$obj->value,1); - print ''; + print $form->selectyesno('constvalue'.(empty($strictw3c)?'':'[]'),$obj->value,1); } - else + elseif (preg_match('/emailtemplate/', $obj->type)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + $formmail = new FormMail($db); + + $tmp=explode(':', $obj->type); + + $nboftemplates = $formmail->fetchAllEMailTemplate($tmp[1], $user, null, -1); // We set lang=null to get in priority record with no lang + //$arraydefaultmessage = $formmail->getEMailTemplate($db, $tmp[1], $user, null, 0, 1, ''); + $arrayofmessagename=array(); + if (is_array($formmail->lines_model)) + { + foreach($formmail->lines_model as $modelmail) + { + //var_dump($modelmail); + $moreonlabel=''; + if (! empty($arrayofmessagename[$modelmail->label])) $moreonlabel=' ('.$langs->trans("SeveralLangugeVariatFound").')'; + $arrayofmessagename[$modelmail->label]=$langs->trans(preg_replace('/\(|\)/','',$modelmail->label)).$moreonlabel; + } + } + //var_dump($arraydefaultmessage); + //var_dump($arrayofmessagename); + print $form->selectarray('constvalue_'.$obj->name, $arrayofmessagename, $obj->value, 'None', 1, 0, '', 0, 0, 0, '', '', 1); + } + else // type = 'string' ou 'chaine' { print ''; - print ''; } print '
    '."\n"; - $param="socid=".$object->id; - if ($search_status != '') $param.='&search_status='.$search_status; - if ($search_name != '') $param.='&search_name='.urlencode($search_name); + $param="socid=".urlencode($object->id); + if ($search_status != '') $param.='&search_status='.urlencode($search_status); + if ($search_name != '') $param.='&search_name='.urlencode($search_name); + if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); + // Add $param from extra fields + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; $sql = "SELECT t.rowid, t.lastname, t.firstname, t.fk_pays as country_id, t.civility, t.poste, t.phone as phone_pro, t.phone_mobile, t.phone_perso, t.fax, t.email, t.skype, t.statut, t.photo,"; $sql .= " t.civility as civility_id, t.address, t.zip, t.town"; @@ -880,7 +961,9 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople_extrafields as ef on (t.rowid = ef.fk_object)"; $sql .= " WHERE t.fk_soc = ".$object->id; if ($search_status!='' && $search_status != '-1') $sql .= " AND t.statut = ".$db->escape($search_status); - if ($search_name) $sql .= " AND (t.lastname LIKE '%".$db->escape($search_name)."%' OR t.firstname LIKE '%".$db->escape($search_name)."%')"; + if ($search_name) $sql .= natural_search(array('t.lastname', 't.firstname'), $search_name); + // Add where from extra fields + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; if ($sortfield == "t.name") $sql.=" ORDER BY t.lastname $sortorder, t.firstname $sortorder"; else $sql.= " ORDER BY $sortfield $sortorder"; @@ -1044,7 +1127,7 @@ function show_contacts($conf,$langs,$db,$object,$backtopage='') // Edit if ($user->rights->societe->contact->creer) { - print ''; + print ''; print img_edit(); print ''; } @@ -1467,10 +1550,17 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon='', $noprint= $out.=getTitleFieldOfList('', 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'maxwidthsearch '); $out.=''; + require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php'; + $caction=new CActionComm($db); + $arraylist=$caction->liste_array(1, 'code', '', (empty($conf->global->AGENDA_USE_EVENT_TYPE)?1:0), '', 1); + foreach ($histo as $key=>$value) { $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo + $actionstatic->type_picto=$histo[$key]['apicto']; + $actionstatic->type_code=$histo[$key]['acode']; + $out.=''; // Done or todo @@ -1501,26 +1591,26 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon='', $noprint= $out.=''; // Title $out.=''; + $return.= ''; } $return.= '
    '; if (! empty($conf->global->AGENDA_USE_EVENT_TYPE)) { - if ($histo[$key]['apicto']) $out.=img_picto('', $histo[$key]['apicto']); + if ($actionstatic->type_picto) print img_picto('', $actionstatic->type_picto); else { - if ($histo[$key]['acode'] == 'AC_TEL') $out.=img_picto('', 'object_phoning').' '; - if ($histo[$key]['acode'] == 'AC_FAX') $out.=img_picto('', 'object_phoning_fax').' '; - if ($histo[$key]['acode'] == 'AC_EMAIL') $out.=img_picto('', 'object_email').' '; + if ($actionstatic->type_code == 'AC_RDV') $out.= img_picto('', 'object_group', '', false, 0, 0, '', 'paddingright').' '; + elseif ($actionstatic->type_code == 'AC_TEL') $out.= img_picto('', 'object_phoning', '', false, 0, 0, '', 'paddingright').' '; + elseif ($actionstatic->type_code == 'AC_FAX') $out.= img_picto('', 'object_phoning_fax', '', false, 0, 0, '', 'paddingright').' '; + elseif ($actionstatic->type_code == 'AC_EMAIL') $out.= img_picto('', 'object_email', '', false, 0, 0, '', 'paddingright').' '; + elseif ($actionstatic->type_code == 'AC_INT') $out.= img_picto('', 'object_intervention', '', false, 0, 0, '', 'paddingright').' '; + elseif (! preg_match('/_AUTO/', $actionstatic->type_code)) $out.= img_picto('', 'object_action', '', false, 0, 0, '', 'paddingright').' '; } - $out.=$actionstatic->type; - } - else { - $typelabel = $actionstatic->type; - if ($histo[$key]['acode'] != 'AC_OTH_AUTO') $typelabel = $langs->trans("ActionAC_MANUAL"); - $out.=$typelabel; } + $labeltype=$actionstatic->type_code; + if (empty($conf->global->AGENDA_USE_EVENT_TYPE) && empty($arraylist[$labeltype])) $labeltype='AC_OTH'; + if (! empty($arraylist[$labeltype])) $labeltype=$arraylist[$labeltype]; + $out.= dol_trunc($labeltype,28); $out.=''; if (isset($histo[$key]['type']) && $histo[$key]['type']=='action') { - $actionstatic->type_code=$histo[$key]['acode']; $transcode=$langs->trans("Action".$histo[$key]['acode']); $libelle=($transcode!="Action".$histo[$key]['acode']?$transcode:$histo[$key]['alabel']); //$actionstatic->libelle=$libelle; @@ -1572,7 +1662,7 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon='', $noprint= $propalstatic->type=$histo[$key]['ftype']; $out.=$propalstatic->getNomUrl(1); } else { - $out.= $langs->trans("ProposalDeleted"); + //$out.= ''.$langs->trans("ProposalDeleted").''; } } elseif (($histo[$key]['elementtype'] == 'order' || $histo[$key]['elementtype'] == 'commande') && ! empty($conf->commande->enabled)) @@ -1583,7 +1673,7 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon='', $noprint= $orderstatic->type=$histo[$key]['ftype']; $out.=$orderstatic->getNomUrl(1); } else { - $out.= $langs->trans("OrderDeleted"); + //$out.= ''.$langs->trans("OrderDeleted").''; } } elseif (($histo[$key]['elementtype'] == 'invoice' || $histo[$key]['elementtype'] == 'facture') && ! empty($conf->facture->enabled)) @@ -1594,7 +1684,7 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon='', $noprint= $facturestatic->type=$histo[$key]['ftype']; $out.=$facturestatic->getNomUrl(1,'compta'); } else { - $out.= $langs->trans("InvoiceDeleted"); + //$out.= ''.$langs->trans("InvoiceDeleted").''; } } else $out.=' '; @@ -1707,3 +1797,5 @@ function show_subsidiaries($conf,$langs,$db,$object) return $i; } + + diff --git a/htdocs/core/lib/doc.lib.php b/htdocs/core/lib/doc.lib.php index 99e576ad057..ae8410bd188 100644 --- a/htdocs/core/lib/doc.lib.php +++ b/htdocs/core/lib/doc.lib.php @@ -73,13 +73,15 @@ function doc_getlinedesc($line,$outputlangs,$hideref=0,$hidedesc=0,$issupplierli { $discount=new DiscountAbsolute($db); $discount->fetch($line->fk_remise_except); - $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromCreditNote",$discount->ref_facture_source); + $sourceref=!empty($discount->discount_type)?$discount->ref_invoive_supplier_source:$discount->ref_facture_source; + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromCreditNote",$sourceref); } elseif ($desc == '(DEPOSIT)' && $line->fk_remise_except) { $discount=new DiscountAbsolute($db); $discount->fetch($line->fk_remise_except); - $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromDeposit",$discount->ref_facture_source); + $sourceref=!empty($discount->discount_type)?$discount->ref_invoive_supplier_source:$discount->ref_facture_source; + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromDeposit",$sourceref); // Add date of deposit if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) $libelleproduitservice.=' ('.dol_print_date($discount->datec,'day','',$outputlangs).')'; } @@ -89,6 +91,12 @@ function doc_getlinedesc($line,$outputlangs,$hideref=0,$hidedesc=0,$issupplierli $discount->fetch($line->fk_remise_except); $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromExcessReceived",$discount->ref_facture_source); } + elseif ($desc == '(EXCESS PAID)' && $line->fk_remise_except) + { + $discount=new DiscountAbsolute($db); + $discount->fetch($line->fk_remise_except); + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromExcessPaid",$discount->ref_invoice_supplier_source); + } else { if ($idprod) diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 85d80f646d0..6ea633f1f1d 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -52,10 +52,11 @@ function dol_basename($pathfile) * @param int $mode 0=Return array minimum keys loaded (faster), 1=Force all keys like date and size to be loaded (slower), 2=Force load of date only, 3=Force load of size only * @param int $nohook Disable all hooks * @param string $relativename For recursive purpose only. Must be "" at first call. + * @param string $donotfollowsymlinks Do not follow symbolic links * @return array Array of array('name'=>'xxx','fullname'=>'/abc/xxx','date'=>'yyy','size'=>99,'type'=>'dir|file',...) * @see dol_dir_list_indatabase */ -function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="") +function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0) { global $db, $hookmanager; global $object; @@ -159,7 +160,11 @@ function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefil // if we're in a directory and we want recursive behavior, call this function again if ($recursive) { - $file_list = array_merge($file_list, dol_dir_list($path."/".$file, $types, $recursive, $filter, $excludefilter, $sortcriteria, $sortorder, $mode, $nohook, ($relativename!=''?$relativename.'/':'').$file)); + if (empty($donotfollowsymlinks) || ! is_link($path."/".$file)) + { + //var_dump('eee '. $path."/".$file. ' '.is_dir($path."/".$file).' '.is_link($path."/".$file)); + $file_list = array_merge($file_list, dol_dir_list($path."/".$file, $types, $recursive, $filter, $excludefilter, $sortcriteria, $sortorder, $mode, $nohook, ($relativename!=''?$relativename.'/':'').$file, $donotfollowsymlinks)); + } } } else if (! $isdir && (($types == "files") || ($types == "all"))) @@ -292,16 +297,32 @@ function dol_dir_list_in_database($path, $filter="", $excludefilter=null, $sortc * Complete $filearray with data from database. * This will call doldir_list_indatabase to complate filearray. * - * @param array $filearray Array of files get using dol_dir_list + * @param array $filearray Array of files get using dol_dir_list * @param string $relativedir Relative dir from DOL_DATA_ROOT * @return void */ function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir) { - global $db, $user; + global $conf, $db, $user; $filearrayindatabase = dol_dir_list_in_database($relativedir, '', null, 'name', SORT_ASC); + // TODO Remove this when PRODUCT_USE_OLD_PATH_FOR_PHOTO will be removed + global $modulepart; + if ($modulepart == 'produit' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { + global $object; + if (! empty($object->id)) + { + if (! empty($conf->product->enabled)) $upload_dirold = $conf->product->multidir_output[$object->entity].'/'.substr(substr("000".$object->id, -2),1,1).'/'.substr(substr("000".$object->id, -2),0,1).'/'.$object->id."/photos"; + else $upload_dirold = $conf->service->multidir_output[$object->entity].'/'.substr(substr("000".$object->id, -2),1,1).'/'.substr(substr("000".$object->id, -2),0,1).'/'.$object->id."/photos"; + + $relativedirold = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dirold); + $relativedirold = preg_replace('/^[\\/]/','',$relativedirold); + + $filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($relativedirold, '', null, 'name', SORT_ASC)); + } + } + //var_dump($filearray); //var_dump($filearrayindatabase); @@ -950,7 +971,7 @@ function dolCheckVirus($src_file) * Note: * - This function can be used only into a HTML page context. Use dol_move if you are outside. * - Test on antivirus is always done (if antivirus set). - * - Database of files is NOT updated. + * - Database of files is NOT updated (this is done by dol_add_file_process() that calls this function). * * @param string $src_file Source full path filename ($_FILES['field']['tmp_name']) * @param string $dest_file Target full path filename ($_FILES['field']['name']) @@ -1257,6 +1278,7 @@ function dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$ $result=dol_delete_file("$dir/$item", 1, $nophperrors); $count++; if ($result) $countdeleted++; + //else print 'Error on '.$item."\n"; } } } @@ -1267,6 +1289,7 @@ function dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$ $result=dol_delete_dir($dir, $nophperrors); $count++; if ($result) $countdeleted++; + //else print 'Error on '.$dir."\n"; } } } @@ -1554,28 +1577,10 @@ function dol_add_file_process($upload_dir, $allowoverwrite=0, $donotupdatesessio // Update table of files if ($donotupdatesession) { - $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir); - - if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) // If not a tmp dir + $result = addFileIntoDatabaseIndex($upload_dir, basename($destfile), $TFile['name'][$i], 'uploaded', 0); + if ($result < 0) { - $filename = basename($destfile); - $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); - $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); - - include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; - $ecmfile=new EcmFiles($db); - $ecmfile->filepath = $rel_dir; - $ecmfile->filename = $filename; - $ecmfile->label = md5_file(dol_osencode($destfull)); // MD5 of file content - $ecmfile->fullpath_orig = $TFile['name'][$i]; - $ecmfile->gen_or_uploaded = 'uploaded'; - $ecmfile->description = ''; // indexed content - $ecmfile->keyword = ''; // keyword content - $result = $ecmfile->create($user); - if ($result < 0) - { - setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); - } + setEventMessages('FailedToAddFileIntoDatabaseIndex', '', 'warnings'); } } @@ -1679,6 +1684,114 @@ function dol_remove_file_process($filenb,$donotupdatesession=0,$donotdeletefile= } } + +/** + * Add a file into database index. + * Called by dol_add_file_process when uploading a file and on other cases. + * See also commonGenerateDocument that also add/update database index when a file is generated. + * + * @param string $dir Directory name (full real path without ending /) + * @param string $file File name + * @param string $fullpathorig Full path of origin for file (can be '') + * @param string $mode How file was created ('uploaded', 'generated', ...) + * @param int $setsharekey Set also the share key + * @return int <0 if KO, 0 if nothing done, >0 if OK + */ +function addFileIntoDatabaseIndex($dir, $file, $fullpathorig='', $mode='uploaded', $setsharekey=0) +{ + global $db, $user; + + $result = 0; + + $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir); + + if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) // If not a tmp dir + { + $filename = basename($file); + $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); + $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); + + include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; + $ecmfile=new EcmFiles($db); + $ecmfile->filepath = $rel_dir; + $ecmfile->filename = $filename; + $ecmfile->label = md5_file(dol_osencode($dir.'/'.$file)); // MD5 of file content + $ecmfile->fullpath_orig = $fullpathorig; + $ecmfile->gen_or_uploaded = $mode; + $ecmfile->description = ''; // indexed content + $ecmfile->keyword = ''; // keyword content + if ($setsharekey) + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; + $ecmfile->share = getRandomPassword(true); + } + + $result = $ecmfile->create($user); + if ($result < 0) + { + dol_syslog($ecmfile->error); + } + } + + return $result; +} + + +/** + * Delete files into database index using search criterias. + * + * @param string $dir Directory name (full real path without ending /) + * @param string $file File name + * @param string $mode How file was created ('uploaded', 'generated', ...) + * @return int <0 if KO, 0 if nothing done, >0 if OK + */ +function deleteFilesIntoDatabaseIndex($dir, $file, $mode='uploaded') +{ + global $conf, $db, $user; + + $error = 0; + + if (empty($dir)) + { + dol_syslog("deleteFilesIntoDatabaseIndex: dir parameter can't be empty", LOG_ERR); + return -1; + } + + $db->begin(); + + $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir); + + $filename = basename($file); + $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); + $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); + + if (! $error) + { + $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'ecm_files'; + $sql.= ' WHERE entity = '.$conf->entity; + $sql.= " AND filepath = '" . $db->escape($rel_dir) . "'"; + if ($file) $sql.= " AND filename = '" . $db->escape($file) . "'"; + if ($mode) $sql.= " AND gen_or_uploaded = '" . $db->escape($mode) . "'"; + + $resql = $db->query($sql); + if (!$resql) + { + $error++; + dol_syslog(__METHOD__ . ' ' . $db->lasterror(), LOG_ERR); + } + } + + // Commit or rollback + if ($error) { + $db->rollback(); + return - 1 * $error; + } else { + $db->commit(); + return 1; + } +} + + /** * Convert an image file into another format. * This need Imagick php extension. @@ -2057,10 +2170,10 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, $original_file=$conf->facture->dir_output.'/'.$original_file; } // Wrapping pour les apercu propal - elseif ($modulepart == 'apercupropal' && !empty($conf->propal->dir_output)) + elseif ($modulepart == 'apercupropal' && !empty($conf->propal->multidir_output[$entity])) { if ($fuser->rights->propale->{$lire}) $accessallowed=1; - $original_file=$conf->propal->dir_output.'/'.$original_file; + $original_file=$conf->propal->multidir_output[$entity].'/'.$original_file; } // Wrapping pour les apercu commande elseif ($modulepart == 'apercucommande' && !empty($conf->commande->dir_output)) @@ -2105,10 +2218,10 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, $original_file=$conf->expensereport->dir_output.'/'.$original_file; } // Wrapping pour les images des stats propales - elseif ($modulepart == 'propalstats' && !empty($conf->propal->dir_temp)) + elseif ($modulepart == 'propalstats' && !empty($conf->propal->multidir_temp[$entity])) { if ($fuser->rights->propale->{$lire}) $accessallowed=1; - $original_file=$conf->propal->dir_temp.'/'.$original_file; + $original_file=$conf->propal->multidir_temp[$entity].'/'.$original_file; } // Wrapping pour les images des stats commandes elseif ($modulepart == 'orderstats' && !empty($conf->commande->dir_temp)) @@ -2272,13 +2385,13 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; } // Wrapping for mass actions - else if ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->dir_output)) + else if ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->multidir_output[$entity])) { if ($fuser->rights->propal->{$lire} || preg_match('/^specimen/i',$original_file)) { $accessallowed=1; } - $original_file=$conf->propal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; + $original_file=$conf->propal->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file; } else if ($modulepart == 'massfilesarea_orders') { @@ -2312,7 +2425,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, } $original_file=$conf->ficheinter->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; } - else if ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->propal->dir_output)) + else if ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->supplier_proposal->dir_output)) { if ($fuser->rights->supplier_proposal->{$lire} || preg_match('/^specimen/i',$original_file)) { @@ -2367,14 +2480,13 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, //$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; } // Wrapping pour les propales - else if ($modulepart == 'propal' && !empty($conf->propal->dir_output)) + else if ($modulepart == 'propal' && !empty($conf->propal->multidir_output[$entity])) { if ($fuser->rights->propale->{$lire} || preg_match('/^specimen/i',$original_file)) { $accessallowed=1; } - - $original_file=$conf->propal->dir_output.'/'.$original_file; + $original_file=$conf->propal->multidir_output[$entity].'/'.$original_file; $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; } diff --git a/htdocs/core/lib/fourn.lib.php b/htdocs/core/lib/fourn.lib.php index 44e7774cae6..ac956abf524 100644 --- a/htdocs/core/lib/fourn.lib.php +++ b/htdocs/core/lib/fourn.lib.php @@ -34,7 +34,7 @@ function facturefourn_prepare_head($object) { global $db, $langs, $conf; - + $h = 0; $head = array(); @@ -102,7 +102,7 @@ function facturefourn_prepare_head($object) function ordersupplier_prepare_head($object) { global $db, $langs, $conf, $user; - + $h = 0; $head = array(); @@ -111,15 +111,6 @@ function ordersupplier_prepare_head($object) $head[$h][2] = 'card'; $h++; - if (! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) - { - $langs->load("stocks"); - $head[$h][0] = DOL_URL_ROOT.'/fourn/commande/dispatch.php?id='.$object->id; - $head[$h][1] = $langs->trans("OrderDispatch"); - $head[$h][2] = 'dispatch'; - $h++; - } - if (empty($conf->global->MAIN_DISABLE_CONTACTS_TAB)) { $nbContact = count($object->liste_contact(-1,'internal')) + count($object->liste_contact(-1,'external')); @@ -130,7 +121,16 @@ function ordersupplier_prepare_head($object) $h++; } - // Show more tabs from modules + if (! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) + { + $langs->load("stocks"); + $head[$h][0] = DOL_URL_ROOT.'/fourn/commande/dispatch.php?id='.$object->id; + $head[$h][1] = $langs->trans("OrderDispatch"); + $head[$h][2] = 'dispatch'; + $h++; + } + + // Show more tabs from modules // Entries must be declared in modules descriptor with line // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab // $this->tabs = array('entity:-tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to remove a tab @@ -193,7 +193,7 @@ function supplierorder_admin_prepare_head() $head[$h][1] = $langs->trans("SuppliersInvoice"); $head[$h][2] = 'invoice'; $h++; - + $head[$h][0] = DOL_URL_ROOT."/admin/supplier_payment.php"; $head[$h][1] = $langs->trans("SuppliersPayment"); $head[$h][2] = 'supplierpayment'; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index ce95f2912c8..a1b9c2d2c2c 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -121,11 +121,6 @@ function getEntity($element, $shared=1, $forceentity=null) { global $conf, $mc; - // For backward compatibilty - if ($element == 'actioncomm') $element='agenda'; - if ($element == 'fichinter') $element='intervention'; - if ($element == 'categorie') $element='category'; - if (is_object($mc)) { return $mc->getEntity($element, $shared, $forceentity); @@ -263,6 +258,7 @@ function GETPOSTISSET($paramname) * ''=no check (deprecated) * 'none'=no check (only for param that should have very rich content) * 'int'=check it's numeric (integer or float) + * 'intcomma'=check it's integer+comma ('1,2,3,4...') * 'alpha'=check it's text and sign * 'aZ'=check it's a-z only * 'aZ09'=check it's simple alpha string (recommended for keys) @@ -549,10 +545,10 @@ function GETPOST($paramname, $check='none', $method=0, $filter=NULL, $options=NU case 'array': if (! is_array($out) || empty($out)) $out=array(); break; - case 'nohtml': + case 'nohtml': // Recommended for most scalar parameters $out=dol_string_nohtmltag($out, 0); break; - case 'alphanohtml': // Recommended for search params + case 'alphanohtml': // Recommended for search parameters if (! is_array($out)) { $out=trim($out); @@ -1077,11 +1073,12 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename=' * @param string $picto Add a picto on tab title * @param int $pictoisfullpath If 1, image path is a full path. If you set this to 1, you can use url returned by dol_buildpath('/mymodyle/img/myimg.png',1) for $picto. * @param string $morehtmlright Add more html content on right of tabs title + * @param string $morecss More Css * @return void */ -function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='') +function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='') { - print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright); + print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss); } /** @@ -1094,9 +1091,10 @@ function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto * @param string $picto Add a picto on tab title * @param int $pictoisfullpath If 1, image path is a full path. If you set this to 1, you can use url returned by dol_buildpath('/mymodyle/img/myimg.png',1) for $picto. * @param string $morehtmlright Add more html content on right of tabs title + * @param string $morecss More Css * @return string */ -function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='') +function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='') { global $conf, $langs, $hookmanager; @@ -1161,7 +1159,7 @@ function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $pi { if (!empty($links[$i][0])) { - $out.=''.$links[$i][1].''."\n"; + $out.=''.$links[$i][1].''."\n"; } else { @@ -1173,13 +1171,13 @@ function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $pi //print "x $i $active ".$links[$i][2]." z"; if ($isactive) { - $out.=''; + $out.=''; $out.=$links[$i][1]; $out.=''."\n"; } else { - $out.=''; + $out.=''; $out.=$links[$i][1]; $out.=''."\n"; } @@ -1198,14 +1196,14 @@ function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $pi if (isset($links[$i][2]) && $links[$i][2] == 'image') { if (!empty($links[$i][0])) - $outmore.=''.$links[$i][1].''."\n"; + $outmore.=''.$links[$i][1].''."\n"; else $outmore.=''.$links[$i][1].''."\n"; } else if (! empty($links[$i][1])) { - $outmore.=''; + $outmore.=''; $outmore.=preg_replace('/([a-z])\/([a-z])/i', '\\1 / \\2', $links[$i][1]); // Replace x/y with x / y to allow wrap on long composed texts. $outmore.=''."\n"; } @@ -1224,7 +1222,7 @@ function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $pi $tabsname=str_replace("@", "", $picto); $out.='
    '; - $out.=''.$langs->trans("More").'... ('.$nbintab.')'; + $out.=''.$langs->trans("More").'... ('.$nbintab.')'; $out.='
    '; $out.=$outmore; $out.='
    '; @@ -1301,6 +1299,7 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r $maxvisiblephotos=1; $showimage=1; + $entity=(empty($object->entity)?$conf->entity:$object->entity); $showbarcode=empty($conf->barcode->enabled)?0:($object->barcode?1:0); if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0; $modulepart='unknown'; @@ -1327,10 +1326,10 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r if ($object->element == 'product') { $width=80; $cssclass='photoref'; - $showimage=$object->is_photo_available($conf->product->multidir_output[$object->entity]); + $showimage=$object->is_photo_available($conf->product->multidir_output[$entity]); $maxvisiblephotos=(isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO)?$conf->global->PRODUCT_MAX_VISIBLE_PHOTO:5); if ($conf->browser->phone) $maxvisiblephotos=1; - if ($showimage) $morehtmlleft.='
    '.$object->show_photos($conf->product->multidir_output[$object->entity],'small',$maxvisiblephotos,0,0,0,$width,0).'
    '; + if ($showimage) $morehtmlleft.='
    '.$object->show_photos('product', $conf->product->multidir_output[$entity],'small',$maxvisiblephotos,0,0,0,$width,0).'
    '; else { if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) { @@ -1343,6 +1342,25 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r //} } } + elseif ($object->element == 'ticketsup') + { + $width=80; $cssclass='photoref'; + $showimage=$object->is_photo_available($conf->ticketsup->multidir_output[$entity].'/'.$object->track_id); + $maxvisiblephotos=(isset($conf->global->TICKETSUP_MAX_VISIBLE_PHOTO)?$conf->global->TICKETSUP_MAX_VISIBLE_PHOTO:2); + if ($conf->browser->phone) $maxvisiblephotos=1; + if ($showimage) $morehtmlleft.='
    '.$object->show_photos('ticketsup', $conf->ticketsup->multidir_output[$entity],'small',$maxvisiblephotos,0,0,0,$width,0).'
    '; + else + { + if (!empty($conf->global->TICKETSUP_NODISPLAYIFNOPHOTO)) { + $nophoto=''; + $morehtmlleft.='
    '; + } + //elseif ($conf->browser->layout != 'phone') { // Show no photo link + $nophoto='/public/theme/common/nophoto.png'; + $morehtmlleft.='
    No photo
    '; + //} + } + } else { if ($showimage) @@ -1354,7 +1372,7 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) { $objectref = dol_sanitizeFileName($object->ref); - $dir_output = $conf->$modulepart->dir_output . "/"; + $dir_output = $conf->$modulepart->multidir_output[$entity] . "/"; if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) { $subdir = get_exdir($object->id, 2, 0, 0, $object, $modulepart).$objectref; // the objectref dir is not include into get_exdir when used with level=2, so we add it here @@ -1489,6 +1507,14 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r if ($object->frequency == 0) $morehtmlstatus.=$object->getLibStatut(2); else $morehtmlstatus.=$object->getLibStatut(5); } + elseif ($object->element == 'project_task') + { + $object->fk_statut = 1; + if ($object->progress > 0) $object->fk_statut = 2; + if ($object->progress >= 100) $object->fk_statut = 3; + $tmptxt=$object->getLibStatut(5); + $morehtmlstatus.=$tmptxt; // No status on task + } else { // Generic case $tmptxt=$object->getLibStatut(6); if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5); @@ -1999,7 +2025,7 @@ function dol_now($mode='gmt') }*/ else if ($mode == 'tzuser') // Time for now with user timezone added { - //print 'eeee'.time().'-'.mktime().'-'.gmmktime(); + //print 'time: '.time().'-'.mktime().'-'.gmmktime(); $offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60; $offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60; $ret=(int) dol_now('gmt')+($offsettz+$offsetdst); @@ -2085,7 +2111,7 @@ function dol_print_url($url,$target='_blank',$max=32,$withpicto=0) */ function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64,$showinvalid=1,$withpicto=0) { - global $conf,$user,$langs; + global $conf,$user,$langs,$hookmanager; $newemail=$email; @@ -2120,7 +2146,15 @@ function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64,$showinvalid= $newemail.=img_warning($langs->trans("ErrorBadEMail",$email)); } } - return '
    '.($withpicto?img_picto($langs->trans("EMail"), 'object_email.png').' ':'').$newemail.'
    '; + + $rep = '
    '.($withpicto?img_picto($langs->trans("EMail"), 'object_email.png').' ':'').$newemail.'
    '; + if ($hookmanager) { + $parameters = array('cid' => $cid, 'socid' => $socid,'addlink' => $addlink, 'picto' => $withpicto); + $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email); + $rep.=$hookmanager->resPrint; + } + + return $rep; } /** @@ -2530,12 +2564,12 @@ function dol_print_phone($phone,$countrycode='',$cid=0,$socid=0,$addlink='',$sep $rep=''; if ($hookmanager) { - $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid,'titlealt' => $titlealt, 'picto' => $withpicto); - $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone); - $rep.=$hookmanager->resPrint; - } - if (empty($reshook)) - { + $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid,'titlealt' => $titlealt, 'picto' => $withpicto); + $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone); + $rep.=$hookmanager->resPrint; + } + if (empty($reshook)) + { $picto = ''; if($withpicto){ if($withpicto=='fax'){ @@ -3026,7 +3060,7 @@ function dol_trunc($string,$size=40,$trunc='right',$stringencoding='UTF-8',$nodo * Example: picto.png@mymodule if picto.png is stored into htdocs/mymodule/img * Example: /mydir/mysubdir/picto.png if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1) * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') - * @param int $pictoisfullpath If 1, image path is a full path + * @param boolean|int $pictoisfullpath If true or 1, image path is a full path * @param int $srconly Return only content of the src attribute of img. * @param int $notitle 1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip. * @param string $alt Force alt for bind peoplae @@ -3038,55 +3072,124 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $ { global $conf, $langs; + // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto + $url = DOL_URL_ROOT; + $theme = $conf->theme; + $path = 'theme/'.$theme; + // Define fullpathpicto to use into src - if ($pictoisfullpath) - { + if ($pictoisfullpath) { // Clean parameters - if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) $picto .= '.png'; + if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) { + $picto .= '.png'; + } $fullpathpicto = $picto; } - else - { + else { + $pictowithoutext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto); + //if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on'))) - if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on'))) - { - $fakey = $picto; $facolor=''; $fasize=''; - if ($picto == 'switch_off') { $fakey = 'fa-toggle-off'; $facolor='#999'; $fasize='2em'; } - if ($picto == 'switch_on') { $fakey = 'fa-toggle-on'; $facolor='#227722'; $fasize='2em'; } - if ($picto == 'off') { $fakey = 'fa-square-o'; $fasize='1.3em'; } - if ($picto == 'on') { $fakey = 'fa-check-square-o'; $fasize='1.3em'; } - $enabledisablehtml=''; - $enabledisablehtml.=''; - if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $enabledisablehtml.=$titlealt; - $enabledisablehtml.=''; + if (empty($srconly) && in_array($pictowithoutext, array('bank', 'delete', 'edit', 'off', 'on', 'play', 'playdisabled', 'printer', 'resize', 'switch_off', 'switch_on', 'unlink', 'uparrow'))) { + $fakey = $pictowithoutext; + $facolor = ''; + $fasize = ''; + if ($pictowithoutext == 'switch_off') { + $fakey = 'fa-toggle-off'; + $facolor = '#999'; + $fasize = '2em'; + } + elseif ($pictowithoutext == 'switch_on') { + $fakey = 'fa-toggle-on'; + $facolor = '#227722'; + $fasize = '2em'; + } + elseif ($pictowithoutext == 'off') { + $fakey = 'fa-square-o'; + $fasize = '1.3em'; + } + elseif ($pictowithoutext == 'on') { + $fakey = 'fa-check-square-o'; + $fasize = '1.3em'; + } + elseif ($pictowithoutext == 'bank') { + $fakey = 'fa-bank'; + $facolor = '#444'; + } + elseif ($pictowithoutext == 'delete') { + $fakey = 'fa-trash'; + $facolor = '#444'; + } + elseif ($pictowithoutext == 'edit') { + $fakey = 'fa-pencil'; + $facolor = '#444'; + } + elseif ($pictowithoutext == 'printer') { + $fakey = 'fa-print'; + $fasize = '1.2em'; + $facolor = '#444'; + } + elseif ($pictowithoutext == 'resize') { + $fakey = 'fa-crop'; + $facolor = '#444'; + } + elseif ($pictowithoutext == 'uparrow') { + $fakey = 'fa-mail-forward'; + $facolor = '#555'; + } + elseif ($pictowithoutext == 'unlink') { + $fakey = 'fa-chain-broken'; + $facolor = '#555'; + } + elseif ($pictowithoutext == 'playdisabled') { + $fakey = 'fa-play'; + $facolor = '#ccc'; + } + else { + $fakey = 'fa-'.$pictowithoutext; + $facolor = '#444'; + } + + if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) { + $morecss.= ($morecss?' ':'').$reg[1]; + } + $enabledisablehtml = ''; + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $enabledisablehtml.= $titlealt; + } + $enabledisablehtml.= ''; + return $enabledisablehtml; } - // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto - $url = DOL_URL_ROOT; - $theme = $conf->theme; - - $path = 'theme/'.$theme; - if (! empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module - else if (! empty($conf->global->MAIN_OVERWRITE_THEME_RES)) $path = $conf->global->MAIN_OVERWRITE_THEME_RES.'/theme/'.$conf->global->MAIN_OVERWRITE_THEME_RES; // To allow an external module to overwrite image resources whatever is activated theme - else if (! empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module + if (! empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) { + $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module + } + else if (! empty($conf->global->MAIN_OVERWRITE_THEME_RES)) { + $path = $conf->global->MAIN_OVERWRITE_THEME_RES.'/theme/'.$conf->global->MAIN_OVERWRITE_THEME_RES; // To allow an external module to overwrite image resources whatever is activated theme + } + else if (! empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) { + $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module + } // If we ask an image into $url/$mymodule/img (instead of default path) - if (preg_match('/^([^@]+)@([^@]+)$/i',$picto,$regs)) - { + if (preg_match('/^([^@]+)@([^@]+)$/i',$picto,$regs)) { $picto = $regs[1]; $path = $regs[2]; // $path is $mymodule } // Clean parameters - if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) $picto .= '.png'; + if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) { + $picto .= '.png'; + } // If alt path are defined, define url where img file is, according to physical path - foreach ($conf->file->dol_document_root as $type => $dirroot) // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...) - { - if ($type == 'main') continue; - if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded - { - $url=DOL_URL_ROOT.$conf->file->dol_url_root[$type]; + // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...) + foreach ($conf->file->dol_document_root as $type => $dirroot) { + if ($type == 'main') { + continue; + } + // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded + if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) { + $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type]; break; } } @@ -3095,15 +3198,16 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $ $fullpathpicto = $url.'/'.$path.'/img/'.$picto; } - if ($srconly) return $fullpathpicto; - else - { + if ($srconly) { + return $fullpathpicto; + } + else { // tag title is used for tooltip on , tag alt can be used with very simple text on image for bind people //$tmparray=array(0=>$titlealt); //if (empty($notitle) && preg_match('/:[^\s0-9]/',$titlealt)) $tmparray=explode(':',$titlealt); // We explode if we have TextA:TextB. Not if we have TextA: TextB //$title=$tmparray[0]; //$alt=empty($tmparray[1])?'':$tmparray[1]; - $title=$titlealt; + $title = $titlealt; return ''.dol_escape_htmltag($alt).''; // Alt is used for accessibility, title for popup } } @@ -3301,6 +3405,7 @@ function img_delete($titlealt = 'default', $other = 'class="pictodelete"') if ($titlealt == 'default') $titlealt = $langs->trans('Delete'); return img_picto($titlealt, 'delete.png', $other); + //return ''; } /** @@ -3520,6 +3625,24 @@ function img_allow($allow, $titlealt = 'default') return '-'; } +/** + * Return image of a credit card according to its brand name + * + * @param string $brand Brand name of credit card + * @return string Return img tag + */ +function img_credit_card($brand) +{ + if ($brand == 'Visa') {$brand='cc-visa';} + elseif ($brand == 'MasterCard') {$brand='cc-mastercard';} + elseif ($brand == 'American Express') {$brand='cc-amex';} + elseif ($brand == 'Discover') {$brand='cc-discover';} + elseif ($brand == 'JCB') {$brand='cc-jcb';} + elseif ($brand == 'Diners Club') {$brand='cc-diners-club';} + elseif (! in_array($brand, array('cc-visa','cc-mastercard','cc-amex','cc-discover','cc-jcb','cc-diners-club'))) {$brand='credit-card';} + + return ''; +} /** * Show MIME img of a file @@ -3984,7 +4107,7 @@ function load_fiche_titre($titre, $morehtmlright='', $picto='title_generic.png', } if (dol_strlen($morehtmlright)) { - $return.= '
    '.$morehtmlright.''.$morehtmlright.'
    '."\n"; @@ -4664,7 +4787,7 @@ function getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1) { global $db, $mysoc; - dol_syslog("getTaxesFromId vatrowid=".$vatrate); + dol_syslog("getTaxesFromId vat id or rate = ".$vatrate); // Search local taxes $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy"; @@ -4681,8 +4804,9 @@ function getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1) } $sql.=", ".MAIN_DB_PREFIX."c_country as c"; - if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'"; // local tax in spain use the buyer country ?? - else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'"; + /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'"; // vat in spain use the buyer country ?? + else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";*/ + $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'"; $sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'"; } @@ -5285,35 +5409,41 @@ function picto_required() /** * Clean a string from all HTML tags and entities. * This function differs from strip_tags because: - * -
    are replace with \n - * - if entities are found, they are decoded before the strip - * - you can decide to convert line feed into spaces + * -
    are replaced with \n if removelinefeed=0 or 1 + * - if entities are found, they are decoded BEFORE the strip + * - you can decide to convert line feed into a space * * @param string $stringtoclean String to clean - * @param integer $removelinefeed 1=Replace also new lines by a space, 0=Only last one are removed + * @param integer $removelinefeed 1=Replace all new lines by 1 space, 0=Only ending new lines are removed others are replaced with \n, 2=Ending new lines are removed but others are kept with a same number of \n than nb of
    when there is both "...
    \n..." * @param string $pagecodeto Encoding of input/output string + * @param integer $strip_tags 0=Use internal strip, 1=Use strip_tags() php function (bugged when text contains a < char that is not for a html tag) * @return string String cleaned * * @see dol_escape_htmltag strip_tags */ -function dol_string_nohtmltag($stringtoclean,$removelinefeed=1,$pagecodeto='UTF-8') +function dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0) { - // TODO Try to replace with strip_tags($stringtoclean) - $pattern = "/<[^<>]+>/"; - $stringtoclean = preg_replace('/]*>/', "\n", $stringtoclean); - $temp = dol_html_entity_decode($stringtoclean,ENT_COMPAT,$pagecodeto); + if ($removelinefeed == 2) $stringtoclean = preg_replace('/]*>\n+/ims', '
    ', $stringtoclean); + $temp = preg_replace('/]*>/i', "\n", $stringtoclean); - // Exemple of $temp: 0000-021 - $temp = preg_replace($pattern,"",$temp); // pass 1 - // $temp after pass 1: 0000-021 - $temp = preg_replace($pattern,"",$temp); // pass 2 - // $temp after pass 2: 0000-021 + if ($strip_tags) { + $temp = strip_tags($temp); + } else { + $pattern = "/<[^<>]+>/"; + // Exemple of $temp: 0000-021 + $temp = preg_replace($pattern,"",$temp); // pass 1 + // $temp after pass 1: 0000-021 + $temp = preg_replace($pattern,"",$temp); // pass 2 + // $temp after pass 2: 0000-021 + } + + $temp = dol_html_entity_decode($temp,ENT_COMPAT,$pagecodeto); // Supprime aussi les retours - if ($removelinefeed) $temp=str_replace(array("\r\n","\r","\n")," ",$temp); + if ($removelinefeed == 1) $temp=str_replace(array("\r\n","\r","\n")," ",$temp); // et les espaces doubles - while(strpos($temp," ")) + while (strpos($temp," ")) { $temp = str_replace(" "," ",$temp); } @@ -5622,6 +5752,7 @@ function dol_textishtml($msg,$option=0) elseif (preg_match('//i',$msg)) return true; elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg)) return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp) elseif (preg_match('/&#[0-9]{2,3};/i',$msg)) return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp) + return false; } } @@ -5665,11 +5796,31 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob $substitutionarray=array(); - if (empty($exclude) || ! in_array('system', $exclude)) + if (empty($exclude) || ! in_array('user', $exclude)) { - $substitutionarray['__(AnyTranslationKey)__']=$outputlangs->trans('TranslationOfKey'); - $substitutionarray['__[AnyConstantKey]__']=$outputlangs->trans('ValueOfConstant'); - $substitutionarray['__DOL_MAIN_URL_ROOT__']=DOL_MAIN_URL_ROOT; + // Add SIGNATURE into substitutionarray first, so, when we will make the substitution, + // this will include signature content first and then replace var found into content of signature + $signature = $user->signature; + $substitutionarray=array_merge($substitutionarray, array( + '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '') + ) + ); + // For backward compatibility + if ($onlykey != 2) + { + $substitutionarray['__SIGNATURE__'] = (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : ''); + } + + $substitutionarray=array_merge($substitutionarray, array( + '__USER_ID__' => (string) $user->id, + '__USER_LOGIN__' => (string) $user->login, + '__USER_LASTNAME__' => (string) $user->lastname, + '__USER_FIRSTNAME__' => (string) $user->firstname, + '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs), + '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'), + '__USER_REMOTE_IP__' => (string) $_SERVER['REMOTE_ADDR'] + ) + ); } if ((empty($exclude) || ! in_array('mycompany', $exclude)) && is_object($mysoc)) { @@ -5691,6 +5842,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id )); } + if (($onlykey || is_object($object)) && (empty($exclude) || ! in_array('object', $exclude))) { if ($onlykey) @@ -5703,9 +5855,11 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__'; $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__'; + $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__'; - if (is_object($object) && $object->element == 'shipping') + if (is_object($object) && $object->element == 'member') { + $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__'; $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__'; $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__'; $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__'; @@ -5719,7 +5873,8 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service'; $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service'; - $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'LinkToPayOnlineIfApplicable'; + $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable'; + $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable'; $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)'; $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)'; $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order'; @@ -5739,11 +5894,12 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : '')); $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : ''); - // TODO USe this ? + // TODO Use this ? $msgishtml = 0; $birthday = dol_print_date($object->birth,'day'); + $substitutionarray['__MEMBER_ID__']=$object->id; if (method_exists($object, 'getCivilityLabel')) $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel(); $substitutionarray['__MEMBER_FIRSTNAME__']=$msgishtml?dol_htmlentitiesbr($object->firstname):$object->firstname; $substitutionarray['__MEMBER_LASTNAME__']=$msgishtml?dol_htmlentitiesbr($object->lastname):$object->lastname; @@ -5766,11 +5922,15 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob { $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object)?$object->id:''); $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object)?$object->name:''); + $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object)?$object->name_alias:''); + $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object)?$object->email:''); } elseif (is_object($object->thirdparty) && $object->thirdparty->id > 0) { $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty)?$object->thirdparty->id:''); $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty)?$object->thirdparty->name:''); + $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty)?$object->thirdparty->name_alias:''); + $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty)?$object->thirdparty->email:''); } if (is_object($object->projet) && $object->projet->id > 0) @@ -5812,7 +5972,27 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob } } - $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'TODO'; + // Complete substitution array with the url to make online payment + $paymenturl=''; + if (empty($substitutionarray['__REF__'])) + { + $paymenturl=''; + } + else + { + // Set the online payment url link into __ONLINE_PAYMENT_URL__ key + require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; + $outputlangs->load('paypal'); + $typeforonlinepayment='free'; + if (is_object($object) && $object->element == 'commande') $typeforonlinepayment='order'; + if (is_object($object) && $object->element == 'facture') $typeforonlinepayment='invoice'; + if (is_object($object) && $object->element == 'member') $typeforonlinepayment='member'; + $url=getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']); + $paymenturl=$url; + } + + $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__']=($paymenturl?$outputlangs->trans("PredefinedMailContentLink", $paymenturl):''); + $substitutionarray['__ONLINE_PAYMENT_URL__']=$paymenturl; } } if (empty($exclude) || ! in_array('objectamount', $exclude)) @@ -5862,31 +6042,11 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob )); } - if (empty($exclude) || ! in_array('user', $exclude)) + if (empty($exclude) || ! in_array('system', $exclude)) { - // Add SIGNATURE into substitutionarray first, so, when we will make the substitution, - // this will also replace var found into content of signature - $signature = $user->signature; - $substitutionarray=array_merge($substitutionarray, array( - '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '') - ) - ); - // For backward compatibility - if ($onlykey != 2) - { - $substitutionarray['__SIGNATURE__'] = (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : ''); - } - - $substitutionarray=array_merge($substitutionarray, array( - '__USER_ID__' => (string) $user->id, - '__USER_LOGIN__' => (string) $user->login, - '__USER_LASTNAME__' => (string) $user->lastname, - '__USER_FIRSTNAME__' => (string) $user->firstname, - '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs), - '__USER_SUPERVISOR_ID__' => (string) $user->fk_user, - '__USER_REMOTE_IP__' => (string) $_SERVER['REMOTE_ADDR'] - ) - ); + $substitutionarray['__(AnyTranslationKey)__']=$outputlangs->trans('TranslationOfKey'); + $substitutionarray['__[AnyConstantKey]__']=$outputlangs->trans('ValueOfConstant'); + $substitutionarray['__DOL_MAIN_URL_ROOT__']=DOL_MAIN_URL_ROOT; } if (! empty($conf->multicompany->enabled)) { @@ -5897,8 +6057,8 @@ function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $ob } /** - * Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval). - * Texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced. + * Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval), + * and texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced. * Example of usage: * $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $thirdparty); * complete_substitutions_array($substitutionarray, $langs, $thirdparty); @@ -5942,7 +6102,8 @@ function make_substitutions($text, $substitutionarray, $outputlangs=null) if (dol_textishtml($text,1)) $msgishtml = 1; $keyfound = $reg[1]; - $newval=empty($conf->global->$keyfound)?'':$conf->global->$keyfound; + if (preg_match('/(_pass|password|secret|_key|key$)/i', $keyfound)) $newval = '*****forbidden*****'; + else $newval=empty($conf->global->$keyfound)?'':$conf->global->$keyfound; $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml?dol_htmlentitiesbr($newval):$newval, $text); } @@ -6138,12 +6299,19 @@ function setEventMessage($mesgs, $style='mesgs') */ function setEventMessages($mesg, $mesgs, $style='mesgs') { - if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages'); - if (empty($mesgs)) setEventMessage($mesg, $style); + if (empty($mesg) && empty($mesgs)) + { + dol_syslog("Try to add a message in stack with empty message", LOG_WARNING); + } else { - if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style); // Add message string if not already into array - setEventMessage($mesgs, $style); + if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages'); + if (empty($mesgs)) setEventMessage($mesg, $style); + else + { + if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style); // Add message string if not already into array + setEventMessage($mesgs, $style); + } } } @@ -6362,27 +6530,30 @@ function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensiti // Clean parameters $order=strtolower($order); - $sizearray=count($array); - if (is_array($array) && $sizearray>0) + if (is_array($array)) { - $temp = array(); - foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index]; - - if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp); - else + $sizearray=count($array); + if ($sizearray>0) { - ($case_sensitive) ? natsort($temp) : natcasesort($temp); - if($order!='asc') $temp=array_reverse($temp,TRUE); + $temp = array(); + foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index]; + + if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp); + else + { + ($case_sensitive) ? natsort($temp) : natcasesort($temp); + if($order!='asc') $temp=array_reverse($temp,TRUE); + } + + $sorted = array(); + + foreach(array_keys($temp) as $key) + { + (is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key]; + } + + return $sorted; } - - $sorted = array(); - - foreach(array_keys($temp) as $key) - { - (is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key]; - } - - return $sorted; } return $array; } @@ -6980,6 +7151,8 @@ function natural_search($fields, $value, $mode=0, $nofirstand=0) $i3 = 0; foreach($tmpcrits as $tmpcrit) { + if(empty($tmpcrit)) continue; + $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : ''); if (preg_match('/\.(id|rowid)$/', $field)) // Special case for rowid that is sometimes a ref so used as a search field @@ -7102,7 +7275,7 @@ function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='' if ($alldata == 1) { - if ($num_mime !== false) return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath), 'mime'=>dol_mimetype($relativepath), ); + if ($num_mime !== false) return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param?'&'.$param:''), 'mime'=>dol_mimetype($relativepath), ); else return array(); } diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index 3cbef60f5fd..dc223f58ec3 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -715,9 +715,12 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m global $conf,$user; if (! is_object($objsoc)) $valueforccc=$objsoc; - else if($table == "commande_fournisseur" || $table == "facture_fourn" ) $valueforccc=$objsoc->code_fournisseur; + else if ($table == "commande_fournisseur" || $table == "facture_fourn" ) $valueforccc=$objsoc->code_fournisseur; else $valueforccc=$objsoc->code_client; + $sharetable = $table; + if ($table == 'facture' || $table == 'invoice') $sharetable = 'invoicenumber'; // for getEntity function + // Clean parameters if ($date == '') $date=dol_now(); // We use local year and month of PHP server to search numbers // but we should use local year and month of user @@ -799,15 +802,15 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m } // Personalized field {XXX-1} à {XXX-9} - /*$maskperso=array(); + $maskperso=array(); $maskpersonew=array(); $tmpmask=$mask; - while (preg_match('/\{([A-Z]+)\-([1-9])\}/i',$tmpmask,$regKey)) + while (preg_match('/\{([A-Z]+)\-([1-9])\}/',$tmpmask,$regKey)) { - $maskperso[$regKey[1]]='\{'.$regKey[1]+'\-'.$regKey[2].'\}'; - $maskpersonew[$regKey[1]]=str_pad('', '_', $regKey[2], STR_PAD_RIGHT); - $tmpmask=preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew, $tmpmask); - }*/ + $maskperso[$regKey[1]]='{'.$regKey[1].'-'.$regKey[2].'}'; + $maskpersonew[$regKey[1]]=str_pad('', $regKey[2], '_', STR_PAD_RIGHT); + $tmpmask=preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew[$regKey[1]], $tmpmask); + } if (strstr($mask,'user_extra_')) { @@ -824,10 +827,10 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m $maskwithonlyymcode=preg_replace('/\{(c+)(0*)\}/i',$maskrefclient,$maskwithonlyymcode); $maskwithonlyymcode=preg_replace('/\{(t+)\}/i',$masktype_value,$maskwithonlyymcode); $maskwithonlyymcode=preg_replace('/\{(u+)\}/i',$maskuser_value,$maskwithonlyymcode); - /*foreach($maskperso as $key => $val) + foreach($maskperso as $key => $val) { - $maskwithonlyymcode=preg_replace('/'.$val.'/i', $maskpersonew[$key], $maskwithonlyymcode); - }*/ + $maskwithonlyymcode=preg_replace('/'.preg_quote($val,'/').'/i', $maskpersonew[$key], $maskwithonlyymcode); + } $maskwithnocode=$maskwithonlyymcode; $maskwithnocode=preg_replace('/\{yyyy\}/i','yyyy',$maskwithnocode); $maskwithnocode=preg_replace('/\{yy\}/i','yy',$maskwithnocode); @@ -971,6 +974,10 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'),str_pad("",dol_strlen($maskrefclient),"_"),$maskLike); if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'),$masktype_value,$maskLike); if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'),$maskuser_value,$maskLike); + foreach($maskperso as $key => $val) + { + $maskLike = str_replace(dol_string_nospecial($maskperso[$key]),$maskpersonew[$key],$maskLike); + } // Get counter in database $counter=0; @@ -979,7 +986,7 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m $sql.= " WHERE ".$field." LIKE '".$maskLike."'"; $sql.= " AND ".$field." NOT LIKE '(PROV%)'"; if ($bentityon) // only if entity enable - $sql.= " AND entity IN (".getEntity($table, 1).")"; + $sql.= " AND entity IN (".getEntity($sharetable).")"; if ($where) $sql.=$where; if ($sqlwhere) $sql.=' AND '.$sqlwhere; @@ -1027,7 +1034,7 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m $sql.= " WHERE ".$field." LIKE '".$maskLike."'"; $sql.= " AND ".$field." NOT LIKE '%PROV%'"; if ($bentityon) // only if entity enable - $sql.= " AND entity IN (".getEntity($table, 1).")"; + $sql.= " AND entity IN (".getEntity($sharetable).")"; if ($where) $sql.=$where; if ($sqlwhere) $sql.=' AND '.$sqlwhere; @@ -1081,7 +1088,7 @@ function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$m //$sql.= " WHERE ".$field." not like '(%'"; $maskrefclient_sql.= " WHERE ".$field." LIKE '".$maskrefclient_maskLike."'"; if ($bentityon) // only if entity enable - $maskrefclient_sql.= " AND entity IN (".getEntity($table, 1).")"; + $maskrefclient_sql.= " AND entity IN (".getEntity($sharetable).")"; if ($where) $maskrefclient_sql.=$where; //use the same optional where as general mask if ($sqlwhere) $maskrefclient_sql.=' AND '.$sqlwhere; //use the same sqlwhere as general mask $maskrefclient_sql.=' AND (SUBSTRING('.$field.', '.(strpos($maskwithnocode,$maskrefclient)+1).', '.dol_strlen($maskrefclient_maskclientcode).")='".$maskrefclient_clientcode."')"; @@ -1448,7 +1455,7 @@ function dol_set_user_param($db, $conf, &$user, $tab) foreach ($tab as $key => $value) { if ($i > 0) $sql.=','; - $sql.="'".$key."'"; + $sql.="'".$db->escape($key)."'"; $i++; } $sql.= ")"; @@ -1469,7 +1476,7 @@ function dol_set_user_param($db, $conf, &$user, $tab) { $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)"; $sql.= " VALUES (".$user->id.",".$conf->entity.","; - $sql.= " '".$key."','".$db->escape($value)."')"; + $sql.= " '".$db->escape($key)."','".$db->escape($value)."')"; dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG); $result=$db->query($sql); diff --git a/htdocs/core/lib/member.lib.php b/htdocs/core/lib/member.lib.php index 4da1ed91fae..3148d484ab5 100644 --- a/htdocs/core/lib/member.lib.php +++ b/htdocs/core/lib/member.lib.php @@ -55,9 +55,11 @@ function member_prepare_head(Adherent $object) if (! empty($user->rights->adherent->cotisation->lire)) { + $nbSubscription = is_array($object->subscriptions)?count($object->subscriptions):0; $head[$h][0] = DOL_URL_ROOT.'/adherents/subscription.php?rowid='.$object->id; $head[$h][1] = $langs->trans("Subscriptions"); $head[$h][2] = 'subscription'; + if ($nbSubscription > 0) $head[$h][1].= ' '.$nbSubscription.''; $h++; } diff --git a/htdocs/core/lib/modulebuilder.lib.php b/htdocs/core/lib/modulebuilder.lib.php index 8e2cba43097..1fec9c1b024 100644 --- a/htdocs/core/lib/modulebuilder.lib.php +++ b/htdocs/core/lib/modulebuilder.lib.php @@ -127,15 +127,18 @@ function rebuildObjectClass($destdir, $module, $objectname, $newmask, $readdir=' { $i++; $texttoinsert.= "\t\t'".$key."' => array('type'=>'".$val['type']."', 'label'=>'".$val['label']."',"; - $texttoinsert.= " 'visible'=>".($val['visible']!=''?$val['visible']:-1).","; $texttoinsert.= " 'enabled'=>".($val['enabled']!=''?$val['enabled']:1).","; + $texttoinsert.= " 'visible'=>".($val['visible']!=''?$val['visible']:-1).","; $texttoinsert.= " 'position'=>".($val['position']!=''?$val['position']:50).","; $texttoinsert.= " 'notnull'=>".($val['notnull']!=''?$val['notnull']:-1).","; - if ($val['index']) $texttoinsert.= " 'index'=>".$val['index'].","; - if ($val['searchall']) $texttoinsert.= " 'searchall'=>".$val['searchall'].","; - if ($val['comment']) $texttoinsert.= " 'comment'=>\"".preg_replace('/"/', '', $val['comment'])."\","; // addslashes is escape for PHP - if ($val['isameasure']) $texttoinsert.= " 'isameasure'=>'".$val['isameasure']."',"; - if ($val['help']) $texttoinsert.= " 'help'=>\"".preg_replace('/"/', '', $val['help'])."\","; // addslashes is escape for PHP + if ($val['default']) $texttoinsert.= " 'default'=>'".$val['default']."',"; + if ($val['index']) $texttoinsert.= " 'index'=>".$val['index'].","; + if ($val['searchall']) $texttoinsert.= " 'searchall'=>".$val['searchall'].","; + if ($val['isameasure']) $texttoinsert.= " 'isameasure'=>'".$val['isameasure']."',"; + if ($val['foreignkey']) $texttoinsert.= " 'foreignkey'=>'".$val['foreignkey']."',"; + if ($val['help']) $texttoinsert.= " 'help'=>\"".preg_replace('/"/', '', $val['help'])."\","; + if ($val['comment']) $texttoinsert.= " 'comment'=>\"".preg_replace('/"/', '', $val['comment'])."\","; + if ($val['showoncombobox']) $texttoinsert.= " 'showoncombobox'=>'".$val['showoncombobox']."',"; if ($val['arrayofkeyval']) { $texttoinsert.= " 'arrayofkeyval'=>array("; @@ -301,11 +304,20 @@ function rebuildObjectSql($destdir, $module, $objectname, $newmask, $readdir='', foreach($object->fields as $key => $val) { $i++; - if ($val['index']) + if (! empty($val['index'])) { $texttoinsert.= "ALTER TABLE llx_".strtolower($module).'_'.strtolower($objectname)." ADD INDEX idx_".strtolower($module).'_'.strtolower($objectname)."_".$key." (".$key.");"; $texttoinsert.= "\n"; } + if (! empty($val['foreignkey'])) + { + $tmp=explode('.',$val['foreignkey']); + if (! empty($tmp[0]) && ! empty($tmp[1])) + { + $texttoinsert.= "ALTER TABLE llx_".strtolower($module).'_'.strtolower($objectname)." ADD CONSTRAINT llx_".strtolower($module).'_'.strtolower($objectname)."_".$key." FOREIGN KEY (".$key.") REFERENCES ".$tmp[0]."(".$tmp[1].");"; + $texttoinsert.= "\n"; + } + } } } $texttoinsert.= '-- END MODULEBUILDER INDEXES'; diff --git a/htdocs/core/lib/oauth.lib.php b/htdocs/core/lib/oauth.lib.php index 0149b581fa2..a1582cceca5 100644 --- a/htdocs/core/lib/oauth.lib.php +++ b/htdocs/core/lib/oauth.lib.php @@ -27,12 +27,15 @@ $supportedoauth2array=array( 'OAUTH_GOOGLE_NAME'=>'google', ); - if ($conf->global->MAIN_FEATURES_LEVEL >= 2) { - $supportedoauth2array['OAUTH_GITHUB_NAME']='github'; + $supportedoauth2array['OAUTH_STRIPE_TEST_NAME']='stripetest'; + $supportedoauth2array['OAUTH_STRIPE_LIVE_NAME']='stripelive'; } $supportedoauth2array['OAUTH_GITHUB_NAME']='github'; + + + // API access parameters OAUTH $list = array ( array( @@ -217,7 +220,17 @@ $list = array ( 'OAUTH_STRAVA_ID', 'OAUTH_STRAVA_SECRET', ), - array( + array( + 'OAUTH_STRIPE_TEST_NAME', + 'OAUTH_STRIPE_TEST_ID', + 'STRIPE_TEST_SECRET_KEY', + ), + array( + 'OAUTH_STRIPE_LIVE_NAME', + 'OAUTH_STRIPE_LIVE_ID', + 'STRIPE_LIVE_SECRET_KEY', + ), + array( 'OAUTH_TUMBLR_NAME', 'OAUTH_TUMBLR_ID', 'OAUTH_TUMBLR_SECRET', @@ -266,12 +279,12 @@ function oauthadmin_prepare_head() $head[$h][1] = $langs->trans("OAuthServices"); $head[$h][2] = 'services'; $h++; - + $head[$h][0] = dol_buildpath('/admin/oauthlogintokens.php', 1); $head[$h][1] = $langs->trans("TokenManager"); $head[$h][2] = 'tokengeneration'; $h++; - + complete_head_from_modules($conf, $langs, null, $head, $h, 'oauthadmin'); complete_head_from_modules($conf, $langs, null, $head, $h, 'oauthadmin', 'remove'); diff --git a/htdocs/core/lib/payments.lib.php b/htdocs/core/lib/payments.lib.php index 73f8e507caf..df97b133522 100644 --- a/htdocs/core/lib/payments.lib.php +++ b/htdocs/core/lib/payments.lib.php @@ -190,7 +190,7 @@ function getOnlinePaymentUrl($mode, $type, $ref='', $amount='9.99', $freetag='yo } } } - if ($type == 'membersubscription') + if ($type == 'member' || $type == 'membersubscription') { $out=DOL_MAIN_URL_ROOT.'/public/payment/newpayment.php?source=membersubscription&ref='.($mode?'':''); if ($mode == 1) $out.='member_ref'; @@ -210,7 +210,7 @@ function getOnlinePaymentUrl($mode, $type, $ref='', $amount='9.99', $freetag='yo } // For multicompany - if (! empty($out)) $out.="&entity=".$conf->entity; // Check the entity because He may be the same reference in several entities + if (! empty($out) && ! empty($conf->multicompany->enabled)) $out.="&entity=".$conf->entity; // Check the entity because we may have the same reference in several entities return $out; } @@ -227,7 +227,7 @@ function getOnlinePaymentUrl($mode, $type, $ref='', $amount='9.99', $freetag='yo * @param Object $object Object related to payment * @return void */ -function htmlPrintOnlinePaymentFooter($fromcompany,$langs,$addformmessage=0,$suffix='',$object=null) +function htmlPrintOnlinePaymentFooter($fromcompany, $langs, $addformmessage=0, $suffix='', $object=null) { global $conf; diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index 377f2215b6d..43f5aaaf579 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -1003,7 +1003,7 @@ function pdf_pagefoot(&$pdf,$outputlangs,$paramfreetext,$fromcompany,$marge_bass $freetextheight=0; if ($line) // Free text { - //$line="eee
    \nfdsfsdf
    \nghfghg
    "; + //$line="sample text
    \nfdsfsdf
    \nghfghg
    "; if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) { $width=20000; $align='L'; // By default, ask a manual break: We use a large value 20000, to not have automatic wrap. This make user understand, he need to add CR on its text. @@ -1236,13 +1236,15 @@ function pdf_getlinedesc($object,$i,$outputlangs,$hideref=0,$hidedesc=0,$issuppl { $discount=new DiscountAbsolute($db); $discount->fetch($object->lines[$i]->fk_remise_except); - $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromCreditNote",$discount->ref_facture_source); + $sourceref=!empty($discount->discount_type)?$discount->ref_invoive_supplier_source:$discount->ref_facture_source; + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromCreditNote",$sourceref); } elseif ($desc == '(DEPOSIT)' && $object->lines[$i]->fk_remise_except) { $discount=new DiscountAbsolute($db); $discount->fetch($object->lines[$i]->fk_remise_except); - $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromDeposit",$discount->ref_facture_source); + $sourceref=!empty($discount->discount_type)?$discount->ref_invoive_supplier_source:$discount->ref_facture_source; + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromDeposit",$sourceref); // Add date of deposit if (! empty($conf->global->INVOICE_ADD_DEPOSIT_DATE)) echo ' ('.dol_print_date($discount->datec,'day','',$outputlangs).')'; } @@ -1252,6 +1254,12 @@ function pdf_getlinedesc($object,$i,$outputlangs,$hideref=0,$hidedesc=0,$issuppl $discount->fetch($object->lines[$i]->fk_remise_except); $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromExcessReceived",$discount->ref_facture_source); } + elseif ($desc == '(EXCESS PAID)' && $object->lines[$i]->fk_remise_except) + { + $discount=new DiscountAbsolute($db); + $discount->fetch($object->lines[$i]->fk_remise_except); + $libelleproduitservice=$outputlangs->transnoentitiesnoconv("DiscountFromExcessPaid",$discount->ref_invoice_supplier_source); + } else { if ($idprod) diff --git a/htdocs/core/lib/product.lib.php b/htdocs/core/lib/product.lib.php index 5542dfcf1b5..8d40f11d472 100644 --- a/htdocs/core/lib/product.lib.php +++ b/htdocs/core/lib/product.lib.php @@ -311,7 +311,7 @@ function show_stats_for_company($product,$socid) $nblines = 0; print ''; - print ''.$langs->trans("Referers").''; + print ''.$langs->trans("Referers").''; print ''.$langs->trans("NbOfThirdParties").''; print ''.$langs->trans("NbOfObjectReferers").''; print ''.$langs->trans("TotalQuantity").''; diff --git a/htdocs/core/lib/project.lib.php b/htdocs/core/lib/project.lib.php index 9070cb307f7..c82839d8499 100644 --- a/htdocs/core/lib/project.lib.php +++ b/htdocs/core/lib/project.lib.php @@ -331,7 +331,7 @@ function project_admin_prepare_head() */ function projectLinesa(&$inc, $parent, &$lines, &$level, $var, $showproject, &$taskrole, $projectsListId='', $addordertick=0, $projectidfortotallink=0) { - global $user, $bc, $langs; + global $user, $bc, $langs, $conf; global $projectstatic, $taskstatic; $lastprojectid=0; @@ -1298,11 +1298,11 @@ function projectLinesPerWeek(&$inc, $firstdaytoshow, $fuser, $parent, $lines, &$ } $tableCell =''; + $placeholder=''; if ($alreadyspent) { $tableCell.=''; //$placeholder=' placeholder="00:00"'; - $placeholder=''; //$tableCell.='+'; } $tableCell.=''; print ''; - $sql.= " FROM ".MAIN_DB_PREFIX."projet as p"; + $sql= " FROM ".MAIN_DB_PREFIX."projet as p"; if ($mytasks) { $sql.= ", ".MAIN_DB_PREFIX."projet_task as t"; @@ -1525,6 +1525,8 @@ function print_projecttasks_array($db, $form, $socid, $projectsListId, $mytasks= print_liste_field_titre("Status","","","","",'align="right"',$sortfield,$sortorder); print "\n"; + $total_plannedworkload=0; + $total_declaredprogressworkload=0; while ($i < $num) { $objp = $db->fetch_object($resql); diff --git a/htdocs/core/lib/propal.lib.php b/htdocs/core/lib/propal.lib.php index f0d42888745..c50661de831 100644 --- a/htdocs/core/lib/propal.lib.php +++ b/htdocs/core/lib/propal.lib.php @@ -35,7 +35,7 @@ function propal_prepare_head($object) $langs->load("propal"); $langs->load("compta"); $langs->load("companies"); - + $h = 0; $head = array(); @@ -86,7 +86,7 @@ function propal_prepare_head($object) require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; - $upload_dir = $conf->propal->dir_output . "/" . dol_sanitizeFileName($object->ref); + $upload_dir = $conf->propal->multidir_output[$object->entity] . "/" . dol_sanitizeFileName($object->ref); $nbFiles = count(dol_dir_list($upload_dir,'files',0,'','(\.meta|_preview.*\.png)$')); $nbLinks=Link::count($db, $object->element, $object->id); $head[$h][0] = DOL_URL_ROOT.'/comm/propal/document.php?id='.$object->id; diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php index dda89a7425d..a367579e1cd 100644 --- a/htdocs/core/lib/security.lib.php +++ b/htdocs/core/lib/security.lib.php @@ -70,11 +70,11 @@ function dol_decode($chain) /** * Returns a hash of a string. - * If constant MAIN_SECURITY_HASH_ALGO is defined, we use this function as hashing function. - * If constant MAIN_SECURITY_SALT is defined, we use it as a salt. + * If constant MAIN_SECURITY_HASH_ALGO is defined, we use this function as hashing function (recommanded value is 'password_hash') + * If constant MAIN_SECURITY_SALT is defined, we use it as a salt (used only if hashing algorightm is something else than 'password_hash'). * * @param string $chain String to hash - * @param string $type Type of hash ('0':auto, '1':sha1, '2':sha1+md5, '3':md5, '4':md5 for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. + * @param string $type Type of hash ('0':auto will use MAIN_SECURITY_HASH_ALGO then md5, '1':sha1, '2':sha1+md5, '3':md5, '4':md5 for OpenLdap, '5':sha256). Use '3' here, if hash is not needed for security purpose, for security need, prefer '0'. * @return string Hash of string * @getRandomPassword */ @@ -83,8 +83,10 @@ function dol_hash($chain, $type='0') global $conf; // No need to add salt for password_hash - if ($type == '0' && ! empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_hash')) - return password_hash($chain, PASSWORD_DEFAULT); + if (($type == '0' || $type == 'auto') && ! empty($conf->global->MAIN_SECURITY_HASH_ALGO) && $conf->global->MAIN_SECURITY_HASH_ALGO == 'password_hash' && function_exists('password_hash')) + { + return password_hash($chain, PASSWORD_DEFAULT); + } // Salt value if (! empty($conf->global->MAIN_SECURITY_SALT)) $chain=$conf->global->MAIN_SECURITY_SALT.$chain; diff --git a/htdocs/core/lib/supplier_proposal.lib.php b/htdocs/core/lib/supplier_proposal.lib.php index 580fecf0667..1756c92a32a 100644 --- a/htdocs/core/lib/supplier_proposal.lib.php +++ b/htdocs/core/lib/supplier_proposal.lib.php @@ -43,6 +43,15 @@ function supplier_proposal_prepare_head($object) $head[$h][2] = 'comm'; $h++; + if (empty($conf->global->MAIN_DISABLE_CONTACTS_TAB)) + { + $nbContact = count($object->liste_contact(-1,'internal')) + count($object->liste_contact(-1,'external')); + $head[$h][0] = DOL_URL_ROOT.'/supplier_proposal/contact.php?id='.$object->id; + $head[$h][1] = $langs->trans('ContactsAddresses'); + if ($nbContact > 0) $head[$h][1].= ' '.$nbContact.''; + $head[$h][2] = 'contact'; + $h++; + } // Show more tabs from modules // Entries must be declared in modules descriptor with line diff --git a/htdocs/core/lib/tax.lib.php b/htdocs/core/lib/tax.lib.php index 400ed453004..26c31a4eee0 100644 --- a/htdocs/core/lib/tax.lib.php +++ b/htdocs/core/lib/tax.lib.php @@ -32,7 +32,7 @@ * Prepare array with list of tabs * * @param ChargeSociales $object Object related to tabs - * @return array Array of tabs to show + * @return array Array of tabs to show */ function tax_prepare_head(ChargeSociales $object) { @@ -184,7 +184,6 @@ function vat_by_thirdparty($db, $y, $date_start, $date_end, $modetax, $direction * Gets Tax to collect for the given year (and given quarter or month) * The function gets the Tax in split results, as the Tax declaration asks * to report the amounts for different Tax rates as different lines. - * This function also accounts recurrent invoices. * * @param string $type Tax type, either 'vat', 'localtax1' or 'localtax2' * @param DoliDB $db Database handler object @@ -201,6 +200,12 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire { global $conf; + // If we use date_start and date_end, we must not use $y, $m, $q + if (($date_start || $date_end) && (! empty($y) || ! empty($m) || ! empty($q))) + { + dol_print_error('', 'Bad value of input parameter for tax_by_date'); + } + $list=array(); if ($direction == 'sell') @@ -236,12 +241,13 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire $total_localtax1='total_localtax1'; $total_localtax2='total_localtax2'; - - // CAS DES BIENS + + // CAS DES BIENS/PRODUITS // Define sql request $sql=''; - if ($modetax == 1) // Option vat on delivery for goods (payment) and debit invoice for services + if (($direction == 'sell' && $conf->global->TAX_MODE_SELL_PRODUCT == 'invoice') + || ($direction == 'buy' && $conf->global->TAX_MODE_BUY_PRODUCT == 'invoice')) { // Count on delivery date (use invoice date as delivery is unknown) $sql = "SELECT d.rowid, d.product_type as dtype, d.".$fk_facture." as facid, d.$f_rate as rate, d.total_ht as total_ht, d.total_ttc as total_ttc, d.".$total_tva." as total_vat, d.description as descr,"; @@ -273,44 +279,48 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire if ($q) $sql.= " AND (date_format(f.datef,'%m') > ".(($q-1)*3)." AND date_format(f.datef,'%m') <= ".($q*3).")"; if ($date_start && $date_end) $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'"; $sql.= " AND (d.product_type = 0"; // Limit to products - $sql.= " AND d.date_start is null AND d.date_end IS NULL)"; // enhance detection of service + $sql.= " AND d.date_start is null AND d.date_end IS NULL)"; // enhance detection of products $sql.= " ORDER BY d.rowid, d.".$fk_facture; } - else // Option vat on delivery for goods (payments) and payments for services + else { - // Count on delivery date (use invoice date as delivery is unknown) - $sql = "SELECT d.rowid, d.product_type as dtype, d.".$fk_facture." as facid, d.$f_rate as rate, d.total_ht as total_ht, d.total_ttc as total_ttc, d.".$total_tva." as total_vat, d.description as descr,"; - $sql .=" d.".$total_localtax1." as total_localtax1, d.".$total_localtax2." as total_localtax2, "; - $sql.= " d.date_start as date_start, d.date_end as date_end,"; - $sql.= " f.".$invoicefieldref." as facnum, f.type, f.total_ttc as ftotal_ttc, f.datef as date_f, s.nom as company_name, s.rowid as company_id,"; - $sql.= " p.rowid as pid, p.ref as pref, p.fk_product_type as ptype,"; - $sql.= " 0 as payment_id, 0 as payment_amount"; - $sql.= " FROM ".MAIN_DB_PREFIX.$invoicetable." as f,"; - $sql.= " ".MAIN_DB_PREFIX."societe as s,"; - $sql.= " ".MAIN_DB_PREFIX.$invoicedettable." as d" ; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p on d.fk_product = p.rowid"; - $sql.= " WHERE f.entity = " . $conf->entity; - $sql.= " AND f.fk_statut in (1,2)"; // Validated or paid (partially or completely) - if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $sql.= " AND f.type IN (0,1,2,5)"; - else $sql.= " AND f.type IN (0,1,2,3,5)"; - $sql.= " AND f.rowid = d.".$fk_facture; - $sql.= " AND s.rowid = f.fk_soc"; - if ($y && $m) - { - $sql.= " AND f.datef >= '".$db->idate(dol_get_first_day($y,$m,false))."'"; - $sql.= " AND f.datef <= '".$db->idate(dol_get_last_day($y,$m,false))."'"; - } - else if ($y) - { - $sql.= " AND f.datef >= '".$db->idate(dol_get_first_day($y,1,false))."'"; - $sql.= " AND f.datef <= '".$db->idate(dol_get_last_day($y,12,false))."'"; - } - if ($q) $sql.= " AND (date_format(f.datef,'%m') > ".(($q-1)*3)." AND date_format(f.datef,'%m') <= ".($q*3).")"; - if ($date_start && $date_end) $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'"; - $sql.= " AND (d.product_type = 0"; // Limit to products - $sql.= " AND d.date_start is null AND d.date_end IS NULL)"; // enhance detection of service - $sql.= " ORDER BY d.rowid, d.".$fk_facture; - //print $sql; + // Count on payments date + $sql = "SELECT d.rowid, d.product_type as dtype, d.".$fk_facture." as facid, d.$f_rate as rate, d.total_ht as total_ht, d.total_ttc as total_ttc, d.".$total_tva." as total_vat, d.description as descr,"; + $sql .=" d.".$total_localtax1." as total_localtax1, d.".$total_localtax2." as total_localtax2, "; + $sql.= " d.date_start as date_start, d.date_end as date_end,"; + $sql.= " f.".$invoicefieldref." as facnum, f.type, f.total_ttc as ftotal_ttc, f.datef, s.nom as company_name, s.rowid as company_id,"; + $sql.= " p.rowid as pid, p.ref as pref, p.fk_product_type as ptype,"; + $sql.= " pf.".$fk_payment." as payment_id, pf.amount as payment_amount,"; + $sql.= " pa.datep as datep"; + $sql.= " FROM ".MAIN_DB_PREFIX.$invoicetable." as f,"; + $sql.= " ".MAIN_DB_PREFIX.$paymentfacturetable." as pf,"; + $sql.= " ".MAIN_DB_PREFIX.$paymenttable." as pa,"; + $sql.= " ".MAIN_DB_PREFIX."societe as s,"; + $sql.= " ".MAIN_DB_PREFIX.$invoicedettable." as d"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p on d.fk_product = p.rowid"; + $sql.= " WHERE f.entity = " . $conf->entity; + $sql.= " AND f.fk_statut in (1,2)"; // Paid (partially or completely) + if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $sql.= " AND f.type IN (0,1,2,5)"; + else $sql.= " AND f.type IN (0,1,2,3,5)"; + $sql.= " AND f.rowid = d.".$fk_facture; + $sql.= " AND s.rowid = f.fk_soc"; + $sql.= " AND pf.".$fk_facture2." = f.rowid"; + $sql.= " AND pa.rowid = pf.".$fk_payment; + if ($y && $m) + { + $sql.= " AND pa.datep >= '".$db->idate(dol_get_first_day($y,$m,false))."'"; + $sql.= " AND pa.datep <= '".$db->idate(dol_get_last_day($y,$m,false))."'"; + } + else if ($y) + { + $sql.= " AND pa.datep >= '".$db->idate(dol_get_first_day($y,1,false))."'"; + $sql.= " AND pa.datep <= '".$db->idate(dol_get_last_day($y,12,false))."'"; + } + if ($q) $sql.= " AND (date_format(pa.datep,'%m') > ".(($q-1)*3)." AND date_format(pa.datep,'%m') <= ".($q*3).")"; + if ($date_start && $date_end) $sql.= " AND pa.datep >= '".$db->idate($date_start)."' AND pa.datep <= '".$db->idate($date_end)."'"; + $sql.= " AND (d.product_type = 0"; // Limit to products + $sql.= " AND d.date_start is null AND d.date_end IS NULL)"; // enhance detection of products + $sql.= " ORDER BY d.rowid, d.".$fk_facture.", pf.rowid"; } //print $sql.'
    '; @@ -318,7 +328,7 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire if ($sql == 'TODO') return -2; if ($sql != 'TODO') { - dol_syslog("Tax.lib.php::vat_by_date", LOG_DEBUG); + dol_syslog("Tax.lib.php::tax_by_date", LOG_DEBUG); $resql = $db->query($sql); if ($resql) @@ -342,7 +352,8 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire } $list[$assoc['rate']]['dtotal_ttc'][] = $assoc['total_ttc']; $list[$assoc['rate']]['dtype'][] = $assoc['dtype']; - $list[$assoc['rate']]['datef'][] = $assoc['datef']; + $list[$assoc['rate']]['datef'][] = $db->jdate($assoc['datef']); + $list[$assoc['rate']]['datep'][] = $db->jdate($assoc['datep']); $list[$assoc['rate']]['company_name'][] = $assoc['company_name']; $list[$assoc['rate']]['company_id'][] = $assoc['company_id']; $list[$assoc['rate']]['ddate_start'][] = $db->jdate($assoc['date_start']); @@ -381,7 +392,8 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire // Define sql request $sql=''; - if ($modetax == 1) // Option vat on delivery for goods (payment) and debit invoice for services + if (($direction == 'sell' && $conf->global->TAX_MODE_SELL_SERVICE == 'invoice') + || ($direction == 'buy' && $conf->global->TAX_MODE_BUY_SERVICE == 'invoice')) { // Count on invoice date $sql = "SELECT d.rowid, d.product_type as dtype, d.".$fk_facture." as facid, d.$f_rate as rate, d.total_ht as total_ht, d.total_ttc as total_ttc, d.".$total_tva." as total_vat, d.description as descr,"; @@ -414,9 +426,9 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire if ($date_start && $date_end) $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'"; $sql.= " AND (d.product_type = 1"; // Limit to services $sql.= " OR d.date_start is NOT null OR d.date_end IS NOT NULL)"; // enhance detection of service - $sql.= " ORDER BY d.rowid, d.".$fk_facture; + $sql.= " ORDER BY d.rowid, d.".$fk_facture; } - else // Option vat on delivery for goods (payments) and payments for services + else { // Count on payments date $sql = "SELECT d.rowid, d.product_type as dtype, d.".$fk_facture." as facid, d.$f_rate as rate, d.total_ht as total_ht, d.total_ttc as total_ttc, d.".$total_tva." as total_vat, d.description as descr,"; @@ -424,7 +436,8 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire $sql.= " d.date_start as date_start, d.date_end as date_end,"; $sql.= " f.".$invoicefieldref." as facnum, f.type, f.total_ttc as ftotal_ttc, f.datef, s.nom as company_name, s.rowid as company_id,"; $sql.= " p.rowid as pid, p.ref as pref, p.fk_product_type as ptype,"; - $sql.= " pf.".$fk_payment." as payment_id, pf.amount as payment_amount"; + $sql.= " pf.".$fk_payment." as payment_id, pf.amount as payment_amount,"; + $sql.= " pa.datep as datep"; $sql.= " FROM ".MAIN_DB_PREFIX.$invoicetable." as f,"; $sql.= " ".MAIN_DB_PREFIX.$paymentfacturetable." as pf,"; $sql.= " ".MAIN_DB_PREFIX.$paymenttable." as pa,"; @@ -451,20 +464,20 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire } if ($q) $sql.= " AND (date_format(pa.datep,'%m') > ".(($q-1)*3)." AND date_format(pa.datep,'%m') <= ".($q*3).")"; if ($date_start && $date_end) $sql.= " AND pa.datep >= '".$db->idate($date_start)."' AND pa.datep <= '".$db->idate($date_end)."'"; - $sql.= " AND (d.product_type = 1"; // Limit to services + $sql.= " AND (d.product_type = 1"; // Limit to services $sql.= " OR d.date_start is NOT null OR d.date_end IS NOT NULL)"; // enhance detection of service $sql.= " ORDER BY d.rowid, d.".$fk_facture.", pf.rowid"; } if (! $sql) { - dol_syslog("Tax.lib.php::vat_by_date no accountancy module enabled".$sql,LOG_ERR); + dol_syslog("Tax.lib.php::tax_by_date no accountancy module enabled".$sql,LOG_ERR); return -1; // -1 = Not accountancy module enabled } if ($sql == 'TODO') return -2; // -2 = Feature not yet available if ($sql != 'TODO') { - dol_syslog("Tax.lib.php::vat_by_date", LOG_DEBUG); + dol_syslog("Tax.lib.php::tax_by_date", LOG_DEBUG); $resql = $db->query($sql); if ($resql) { @@ -487,7 +500,8 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire } $list[$assoc['rate']]['dtotal_ttc'][] = $assoc['total_ttc']; $list[$assoc['rate']]['dtype'][] = $assoc['dtype']; - $list[$assoc['rate']]['datef'][] = $assoc['datef']; + $list[$assoc['rate']]['datef'][] = $db->jdate($assoc['datef']); + $list[$assoc['rate']]['datep'][] = $db->jdate($assoc['datep']); $list[$assoc['rate']]['company_name'][] = $assoc['company_name']; $list[$assoc['rate']]['company_id'][] = $assoc['company_id']; $list[$assoc['rate']]['ddate_start'][] = $db->jdate($assoc['date_start']); @@ -550,19 +564,19 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire } if ($q) $sql.= " AND (date_format(p.datep,'%m') > ".(($q-1)*3)." AND date_format(p.datep,'%m') <= ".($q*3).")"; if ($date_start && $date_end) $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'"; - $sql.= " AND (d.product_type = -1"; + $sql.= " AND (d.product_type = -1"; $sql.= " OR e.date_debut is NOT null OR e.date_fin IS NOT NULL)"; // enhance detection of service $sql.= " ORDER BY e.rowid"; if (! $sql) { - dol_syslog("Tax.lib.php::vat_by_date no accountancy module enabled".$sql,LOG_ERR); + dol_syslog("Tax.lib.php::tax_by_date no accountancy module enabled".$sql,LOG_ERR); return -1; // -1 = Not accountancy module enabled } if ($sql == 'TODO') return -2; // -2 = Feature not yet available if ($sql != 'TODO') { - dol_syslog("Tax.lib.php::vat_by_date", LOG_DEBUG); + dol_syslog("Tax.lib.php::tax_by_date", LOG_DEBUG); $resql = $db->query($sql); if ($resql) { @@ -624,24 +638,3 @@ function tax_by_date($type, $db, $y, $q, $date_start, $date_end, $modetax, $dire return $list; } -/** - * Gets VAT to collect for the given year (and given quarter or month) - * The function gets the VAT in split results, as the VAT declaration asks - * to report the amounts for different VAT rates as different lines. - * This function also accounts recurrent invoices. - * - * @param DoliDB $db Database handler object - * @param int $y Year - * @param int $q Quarter - * @param string $date_start Start date - * @param string $date_end End date - * @param int $modetax 0 or 1 (option vat on debit) - * @param int $direction 'sell' (customer invoice) or 'buy' (supplier invoices) - * @param int $m Month - * @return array List of quarters with vat - */ -function vat_by_date ($db, $y, $q, $date_start, $date_end, $modetax, $direction, $m=0) -{ - return tax_by_date('vat', $db, $y, $q, $date_start, $date_end, $modetax, $direction, $m); -} - diff --git a/htdocs/core/lib/ticketsup.lib.php b/htdocs/core/lib/ticketsup.lib.php new file mode 100644 index 00000000000..040dad6c656 --- /dev/null +++ b/htdocs/core/lib/ticketsup.lib.php @@ -0,0 +1,201 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file core/lib/ticketsup.lib.php + * \ingroup ticketsup + * \brief This file is a library for TicketSup module + */ + +/** + * Build tabs for admin page + * + * @return array + */ +function ticketsupAdminPrepareHead() +{ + global $langs, $conf; + + $langs->load("ticketsup"); + + $h = 0; + $head = array(); + + $head[$h][0] = DOL_URL_ROOT.'/admin/ticketsup.php'; + $head[$h][1] = $langs->trans("TicketSupSettings"); + $head[$h][2] = 'settings'; + $h++; + $head[$h][0] = DOL_URL_ROOT.'/admin/ticketsup_extrafields.php'; + $head[$h][1] = $langs->trans("ExtraFieldsTicketSup"); + $head[$h][2] = 'attributes'; + $h++; + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + //$this->tabs = array( + // 'entity:+tabname:Title:@ticketsup:/ticketsup/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@ticketsup:/ticketsup/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'ticketsupadmin'); + + return $head; +} + +/** + * Build tabs for a Ticketsup object + * + * @param Ticketsup $object Object Ticket + * @return array Array of tabs + */ +function ticketsup_prepare_head($object) +{ + global $db, $langs, $conf, $user; + + $h = 0; + $head = array(); + $head[$h][0] = DOL_URL_ROOT.'/ticketsup/card.php?action=view&track_id=' . $object->track_id; + $head[$h][1] = $langs->trans("Card"); + $head[$h][2] = 'tabTicketsup'; + $h++; + + + if (empty($conf->global->MAIN_DISABLE_CONTACTS_TAB) && empty($user->socid)) + { + $nbContact = count($object->liste_contact(-1,'internal')) + count($object->liste_contact(-1,'external')); + $head[$h][0] = DOL_URL_ROOT.'/ticketsup/contact.php?track_id='.$object->track_id; + $head[$h][1] = $langs->trans('ContactsAddresses'); + if ($nbContact > 0) $head[$h][1].= ' '.$nbContact.''; + $head[$h][2] = 'contact'; + $h++; + } + + complete_head_from_modules($conf, $langs, $object, $head, $h, 'ticketsup'); + + // Attached files + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + $upload_dir = $conf->ticketsup->dir_output . "/" . $object->track_id; + $nbFiles = count(dol_dir_list($upload_dir, 'files')); + $head[$h][0] = dol_buildpath('/ticketsup/document.php', 1) . '?track_id=' . $object->track_id; + $head[$h][1] = $langs->trans("Documents"); + if ($nbFiles > 0) { + $head[$h][1] .= ' ' . $nbFiles . ''; + } + + $head[$h][2] = 'tabTicketDocument'; + $h++; + + + // History + $head[$h][0] = DOL_URL_ROOT.'/ticketsup/history.php?track_id=' . $object->track_id; + $head[$h][1] = $langs->trans('Events'); + $head[$h][2] = 'tabTicketLogs'; + $h++; + + + complete_head_from_modules($conf, $langs, $object, $head, $h, 'ticketsup','remove'); + + + return $head; +} + +/** + * Generate a random id + * + * @param string $car Char to generate key + * @return void + */ +function generate_random_id($car=16) +{ + $string = ""; + $chaine = "abcdefghijklmnopqrstuvwxyz123456789"; + srand((double) microtime() * 1000000); + for ($i = 0; $i < $car; $i++) { + $string .= $chaine[rand() % strlen($chaine)]; + } + return $string; +} + +/** + * Show header for public pages + * + * @param string $title Title + * @param string $head Head array + * @param int $disablejs More content into html header + * @param int $disablehead More content into html header + * @param array $arrayofjs Array of complementary js files + * @param array $arrayofcss Array of complementary css files + * @return void + */ +function llxHeaderTicket($title, $head = "", $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '') +{ + global $user, $conf, $langs, $mysoc; + + top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss); // Show html headers + print ''; + + if (! empty($conf->global->TICKETS_SHOW_COMPANY_LOGO)) { + showlogo(); + } + + print '
    '; +} + +/** + * Show footer for new member + * + * @return void + */ +function llxFooterTicket() +{ + print '
    '; + + printCommonFooter('public'); + + dol_htmloutput_events(); + + print "\n"; + print "\n"; +} + +/** + * Show logo + * + * @return void + */ +function showlogo() +{ + global $conf, $langs, $mysoc; + + // Print logo + $urllogo = DOL_URL_ROOT . '/theme/login_logo.png'; + + if (!empty($mysoc->logo_small) && is_readable($conf->mycompany->dir_output . '/logos/thumbs/' . $mysoc->logo_small)) { + $urllogo = DOL_URL_ROOT . '/viewimage.php?cache=1&modulepart=companylogo&file=' . urlencode('thumbs/' . $mysoc->logo_small); + } elseif (!empty($mysoc->logo) && is_readable($conf->mycompany->dir_output . '/logos/' . $mysoc->logo)) { + $urllogo = DOL_URL_ROOT . '/viewimage.php?cache=1&modulepart=companylogo&file=' . urlencode($mysoc->logo); + $width = 128; + } elseif (is_readable(DOL_DOCUMENT_ROOT . '/theme/dolibarr_logo.png')) { + $urllogo = DOL_URL_ROOT . '/theme/dolibarr_logo.png'; + } + print '
    '; + print 'Logo
    '; + print '' . ($conf->global->TICKETS_PUBLIC_INTERFACE_TOPIC ? $conf->global->TICKETS_PUBLIC_INTERFACE_TOPIC : $langs->trans("TicketSystem")) . ''; + print '

    '; +} diff --git a/htdocs/core/lib/website.lib.php b/htdocs/core/lib/website.lib.php index b4c542edf9c..50716c27aca 100644 --- a/htdocs/core/lib/website.lib.php +++ b/htdocs/core/lib/website.lib.php @@ -165,47 +165,71 @@ function dolWebsiteSaveContent($content) /** - * Make a redirect to another container + * Make a redirect to another container. * - * @param string $containeralias Path to file to include (must be a page from website root. Example: 'mypage.php' means 'mywebsite/mypage.php') + * @param string $containerref Ref of container to redirect to (must be a page from website root. Example: 'mypage.php' means 'mywebsite/mypage.php'). + * @param string $containeraliasalt Ref of alternative aliases to redirect to. + * @param int $containerid Id of container. * @return void */ -function redirectToContainer($containeralias) +function redirectToContainer($containerref, $containeraliasalt='',$containerid=0) { global $db, $website; $newurl = ''; + $result=0; + + // We make redirect using the alternative alias, we must find the real $containerref + if ($containeraliasalt) + { + include_once DOL_DOCUMENT_ROOT.'/website/class/websitepage.class.php'; + $tmpwebsitepage=new WebsitePage($db); + $result = $tmpwebsitepage->fetch(0, $website->id, '', $containeraliasalt); + if ($result > 0) + { + $containerref = $tmpwebsitepage->pageurl; + } + else + { + print "Error, page contains a redirect to the alternative alias '".$containeraliasalt."' that does not exists in web site (".$website->id." / ".$website->ref.")"; + exit; + } + } if (defined('USEDOLIBARRSERVER')) // When page called from Dolibarr server { // Check new container exists - $tmpwebsitepage=new WebsitePage($db); - $result = $tmpwebsitepage->fetch(0, $website->id, $containeralias); - unset($tmpwebsitepage); + if (! $containeraliasalt) // If containeraliasalt set, we already did the test + { + include_once DOL_DOCUMENT_ROOT.'/website/class/websitepage.class.php'; + $tmpwebsitepage=new WebsitePage($db); + $result = $tmpwebsitepage->fetch(0, $website->id, $containerref); + unset($tmpwebsitepage); + } if ($result > 0) { $currenturi = $_SERVER["REQUEST_URI"]; if (preg_match('/&pageref=([^&]+)/', $currenturi, $regtmp)) { - if ($regtmp[0] == $containeralias) + if ($regtmp[0] == $containerref) { - print "Error, page with uri '.$currenturi.' try a redirect to the same alias page '".$containeralias."' in web site '".$website->ref."'"; + print "Error, page with uri '.$currenturi.' try a redirect to the same alias page '".$containerref."' in web site '".$website->ref."'"; exit; } else { - $newurl = preg_replace('/&pageref=([^&]+)/', '&pageref='.$containeralias, $currenturi); + $newurl = preg_replace('/&pageref=([^&]+)/', '&pageref='.$containerref, $currenturi); } } else { - $newurl = $currenturi.'&pageref='.urlencode($containeralias); + $newurl = $currenturi.'&pageref='.urlencode($containerref); } } } else // When page called from virtual host server { - $newurl = '/'.$containeralias.'.php'; + $newurl = '/'.$containerref.'.php'; } if ($newurl) @@ -215,7 +239,7 @@ function redirectToContainer($containeralias) } else { - print "Error, page contains a redirect to the alias page '".$containeralias."' that does not exists in web site '".$website->ref."'"; + print "Error, page contains a redirect to the alias page '".$containerref."' that does not exists in web site (".$website->id." / ".$website->ref.")"; exit; } } @@ -225,10 +249,10 @@ function redirectToContainer($containeralias) * Clean an HTML page to report only content, so we can include it into another page. * It outputs content of file sanitized from html and body part. * - * @param string $containeralias Path to file to include (must be a page from website root. Example: 'mypage.php' means 'mywebsite/mypage.php') + * @param string $containerref Path to file to include (must be a page from website root. Example: 'mypage.php' means 'mywebsite/mypage.php') * @return void */ -function includeContainer($containeralias) +function includeContainer($containerref) { global $conf, $db, $langs, $mysoc, $user, $website; global $includehtmlcontentopened; @@ -236,9 +260,9 @@ function includeContainer($containeralias) $MAXLEVEL=20; - if (! preg_match('/\.php$/i', $containeralias)) $containeralias.='.php'; + if (! preg_match('/\.php$/i', $containerref)) $containerref.='.php'; - $fullpathfile=DOL_DATA_ROOT.'/website/'.$websitekey.'/'.$containeralias; + $fullpathfile=DOL_DATA_ROOT.'/website/'.$websitekey.'/'.$containerref; if (empty($includehtmlcontentopened)) $includehtmlcontentopened=0; $includehtmlcontentopened++; @@ -261,7 +285,7 @@ function includeContainer($containeralias) if (! $res) { - print 'ERROR: FAILED TO INCLUDE PAGE '.$containeralias.".\n"; + print 'ERROR: FAILED TO INCLUDE PAGE '.$containerref.".\n"; } $includehtmlcontentopened--; @@ -550,6 +574,42 @@ function dolSavePageContent($filetpl, $object, $objectpage) } +/** + * Save content of the index.php page + * + * @param string $pathofwebsite Path of website root + * @param string $fileindex Full path of file index.php + * @param string $filetpl File tpl to index.php page redirect to + * @return boolean True if OK + */ +function dolSaveIndexPage($pathofwebsite, $fileindex, $filetpl) +{ + global $conf; + + $result=0; + + dol_mkdir($pathofwebsite); + dol_delete_file($fileindex); + + $indexcontent = ''."\n"; + $result = file_put_contents($fileindex, $indexcontent); + if (! empty($conf->global->MAIN_UMASK)) + @chmod($fileindex, octdec($conf->global->MAIN_UMASK)); + + return $result; +} + + /** * Save content of a page on disk * diff --git a/htdocs/core/menus/init_menu_auguria.sql b/htdocs/core/menus/init_menu_auguria.sql index 28eb9fa044a..807bd3f0125 100644 --- a/htdocs/core/menus/init_menu_auguria.sql +++ b/htdocs/core/menus/init_menu_auguria.sql @@ -14,7 +14,7 @@ insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, left insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('propal|commande|fournisseur|contrat|ficheinter', '$conf->propal->enabled || $conf->commande->enabled || $conf->supplier_order->enabled || $conf->contrat->enabled || $conf->ficheinter->enabled', 5__+MAX_llx_menu__, __HANDLER__, 'top', 'commercial', '', 0, '/comm/index.php?mainmenu=commercial&leftmenu=', 'Commercial', -1, 'commercial', '$user->rights->societe->lire || $user->rights->societe->contact->lire', '', 2, 40, __ENTITY__); insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('facture|don|tax|salaries|loan|banque', '$conf->comptabilite->enabled || $conf->accounting->enabled || $conf->facture->enabled || $conf->don->enabled || $conf->tax->enabled || $conf->salaries->enabled || $conf->supplier_invoice->enabled || $conf->loan->enabled || $conf->banque->enabled', 6__+MAX_llx_menu__, __HANDLER__, 'top', 'billing', '', 0, '/compta/index.php?mainmenu=billing&leftmenu=', 'MenuFinancial', -1, 'compta', '$user->rights->facture->lire|| $user->rights->don->lire || $user->rights->tax->charges->lire || $user->rights->salaries->read || $user->rights->loan->read || $user->rights->banque->lire', '', 2, 50, __ENTITY__); insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('banque|prelevement', '$conf->banque->enabled || $conf->prelevement->enabled', 14__+MAX_llx_menu__, __HANDLER__, 'top', 'bank', '', 0, '/compta/bank/list.php?mainmenu=bank&leftmenu=bank', 'MenuBankCash', -1, 'banks', '$user->rights->banque->lire || $user->rights->prelevement->bons->lire', '', 0, 52, __ENTITY__); -insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('comptabilite|accounting', '$conf->comptabilite->enabled || $conf->accounting->enabled || $conf->facture->enabled || $conf->don->enabled || $conf->tax->enabled || $conf->salaries->enabled || $conf->supplier_invoice->enabled || $conf->loan->enabled || $conf->banque->enabled', 9__+MAX_llx_menu__, __HANDLER__, 'top', 'accountancy', '', 0, '/compta/index.php?mainmenu=accountancy&leftmenu=accountancy', 'Accountancy', -1, 'compta', '$user->rights->compta->resultat->lire || $user->rights->accounting->mouvements->lire', '', 2, 54, __ENTITY__); +insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('comptabilite|accounting|assets', '$conf->comptabilite->enabled || $conf->accounting->enabled || $conf->assets->enabled || $conf->facture->enabled || $conf->don->enabled || $conf->tax->enabled || $conf->salaries->enabled || $conf->supplier_invoice->enabled || $conf->loan->enabled || $conf->banque->enabled', 9__+MAX_llx_menu__, __HANDLER__, 'top', 'accountancy', '', 0, '/compta/index.php?mainmenu=accountancy&leftmenu=accountancy', 'Accountancy', -1, 'compta', '$user->rights->compta->resultat->lire || $user->rights->accounting->mouvements->lire || $user->rights->assets->read', '', 2, 54, __ENTITY__); insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('projet', '$conf->projet->enabled', 7__+MAX_llx_menu__, __HANDLER__, 'top', 'project', '', 0, '/projet/index.php?mainmenu=project&leftmenu=', 'Projects', -1, 'projects', '$user->rights->projet->lire', '', 2, 70, __ENTITY__); insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '', 8__+MAX_llx_menu__, __HANDLER__, 'top', 'tools', '', 0, '/core/tools.php?mainmenu=tools&leftmenu=', 'Tools', -1, 'other', '', '', 2, 90, __ENTITY__); insert into llx_menu (module, enabled, rowid, menu_handler, type, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('adherent', '$conf->adherent->enabled', 13__+MAX_llx_menu__, __HANDLER__, 'top', 'members', '', 0, '/adherents/index.php?mainmenu=members&leftmenu=', 'Members', -1, 'members', '$user->rights->adherent->lire', '', 2, 110, __ENTITY__); @@ -63,9 +63,9 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="admintools"', __HANDLER__, 'left', 320__+MAX_llx_menu__, 'home', '', 300__+MAX_llx_menu__, '/product/admin/product_tools.php?mainmenu=home&leftmenu=admintools', 'ProductVatMassChange', 1, 'products', '', '', 2, 15, __ENTITY__); -- Home - Menu users and groups insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '1', __HANDLER__, 'left', 400__+MAX_llx_menu__, 'home', 'users', 1__+MAX_llx_menu__, '/user/home.php?leftmenu=users', 'MenuUsersAndGroups', 0, 'users', '', '', 2, 4, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="users"', __HANDLER__, 'left', 401__+MAX_llx_menu__, 'home', '', 400__+MAX_llx_menu__, '/user/index.php?leftmenu=users', 'Users', 1, 'users', '$user->rights->user->user->lire || $user->admin', '', 2, 0, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="users"', __HANDLER__, 'left', 401__+MAX_llx_menu__, 'home', '', 400__+MAX_llx_menu__, '/user/list.php?leftmenu=users', 'Users', 1, 'users', '$user->rights->user->user->lire || $user->admin', '', 2, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="users"', __HANDLER__, 'left', 402__+MAX_llx_menu__, 'home', '', 401__+MAX_llx_menu__, '/user/card.php?leftmenu=users&action=create', 'NewUser', 2, 'users', '($user->rights->user->user->creer || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)', '', 2, 0, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="users"', __HANDLER__, 'left', 403__+MAX_llx_menu__, 'home', '', 400__+MAX_llx_menu__, '/user/group/index.php?leftmenu=users', 'Groups', 1, 'users', '(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->read:$user->rights->user->user->lire) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)', '', 2, 1, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="users"', __HANDLER__, 'left', 403__+MAX_llx_menu__, 'home', '', 400__+MAX_llx_menu__, '/user/group/list.php?leftmenu=users', 'Groups', 1, 'users', '(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->read:$user->rights->user->user->lire) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)', '', 2, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$leftmenu=="users"', __HANDLER__, 'left', 404__+MAX_llx_menu__, 'home', '', 403__+MAX_llx_menu__, '/user/group/card.php?leftmenu=users&action=create', 'NewGroup', 2, 'users', '(($conf->global->MAIN_USE_ADVANCED_PERMS?$user->rights->user->group_advance->write:$user->rights->user->user->creer) || $user->admin) && !(! empty($conf->multicompany->enabled) && $conf->entity > 1 && $conf->global->MULTICOMPANY_TRANSVERSE_MODE)', '', 2, 0, __ENTITY__); -- Third parties insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->societe->enabled', __HANDLER__, 'left', 500__+MAX_llx_menu__, 'companies', 'thirdparties', 2__+MAX_llx_menu__, '/societe/index.php?leftmenu=thirdparties', 'ThirdParty', 0, 'companies', '$user->rights->societe->lire', '', 2, 0, __ENTITY__); @@ -199,6 +199,7 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled', __HANDLER__, 'left', 2210__+MAX_llx_menu__, 'billing', 'tax_sal', 2200__+MAX_llx_menu__, '/compta/salaries/index.php?leftmenu=tax_salary&mainmenu=billing', 'Salaries', 1, 'salaries', '$user->rights->salaries->payment->read', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2211__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/card.php?leftmenu=tax_salary&action=create', 'NewPayment', 2, 'companies', '$user->rights->salaries->payment->write', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2212__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/index.php?leftmenu=tax_salary', 'Payments', 2, 'companies', '$user->rights->salaries->payment->read', '', 0, 3, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->salaries->enabled && $leftmenu=="tax_salary"', __HANDLER__, 'left', 2213__+MAX_llx_menu__, 'billing', '', 2210__+MAX_llx_menu__, '/compta/salaries/stats/index.php?leftmenu=tax_salary', 'Statistics', 2, 'companies', '$user->rights->salaries->payment->read', '', 0, 4, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled', __HANDLER__, 'left', 2220__+MAX_llx_menu__, 'billing', 'tax_loan', 2200__+MAX_llx_menu__, '/loan/index.php?leftmenu=tax_loan&mainmenu=billing', 'Loans', 1, 'loan', '$user->rights->loan->read', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled && $leftmenu=="tax_loan"', __HANDLER__, 'left', 2221__+MAX_llx_menu__, 'billing', '', 2220__+MAX_llx_menu__, '/loan/card.php?leftmenu=tax_loan&action=create', 'NewLoan', 2, 'loan', '$user->rights->loan->write', '', 0, 2, __ENTITY__); --insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->loan->enabled && $leftmenu=="tax_loan"', __HANDLER__, 'left', 2222__+MAX_llx_menu__, 'billing', '', 2220__+MAX_llx_menu__, '/loan/payment/list.php?leftmenu=tax_loan', 'Payments', 2, 'companies', '$user->rights->loan->read', '', 0, 3, __ENTITY__); @@ -206,11 +207,12 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled', __HANDLER__, 'left', 2250__+MAX_llx_menu__, 'billing', 'tax_social', 2200__+MAX_llx_menu__, '/compta/sociales/index.php?leftmenu=tax_social', 'SocialContributions', 1, '', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2251__+MAX_llx_menu__, 'billing', '', 2250__+MAX_llx_menu__, '/compta/sociales/card.php?leftmenu=tax_social&action=create', 'MenuNewSocialContribution', 2, '', '$user->rights->tax->charges->creer', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && $leftmenu=="tax_social"', __HANDLER__, 'left', 2252__+MAX_llx_menu__, 'billing', '', 2250__+MAX_llx_menu__, '/compta/sociales/payments.php?leftmenu=tax_social&mainmenu=billing&mode=sconly', 'Payments', 2, '', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS)', __HANDLER__, 'left', 2300__+MAX_llx_menu__, 'billing', 'tax_vat', 2200__+MAX_llx_menu__, '/compta/tva/index.php?leftmenu=tax_vat&mainmenu=billing', 'VAT', 1, 'companies', '$user->rights->tax->charges->lire', '', 0, 7, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS)', __HANDLER__, 'left', 2300__+MAX_llx_menu__, 'billing', 'tax_vat', 2200__+MAX_llx_menu__, '/compta/tva/list.php?leftmenu=tax_vat&mainmenu=billing', 'VAT', 1, 'companies', '$user->rights->tax->charges->lire', '', 0, 7, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2301__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/card.php?leftmenu=tax_vat&action=create', 'New', 2, 'companies', '$user->rights->tax->charges->creer', '', 0, 0, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2302__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/reglement.php?leftmenu=tax_vat', 'List', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2303__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/clients.php?leftmenu=tax_vat', 'ReportByCustomers', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 2, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2304__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/quadri_detail.php?leftmenu=tax_vat', 'ReportByQuarter', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2302__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/list.php?leftmenu=tax_vat', 'List', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 1, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2303__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/index.php?leftmenu=tax_vat', 'ReportByMonth', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 2, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2304__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/clients.php?leftmenu=tax_vat', 'ReportByCustomers', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 3, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->tax->enabled && empty($conf->global->TAX_DISABLE_VAT_MENUS) && $leftmenu=="tax_vat"', __HANDLER__, 'left', 2305__+MAX_llx_menu__, 'billing', '', 2300__+MAX_llx_menu__, '/compta/tva/quadri_detail.php?leftmenu=tax_vat', 'ReportByQuarter', 2, 'companies', '$user->rights->tax->charges->lire', '', 0, 4, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->banque->enabled && $conf->global->MAIN_FEATURES_LEVEL >= 1', __HANDLER__, 'left', 2350__+MAX_llx_menu__, 'billing', 'tax_various', 2200__+MAX_llx_menu__, '/compta/bank/various_payment/index.php?leftmenu=tax_various&mainmenu=billing', 'MenuVariousPayment', 1, 'banks', '$user->rights->banque->lire', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->banque->enabled && $leftmenu=="tax_various"', __HANDLER__, 'left', 2351__+MAX_llx_menu__, 'billing', '', 2350__+MAX_llx_menu__, '/compta/bank/various_payment/card.php?leftmenu=tax_various&action=create', 'MenuNewVariousPayment', 2, 'various_payment', '$user->rights->banque->modifier', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->banque->enabled && $leftmenu=="tax_various"', __HANDLER__, 'left', 2352__+MAX_llx_menu__, 'billing', '', 2350__+MAX_llx_menu__, '/compta/bank/various_payment/index.php?leftmenu=tax_various', 'List', 2, 'various_payment', '$user->rights->banque->lire', '', 0, 3, __ENTITY__); @@ -273,6 +275,11 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->comptabilite->enabled && $leftmenu=="ca"', __HANDLER__, 'left', 2714__+MAX_llx_menu__, 'accountancy', '', 2703__+MAX_llx_menu__, '/compta/stats/casoc.php?leftmenu=ca', 'ByCompanies', 2, 'main', '$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire', '', 0, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->comptabilite->enabled && $leftmenu=="ca"', __HANDLER__, 'left', 2715__+MAX_llx_menu__, 'accountancy', '', 2703__+MAX_llx_menu__, '/compta/stats/cabyuser.php?leftmenu=ca', 'ByUsers', 2, 'main', '$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->comptabilite->enabled && $leftmenu=="ca"', __HANDLER__, 'left', 2716__+MAX_llx_menu__, 'accountancy', '', 2703__+MAX_llx_menu__, '/compta/stats/cabyprodserv.php?leftmenu=ca', 'ByProductsAndServices', 2, 'main', '$user->rights->compta->resultat->lire || $user->rights->accounting->comptarapport->lire', '', 0, 1, __ENTITY__); +-- Assets +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->assets->enabled', __HANDLER__, 'left', 2800__+MAX_llx_menu__, 'accountancy', 'assets', 10__+MAX_llx_menu__, '/assets/list.php?leftmenu=assets&mainmenu=accountancy', 'MenuAssets', 0, 'assets', '$user->rights->assets->read', '', 2, 4, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->assets->enabled && $leftmenu=="assets"', __HANDLER__, 'left', 2801__+MAX_llx_menu__, 'accountancy', '', 2800__+MAX_llx_menu__, '/assets/card.php?leftmenu=assets&mainmenu=accountancy&action=create', 'MenuNewAsset', 1, 'assets', '$user->rights->assets->write', '', 2, 0, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->assets->enabled && $leftmenu=="assets"', __HANDLER__, 'left', 2802__+MAX_llx_menu__, 'accountancy', '', 2800__+MAX_llx_menu__, '/assets/type.php?leftmenu=assets&mainmenu=accountancy&action=create', 'MenuTypeAssets', 1, 'assets', '$user->rights->assets->write', '', 2, 0, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->assets->enabled && $leftmenu=="assets"', __HANDLER__, 'left', 2803__+MAX_llx_menu__, 'accountancy', '', 2800__+MAX_llx_menu__, '/assets/list.php?leftmenu=assets&mainmenu=accountancy', 'MenuListAssets', 1, 'assets', '$user->rights->assets->read', '', 2, 1, __ENTITY__); -- Check deposit insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', 'empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))', __HANDLER__, 'left', 1711__+MAX_llx_menu__, 'accountancy', 'checks', 14__+MAX_llx_menu__, '/compta/paiement/cheque/index.php?leftmenu=checks&mainmenu=bank', 'MenuChequeDeposits', 0, 'bills', '$user->rights->banque->lire', '', 2, 9, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', 'empty($conf->global->BANK_DISABLE_CHECK_DEPOSIT) && ! empty($conf->banque->enabled) && (! empty($conf->facture->enabled) || ! empty($conf->global->MAIN_MENU_CHEQUE_DEPOSIT_ON))', __HANDLER__, 'left', 1712__+MAX_llx_menu__, 'accountancy', '', 1711__+MAX_llx_menu__, '/compta/paiement/cheque/card.php?leftmenu=checks&action=new', 'NewCheckDeposit', 1, 'compta', '$user->rights->banque->lire', '', 2, 0, __ENTITY__); @@ -311,7 +318,7 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->categorie->enabled', __HANDLER__, 'left', 3804__+MAX_llx_menu__, 'project', 'cat', 7__+MAX_llx_menu__, '/categories/index.php?leftmenu=cat&type=6', 'Categories', 0, 'categories', '$user->rights->categorie->lire', '', 2, 4, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->categorie->enabled', __HANDLER__, 'left', 3805__+MAX_llx_menu__, 'project', '', 3804__+MAX_llx_menu__, '/categories/card.php?action=create&type=6', 'NewCategory', 1, 'categories', '$user->rights->categorie->creer', '', 2, 0, __ENTITY__); -- Tools -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '', __HANDLER__, 'left', 3900__+MAX_llx_menu__, 'tools', 'email_templates', 8__+MAX_llx_menu__, '/admin/mails_templates.php?leftmenu=email_templates', 'EMailTemplates', 0, '', '', '', 0, 0, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', 'empty($user->socid)', __HANDLER__, 'left', 3900__+MAX_llx_menu__, 'tools', 'email_templates', 8__+MAX_llx_menu__, '/admin/mails_templates.php?leftmenu=email_templates', 'EMailTemplates', 0, '', '', '', 0, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->mailing->enabled', __HANDLER__, 'left', 3910__+MAX_llx_menu__, 'tools', 'mailing', 8__+MAX_llx_menu__, '/comm/mailing/index.php?leftmenu=mailing', 'EMailings', 0, 'mails', '$user->rights->mailing->lire', '', 0, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->mailing->enabled', __HANDLER__, 'left', 3911__+MAX_llx_menu__, 'tools', '', 3910__+MAX_llx_menu__, '/comm/mailing/card.php?leftmenu=mailing&action=create', 'NewMailing', 1, 'mails', '$user->rights->mailing->creer', '', 0, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->mailing->enabled', __HANDLER__, 'left', 3912__+MAX_llx_menu__, 'tools', '', 3910__+MAX_llx_menu__, '/comm/mailing/list.php?leftmenu=mailing', 'List', 1, 'mails', '$user->rights->mailing->lire', '', 0, 1, __ENTITY__); @@ -343,14 +350,14 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->adherent->enabled && $conf->categorie->enabled', __HANDLER__, 'left', 5200__+MAX_llx_menu__, 'members', 'cat', 13__+MAX_llx_menu__, '/categories/index.php?leftmenu=cat&type=3', 'MembersCategoriesShort', 0, 'categories', '$user->rights->categorie->lire', '', 2, 3, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->adherent->enabled && $conf->categorie->enabled', __HANDLER__, 'left', 5201__+MAX_llx_menu__, 'members', '', 5200__+MAX_llx_menu__, '/categories/card.php?action=create&type=3', 'NewCategory', 1, 'categories', '$user->rights->categorie->creer', '', 2, 0, __ENTITY__); -- HRM - Employee -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->hrm->enabled', __HANDLER__, 'left', 4600__+MAX_llx_menu__, 'hrm', 'hrm', 15__+MAX_llx_menu__, '/user/index.php?leftmenu=hrm&mode=employee', 'Employees', 0, 'hrm', '$user->rights->hrm->employee->read', '', 0, 1, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->hrm->enabled', __HANDLER__, 'left', 4600__+MAX_llx_menu__, 'hrm', 'hrm', 15__+MAX_llx_menu__, '/user/list.php?leftmenu=hrm&mode=employee', 'Employees', 0, 'hrm', '$user->rights->hrm->employee->read', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->hrm->enabled', __HANDLER__, 'left', 4601__+MAX_llx_menu__, 'hrm', '', 4600__+MAX_llx_menu__, '/user/card.php?action=create&employee=1', 'NewEmployee', 1, 'hrm', '$user->rights->hrm->employee->write', '', 0, 1, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->hrm->enabled', __HANDLER__, 'left', 4602__+MAX_llx_menu__, 'hrm', '', 4600__+MAX_llx_menu__, '/user/index.php?$leftmenu=hrm&mode=employee&contextpage=employeelist', 'List', 1, 'hrm', '$user->rights->hrm->employee->read', '', 0, 2, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->hrm->enabled', __HANDLER__, 'left', 4602__+MAX_llx_menu__, 'hrm', '', 4600__+MAX_llx_menu__, '/user/list.php?$leftmenu=hrm&mode=employee&contextpage=employeelist', 'List', 1, 'hrm', '$user->rights->hrm->employee->read', '', 0, 2, __ENTITY__); -- HRM - Holiday insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5000__+MAX_llx_menu__, 'hrm', 'hrm', 15__+MAX_llx_menu__, '/holiday/list.php?&leftmenu=hrm', 'CPTitreMenu', 0, 'holiday', '$user->rights->holiday->read', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5001__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/card.php?&action=request', 'MenuAddCP', 1, 'holiday', '$user->rights->holiday->write', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5002__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/list.php?&leftmenu=hrm', 'List', 1, 'holiday', '$user->rights->holiday->read', '', 0, 1, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5003__+MAX_llx_menu__, 'hrm', '', 5002__+MAX_llx_menu__, '/holiday/list.php?select_statut=2&leftmenu=hrm', 'ListToApprove', 2, 'trips', '$user->rights->holiday->read', '', 0, 1, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5003__+MAX_llx_menu__, 'hrm', '', 5002__+MAX_llx_menu__, '/holiday/list.php?search_statut=2&leftmenu=hrm', 'ListToApprove', 2, 'trips', '$user->rights->holiday->read', '', 0, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5004__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/define_holiday.php?&action=request', 'MenuConfCP', 1, 'holiday', '$user->rights->holiday->define_holiday', '', 0, 2, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->holiday->enabled', __HANDLER__, 'left', 5005__+MAX_llx_menu__, 'hrm', '', 5000__+MAX_llx_menu__, '/holiday/view_log.php?&action=request', 'MenuLogCP', 1, 'holiday', '$user->rights->holiday->define_holiday', '', 0, 3, __ENTITY__); -- HRM - Trips and expenses (old module) diff --git a/htdocs/core/menus/standard/auguria.lib.php b/htdocs/core/menus/standard/auguria.lib.php index 69f98710b93..6d8c67aff3f 100644 --- a/htdocs/core/menus/standard/auguria.lib.php +++ b/htdocs/core/menus/standard/auguria.lib.php @@ -380,12 +380,13 @@ function print_left_auguria_menu($db,$menu_array_before,$menu_array_after,&$tabM if ($objp->nature == 4 && ! empty($conf->banque->enabled)) $nature="bank"; if ($objp->nature == 5 && ! empty($conf->expensereport->enabled)) $nature="expensereports"; if ($objp->nature == 1) $nature="various"; + if ($objp->nature == 8) $nature="inventory"; if ($objp->nature == 9) $nature="hasnew"; // To enable when page exists if ($conf->global->MAIN_FEATURES_LEVEL < 2) { - if ($nature == 'various' || $nature == 'hasnew') $nature=''; + if ($nature == 'various' || $nature == 'hasnew' || $nature == 'inventory') $nature=''; } if ($nature) @@ -464,6 +465,7 @@ function print_left_auguria_menu($db,$menu_array_before,$menu_array_after,&$tabM if (! is_array($menu_array)) return 0; // Show menu + $invert=empty($conf->global->MAIN_MENU_INVERT)?"":"invert"; if (empty($noout)) { $altok=0; $blockvmenuopened=false; $lastlevel0=''; @@ -485,11 +487,11 @@ function print_left_auguria_menu($db,$menu_array_before,$menu_array_after,&$tabM } if ($altok % 2 == 0) { - print '
    '."\n"; + print '
    '."\n"; } else { - print '
    '."\n"; + print '
    '."\n"; } } @@ -509,24 +511,43 @@ function print_left_auguria_menu($db,$menu_array_before,$menu_array_after,&$tabM $substitarray['__USERID__'] = $user->id; // For backward compatibility $menu_array[$i]['url'] = make_substitutions($menu_array[$i]['url'], $substitarray); - // Add mainmenu in GET url. This make to go back on correct menu even when using Back on browser. - $url=dol_buildpath($menu_array[$i]['url'],1); - - if (! preg_match('/mainmenu=/i',$menu_array[$i]['url'])) + $url = $shorturl = $shorturlwithoutparam = $menu_array[$i]['url']; + if (! preg_match("/^(http:\/\/|https:\/\/)/i",$menu_array[$i]['url'])) { - if (! preg_match('/\?/',$url)) $url.='?'; - else $url.='&'; - $url.='mainmenu='.$mainmenu; + $tmp=explode('?',$menu_array[$i]['url'],2); + $url = $shorturl = $tmp[0]; + $param = (isset($tmp[1])?$tmp[1]:''); // params in url of the menu link + + // Complete param to force leftmenu to '' to close open menu when we click on a link with no leftmenu defined. + if ((! preg_match('/mainmenu/i',$param)) && (! preg_match('/leftmenu/i',$param)) && ! empty($menu_array[$i]['mainmenu'])) + { + $param.=($param?'&':'').'mainmenu='.$menu_array[$i]['mainmenu'].'&leftmenu='; + } + if ((! preg_match('/mainmenu/i',$param)) && (! preg_match('/leftmenu/i',$param)) && empty($menu_array[$i]['mainmenu'])) + { + $param.=($param?'&':'').'leftmenu='; + } + //$url.="idmenu=".$menu_array[$i]['rowid']; // Already done by menuLoad + $url = dol_buildpath($url,1).($param?'?'.$param:''); + $shorturlwithoutparam = $shorturl; + $shorturl = $shorturl.($param?'?'.$param:''); } - print ''."\n"; + + print ''."\n"; // Menu level 0 if ($menu_array[$i]['level'] == 0) { if ($menu_array[$i]['enabled']) // Enabled so visible { - print ''; + print ''."\n"; $lastlevel0='enabled'; } else if ($showmenu) // Not enabled but visible (so greyed) @@ -543,6 +564,7 @@ function print_left_auguria_menu($db,$menu_array_before,$menu_array_after,&$tabM print ''."\n"; } } + // Menu level > 0 if ($menu_array[$i]['level'] > 0) { @@ -552,10 +574,10 @@ function print_left_auguria_menu($db,$menu_array_before,$menu_array_after,&$tabM if ($menu_array[$i]['enabled'] && $lastlevel0 == 'enabled') // Enabled so visible, except if parent was not enabled. { print '
    - + diff --git a/htdocs/core/modules/dons/html_cerfafr.modules.php b/htdocs/core/modules/dons/html_cerfafr.modules.php index 0f7f2aa2c36..c9d2b4984ad 100644 --- a/htdocs/core/modules/dons/html_cerfafr.modules.php +++ b/htdocs/core/modules/dons/html_cerfafr.modules.php @@ -3,7 +3,7 @@ * Copyright (C) 2005-2006 Laurent Destailleur * Copyright (C) 2012 Regis Houssin * Copyright (C) 2012 Marcos García - * Copyright (C) 2014-2015 Alexandre Spangaro + * Copyright (C) 2014-2015 Alexandre Spangaro * Copyright (C) 2015 Benoit Bruchard * * This program is free software; you can redistribute it and/or modify @@ -89,7 +89,7 @@ class html_cerfafr extends ModeleDon $outputlangs->load("donations"); $currency = !empty($currency) ? $currency : $conf->currency; - + if (! empty($conf->don->dir_output)) { // Definition of the object don (for upward compatibility) @@ -134,21 +134,21 @@ class html_cerfafr extends ModeleDon $paymentmode = $formclass->cache_types_paiements[$don->modepaiementid]['label']; } else $paymentmode = ''; - - if ($don->modepaiementid==7){ + + if ($don->modepaymentcode=='CHQ'){ $ModePaiement = ''; } - else if ($don->modepaiementid==4){ + else if ($don->modepaymentcode=='LIQ'){ $ModePaiement = ''; } - else if ($don->modepaiementid==2 || $don->modepaiementid==3 || $don->modepaiementid==6){ + else if ($don->modepaymentcode=='VIR' || $don->modepaymentcode=='PRE' || $don->modepaymentcode=='CB'){ $ModePaiement = ''; - } - else + } + else { $ModePaiement = ''; } - + /* if (empty($don->societe)) { @@ -159,14 +159,14 @@ class html_cerfafr extends ModeleDon $CodeDon = ''; } */ - + // Define contents $donmodel=DOL_DOCUMENT_ROOT ."/core/modules/dons/html_cerfafr.html"; $form = implode('', file($donmodel)); $form = str_replace('__REF__',$don->id,$form); $form = str_replace('__DATE__',dol_print_date($don->date,'day',false,$outputlangs),$form); //$form = str_replace('__IP__',$user->ip,$form); // TODO $user->ip not exist - $form = str_replace('__AMOUNT__',$don->amount,$form); + $form = str_replace('__AMOUNT__', price($don->amount), $form); $form = str_replace('__AMOUNTLETTERS__',chiffre_en_lettre($don->amount),$form); $form = str_replace('__CURRENCY__',$outputlangs->transnoentitiesnoconv("Currency".$currency),$form); $form = str_replace('__CURRENCYCODE__',$conf->currency,$form); @@ -183,7 +183,6 @@ class html_cerfafr extends ModeleDon $form = str_replace('__DONATOR_ZIP__',$don->zip,$form); $form = str_replace('__DONATOR_TOWN__',$don->town,$form); $form = str_replace('__PAYMENTMODE_LIB__ ', $paymentmode,$form); - $form = str_replace('__ModePaiement__', $ModePaiement,$form); $form = str_replace('__NOW__',dol_print_date($now,'day',false,$outputlangs),$form); $form = str_replace('__DonationRef__',$outputlangs->trans("DonationRef"),$form); $form = str_replace('__DonationTitle__',$outputlangs->trans("DonationTitle"),$form); @@ -204,6 +203,8 @@ class html_cerfafr extends ModeleDon $form = str_replace('__IConfirmDonationReception__',$outputlangs->trans("IConfirmDonationReception"),$form); $form = str_replace('__DonationMessage__',$conf->global->DONATION_MESSAGE,$form); + $form = str_replace('__ModePaiement__', $ModePaiement,$form); + $frencharticle=''; if (preg_match('/fr/i',$outputlangs->defaultlang)) $frencharticle='Article 200, 238 bis et 885-0 V bis A du code général des impôts (CGI)'; $form = str_replace('__FrenchArticle__',$frencharticle,$form); @@ -260,7 +261,7 @@ class html_cerfafr extends ModeleDon @chmod($file, octdec($conf->global->MAIN_UMASK)); $this->result = array('fullpath'=>$file); - + return 1; } else @@ -394,34 +395,34 @@ function chiffre_en_lettre($montant, $devise1='', $devise2='') if($cent[$i]==1) $trio[$i]='cent'; else if($cent[$i]!=0 || $cent[$i]!='') $trio[$i]=$chif[$cent[$i]] .' cents'; } - - + + $chif2=array('', 'dix', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante', 'soixante-dix', 'quatre-vingts', 'quatre-vingts dix'); $secon_c=$chif2[$dix_c]; if($cent_c==1) $trio_c='cent'; else if($cent_c!=0 || $cent_c!='') $trio_c=$chif[$cent_c] .' cents'; - + if(($cent[3]==0 || $cent[3]=='') && ($dix[3]==0 || $dix[3]=='') && ($unite[3]==1)) $somme = $trio[3]. ' ' .$secon[3]. ' ' . $prim[3]. ' million '; else if(($cent[3]!=0 && $cent[3]!='') || ($dix[3]!=0 && $dix[3]!='') || ($unite[3]!=0 && $unite[3]!='')) $somme = $trio[3]. ' ' .$secon[3]. ' ' . $prim[3]. ' millions '; else $somme = $trio[3]. ' ' .$secon[3]. ' ' . $prim[3]; - + if(($cent[2]==0 || $cent[2]=='') && ($dix[2]==0 || $dix[2]=='') && ($unite[2]==1)) $somme = $somme.' mille '; else if(($cent[2]!=0 && $cent[2]!='') || ($dix[2]!=0 && $dix[2]!='') || ($unite[2]!=0 && $unite[2]!='')) $somme = $somme. $trio[2]. ' ' .$secon[2]. ' ' . $prim[2]. ' milles '; else $somme = $somme. $trio[2]. ' ' .$secon[2]. ' ' . $prim[2]; - + $somme = $somme. $trio[1]. ' ' .$secon[1]. ' ' . $prim[1]; - + $somme = $somme. ' '. $dev1 .' ' ; - + if(($cent_c=='0' || $cent_c=='') && ($dix_c=='0' || $dix_c=='')) return $somme. ' et zéro '. $dev2; else return $somme. $trio_c. ' ' .$secon_c. ' ' . $dev2; - + } diff --git a/htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php b/htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php index 77a5677c003..9c45c135ac3 100644 --- a/htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php +++ b/htdocs/core/modules/expedition/doc/doc_generic_shipment_odt.modules.php @@ -437,30 +437,42 @@ class doc_generic_shipment_odt extends ModelePdfExpedition // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->lines as $line) - { - $tmparray=$this->get_substitutionarray_shipment_lines($line,$outputlangs); - complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) - { - try - { - $listlines->setVars($key, $val, true, 'UTF-8'); - } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } - } - $listlines->merge(); + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_shipment_lines($line,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -511,7 +523,7 @@ class doc_generic_shipment_odt extends ModelePdfExpedition $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/modules/expedition/doc/pdf_merou.modules.php b/htdocs/core/modules/expedition/doc/pdf_merou.modules.php index 68795126487..1609ef8283f 100644 --- a/htdocs/core/modules/expedition/doc/pdf_merou.modules.php +++ b/htdocs/core/modules/expedition/doc/pdf_merou.modules.php @@ -91,16 +91,9 @@ class pdf_merou extends ModelePdfExpedition if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("bills"); - $outputlangs->load("products"); - $outputlangs->load("propal"); - $outputlangs->load("deliveries"); - $outputlangs->load("sendings"); - $outputlangs->load("productbatch"); + + // Translations + $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies", "propal", "deliveries", "sendings", "productbatch")); if ($conf->expedition->dir_output) { @@ -392,9 +385,9 @@ class pdf_merou extends ModelePdfExpedition { global $langs; $default_font_size = pdf_getPDFFontSize($outputlangs); - - $langs->load("main"); - $langs->load("bills"); + + // Translations + $langs->loadLangs(array("main", "bills")); if (empty($hidetop)) { @@ -542,7 +535,7 @@ class pdf_merou extends ModelePdfExpedition $pdf->SetTextColor(0,0,0); // Sender properties - $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); $pdf->SetFont('','', $default_font_size - 3); $pdf->SetXY($blSocX,$blSocY+4); diff --git a/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php b/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php index f7eca910b7e..1b7c1edcd7b 100644 --- a/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php +++ b/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php @@ -122,16 +122,9 @@ class pdf_rouget extends ModelePdfExpedition if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("bills"); - $outputlangs->load("products"); - $outputlangs->load("propal"); - $outputlangs->load("deliveries"); - $outputlangs->load("sendings"); - $outputlangs->load("productbatch"); + + // Translations + $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies", "propal", "deliveries", "sendings", "productbatch")); $nblignes = count($object->lines); @@ -935,7 +928,7 @@ class pdf_rouget extends ModelePdfExpedition $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$outputlangs->transnoentities("Name").": ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; } - $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=!empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42; diff --git a/htdocs/core/modules/expensereport/doc/pdf_standard.modules.php b/htdocs/core/modules/expensereport/doc/pdf_standard.modules.php index 15d48694277..32fe36044b3 100644 --- a/htdocs/core/modules/expensereport/doc/pdf_standard.modules.php +++ b/htdocs/core/modules/expensereport/doc/pdf_standard.modules.php @@ -1,7 +1,7 @@ * Copyright (C) 2015 Alexandre Spangaro - * Copyright (C) 2016 Philippe Grand + * Copyright (C) 2016-2018 Philippe Grand * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,10 +38,25 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; */ class pdf_standard extends ModeleExpenseReport { - var $db; - var $name; - var $description; - var $type; + /** + * @var DoliDb Database handler + */ + public $db; + + /** + * @var string model name + */ + public $name; + + /** + * @var string model description (short text) + */ + public $description; + + /** + * @var string document type + */ + public $type; var $phpmin = array(4,3,0); // Minimum version of PHP required by module var $version = 'dolibarr'; @@ -64,11 +79,10 @@ class pdf_standard extends ModeleExpenseReport */ function __construct($db) { - global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("trips"); - $langs->load("projects"); + global $conf, $langs, $mysoc; + + // Translations + $langs->loadLangs(array("main", "trips", "projects")); $this->db = $db; $this->name = ""; @@ -154,11 +168,9 @@ class pdf_standard extends ModeleExpenseReport if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("trips"); - $outputlangs->load("projects"); + + // Translations + $outputlangs->loadLangs(array("main", "trips", "projects", "dict")); $nblignes = count($object->lines); @@ -328,7 +340,15 @@ class pdf_standard extends ModeleExpenseReport $nextColumnPosX = $this->posxprojet; } - $pdf->MultiCell($nextColumnPosX-$this->posxtype-0.8, 4, dol_trunc($outputlangs->transnoentities($object->lines[$i]->type_fees_code), 10), 0, 'C'); + $expensereporttypecode = $object->lines[$i]->type_fees_code; + $expensereporttypecodetoshow = $outputlangs->transnoentities($expensereporttypecode); + if ($expensereporttypecodetoshow == $expensereporttypecode) + { + $expensereporttypecodetoshow = preg_replace('/^(EX_|TF_)/', '', $expensereporttypecodetoshow); + } + $expensereporttypecodetoshow = dol_trunc($expensereporttypecodetoshow, 9); // 10 is too much + + $pdf->MultiCell($nextColumnPosX-$this->posxtype-0.8, 4, $expensereporttypecodetoshow, 0, 'C'); // Project if (! empty($conf->projet->enabled)) @@ -507,10 +527,10 @@ class pdf_standard extends ModeleExpenseReport function _pagehead(&$pdf, $object, $showaddress, $outputlangs) { global $conf,$langs,$hookmanager; + + // Translations + $outputlangs->loadLangs(array("main", "trips", "companies")); - $outputlangs->load("main"); - $outputlangs->load("trips"); - $outputlangs->load("companies"); $default_font_size = pdf_getPDFFontSize($outputlangs); /* diff --git a/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php b/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php index 79b848c5773..6d9d5172a7f 100644 --- a/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php +++ b/htdocs/core/modules/facture/doc/doc_generic_invoice_odt.modules.php @@ -361,20 +361,22 @@ class doc_generic_invoice_odt extends ModelePDFFactures { } - // Make substitutions into odt + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties=$this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_propal=is_object($propal_object)?$this->get_substitutionarray_object($propal_object,$outputlangs,'propal'):array(); $array_other=$this->get_substitutionarray_other($outputlangs); - // retrieve contact information for use in invoice as contact_xxx tags - $array_thirdparty_contact = array(); - if ($usecontact) - $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); + // retrieve contact information for use in invoice as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); - $tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_objet,$array_propal,$array_other,$array_thirdparty_contact); + $tmparray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_propal,$array_other,$array_thirdparty_contact); complete_substitutions_array($tmparray, $outputlangs, $object); + // Call the ODTSubstitution hook $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks @@ -401,30 +403,42 @@ class doc_generic_invoice_odt extends ModelePDFFactures // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->lines as $line) - { - $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); - complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) - { - try - { - $listlines->setVars($key, $val, true, 'UTF-8'); - } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } - } - $listlines->merge(); + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -475,7 +489,7 @@ class doc_generic_invoice_odt extends ModelePDFFactures $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php index 8050abf811b..17b652ed2db 100644 --- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php @@ -42,24 +42,52 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; */ class pdf_crabe extends ModelePDFFactures { - var $db; - var $name; - var $description; - var $update_main_doc_field; // Save the name of generated file as the main doc when generating a doc with this template - var $type; + /** + * @var DoliDb Database handler + */ + public $db; - var $phpmin = array(4,3,0); // Minimum version of PHP required by module - var $version = 'dolibarr'; + /** + * @var string model name + */ + public $name; - var $page_largeur; - var $page_hauteur; - var $format; - var $marge_gauche; - var $marge_droite; - var $marge_haute; - var $marge_basse; + /** + * @var string model description (short text) + */ + public $description; - var $emetteur; // Objet societe qui emet + /** + * @var int Save the name of generated file as the main doc when generating a doc with this template + */ + public $update_main_doc_field; + + /** + * @var string document type + */ + public $type; + + /** + * @var array() Minimum version of PHP required by module. + * e.g.: PHP ≥ 5.3 = array(5, 3) + */ + public $phpmin = array(5, 2); + + /** + * Dolibarr version of the loaded document + * @public string + */ + public $version = 'dolibarr'; + + public $page_largeur; + public $page_hauteur; + public $format; + public $marge_gauche; + public $marge_droite; + public $marge_haute; + public $marge_basse; + + public $emetteur; // Objet societe qui emet /** * @var bool Situation invoice type @@ -80,9 +108,9 @@ class pdf_crabe extends ModelePDFFactures function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); + + // Translations + $langs->loadLangs(array("main", "bills")); $this->db = $db; $this->name = "crabe"; @@ -176,12 +204,9 @@ class pdf_crabe extends ModelePDFFactures if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("bills"); - $outputlangs->load("products"); + + // Translations + $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies")); $nblignes = count($object->lines); @@ -828,7 +853,7 @@ class pdf_crabe extends ModelePDFFactures $sql = "SELECT p.datep as date, p.fk_paiement, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,"; $sql.= " cp.code"; $sql.= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."paiement as p"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as cp ON p.fk_paiement = cp.id AND cp.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as cp ON p.fk_paiement = cp.id"; $sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = ".$object->id; //$sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = 1"; $sql.= " ORDER BY p.datep"; @@ -1517,12 +1542,10 @@ class pdf_crabe extends ModelePDFFactures */ function _pagehead(&$pdf, $object, $showaddress, $outputlangs) { - global $conf,$langs; - - $outputlangs->load("main"); - $outputlangs->load("bills"); - $outputlangs->load("propal"); - $outputlangs->load("companies"); + global $conf, $langs; + + // Translations + $outputlangs->loadLangs(array("main", "bills", "propal", "companies")); $default_font_size = pdf_getPDFFontSize($outputlangs); @@ -1688,11 +1711,11 @@ class pdf_crabe extends ModelePDFFactures { $top_shift = $pdf->getY() - $current_y; } - + if ($showaddress) { // Sender properties - $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=!empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42; diff --git a/htdocs/core/modules/facture/mod_facture_mars.php b/htdocs/core/modules/facture/mod_facture_mars.php index 85dff1aa0e1..831dce5d3e4 100644 --- a/htdocs/core/modules/facture/mod_facture_mars.php +++ b/htdocs/core/modules/facture/mod_facture_mars.php @@ -153,7 +153,7 @@ class mod_facture_mars extends ModeleNumRefFactures $sql = "SELECT MAX(CAST(SUBSTRING(facnumber FROM ".$posindice.") AS SIGNED)) as max"; // This is standard SQL $sql.= " FROM ".MAIN_DB_PREFIX."facture"; $sql.= " WHERE facnumber LIKE '".$prefix."____-%'"; - $sql.= " AND entity IN (".getEntity('facture').")"; + $sql.= " AND entity IN (".getEntity('invoicenumber').")"; $resql=$db->query($sql); dol_syslog(get_class($this)."::getNextValue", LOG_DEBUG); @@ -177,7 +177,7 @@ class mod_facture_mars extends ModeleNumRefFactures $sql = "SELECT facnumber as ref"; $sql.= " FROM ".MAIN_DB_PREFIX."facture"; $sql.= " WHERE facnumber LIKE '".$prefix."____-".$num."'"; - $sql.= " AND entity IN (".getEntity('facture').")"; + $sql.= " AND entity IN (".getEntity('invoicenumber').")"; dol_syslog(get_class($this)."::getNextValue", LOG_DEBUG); $resql=$db->query($sql); diff --git a/htdocs/core/modules/facture/mod_facture_terre.php b/htdocs/core/modules/facture/mod_facture_terre.php index 4c27340f270..174d8314784 100644 --- a/htdocs/core/modules/facture/mod_facture_terre.php +++ b/htdocs/core/modules/facture/mod_facture_terre.php @@ -169,7 +169,7 @@ class mod_facture_terre extends ModeleNumRefFactures $sql = "SELECT MAX(CAST(SUBSTRING(facnumber FROM ".$posindice.") AS SIGNED)) as max"; // This is standard SQL $sql.= " FROM ".MAIN_DB_PREFIX."facture"; $sql.= " WHERE facnumber LIKE '".$prefix."____-%'"; - $sql.= " AND entity IN (".getEntity('facture').")"; + $sql.= " AND entity IN (".getEntity('invoicenumber').")"; $resql=$db->query($sql); dol_syslog(get_class($this)."::getNextValue", LOG_DEBUG); @@ -193,7 +193,7 @@ class mod_facture_terre extends ModeleNumRefFactures $sql = "SELECT facnumber as ref"; $sql.= " FROM ".MAIN_DB_PREFIX."facture"; $sql.= " WHERE facnumber LIKE '".$prefix."____-".$num."'"; - $sql.= " AND entity IN (".getEntity('facture').")"; + $sql.= " AND entity IN (".getEntity('invoicenumber').")"; dol_syslog(get_class($this)."::getNextValue", LOG_DEBUG); $resql=$db->query($sql); diff --git a/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php b/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php index f63f05b07f9..26cdc3f5dfc 100644 --- a/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php +++ b/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php @@ -112,11 +112,9 @@ class pdf_soleil extends ModelePDFFicheinter if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("interventions"); + + // Translations + $outputlangs->loadLangs(array("main", "interventions", "dict", "companies")); if ($conf->ficheinter->dir_output) { @@ -584,7 +582,7 @@ class pdf_soleil extends ModelePDFFicheinter $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$outputlangs->transnoentities("Name").": ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; } - $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42; diff --git a/htdocs/core/modules/import/import_csv.modules.php b/htdocs/core/modules/import/import_csv.modules.php index d4ffc513521..a6473eaeb8e 100644 --- a/htdocs/core/modules/import/import_csv.modules.php +++ b/htdocs/core/modules/import/import_csv.modules.php @@ -591,6 +591,7 @@ class ImportCsv extends ModeleImports if (!empty($updatekeys)) { // We do SELECT to get the rowid, if we already have the rowid, it's to be used below for related tables (extrafields) + if (empty($lastinsertid)) { $sqlSelect = 'SELECT rowid FROM '.$tablename; @@ -626,6 +627,34 @@ class ImportCsv extends ModeleImports $this->errors[$error]['type']='SQL'; $error++; } + } else { + // We have a last INSERT ID. Check if we have a row referencing this foreign key. + // This is required when updating table with some extrafields. When inserting a record in parent table, we can make + // a direct insert into subtable extrafields, but when me wake an update, the insertid is defined and the child record + // may already exists. So we rescan the extrafield table to be know if record exists or not for the rowid. + $sqlSelect = 'SELECT rowid FROM '.$tablename; + + if(empty($keyfield)) $keyfield = 'rowid'; + $sqlSelect .= ' WHERE '.$keyfield.' = '.$lastinsertid; + + $resql=$this->db->query($sqlSelect); + if($resql) { + $res = $this->db->fetch_object($resql); + if($resql->num_rows == 1) { + // We have a row referencing this last foreign key, continue with UPDATE. + } else { + // No record found referencing this last foreign key, + // force $lastinsertid to 0 so we INSERT below. + $lastinsertid = 0; + } + } + else + { + //print 'E'; + $this->errors[$error]['lib']=$this->db->lasterror(); + $this->errors[$error]['type']='SQL'; + $error++; + } } if (!empty($lastinsertid)) { diff --git a/htdocs/core/modules/import/import_xlsx.modules.php b/htdocs/core/modules/import/import_xlsx.modules.php index 214ce91b032..7f222f523ca 100644 --- a/htdocs/core/modules/import/import_xlsx.modules.php +++ b/htdocs/core/modules/import/import_xlsx.modules.php @@ -651,6 +651,34 @@ class ImportXlsx extends ModeleImports $this->errors[$error]['type']='SQL'; $error++; } + } else { + // We have a last INSERT ID. Check if we have a row referencing this foreign key. + // This is required when updating table with some extrafields. When inserting a record in parent table, we can make + // a direct insert into subtable extrafields, but when me wake an update, the insertid is defined and the child record + // may already exists. So we rescan the extrafield table to be know if record exists or not for the rowid. + $sqlSelect = 'SELECT rowid FROM '.$tablename; + + if(empty($keyfield)) $keyfield = 'rowid'; + $sqlSelect .= ' WHERE '.$keyfield.' = '.$lastinsertid; + + $resql=$this->db->query($sqlSelect); + if($resql) { + $res = $this->db->fetch_object($resql); + if($resql->num_rows == 1) { + // We have a row referencing this last foreign key, continue with UPDATE. + } else { + // No record found referencing this last foreign key, + // force $lastinsertid to 0 so we INSERT below. + $lastinsertid = 0; + } + } + else + { + //print 'E'; + $this->errors[$error]['lib']=$this->db->lasterror(); + $this->errors[$error]['type']='SQL'; + $error++; + } } if (!empty($lastinsertid)) { diff --git a/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php b/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php index fcb0bb6b057..6c37a706064 100644 --- a/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php +++ b/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php @@ -3,7 +3,7 @@ * Copyright (C) 2005-2014 Regis Houssin * Copyright (C) 2007 Franky Van Liedekerke * Copyright (C) 2008 Chiptronik - * Copyright (C) 2011-2012 Philippe Grand + * Copyright (C) 2011-2018 Philippe Grand * Copyright (C) 2015 Marcos García * This program is free software; you can redistribute it and/or modify @@ -65,11 +65,9 @@ class pdf_typhon extends ModelePDFDeliveryOrder function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); - $langs->load("sendings"); - $langs->load("companies"); + + // Translations + $langs->loadLangs(array("main", "bills", "sendings", "companies")); $this->db = $db; $this->name = "typhon"; @@ -139,14 +137,9 @@ class pdf_typhon extends ModelePDFDeliveryOrder if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("bills"); - $outputlangs->load("products"); - $outputlangs->load("deliveries"); - $outputlangs->load("sendings"); + + // Translations + $outputlangs->loadLangs(array("main", "dict", "companies", "bills", "products", "sendings", "deliveries")); if ($conf->expedition->dir_output) { @@ -799,7 +792,7 @@ class pdf_typhon extends ModelePDFDeliveryOrder if ($showaddress) { // Sender properties - $carac_emetteur = pdf_build_address($outputlangs,$this->emetteur); + $carac_emetteur = pdf_build_address($outputlangs,$this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42; diff --git a/htdocs/core/modules/mailings/advthirdparties.modules.php b/htdocs/core/modules/mailings/advthirdparties.modules.php index 9252ae5ec3a..e6f0255b9d9 100644 --- a/htdocs/core/modules/mailings/advthirdparties.modules.php +++ b/htdocs/core/modules/mailings/advthirdparties.modules.php @@ -100,7 +100,7 @@ class mailing_advthirdparties extends MailingTargets 'source_url' => $this->url($obj->id,'thirdparty'), 'source_id' => $obj->id, 'source_type' => 'thirdparty' - ); + ); } } @@ -289,11 +289,11 @@ class mailing_advthirdparties extends MailingTargets if ($type=='thirdparty') { $companystatic=new Societe($this->db); $companystatic->fetch($id); - return $companystatic->getNomUrl(0); + return $companystatic->getNomUrl(0, '', 0, 1); } elseif ($type=='contact') { $contactstatic=new Contact($this->db); $contactstatic->fetch($id); - return $contactstatic->getNomUrl(0); + return $contactstatic->getNomUrl(0, '', 0, '', -1, 0); } } diff --git a/htdocs/core/modules/mailings/fraise.modules.php b/htdocs/core/modules/mailings/fraise.modules.php index a1674d6ab08..f71b6b925f0 100644 --- a/htdocs/core/modules/mailings/fraise.modules.php +++ b/htdocs/core/modules/mailings/fraise.modules.php @@ -127,7 +127,7 @@ class mailing_fraise extends MailingTargets $s.='
    '; @@ -354,21 +354,22 @@ class doc_generic_order_odt extends ModelePDFCommandes { } - // Make substitutions into odt + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties=$this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_other=$this->get_substitutionarray_other($outputlangs); - // retrieve contact information for use in order as contact_xxx tags - $array_thirdparty_contact = array(); - if ($usecontact) - $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); + // retrieve contact information for use in order as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); - $tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_thirdparty_contact); + $tmparray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_thirdparty_contact); complete_substitutions_array($tmparray, $outputlangs, $object); + // Call the ODTSubstitution hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks @@ -392,30 +393,42 @@ class doc_generic_order_odt extends ModelePDFCommandes // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->lines as $line) - { - $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); - complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) - { - try - { - $listlines->setVars($key, $val, true, 'UTF-8'); - } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } - } - $listlines->merge(); + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -468,7 +481,7 @@ class doc_generic_order_odt extends ModelePDFCommandes $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/modules/commande/doc/pdf_einstein.modules.php b/htdocs/core/modules/commande/doc/pdf_einstein.modules.php index 7c24e49e89b..738a49fe81e 100644 --- a/htdocs/core/modules/commande/doc/pdf_einstein.modules.php +++ b/htdocs/core/modules/commande/doc/pdf_einstein.modules.php @@ -97,10 +97,9 @@ class pdf_einstein extends ModelePDFCommandes public function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); - $langs->load("products"); + + // Translations + $langs->loadLangs(array("main", "bills", "products")); $this->db = $db; $this->name = "einstein"; @@ -183,21 +182,16 @@ class pdf_einstein extends ModelePDFCommandes * @param int $hideref Do not show ref * @return int 1=OK, 0=KO */ - function write_file($object,$outputlangs,$srctemplatepath='',$hidedetails=0,$hidedesc=0,$hideref=0) + function write_file($object, $outputlangs, $srctemplatepath='', $hidedetails=0, $hidedesc=0, $hideref=0) { - global $user,$langs,$conf,$mysoc,$db,$hookmanager,$nblignes; + global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblignes; if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("bills"); - $outputlangs->load("products"); - $outputlangs->load("orders"); - $outputlangs->load("deliveries"); + + // Translations + $outputlangs->loadLangs(array("main", "dict", "companies", "bills", "products", "orders", "deliveries")); $nblignes = count($object->lines); @@ -1215,12 +1209,10 @@ class pdf_einstein extends ModelePDFCommandes function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $titlekey="PdfOrderTitle") { global $conf,$langs,$hookmanager; + + // Translations + $outputlangs->loadLangs(array("main", "bills", "propal", "orders", "companies")); - $outputlangs->load("main"); - $outputlangs->load("bills"); - $outputlangs->load("propal"); - $outputlangs->load("companies"); - $outputlangs->load("orders"); $default_font_size = pdf_getPDFFontSize($outputlangs); pdf_pagehead($pdf,$outputlangs,$this->page_hauteur); @@ -1330,7 +1322,7 @@ class pdf_einstein extends ModelePDFCommandes $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$labelbeforecontactname." ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; } - $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42+$top_shift; diff --git a/htdocs/core/modules/contract/doc/doc_generic_contract_odt.modules.php b/htdocs/core/modules/contract/doc/doc_generic_contract_odt.modules.php index d99d43396bc..98b644c8cce 100644 --- a/htdocs/core/modules/contract/doc/doc_generic_contract_odt.modules.php +++ b/htdocs/core/modules/contract/doc/doc_generic_contract_odt.modules.php @@ -290,27 +290,40 @@ class doc_generic_contract_odt extends ModelePDFContract { $socobject=$object->thirdparty; } - // Make substitution - $substitutionarray=array( - '__FROM_NAME__' => $this->emetteur->name, - '__FROM_EMAIL__' => $this->emetteur->email, - '__TOTAL_TTC__' => $object->total_ttc, - '__TOTAL_HT__' => $object->total_ht, - '__TOTAL_VAT__' => $object->total_vat - ); - complete_substitutions_array($substitutionarray, $langs, $object); + + $object->fetch_optionals(); + + + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties=$this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($object,$outputlangs); // complete with vars not set as properties by get_substitutionarray_each_var_object + $array_user=$this->get_substitutionarray_user($user,$outputlangs); + $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); + $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); + $array_other=$this->get_substitutionarray_other($outputlangs); + // retrieve contact information for use in contract as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); + + $substitutionarray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_thirdparty_contact); + complete_substitutions_array($substitutionarray, $outputlangs, $object); + + $tmparray = $substitutionarray; + // Call the ODTSubstitution hook - $parameters=array('file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$substitutionarray); + $parameters=array('file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks // Line of free text $newfreetext=''; - $paramfreetext='contract_FREE_TEXT'; + $paramfreetext='CONTRACT_FREE_TEXT'; if (! empty($conf->global->$paramfreetext)) { - $newfreetext=make_substitutions($conf->global->$paramfreetext,$substitutionarray); + $newfreetext=make_substitutions($conf->global->$paramfreetext,$tmparray); } + // Open and load template require_once ODTPHP_PATH.'odf.php'; try { @@ -344,24 +357,6 @@ class doc_generic_contract_odt extends ModelePDFContract { } - // Make substitutions into odt - $array_contract=$this->get_substitutionarray_each_var_object($object, $outputlangs); - $array_user=$this->get_substitutionarray_user($user,$outputlangs); - $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); - $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($object,$outputlangs); - $array_other=$this->get_substitutionarray_other($outputlangs); - // retrieve contact information for use in contract as contact_xxx tags - $array_thirdparty_contact = array(); - if ($usecontact) - $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); - - $tmparray = array_merge($array_contract,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_thirdparty_contact); - complete_substitutions_array($tmparray, $outputlangs, $object); - $object->fetch_optionals(); - // Call the ODTSubstitution hook - $parameters=array('file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); - $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks foreach($tmparray as $key=>$value) { try { @@ -379,33 +374,46 @@ class doc_generic_contract_odt extends ModelePDFContract { } } + // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->lines as $line) - { - $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); - complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) - { - try - { - $listlines->setVars($key, $val, true, 'UTF-8'); - } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } - } - $listlines->merge(); + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -456,7 +464,7 @@ class doc_generic_contract_odt extends ModelePDFContract $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/modules/contract/doc/pdf_strato.modules.php b/htdocs/core/modules/contract/doc/pdf_strato.modules.php index 2cea64a72a6..62501d4881e 100644 --- a/htdocs/core/modules/contract/doc/pdf_strato.modules.php +++ b/htdocs/core/modules/contract/doc/pdf_strato.modules.php @@ -4,7 +4,7 @@ * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2008 Raphael Bertrand (Resultic) * Copyright (C) 2011 Fabrice CHERRIER - * Copyright (C) 2013 Philippe Grand + * Copyright (C) 2013-2018 Philippe Grand * Copyright (C) 2015 Marcos García * * This program is free software; you can redistribute it and/or modify @@ -126,11 +126,9 @@ class pdf_strato extends ModelePDFContract if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("contracts"); + + // Translations + $outputlangs->loadLangs(array("main", "dict", "companies", "contracts")); if ($conf->contrat->dir_output) { @@ -495,12 +493,11 @@ class pdf_strato extends ModelePDFContract function _pagehead(&$pdf, $object, $showaddress, $outputlangs) { global $conf,$langs; + $default_font_size = pdf_getPDFFontSize($outputlangs); - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("contract"); + + // Translations + $outputlangs->loadLangs(array("main", "dict", "contract", "companies")); pdf_pagehead($pdf,$outputlangs,$this->page_hauteur); @@ -583,7 +580,7 @@ class pdf_strato extends ModelePDFContract $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$outputlangs->transnoentities("Name").": ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; } - $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42; diff --git a/htdocs/core/modules/dons/html_cerfafr.html b/htdocs/core/modules/dons/html_cerfafr.html index bd066d73a05..614f22cf998 100644 --- a/htdocs/core/modules/dons/html_cerfafr.html +++ b/htdocs/core/modules/dons/html_cerfafr.html @@ -72,7 +72,7 @@ Fondation d'entreprise
    Oeuvre ou organisme d'intérêt général
    Remise d\'espèces Chèque Virement, prélèvement, carte bancaire Remise d\'espèces Chèque Virement, prélèvement, carte bancaire Remise d\'espèces Chèque Virement, prélèvement, carte bancaire Remise d\'espèces Chèque Virement, prélèvement, carte bancaire 200 du CGI 238 bis du CGI 885-0 V bis A du CGI
    '; + + // This was a callback request from service, get the token + try { + //var_dump($_GET['code']); + //var_dump($state); + //var_dump($apiService); // OAuth\OAuth2\Service\GitHub + + //$token = $apiService->requestAccessToken($_GET['code'], $state); + $token = $apiService->requestAccessToken($_GET['code']); + // Github is a service that does not need state to be stored. + // Into constructor of GitHub, the call + // parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri) + // has not the ending parameter to true like the Google class constructor. + + setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token + + $backtourl = $_SESSION["backtourlsavedbeforeoauthjump"]; + unset($_SESSION["backtourlsavedbeforeoauthjump"]); + + header('Location: ' . $backtourl); + exit(); + } catch (Exception $e) { + print $e->getMessage(); + } +} +else // If entry on page with no parameter, we arrive here +{ + $_SESSION["backtourlsavedbeforeoauthjump"]=$backtourl; + + // This may create record into oauth_state before the header redirect. + // Creation of record with state in this tables depend on the Provider used (see its constructor). + if (GETPOST('state')) + { + $url = $apiService->getAuthorizationUri(array('state'=>GETPOST('state'))); + } + else + { + //$url = $apiService->getAuthorizationUri(); // Parameter state will be randomly generated + //https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_AX27ut70tJ1j6eyFCV3ObEXhNOo2jY6V&scope=read_write + $url = 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id='.$conf->global->OAUTH_STRIPE_TEST_ID.'&scope=read_write'; + } + + // we go on oauth provider authorization page + header('Location: ' . $url); + exit(); +} + + +/* + * View + */ + +// No view at all, just actions + +$db->close(); + diff --git a/htdocs/core/modules/product/doc/doc_generic_product_odt.modules.php b/htdocs/core/modules/product/doc/doc_generic_product_odt.modules.php index 8af397d15c1..6c5fa8652f9 100644 --- a/htdocs/core/modules/product/doc/doc_generic_product_odt.modules.php +++ b/htdocs/core/modules/product/doc/doc_generic_product_odt.modules.php @@ -95,7 +95,7 @@ class doc_generic_product_odt extends ModelePDFProduct function info($langs) { global $conf,$langs; - + $langs->load("companies"); $langs->load("errors"); @@ -361,6 +361,7 @@ class doc_generic_product_odt extends ModelePDFProduct //print html_entity_decode($odfHandler->__toString()); //print exit; + $object->fetch_optionals(); // Make substitutions into odt of freetext try { @@ -369,25 +370,26 @@ class doc_generic_product_odt extends ModelePDFProduct catch(OdfException $e) { } - - // Make substitutions into odt - $array_global = $this->get_substitutionarray_each_var_object($object, $outputlangs); + + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties = $this->get_substitutionarray_each_var_object($object, $outputlangs); + //$array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - //$array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_other=$this->get_substitutionarray_other($outputlangs); - // retrieve contact information for use in product as contact_xxx tags - $array_thirdparty_contact = array(); - if ($usecontact) - $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); + // retrieve contact information for use in product as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); - $tmparray = array_merge($array_global,$array_user,$array_soc,$array_thirdparty,$array_other,$array_thirdparty_contact); + $tmparray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_other,$array_thirdparty_contact); complete_substitutions_array($tmparray, $outputlangs, $object); - $object->fetch_optionals(); + // Call the ODTSubstitution hook $parameters=array('file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key=>$value) { try { diff --git a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php index 35e8ace270e..5c0d025252e 100644 --- a/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php +++ b/htdocs/core/modules/project/doc/doc_generic_project_odt.modules.php @@ -569,24 +569,25 @@ class doc_generic_project_odt extends ModelePDFProjects //print exit; - - - // Make substitutions into odt of user info + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties = $this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_other=$this->get_substitutionarray_other($outputlangs); - // retrieve contact information for use in project as contact_xxx tags - $array_project_contact = array(); - if ($usecontact) - $array_project_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); + // retrieve contact information for use in project as contact_xxx tags + $array_project_contact = array(); + if ($usecontact) $array_project_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); - $tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_project_contact); + $tmparray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_project_contact); complete_substitutions_array($tmparray, $outputlangs, $object); + // Call the ODTSubstitution hook $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key=>$value) { try { diff --git a/htdocs/core/modules/project/doc/pdf_baleine.modules.php b/htdocs/core/modules/project/doc/pdf_baleine.modules.php index 66108da54f8..4b6273388ad 100644 --- a/htdocs/core/modules/project/doc/pdf_baleine.modules.php +++ b/htdocs/core/modules/project/doc/pdf_baleine.modules.php @@ -48,10 +48,9 @@ class pdf_baleine extends ModelePDFProjects function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("projects"); - $langs->load("companies"); + + // Translations + $langs->loadLangs(array("main", "projects", "companies")); $this->db = $db; $this->name = "baleine"; diff --git a/htdocs/core/modules/project/doc/pdf_beluga.modules.php b/htdocs/core/modules/project/doc/pdf_beluga.modules.php index f2d06d2ebb2..959cc152a0f 100644 --- a/htdocs/core/modules/project/doc/pdf_beluga.modules.php +++ b/htdocs/core/modules/project/doc/pdf_beluga.modules.php @@ -64,10 +64,9 @@ class pdf_beluga extends ModelePDFProjects function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("projects"); - $langs->load("companies"); + + // Translations + $langs->loadLangs(array("main", "projects", "companies")); $this->db = $db; $this->name = "beluga"; diff --git a/htdocs/core/modules/project/doc/pdf_timespent.modules.php b/htdocs/core/modules/project/doc/pdf_timespent.modules.php index 21853f1bbf8..6843e9c5a33 100644 --- a/htdocs/core/modules/project/doc/pdf_timespent.modules.php +++ b/htdocs/core/modules/project/doc/pdf_timespent.modules.php @@ -47,10 +47,9 @@ class pdf_timespent extends ModelePDFProjects function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("projects"); - $langs->load("companies"); + + // Translations + $langs->loadLangs(array("main", "projects", "companies")); $this->db = $db; $this->name = "timespent"; diff --git a/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php b/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php index c846d7f6a23..5f7ff754a65 100644 --- a/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php +++ b/htdocs/core/modules/project/task/doc/doc_generic_task_odt.modules.php @@ -507,17 +507,18 @@ class doc_generic_task_odt extends ModelePDFTask //print exit; - - - // Make substitutions into odt of user info + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties = $this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($project,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($project,$outputlangs); $array_other=$this->get_substitutionarray_other($outputlangs); - $tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_objet,$array_other); + $tmparray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other); complete_substitutions_array($tmparray, $outputlangs, $object); + foreach($tmparray as $key=>$value) { try { diff --git a/htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php b/htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php index 372edb5e5de..0b71e9e862f 100644 --- a/htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php +++ b/htdocs/core/modules/propale/doc/doc_generic_proposal_odt.modules.php @@ -246,7 +246,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales $outputlangs->load("companies"); $outputlangs->load("bills"); - if ($conf->propal->dir_output) + if ($conf->propal->multidir_output[$conf->entity]) { // If $object is id instead of object if (! is_object($object)) @@ -261,7 +261,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales } } - $dir = $conf->propal->dir_output; + $dir = $conf->propal->multidir_output[$object->entity]; $objectref = dol_sanitizeFileName($object->ref); if (! preg_match('/specimen/i',$objectref)) $dir.= "/" . $objectref; $file = $dir . "/" . $objectref . ".odt"; @@ -303,7 +303,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales //print "file=".$file; //print "conf->propal->dir_temp=".$conf->propal->dir_temp; - dol_mkdir($conf->propal->dir_temp); + dol_mkdir($conf->propal->multidir_temp[$object->entity]); // If CUSTOMER contact defined on proposal, we use it @@ -357,7 +357,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales $odfHandler = new odf( $srctemplatepath, array( - 'PATH_TO_TMP' => $conf->propal->dir_temp, + 'PATH_TO_TMP' => $conf->propal->multidir_temp[$object->entity], 'ZIP_PROXY' => 'PclZipProxy', // PhpZipProxy or PclZipProxy. Got "bad compression method" error when using PhpZipProxy. 'DELIMITER_LEFT' => '{', 'DELIMITER_RIGHT' => '}' @@ -375,6 +375,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales //print html_entity_decode($odfHandler->__toString()); //print exit; + $object->fetch_optionals(); // Make substitutions into odt of freetext try { @@ -384,23 +385,25 @@ class doc_generic_proposal_odt extends ModelePDFPropales { } - // Make substitutions into odt + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties=$this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_other=$this->get_substitutionarray_other($outputlangs); - // retrieve contact information for use in proposal as contact_xxx tags - $array_thirdparty_contact = array(); - if ($usecontact) - $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); + // retrieve contact information for use in proposal as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject,$outputlangs,'contact'); - $tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_thirdparty_contact); + $tmparray = array_merge($substitutionarray,$array_object_from_properties,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other,$array_thirdparty_contact); complete_substitutions_array($tmparray, $outputlangs, $object); - $object->fetch_optionals(); + // Call the ODTSubstitution hook $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key=>$value) { try { @@ -421,30 +424,42 @@ class doc_generic_proposal_odt extends ModelePDFPropales // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->lines as $line) - { - $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); - complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) - { - try - { - $listlines->setVars($key, $val, true, 'UTF-8'); - } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } - } - $listlines->merge(); + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -495,7 +510,7 @@ class doc_generic_proposal_odt extends ModelePDFPropales $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/modules/propale/doc/pdf_azur.modules.php b/htdocs/core/modules/propale/doc/pdf_azur.modules.php index 7be8b6a0d08..c27b8a7008e 100644 --- a/htdocs/core/modules/propale/doc/pdf_azur.modules.php +++ b/htdocs/core/modules/propale/doc/pdf_azur.modules.php @@ -68,9 +68,9 @@ class pdf_azur extends ModelePDFPropales function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); + + // Translations + $langs->loadLangs(array("main", "bills")); $this->db = $db; $this->name = "azur"; @@ -230,7 +230,7 @@ class pdf_azur extends ModelePDFPropales if (count($realpatharray) == 0) $this->posxpicture=$this->posxtva; - if ($conf->propal->dir_output) + if ($conf->propal->multidir_output[$conf->entity]) { $object->fetch_thirdparty(); @@ -239,13 +239,13 @@ class pdf_azur extends ModelePDFPropales // Definition of $dir and $file if ($object->specimen) { - $dir = $conf->propal->dir_output; + $dir = $conf->propal->multidir_output[$conf->entity]; $file = $dir . "/SPECIMEN.pdf"; } else { $objectref = dol_sanitizeFileName($object->ref); - $dir = $conf->propal->dir_output . "/" . $objectref; + $dir = $conf->propal->multidir_output[$object->entity] . "/" . $objectref; $file = $dir . "/" . $objectref . ".pdf"; } @@ -1501,7 +1501,7 @@ class pdf_azur extends ModelePDFPropales { $top_shift = $pdf->getY() - $current_y; } - + if ($showaddress) { // Sender properties @@ -1515,7 +1515,7 @@ class pdf_azur extends ModelePDFPropales $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$labelbeforecontactname." ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; } - $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42+$top_shift; diff --git a/htdocs/core/modules/rapport/pdf_paiement.class.php b/htdocs/core/modules/rapport/pdf_paiement.class.php index 2bb784aa578..2594a4a0b6a 100644 --- a/htdocs/core/modules/rapport/pdf_paiement.class.php +++ b/htdocs/core/modules/rapport/pdf_paiement.class.php @@ -187,7 +187,7 @@ class pdf_paiement if (! empty($conf->banque->enabled)) $sql.= ", ba.ref as bankaccount"; $sql.= ", p.rowid as prowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."paiement as p LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_paiement = c.id AND c.entity IN (" . getEntity('c_paiement').")"; + $sql.= " FROM ".MAIN_DB_PREFIX."paiement as p LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_paiement = c.id"; $sql.= ", ".MAIN_DB_PREFIX."facture as f,"; $sql.= " ".MAIN_DB_PREFIX."paiement_facture as pf,"; if (! empty($conf->banque->enabled)) @@ -218,7 +218,7 @@ class pdf_paiement if (! empty($conf->banque->enabled)) $sql.= ", ba.ref as bankaccount"; $sql.= ", p.rowid as prowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."paiementfourn as p LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_paiement = c.id AND c.entity IN (".getEntity('c_paiement').")"; + $sql.= " FROM ".MAIN_DB_PREFIX."paiementfourn as p LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_paiement = c.id"; $sql.= ", ".MAIN_DB_PREFIX."facture_fourn as f,"; $sql.= " ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf,"; if (! empty($conf->banque->enabled)) diff --git a/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php b/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php index 0a57ab1e449..22b746486c8 100644 --- a/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php +++ b/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php @@ -63,9 +63,9 @@ class pdf_canelle extends ModelePDFSuppliersInvoices function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); + + // Translations + $langs->loadLangs(array("main", "bills")); $this->db = $db; $this->name = "canelle"; @@ -933,7 +933,7 @@ class pdf_canelle extends ModelePDFSuppliersInvoices $sql = "SELECT p.datep as date, p.fk_paiement as type, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,"; $sql.= " cp.code"; $sql.= " FROM ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf, ".MAIN_DB_PREFIX."paiementfourn as p"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as cp ON p.fk_paiement = cp.id AND cp.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as cp ON p.fk_paiement = cp.id"; $sql.= " WHERE pf.fk_paiementfourn = p.rowid and pf.fk_facturefourn = ".$object->id; $sql.= " ORDER BY p.datep"; $resql=$this->db->query($sql); @@ -1086,7 +1086,7 @@ class pdf_canelle extends ModelePDFSuppliersInvoices if ($showaddress) { // Sender properties - $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42; diff --git a/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php b/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php index d272957d80d..374dde2c64f 100644 --- a/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php +++ b/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php @@ -3,7 +3,7 @@ * Copyright (C) 2005-2011 Regis Houssin * Copyright (C) 2007 Franky Van Liedekerke * Copyright (C) 2010-2014 Juanjo Menent - * Copyright (C) 2015 Marcos García + * Copyright (C) 2015 Marcos García * Copyright (C) 2017 Ferran Marcet * * This program is free software; you can redistribute it and/or modify @@ -68,8 +68,8 @@ class pdf_muscadet extends ModelePDFSuppliersOrders { global $conf,$langs,$mysoc; - $langs->load("main"); - $langs->load("bills"); + // Translations + $langs->loadLangs(array("main", "bills")); $this->db = $db; $this->name = "muscadet"; @@ -1211,7 +1211,7 @@ class pdf_muscadet extends ModelePDFSuppliersOrders if ($showaddress) { // Sender properties - $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42; diff --git a/htdocs/core/modules/supplier_payment/doc/pdf_standard.modules.php b/htdocs/core/modules/supplier_payment/doc/pdf_standard.modules.php index ffecb1dbdd7..8c9ed049841 100644 --- a/htdocs/core/modules/supplier_payment/doc/pdf_standard.modules.php +++ b/htdocs/core/modules/supplier_payment/doc/pdf_standard.modules.php @@ -65,9 +65,9 @@ class pdf_standard extends ModelePDFSuppliersPayments function __construct($db) { global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); + + // Translations + $langs->loadLangs(array("main", "bills")); $this->db = $db; $this->name = "standard"; diff --git a/htdocs/core/modules/supplier_proposal/doc/doc_generic_supplier_proposal_odt.modules.php b/htdocs/core/modules/supplier_proposal/doc/doc_generic_supplier_proposal_odt.modules.php index 4f445501669..a8497416501 100644 --- a/htdocs/core/modules/supplier_proposal/doc/doc_generic_supplier_proposal_odt.modules.php +++ b/htdocs/core/modules/supplier_proposal/doc/doc_generic_supplier_proposal_odt.modules.php @@ -381,18 +381,21 @@ class doc_generic_supplier_proposal_odt extends ModelePDFSupplierProposal { } - // Make substitutions into odt + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_user=$this->get_substitutionarray_user($user,$outputlangs); $array_soc=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); - $array_objet=$this->get_substitutionarray_object($object,$outputlangs); $array_other=$this->get_substitutionarray_other($outputlangs); - $tmparray = array_merge($array_user,$array_soc,$array_thirdparty,$array_objet,$array_other); + $tmparray = array_merge($substitutionarray,$array_user,$array_soc,$array_thirdparty,$array_objet,$array_other); complete_substitutions_array($tmparray, $outputlangs, $object); + // Call the ODTSubstitution hook $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key=>$value) { try { @@ -413,30 +416,42 @@ class doc_generic_supplier_proposal_odt extends ModelePDFSupplierProposal // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->lines as $line) - { - $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); - complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) - { - try - { - $listlines->setVars($key, $val, true, 'UTF-8'); - } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } - } - $listlines->merge(); + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -487,7 +502,7 @@ class doc_generic_supplier_proposal_odt extends ModelePDFSupplierProposal $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/modules/supplier_proposal/doc/pdf_aurore.modules.php b/htdocs/core/modules/supplier_proposal/doc/pdf_aurore.modules.php index 7411933dd26..52fbeac21d5 100644 --- a/htdocs/core/modules/supplier_proposal/doc/pdf_aurore.modules.php +++ b/htdocs/core/modules/supplier_proposal/doc/pdf_aurore.modules.php @@ -38,23 +38,78 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; */ class pdf_aurore extends ModelePDFSupplierProposal { - var $db; - var $name; - var $description; - var $type; + /** + * @var DoliDb Database handler + */ + public $db; + + /** + * @var string model name + */ + public $name; + + /** + * @var string model description (short text) + */ + public $description; + + /** + * @var string document type + */ + public $type; - var $phpmin = array(4,3,0); // Minimum version of PHP required by module - var $version = 'dolibarr'; + /** + * @var array() Minimum version of PHP required by module. + * e.g.: PHP ≥ 5.3 = array(5, 3) + */ + public $phpmin = array(5, 2); + + /** + * Dolibarr version of the loaded document + * @public string + */ + public $version = 'dolibarr'; - var $page_largeur; - var $page_hauteur; - var $format; - var $marge_gauche; - var $marge_droite; - var $marge_haute; - var $marge_basse; + /** + * @var int page_largeur + */ + public $page_largeur; + + /** + * @var int page_hauteur + */ + public $page_hauteur; + + /** + * @var array format + */ + public $format; + + /** + * @var int marge_gauche + */ + public $marge_gauche; + + /** + * @var int marge_droite + */ + public $marge_droite; + + /** + * @var int marge_haute + */ + public $marge_haute; + + /** + * @var int marge_basse + */ + public $marge_basse; - var $emetteur; // Objet societe qui emet + /** + * Issuer + * @var Societe + */ + public $emetteur; /** @@ -62,12 +117,12 @@ class pdf_aurore extends ModelePDFSupplierProposal * * @param DoliDB $db Database handler */ - function __construct($db) + public function __construct($db) { - global $conf,$langs,$mysoc; - - $langs->load("main"); - $langs->load("bills"); + global $conf, $langs, $mysoc; + + // Translations + $langs->loadLangs(array("main", "bills")); $this->db = $db; $this->name = "aurore"; @@ -146,13 +201,9 @@ class pdf_aurore extends ModelePDFSupplierProposal if (! is_object($outputlangs)) $outputlangs=$langs; // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO if (! empty($conf->global->MAIN_USE_FPDF)) $outputlangs->charset_output='ISO-8859-1'; - - $outputlangs->load("main"); - $outputlangs->load("dict"); - $outputlangs->load("companies"); - $outputlangs->load("bills"); - $outputlangs->load("supplier_proposal"); - $outputlangs->load("products"); + + // Translations + $outputlangs->loadLangs(array("main", "dict", "companies", "bills", "products", "supplier_proposal")); $nblignes = count($object->lines); @@ -1188,11 +1239,9 @@ class pdf_aurore extends ModelePDFSupplierProposal function _pagehead(&$pdf, $object, $showaddress, $outputlangs) { global $conf,$langs; - - $outputlangs->load("main"); - $outputlangs->load("bills"); - $outputlangs->load("supplier_proposal"); - $outputlangs->load("companies"); + + // Translations + $outputlangs->loadLangs(array("main", "bills", "supplier_proposal", "companies")); $default_font_size = pdf_getPDFFontSize($outputlangs); @@ -1306,7 +1355,7 @@ class pdf_aurore extends ModelePDFSupplierProposal $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$labelbeforecontactname.": ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; } - $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty); + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); // Show sender $posy=42; diff --git a/htdocs/core/modules/syslog/mod_syslog_file.php b/htdocs/core/modules/syslog/mod_syslog_file.php index 92a70ab3f62..688f963f455 100644 --- a/htdocs/core/modules/syslog/mod_syslog_file.php +++ b/htdocs/core/modules/syslog/mod_syslog_file.php @@ -109,7 +109,7 @@ class mod_syslog_file extends LogHandler implements LogHandlerInterface if (empty($conf->global->SYSLOG_FILE)) $tmp=DOL_DATA_ROOT.'/dolibarr.log'; else $tmp=str_replace('DOL_DATA_ROOT', DOL_DATA_ROOT, $conf->global->SYSLOG_FILE); - if (! empty($conf->global->SYSLOG_FILE_ONEPERSESSION)) // file depend on session name that is same for all user, not per user value of the session id + if (! empty($conf->global->SYSLOG_FILE_ONEPERSESSION)) // file depend on session name (Note that session name is same for all users and is not a per user value) { $suffixinfilename = '_'.session_name(); } diff --git a/htdocs/core/modules/ticketsup/mod_ticketsup_simple.php b/htdocs/core/modules/ticketsup/mod_ticketsup_simple.php new file mode 100644 index 00000000000..7aa636b0780 --- /dev/null +++ b/htdocs/core/modules/ticketsup/mod_ticketsup_simple.php @@ -0,0 +1,144 @@ + + * Copyright (C) 2010 Laurent Destailleur + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see http://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/ticketsup/mod_ticketsup_simple.php + * \ingroup ticketsup + * \brief File with class to manage the numbering module Simple for ticketsup references + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/modules_ticketsup.php'; + +/** + * Class to manage the numbering module Simple for ticketsup references + */ +class mod_ticketsup_simple extends ModeleNumRefTicketsup +{ + public $version = 'dolibarr'; // 'development', 'experimental', 'dolibarr' + public $prefix = 'TS'; + public $error = ''; + public $nom = "Simple"; + public $name = "Simple"; + + /** + * Return description of numbering module + * + * @return string Text with description + */ + public function info() + { + global $langs; + return $langs->trans("SimpleNumRefModelDesc", $this->prefix); + } + + /** + * Return an example of numbering module values + * + * @return string Example + */ + public function getExample() + { + return $this->prefix . "0501-0001"; + } + + /** + * Test si les numeros deja en vigueur dans la base ne provoquent pas de + * de conflits qui empechera cette numerotation de fonctionner. + * + * @return boolean false si conflit, true si ok + */ + public function canBeActivated() + { + global $conf, $langs, $db; + + $coyymm = ''; + $max = ''; + + $posindice = 8; + $sql = "SELECT MAX(CAST(SUBSTRING(ref FROM " . $posindice . ") AS SIGNED)) as max"; + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup"; + $search = $this->prefix . "____-%"; + $sql .= " WHERE ref LIKE '" . $search ."'"; + $sql .= " AND entity = " . $conf->entity; + $resql = $db->query($sql); + if ($resql) { + $row = $db->fetch_row($resql); + if ($row) { + $coyymm = substr($row[0], 0, 6); + $max = $row[0]; + } + } + if (!$coyymm || preg_match('/' . $this->prefix . '[0-9][0-9][0-9][0-9]/i', $coyymm)) { + return true; + } else { + $langs->load("errors"); + $this->error = $langs->trans('ErrorNumRefModel', $max); + return false; + } + } + + /** + * Return next value + * + * @param Societe $objsoc Object third party + * @param Project $ticketsup Object ticketsup + * @return string Value if OK, 0 if KO + */ + public function getNextValue($objsoc, $ticketsup) + { + global $db, $conf; + + // D'abord on recupere la valeur max + $posindice = 8; + $sql = "SELECT MAX(CAST(SUBSTRING(ref FROM " . $posindice . ") AS SIGNED)) as max"; + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup"; + $search = $this->prefix . "____-%"; + $sql .= " WHERE ref LIKE '" . $search ."'"; + $sql .= " AND entity = " . $conf->entity; + + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $max = intval($obj->max); + } else { + $max = 0; + } + } else { + dol_syslog("mod_ticketsup_simple::getNextValue", LOG_DEBUG); + return -1; + } + + $date = empty($ticketsup->datec) ? dol_now() : $ticketsup->datec; + + //$yymm = strftime("%y%m",time()); + $yymm = strftime("%y%m", $date); + + if ($max >= (pow(10, 4) - 1)) { + $num = $max + 1; + } // If counter > 9999, we do not format on 4 chars, we take number as it is + else { + $num = sprintf("%04s", $max + 1); + } + + dol_syslog("mod_ticketsup_simple::getNextValue return " . $this->prefix . $yymm . "-" . $num); + return $this->prefix . $yymm . "-" . $num; + } + +} diff --git a/htdocs/core/modules/ticketsup/mod_ticketsup_universal.php b/htdocs/core/modules/ticketsup/mod_ticketsup_universal.php new file mode 100644 index 00000000000..881ba5a7f15 --- /dev/null +++ b/htdocs/core/modules/ticketsup/mod_ticketsup_universal.php @@ -0,0 +1,125 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see http://www.gnu.org/ + */ + +/** + * \file ticketsup/core/modules/ticketsup/mod_ticketsup_universal.php + * \ingroup ticketsup + * \brief Fichier contenant la classe du modele de numerotation de reference de projet Universal + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/modules_ticketsup.php'; + +/** + * Classe du modele de numerotation de reference de projet Universal + */ +class mod_ticketsup_universal extends ModeleNumRefTicketsup +{ + public $version = 'dolibarr'; // 'development', 'experimental', 'dolibarr' + public $error = ''; + public $nom = 'Universal'; + public $name = 'Universal'; + + /** + * Renvoi la description du modele de numerotation + * + * @return string Texte descripif + */ + public function info() + { + global $conf, $langs; + + $langs->load("ticketsup"); + $langs->load("admin"); + + $form = new Form($this->db); + + $texte = $langs->trans('GenericNumRefModelDesc') . "
    \n"; + $texte .= ''; + $texte .= ''; + $texte .= ''; + $texte .= ''; + $texte .= '
    '; + + $tooltip = $langs->trans("GenericMaskCodes", $langs->transnoentities("Ticketsup"), $langs->transnoentities("Ticketsup")); + $tooltip .= $langs->trans("GenericMaskCodes2"); + $tooltip .= $langs->trans("GenericMaskCodes3"); + $tooltip .= $langs->trans("GenericMaskCodes4a", $langs->transnoentities("Ticketsup"), $langs->transnoentities("Ticketsup")); + $tooltip .= $langs->trans("GenericMaskCodes5"); + + // Parametrage du prefix + $texte .= ''; + $texte .= ''; + + $texte .= ''; + + $texte .= ''; + + $texte .= '
    ' . $langs->trans("Mask") . ':' . $form->textwithpicto('', $tooltip, 1, 1) . ' 
    '; + $texte .= ''; + + return $texte; + } + + /** + * Renvoi un exemple de numerotation + * + * @return string Example + */ + public function getExample() + { + global $conf, $langs, $mysoc; + + $old_code_client = $mysoc->code_client; + $mysoc->code_client = 'CCCCCCCCCC'; + $numExample = $this->getNextValue($mysoc, ''); + $mysoc->code_client = $old_code_client; + + if (!$numExample) { + $numExample = $langs->trans('NotConfigured'); + } + return $numExample; + } + + /** + * Return next value + * + * @param Societe $objsoc Object third party + * @param Project $ticketsup Object ticketsup + * @return string Value if OK, 0 if KO + */ + public function getNextValue($objsoc, $ticketsup) + { + global $db, $conf; + + include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php'; + + // On defini critere recherche compteur + $mask = $conf->global->TICKETSUP_UNIVERSAL_MASK; + + if (!$mask) { + $this->error = 'NotConfigured'; + return 0; + } + + $date = empty($ticketsup->date_c) ? dol_now() : $ticketsup->datec; + $numFinal = get_next_value($db, $mask, 'ticketsup', 'ref', '', $objsoc->code_client, $date); + + return $numFinal; + } + +} diff --git a/htdocs/core/modules/usergroup/doc/doc_generic_usergroup_odt.modules.php b/htdocs/core/modules/usergroup/doc/doc_generic_usergroup_odt.modules.php index d237e1bce38..443981907e0 100644 --- a/htdocs/core/modules/usergroup/doc/doc_generic_usergroup_odt.modules.php +++ b/htdocs/core/modules/usergroup/doc/doc_generic_usergroup_odt.modules.php @@ -389,7 +389,7 @@ class doc_generic_usergroup_odt extends ModelePDFUserGroup $reshook=$hookmanager->executeHooks('ODTSubstitution',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks foreach($tmparray as $key=>$value) { - try + try { if (preg_match('/logo$/',$key)) // Image { @@ -408,34 +408,46 @@ class doc_generic_usergroup_odt extends ModelePDFUserGroup // Replace tags of lines try { - $listlines = $odfHandler->setSegment('lines'); - foreach ($object->members as $u) + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) { - $tmparray=$this->get_substitutionarray_each_var_object($u,$outputlangs); - unset($tmparray['object_pass']); - unset($tmparray['object_pass_indatabase']); - complete_substitutions_array($tmparray, $outputlangs, $object, $user, "completesubstitutionarray_users"); - // Call the ODTSubstitutionLine hook - $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$u); - $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - foreach($tmparray as $key => $val) + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->members as $u) { - try + $tmparray=$this->get_substitutionarray_each_var_object($u,$outputlangs); + unset($tmparray['object_pass']); + unset($tmparray['object_pass_indatabase']); + complete_substitutions_array($tmparray, $outputlangs, $object, $user, "completesubstitutionarray_users"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$u); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) { - if(!is_array($val)) { - $listlines->setVars($key, $val, true, 'UTF-8'); + try + { + if(!is_array($val)) { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { } } - catch(OdfException $e) - { - } - catch(SegmentException $e) - { - } + $listlines->merge(); } - $listlines->merge(); + $odfHandler->mergeSegment($listlines); } - $odfHandler->mergeSegment($listlines); } catch(OdfException $e) { @@ -443,7 +455,7 @@ class doc_generic_usergroup_odt extends ModelePDFUserGroup dol_syslog($this->error, LOG_WARNING); return -1; } - + // Replace labels translated $tmparray=$outputlangs->get_translations_for_substitutions(); foreach($tmparray as $key=>$value) @@ -486,7 +498,7 @@ class doc_generic_usergroup_odt extends ModelePDFUserGroup $odfHandler=null; // Destroy object $this->result = array('fullpath'=>$file); - + return 1; // Success } else diff --git a/htdocs/core/photos_resize.php b/htdocs/core/photos_resize.php index 9a11523f39b..e51e7ee660b 100644 --- a/htdocs/core/photos_resize.php +++ b/htdocs/core/photos_resize.php @@ -53,16 +53,22 @@ elseif ($modulepart == 'project') if (! $user->rights->projet->lire) accessforbidden(); $accessallowed=1; } +elseif ($modulepart == 'expensereport') +{ + $result=restrictedArea($user,'expensereport',$id,'expensereport'); + if (! $user->rights->expensereport->lire) accessforbidden(); + $accessallowed=1; +} elseif ($modulepart == 'holiday') { $result=restrictedArea($user,'holiday',$id,'holiday'); if (! $user->rights->holiday->read) accessforbidden(); $accessallowed=1; } -elseif ($modulepart == 'expensereport') +elseif ($modulepart == 'member') { - $result=restrictedArea($user,'expensereport',$id,'expensereport'); - if (! $user->rights->expensereport->lire) accessforbidden(); + $result=restrictedArea($user, 'adherent', $id, '', '', 'fk_soc', 'rowid'); + if (! $user->rights->adherent->lire) accessforbidden(); $accessallowed=1; } elseif ($modulepart == 'user') @@ -77,6 +83,12 @@ elseif ($modulepart == 'societe') if (! $user->rights->societe->lire) accessforbidden(); $accessallowed=1; } +elseif ($modulepart == 'ticketsup') +{ + $result=restrictedArea($user,'ticketsup',$id,'ticketsup'); + if (! $user->rights->ticketsup->read) accessforbidden(); + $accessallowed=1; +} // Security: // Limit access if permissions are wrong @@ -121,6 +133,17 @@ elseif ($modulepart == 'holiday') $dir=$conf->holiday->dir_output; // By default } } +elseif ($modulepart == 'member') +{ + require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; + $object = new Adherent($db); + if ($id > 0) + { + $result = $object->fetch($id); + if ($result <= 0) dol_print_error($db,'Failed to load object'); + $dir=$conf->adherent->dir_output; // By default + } +} elseif ($modulepart == 'societe') { require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; @@ -154,15 +177,31 @@ elseif ($modulepart == 'expensereport') $dir=$conf->expensereport->dir_output; // By default } } +elseif ($modulepart == 'ticketsup') +{ + require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/ticketsup.class.php'; + $object = new Ticketsup($db); + if ($id > 0) + { + $result = $object->fetch($id); + if ($result <= 0) dol_print_error($db,'Failed to load object'); + $dir=$conf->ticketsup->dir_output; // By default + } +} +else { + print 'Action crop for module part '.$modulepart.' is not supported yet.'; +} if (empty($backtourl)) { if (in_array($modulepart, array('product','produit','service','produit|service'))) $backtourl=DOL_URL_ROOT."/product/document.php?id=".$id.'&file='.urldecode($_POST["file"]); else if (in_array($modulepart, array('expensereport'))) $backtourl=DOL_URL_ROOT."/expensereport/document.php?id=".$id.'&file='.urldecode($_POST["file"]); - else if (in_array($modulepart, array('holiday'))) $backtourl=DOL_URL_ROOT."/holiday/document.php?id=".$id.'&file='.urldecode($_POST["file"]); - else if (in_array($modulepart, array('project'))) $backtourl=DOL_URL_ROOT."/projet/document.php?id=".$id.'&file='.urldecode($_POST["file"]); - else if (in_array($modulepart, array('user'))) $backtourl=DOL_URL_ROOT."/user/document.php?id=".$id.'&file='.urldecode($_POST["file"]); - else if (in_array($modulepart, array('societe'))) $backtourl=DOL_URL_ROOT."/societe/document.php?id=".$id.'&file='.urldecode($_POST["file"]); + else if (in_array($modulepart, array('holiday'))) $backtourl=DOL_URL_ROOT."/holiday/document.php?id=".$id.'&file='.urldecode($_POST["file"]); + else if (in_array($modulepart, array('member'))) $backtourl=DOL_URL_ROOT."/adherents/document.php?id=".$id.'&file='.urldecode($_POST["file"]); + else if (in_array($modulepart, array('project'))) $backtourl=DOL_URL_ROOT."/projet/document.php?id=".$id.'&file='.urldecode($_POST["file"]); + else if (in_array($modulepart, array('societe'))) $backtourl=DOL_URL_ROOT."/societe/document.php?id=".$id.'&file='.urldecode($_POST["file"]); + else if (in_array($modulepart, array('ticketsup'))) $backtourl=DOL_URL_ROOT."/ticketsup/document.php?id=".$id.'&file='.urldecode($_POST["file"]); + else if (in_array($modulepart, array('user'))) $backtourl=DOL_URL_ROOT."/user/document.php?id=".$id.'&file='.urldecode($_POST["file"]); } diff --git a/htdocs/core/search.php b/htdocs/core/search.php index 05b03064096..e1be41b36ac 100644 --- a/htdocs/core/search.php +++ b/htdocs/core/search.php @@ -132,12 +132,12 @@ if (GETPOST('search_task') != '') if (GETPOST('search_user') != '') { - header("Location: ".DOL_URL_ROOT.'/user/index.php?mode=search&sall='.urlencode(GETPOST('search_user'))); + header("Location: ".DOL_URL_ROOT.'/user/list.php?mode=search&sall='.urlencode(GETPOST('search_user'))); exit; } if (GETPOST('search_group') != '') { - header("Location: ".DOL_URL_ROOT.'/user/group/index.php?mode=search&sall='.urlencode(GETPOST('search_group'))); + header("Location: ".DOL_URL_ROOT.'/user/group/list.php?mode=search&sall='.urlencode(GETPOST('search_group'))); exit; } diff --git a/htdocs/core/tpl/admin_extrafields_add.tpl.php b/htdocs/core/tpl/admin_extrafields_add.tpl.php index fdf2f722932..51e6c518a7a 100644 --- a/htdocs/core/tpl/admin_extrafields_add.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_add.tpl.php @@ -92,15 +92,17 @@ $langs->load("modulebuilder"); else if (type == 'double') { size.val('24,8').removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();} else if (type == 'int') { size.val('10').removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();} else if (type == 'text') { size.val('2000').removeAttr('disabled'); unique.prop('disabled', true).removeAttr('checked'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); } + else if (type == 'html') { size.val('2000').removeAttr('disabled'); unique.prop('disabled', true).removeAttr('checked'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); } else if (type == 'varchar') { size.val('255').removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); } + else if (type == 'password') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); default_value.val('').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").show();} else if (type == 'boolean') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();} else if (type == 'price') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();} - else if (type == 'select') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'sellist') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").show();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'radio') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'checkbox') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'chkbxlst') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();} - else if (type == 'link') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();} + else if (type == 'select') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'sellist') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").show();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'radio') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'checkbox') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'chkbxlst') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'link') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();jQuery("#helppassword").hide();} else if (type == 'separate') { langfile.val('').prop('disabled',true);size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide(); @@ -165,6 +167,7 @@ $langs->load("modulebuilder"); textwithpicto('', $langs->trans("ExtrafieldParamHelpsellist"),1,0,'', 0, 2, 'helpvalue2')?> textwithpicto('', $langs->trans("ExtrafieldParamHelpchkbxlst"),1,0,'', 0, 2, 'helpvalue3')?> textwithpicto('', $langs->trans("ExtrafieldParamHelplink"),1,0,'', 0, 2, 'helpvalue4')?> + textwithpicto('', $langs->trans("ExtrafieldParamHelpPassword"),1,0,'', 0, 2, 'helpvalue5')?> diff --git a/htdocs/core/tpl/admin_extrafields_edit.tpl.php b/htdocs/core/tpl/admin_extrafields_edit.tpl.php index e0b4927bf19..e08983f9e10 100644 --- a/htdocs/core/tpl/admin_extrafields_edit.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_edit.tpl.php @@ -90,16 +90,18 @@ $langs->load("modulebuilder"); else if (type == 'double') { size.removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();} else if (type == 'int') { size.removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide(); jQuery("#helpchkbxlst").hide();} else if (type == 'text') { size.removeAttr('disabled'); unique.prop('disabled', true).removeAttr('checked'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); } + else if (type == 'html') { size.removeAttr('disabled'); unique.prop('disabled', true).removeAttr('checked'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); } else if (type == 'varchar') { size.removeAttr('disabled'); unique.removeAttr('disabled'); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide(); } + else if (type == 'password') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); default_value.val('').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").show();} else if (type == 'boolean') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();} else if (type == 'price') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpchkbxlst").hide();} - else if (type == 'select') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'sellist') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").show();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'radio') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'checkbox') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} - else if (type == 'chkbxlst') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();} - else if (type == 'link') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();} - else if (type == 'separate') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); default_value.val('').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();} + else if (type == 'select') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'sellist') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").show();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'radio') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'checkbox') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").show();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'chkbxlst') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").show();jQuery("#helplink").hide();jQuery("#helppassword").hide();} + else if (type == 'link') { size.val('').prop('disabled', true); unique.removeAttr('disabled'); jQuery("#value_choice").show();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").show();jQuery("#helppassword").hide();} + else if (type == 'separate') { size.val('').prop('disabled', true); unique.removeAttr('checked').prop('disabled', true); required.val('').prop('disabled', true); default_value.val('').prop('disabled', true); jQuery("#value_choice").hide();jQuery("#helpselect").hide();jQuery("#helpsellist").hide();jQuery("#helpchkbxlst").hide();jQuery("#helplink").hide();jQuery("#helppassword").hide();} else { // type = string size.val('').prop('disabled', true); unique.removeAttr('disabled'); @@ -129,8 +131,8 @@ $langs->load("modulebuilder"); }); - -
    ?attrname=" method="post"> + +?attrname=" id="formeditextrafield" method="post"> @@ -166,7 +168,7 @@ if((($type == 'select') || ($type == 'checkbox') || ($type == 'radio')) && is_ar } } } -elseif (($type== 'sellist') || ($type == 'chkbxlst') || ($type == 'link') ) +elseif (($type== 'sellist') || ($type == 'chkbxlst') || ($type == 'link') || ($type == 'password')) { $paramlist=array_keys($param['options']); $param_chain = $paramlist[0]; @@ -181,7 +183,9 @@ elseif (($type== 'sellist') || ($type == 'chkbxlst') || ($type == 'link') ) array('varchar', 'phone', 'mail', 'url', 'select', 'password'), + 'varchar'=>array('varchar', 'phone', 'mail', 'url', 'select', 'password', 'text', 'html'), + 'text'=>array('text','html'), + 'html'=>array('text','html'), 'password'=>array('password', 'varchar'), 'mail'=>array('varchar', 'phone', 'mail', 'url', 'select'), 'url'=>array('varchar', 'phone', 'mail', 'url', 'select'), @@ -210,7 +214,7 @@ else trans("Size"); ?> - + trans("Value"); ?> @@ -224,6 +228,7 @@ else textwithpicto('', $langs->trans("ExtrafieldParamHelpsellist"),1,0,'', 0, 2, 'helpvalue2')?> textwithpicto('', $langs->trans("ExtrafieldParamHelpchkbxlst"),1,0,'', 0, 2, 'helpvalue3')?> textwithpicto('', $langs->trans("ExtrafieldParamHelplink"),1,0,'', 0, 2, 'helpvalue4')?> + textwithpicto('', $langs->trans("ExtrafieldParamHelpPassword"),1,0,'', 0, 2, 'helpvalue5')?> diff --git a/htdocs/core/tpl/admin_extrafields_view.tpl.php b/htdocs/core/tpl/admin_extrafields_view.tpl.php index 6ab7fecbf10..ecdda360309 100644 --- a/htdocs/core/tpl/admin_extrafields_view.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_view.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2010-2018 Laurent Destailleur * Copyright (C) 2012-2017 Regis Houssin * * This program is free software; you can redistribute it and/or modify @@ -67,7 +67,7 @@ if ($conf->multicompany->enabled) { print ' '; print "\n"; -if (count($extrafields->attributes[$elementtype]['type'])) +if (is_array($extrafields->attributes[$elementtype]['type']) && count($extrafields->attributes[$elementtype]['type'])) { foreach($extrafields->attributes[$elementtype]['type'] as $key => $value) { @@ -91,7 +91,7 @@ if (count($extrafields->attributes[$elementtype]['type'])) if (! empty($conf->multicompany->enabled)) { print ''.($extrafields->attributes[$elementtype]['entityid'][$key]==0?$langs->trans("All"):$extrafields->attributes[$elementtype]['entitylabel'][$key]).''; } - print ''.img_edit().''; + print ''.img_edit().''; print "  ".img_delete()."\n"; print ""; } diff --git a/htdocs/core/tpl/ajax/objectlinked_lineimport.tpl.php b/htdocs/core/tpl/ajax/objectlinked_lineimport.tpl.php new file mode 100644 index 00000000000..ad32f1b9aa8 --- /dev/null +++ b/htdocs/core/tpl/ajax/objectlinked_lineimport.tpl.php @@ -0,0 +1,106 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Protection to avoid direct call of template +if (empty($conf) || ! is_object($conf)) +{ + print "Error, template page can't be called as URL"; + exit; +} + +?> + + + + + \ No newline at end of file diff --git a/htdocs/core/tpl/bloc_comment.tpl.php b/htdocs/core/tpl/bloc_comment.tpl.php index 473c42eb42f..b7e5e732c3a 100644 --- a/htdocs/core/tpl/bloc_comment.tpl.php +++ b/htdocs/core/tpl/bloc_comment.tpl.php @@ -43,7 +43,7 @@ print ''; $desc = GETPOST('comment_description'); -$doleditor = new DolEditor('comment_description', $desc, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '100%'); +$doleditor = new DolEditor('comment_description', $desc, '', 80, 'dolibarr_notes', 'In', 0, true, true, ROWS_3, '100%'); print $doleditor->Create(1); print ''; diff --git a/htdocs/core/tpl/card_presend.tpl.php b/htdocs/core/tpl/card_presend.tpl.php index 43cacab0e1c..5baa70825d8 100644 --- a/htdocs/core/tpl/card_presend.tpl.php +++ b/htdocs/core/tpl/card_presend.tpl.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2017-2018 Laurent Destailleur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ * $modelmail * $defaulttopic * $diroutput + * $arrayoffamiliestoexclude=array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...); */ // Protection to avoid direct call of template @@ -108,6 +109,7 @@ if ($action == 'presend') // Create form for email include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; $formmail = new FormMail($db); + $formmail->param['langsmodels']=(empty($newlang)?$langs->defaultlang:$newlang); $formmail->fromtype = (GETPOST('fromtype')?GETPOST('fromtype'):(!empty($conf->global->MAIN_MAIL_DEFAULT_FROMTYPE)?$conf->global->MAIN_MAIL_DEFAULT_FROMTYPE:'user')); @@ -143,8 +145,11 @@ if ($action == 'presend') } else { - foreach ($object->thirdparty->thirdparty_and_contact_email_array(1) as $key => $value) { - $liste[$key] = $value; + if (is_object($object->thirdparty)) + { + foreach ($object->thirdparty->thirdparty_and_contact_email_array(1) as $key => $value) { + $liste[$key] = $value; + } } } @@ -157,8 +162,11 @@ if ($action == 'presend') $formmail->withdeliveryreceipt = 1; $formmail->withcancel = 1; + //$arrayoffamiliestoexclude=array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...); + if (! isset($arrayoffamiliestoexclude)) $arrayoffamiliestoexclude=null; + // Make substitution in email content - $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, $arrayoffamiliestoexclude, $object); $substitutionarray['__CHECK_READ__'] = (is_object($object) && is_object($object->thirdparty)) ? '' : ''; $substitutionarray['__PERSONALIZED__'] = ''; // deprecated $substitutionarray['__CONTACTCIVNAME__'] = ''; @@ -178,14 +186,6 @@ if ($action == 'presend') $formmail->param['returnurl'] = $_SERVER["PHP_SELF"] . '?id=' . $object->id; $formmail->param['fileinit'] = array($file); - // Init list of files - /*if (GETPOST('mode','alpha') == 'init') - { - $formmail->clear_attached_files(); - - $formmail->add_attached_files($file, basename($file), dol_mimetype($file)); - }*/ - // Show form print $formmail->get_form(); diff --git a/htdocs/core/tpl/contacts.tpl.php b/htdocs/core/tpl/contacts.tpl.php index 27d57d3e48a..0ecf01f9dd1 100644 --- a/htdocs/core/tpl/contacts.tpl.php +++ b/htdocs/core/tpl/contacts.tpl.php @@ -35,10 +35,10 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; $module = $object->element; // Special cases -if ($module == 'propal') { $permission=$user->rights->propale->creer; } -elseif ($module == 'fichinter') { $permission=$user->rights->ficheinter->creer; } -elseif ($module == 'invoice_supplier') { $permission=$user->rights->fournisseur->facture->creer; } +if ($module == 'propal') { $permission=$user->rights->propale->creer; } +elseif ($module == 'fichinter') { $permission=$user->rights->ficheinter->creer; } elseif ($module == 'order_supplier') { $permission=$user->rights->fournisseur->commande->creer; } +elseif ($module == 'invoice_supplier') { $permission=$user->rights->fournisseur->facture->creer; } elseif ($module == 'project') { $permission=$user->rights->projet->creer; } elseif ($module == 'action') { $permission=$user->rights->agenda->myactions->create; } elseif ($module == 'shipping') { $permission=$user->rights->expedition->creer; } @@ -60,8 +60,9 @@ $userstatic=new User($db); ?> +
    -
    +
    '; ?>
    trans("Users"); ?>
    global->MAIN_INFO_SOCIETE_NOM; ?>
    -
    select_dolusers($user->id, 'userid', 0, (! empty($userAlreadySelected)?$userAlreadySelected:null), 0, null, null, 0, 56); ?>
    +
    select_dolusers($user->id, 'userid', 0, (! empty($userAlreadySelected)?$userAlreadySelected:null), 0, null, null, 0, 56, '', 0, '', 'minwidth200imp'); ?>
    - selectCompaniesForNewContact($object, 'id', $selectedCompany, 'newcompany', '', 0); ?> + selectCompaniesForNewContact($object, 'id', $selectedCompany, 'newcompany', '', 0, '', 'minwidth300imp'); ?>
    - select_contacts($selectedCompany, '', 'contactid', 0, '', '', 0, 'minwidth200'); ?> + select_contacts($selectedCompany, '', 'contactid', 0, '', '', 0, 'minwidth100imp'); ?>
    element == 'shipping' && is_object($objectsrc)) $tmpobject=$objectsrc; - $formcompany->selectTypeContact($tmpobject, '', 'type','external'); ?> + $formcompany->selectTypeContact($tmpobject, '', 'type','external', 'position', 0, 'minwidth100imp'); ?>
     
    @@ -201,18 +202,12 @@ if ($permission) { if ($tab[$i]['source']=='internal') { - $userstatic->id=$tab[$i]['id']; - $userstatic->lastname=$tab[$i]['lastname']; - $userstatic->firstname=$tab[$i]['firstname']; - $userstatic->photo=$tab[$i]['photo']; - $userstatic->login=$tab[$i]['login']; + $userstatic->fetch($tab[$i]['id']); echo $userstatic->getNomUrl(-1); } if ($tab[$i]['source']=='external') { - $contactstatic->id=$tab[$i]['id']; - $contactstatic->lastname=$tab[$i]['lastname']; - $contactstatic->firstname=$tab[$i]['firstname']; + $contactstatic->fetch($tab[$i]['id']); echo $contactstatic->getNomUrl(1); } ?> @@ -223,16 +218,10 @@ if ($permission) { id=$tab[$i]['id']; - $userstatic->lastname=$tab[$i]['lastname']; - $userstatic->firstname=$tab[$i]['firstname']; echo $userstatic->LibStatut($tab[$i]['statuscontact'],3); } if ($tab[$i]['source']=='external') { - $contactstatic->id=$tab[$i]['id']; - $contactstatic->lastname=$tab[$i]['lastname']; - $contactstatic->firstname=$tab[$i]['firstname']; echo $contactstatic->LibStatut($tab[$i]['statuscontact'],3); } ?> @@ -240,7 +229,7 @@ if ($permission) {
    diff --git a/htdocs/core/tpl/document_actions_post_headers.tpl.php b/htdocs/core/tpl/document_actions_post_headers.tpl.php index 8b24605607d..65312110cc9 100644 --- a/htdocs/core/tpl/document_actions_post_headers.tpl.php +++ b/htdocs/core/tpl/document_actions_post_headers.tpl.php @@ -86,8 +86,10 @@ $formfile->form_attach_new_file( $savingdocmask ); +// Drag and drop for up and down allowed on product, thirdparty, ... +// The drag and drop call the page core/ajax/row.php $disablemove=1; -if (in_array($modulepart, array('product', 'produit', 'societe', 'user'))) $disablemove=0; // Drag and drop for up and down allowed on product, thirdparty, ... +if (in_array($modulepart, array('product', 'produit', 'societe', 'user', 'ticketsup'))) $disablemove=0; // List of document $formfile->list_of_documents( diff --git a/htdocs/core/tpl/extrafields_list_search_title.tpl.php b/htdocs/core/tpl/extrafields_list_search_title.tpl.php index 8b986b1e4e6..ae5f640828c 100644 --- a/htdocs/core/tpl/extrafields_list_search_title.tpl.php +++ b/htdocs/core/tpl/extrafields_list_search_title.tpl.php @@ -8,16 +8,17 @@ if (empty($conf) || ! is_object($conf)) } // Loop to show all columns of extrafields for the title line -if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) +if (is_array($extrafields->attributes[$object->element]['label']) && count($extrafields->attributes[$object->element]['label'])) { - foreach($extrafields->attribute_label as $key => $val) + foreach($extrafields->attributes[$object->element]['label'] as $key => $val) { if (! empty($arrayfields["ef.".$key]['checked'])) { $align=$extrafields->getAlignFlag($key); $sortonfield = "ef.".$key; - if (! empty($extrafields->attribute_computed[$key])) $sortonfield=''; - print getTitleFieldOfList($langs->trans($extralabels[$key]), 0, $_SERVER["PHP_SELF"], $sortonfield, "", $param, ($align?'align="'.$align.'"':''), $sortfield, $sortorder)."\n"; + if (! empty($extrafields->attributes[$object->element]['computed'][$key])) $sortonfield=''; + if ($extrafields->attributes[$object->element]['type'][$key] == 'separate') print ''; + else print getTitleFieldOfList($langs->trans($extralabels[$key]), 0, $_SERVER["PHP_SELF"], $sortonfield, "", $param, ($align?'align="'.$align.'"':''), $sortfield, $sortorder)."\n"; } } } \ No newline at end of file diff --git a/htdocs/core/tpl/extrafields_view.tpl.php b/htdocs/core/tpl/extrafields_view.tpl.php index 3559df44c9c..e7abadac599 100644 --- a/htdocs/core/tpl/extrafields_view.tpl.php +++ b/htdocs/core/tpl/extrafields_view.tpl.php @@ -151,7 +151,7 @@ if (empty($reshook) && ! empty($extrafields->attributes[$object->table_element][ jQuery(document).ready(function() { function showOptions(child_list, parent_list) { - var val = $("select[name=\"options_"+parent_list+"\"]").val(); + var val = $("select[name="+parent_list+"]").val(); var parentVal = parent_list + ":" + val; if(val > 0) { $("select[name=\""+child_list+"\"] option[parent]").hide(); diff --git a/htdocs/core/tpl/notes.tpl.php b/htdocs/core/tpl/notes.tpl.php index f22b25c88a5..7ba077f065b 100644 --- a/htdocs/core/tpl/notes.tpl.php +++ b/htdocs/core/tpl/notes.tpl.php @@ -78,8 +78,8 @@ print ''."\n"; print '
    '."\n"; if ($module != 'product') { // No public note yet on products - print '
    '."\n"; - print '
    '."\n"; + print '
    '."\n"; + print '
    '."\n"; print $form->editfieldkey("NotePublic", $note_public, $value_public, $object, $permission, $typeofdata, $moreparam, '', 0); print '
    '."\n"; print '
    '."\n"; @@ -88,8 +88,8 @@ if ($module != 'product') { print '
    '."\n"; } if (empty($user->societe_id)) { - print '
    '."\n"; - print '
    '."\n"; + print '
    '."\n"; + print '
    '."\n"; print $form->editfieldkey("NotePrivate", $note_private, $value_private, $object, $permission, $typeofdata, $moreparam, '', 0); print '
    '."\n"; print '
    '."\n"; diff --git a/htdocs/core/tpl/object_discounts.tpl.php b/htdocs/core/tpl/object_discounts.tpl.php new file mode 100644 index 00000000000..70698bfa7eb --- /dev/null +++ b/htdocs/core/tpl/object_discounts.tpl.php @@ -0,0 +1,108 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Needs the following variables defined: + * $object Proposal, order, invoice (including supplier versions) + * $thirdparty Thirdparty of object + * $absolute_discount Amount of fixed discounts available + * $absolute_creditnote Amount of credit notes available + * $discount_type 0 => Customer discounts, 1 => Supplier discounts + * $cannotApplyDiscount Set it to prevent form to apply discount + * $backtopage URL to come back to from discount modification pages + */ + +$classname = get_class($object); +$isInvoice = in_array($object->element, array('facture', 'invoice', 'facture_fourn', 'invoice_supplier')); +$isNewObject = empty($object->id) && empty($object->rowid); + +// Relative and absolute discounts +$addrelativediscount = '' . $langs->trans("EditRelativeDiscount") . ''; +$addabsolutediscount = '' . $langs->trans("EditGlobalDiscounts") . ''; +$viewabsolutediscount = '' . $langs->trans("ViewAvailableGlobalDiscounts") . ''; + +$fixedDiscount = $thirdparty->remise_percent; +if(! empty($discount_type)) { + $fixedDiscount = $thirdparty->remise_supplier_percent; +} + +if ($fixedDiscount > 0) +{ + $translationKey = (! empty($discount_type)) ? 'HasRelativeDiscountFromSupplier' : 'CompanyHasRelativeDiscount'; + print $langs->trans($translationKey, $fixedDiscount).'.'; +} +else +{ + $translationKey = (! empty($discount_type)) ? 'HasNoRelativeDiscountFromSupplier' : 'CompanyHasNoRelativeDiscount'; + print $langs->trans($translationKey).'.'; +} +if($isNewObject) print ' ('.$addrelativediscount.')'; + +// Is there is commercial discount or down payment available ? +if ($absolute_discount > 0) { + + if ($cannotApplyDiscount || ! $isInvoice || $isNewObject || $object->statut > $classname::STATUS_DRAFT || $object->type == $classname::TYPE_CREDIT_NOTE || $object->type == $classname::TYPE_DEPOSIT) { + $translationKey = ! empty($discount_type) ? 'HasAbsoluteDiscountFromSupplier' : 'CompanyHasAbsoluteDiscount'; + $text = $langs->trans($translationKey, price($absolute_discount), $langs->transnoentities("Currency" . $conf->currency)).'.'; + + if ($isInvoice && ! $isNewObject && $object->statut > $classname::STATUS_DRAFT && $object->type != $classname::TYPE_CREDIT_NOTE && $object->type != $classname::TYPE_DEPOSIT) { + $text = $form->textwithpicto($text, $langs->trans('AbsoluteDiscountUse')); + } + + if ($isNewObject) { + $text.= ' ('.$addabsolutediscount.')'; + } + + print '
    '.$text; + } else { + // Discount available of type fixed amount (not credit note) + $more = '(' . $addabsolutediscount . ')'; + $form->form_remise_dispo($_SERVER["PHP_SELF"] . '?facid=' . $object->id, GETPOST('discountid'), 'remise_id', $thirdparty->id, $absolute_discount, $filterabsolutediscount, $resteapayer, $more, 0, $discount_type); + } +} + +// Is there credit notes availables ? +if ($absolute_creditnote > 0) { + + // If validated, we show link "add credit note to payment" + if ($cannotApplyDiscount || ! $isInvoice || $isNewObject || $object->statut != $classname::STATUS_VALIDATED || $object->type == $classname::TYPE_CREDIT_NOTE) { + $translationKey = ! empty($discount_type) ? 'HasCreditNoteFromSupplier' : 'CompanyHasCreditNote'; + $text = $langs->trans($translationKey, price($absolute_creditnote), $langs->transnoentities("Currency" . $conf->currency)) . '.'; + + if ($isInvoice && ! $isNewObject && $object->statut == $classname::STATUS_DRAFT && $object->type != $classname::TYPE_DEPOSIT) { + $text = $form->textwithpicto($text, $langs->trans('CreditNoteDepositUse')); + } + + if ($absolute_discount <= 0 || $isNewObject) { + $text.= '('.$addabsolutediscount.')'; + } + + print '
    '.$text; + } else { // We can add a credit note on a down payment or standard invoice or situation invoice + // There is credit notes discounts available + $more = $isInvoice && ! $isNewObject ? ' (' . $viewabsolutediscount . ')' : ''; + $form->form_remise_dispo($_SERVER["PHP_SELF"] . '?facid=' . $object->id, 0, 'remise_id_for_payment', $thirdparty->id, $absolute_creditnote, $filtercreditnote, 0, $more, 0, $discount_type); // We allow credit note even if amount is higher + } +} + +if($absolute_discount <= 0 && $absolute_creditnote <= 0) { + $translationKey = ! empty($discount_type) ? 'HasNoAbsoluteDiscountFromSupplier' : 'CompanyHasNoAbsoluteDiscount'; + print '
    '.$langs->trans($translationKey).'.'; + + if ($isInvoice && $object->statut == $classname::STATUS_DRAFT && $object->type != $classname::TYPE_CREDIT_NOTE && $object->type != $classname::TYPE_DEPOSIT) { + print ' (' . $addabsolutediscount . ')'; + } +} + diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index d26a31b3cd2..fdef8a03ed8 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -58,6 +58,37 @@ if (empty($inputalsopricewithtax)) $inputalsopricewithtax=0; $colspan = 3; // Col total ht + col edit + col delete if (in_array($object->element,array('propal','commande','order','facture','facturerec','invoice','supplier_proposal','order_supplier','invoice_supplier'))) $colspan++; // With this, there is a column move button //print $object->element; + +// Lines for extrafield +$objectline = null; +if (!empty($extrafieldsline)) +{ + if ($this->table_element_line=='commandedet') { + $objectline = new OrderLine($this->db); + } + elseif ($this->table_element_line=='propaldet') { + $objectline = new PropaleLigne($this->db); + } + elseif ($this->table_element_line=='supplier_proposaldet') { + $objectline = new SupplierProposalLine($this->db); + } + elseif ($this->table_element_line=='facturedet') { + $objectline = new FactureLigne($this->db); + } + elseif ($this->table_element_line=='contratdet') { + $objectline = new ContratLigne($this->db); + } + elseif ($this->table_element_line=='commande_fournisseurdet') { + $objectline = new CommandeFournisseurLigne($this->db); + } + elseif ($this->table_element_line=='facture_fourn_det') { + $objectline = new SupplierInvoiceLine($this->db); + } + elseif ($this->table_element_line=='facturedet_rec') { + $objectline = new FactureLigneRec($this->db); + } +} + ?> @@ -131,7 +162,7 @@ if ($nolinesbefore) { if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) { $coldisplay=2; ?> - + global->MAIN_DISABLE_FREE_LINES)) { @@ -153,7 +184,7 @@ else { if (empty($conf->product->enabled) && empty($conf->service->enabled) && empty($conf->global->CONTRACT_SUPPORT_PRODUCTS)) $forceall=-1; // With contract, by default, no choice at all, except if CONTRACT_SUPPORT_PRODUCTS is set else $forceall=0; } - + // Free line echo ''; // Show radio free line @@ -180,9 +211,9 @@ else { echo ' '; } } - + echo $form->select_type_of_lines(isset($_POST["type"])?GETPOST("type",'alpha',2):-1,'type',1,1,$forceall); - + echo ''; } @@ -283,6 +314,18 @@ else { if (! empty($conf->global->FCKEDITOR_ENABLE_DETAILS_FULL)) $toolbarname='dolibarr_notes'; $doleditor=new DolEditor('dp_desc',GETPOST('dp_desc'),'',100,$toolbarname,'',false,true,$enabled,$nbrows,'98%'); $doleditor->Create(); + + // Show autofill date for recuring invoices + if (! empty($conf->service->enabled) && $object->element == 'facturerec') + { + echo '

    '; + echo $langs->trans('AutoFillDateFrom').' '; + echo $form->selectyesno('date_start_fill', $line->date_start_fill, 1); + echo ' - '; + echo $langs->trans('AutoFillDateTo').' '; + echo $form->selectyesno('date_end_fill', $line->date_end_fill, 1); + echo '
    '; + } ?> @@ -322,8 +365,12 @@ else { print $form->selectUnits($line->fk_unit, "units"); print ''; } + $remise_percent = $buyer->remise_percent; + if($object->element == 'supplier_proposal' || $object->element == 'order_supplier' || $object->element == 'invoice_supplier') { + $remise_percent = $seller->remise_supplier_percent; + } ?> - remise_percent); ?>">% + ">% situation_cycle_ref) { $coldisplay++; @@ -368,46 +415,23 @@ else { - table_element_line=='commandedet') { - $newline = new OrderLine($this->db); - } - elseif ($this->table_element_line=='propaldet') { - $newline = new PropaleLigne($this->db); - } - elseif ($this->table_element_line=='supplier_proposaldet') { - $newline = new SupplierProposalLine($this->db); - } - elseif ($this->table_element_line=='facturedet') { - $newline = new FactureLigne($this->db); - } - elseif ($this->table_element_line=='contratdet') { - $newline = new ContratLigne($this->db); - } - elseif ($this->table_element_line=='commande_fournisseurdet') { - $newline = new CommandeFournisseurLigne($this->db); - } - elseif ($this->table_element_line=='facture_fourn_det') { - $newline = new SupplierInvoiceLine($this->db); - } - elseif ($this->table_element_line=='facturedet_rec') { - $newline = new FactureLigneRec($this->db); - } - if (is_object($newline)) { - print $newline->showOptionals($extrafieldsline, 'edit', array('style'=>$bcnd[$var], 'colspan'=>$coldisplay+8)); - } - } - ?> +showOptionals($extrafieldsline, 'edit', array('style'=>$bcnd[$var], 'colspan'=>$coldisplay+8), '', '', empty($conf->global->MAIN_EXTRAFIELDS_IN_ONE_TD)?0:1); +} +?> + service->enabled) || ($object->element == 'contrat')) && $dateSelector && GETPOST('type') != '0') // We show date field if required { $colspan = 6; + if ($object->element == 'supplier_proposal' || $object->element == 'order_supplier' || $object->element == 'invoice_supplier') // We must have same test in printObjectLines + { + $colspan++; + } if ($this->situation_cycle_ref) { $colspan++; } @@ -586,8 +610,17 @@ jQuery(document).ready(function() { if (editor) { editor.focus(); } } } - if (jQuery('#select_type').val() == '0') jQuery('#trlinefordates').hide(); - else jQuery('#trlinefordates').show(); + console.log("Hide/show date according to product type"); + if (jQuery('#select_type').val() == '0') + { + jQuery('#trlinefordates').hide(); + jQuery('.divlinefordates').hide(); + } + else + { + jQuery('#trlinefordates').show(); + jQuery('.divlinefordates').show(); + } }); $("#prod_entry_mode_predef").on( "click", function() { @@ -595,7 +628,7 @@ jQuery(document).ready(function() { setforpredef(); jQuery('#trlinefordates').show(); }); - + $("#prod_entry_mode_predef").click(); @@ -721,10 +754,11 @@ jQuery(document).ready(function() { ?> /* To process customer price per quantity */ - var pbq = $('option:selected', this).attr('data-pbq'); - var pbqqty = $('option:selected', this).attr('data-pbqqty'); - var pbqpercent = $('option:selected', this).attr('data-pbqpercent'); - if (jQuery('#idprod').val() > 0 && typeof pbq !== "undefined") + var pbq = parseInt($('option:selected', this).attr('data-pbq')); + var pbqqty = parseFloat($('option:selected', this).attr('data-pbqqty')); + var pbqpercent = parseFloat($('option:selected', this).attr('data-pbqpercent')); + + if ((jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val()) && typeof pbq !== "undefined") { console.log("We choose a price by quanty price_by_qty id = "+pbq+" price_by_qty qty = "+pbqqty+" price_by_qty percent = "+pbqpercent); jQuery("#pbq").val(pbq); @@ -778,7 +812,6 @@ function setforfree() { jQuery("#tva_tx").show(); jQuery("#buying_price").val('').show(); jQuery("#fournprice_predef").hide(); - jQuery("#title_fourn_ref").show(); jQuery("#title_vat").show(); jQuery("#title_up_ht").show(); jQuery("#title_up_ht_currency").show(); @@ -791,7 +824,7 @@ function setforfree() { jQuery("#units, #title_units").show(); } function setforpredef() { - console.log("Call setforpredef. We hide some fields"); + console.log("Call setforpredef. We hide some fields and show dates"); jQuery("#select_type").val(-1); jQuery("#prod_entry_mode_free").prop('checked',false).change(); @@ -802,7 +835,6 @@ function setforpredef() { jQuery("#fourn_ref").hide(); jQuery("#tva_tx").hide(); jQuery("#buying_price").show(); - jQuery("#title_fourn_ref").hide(); jQuery("#title_vat").hide(); jQuery("#title_up_ht").hide(); jQuery("#title_up_ht_currency").hide(); @@ -813,6 +845,9 @@ function setforpredef() { jQuery(".np_marginRate").hide(); // May no exists jQuery(".np_markRate").hide(); // May no exists jQuery("#units, #title_units").hide(); + + jQuery('#trlinefordates').show(); + jQuery('.divlinefordates').show(); } diff --git a/htdocs/core/tpl/objectline_edit.tpl.php b/htdocs/core/tpl/objectline_edit.tpl.php index 67adba3a2ae..63321d16506 100644 --- a/htdocs/core/tpl/objectline_edit.tpl.php +++ b/htdocs/core/tpl/objectline_edit.tpl.php @@ -62,7 +62,10 @@ if (!empty($conf->multicurrency->enabled)) $colspan+=2; $coldisplay=-1; // We remove first td ?> > - global->MAIN_VIEW_LINE_NUMBER) ? ' colspan="2"' : ''); ?>>global->MAIN_VIEW_LINE_NUMBER))?2:1; ?> + global->MAIN_VIEW_LINE_NUMBER)) { ?> + + +
    @@ -83,7 +86,7 @@ $coldisplay=-1; // We remove first td echo ' - '.nl2br($line->product_label); ?> -
    +

    @@ -110,6 +113,18 @@ $coldisplay=-1; // We remove first td } else { print ''; } + + // Show autofill date for recuring invoices + if (! empty($conf->service->enabled) && $line->product_type == 1 && $line->element == 'facturedetrec') + { + echo '
    '; + echo $langs->trans('AutoFillDateFrom').' '; + echo $form->selectyesno('date_start_fill', $line->date_start_fill, 1); + echo ' - '; + echo $langs->trans('AutoFillDateTo').' '; + echo $form->selectyesno('date_end_fill', $line->date_end_fill, 1); + } + ?> @@ -117,7 +132,7 @@ $coldisplay=-1; // We remove first td if ($object->element == 'supplier_proposal' || $object->element == 'order_supplier' || $object->element == 'invoice_supplier') // We must have same test in printObjectLines { ?> - + trans("Save"); ?>">
    "> - - showOptionals($extrafieldsline,'edit',array('style'=>$bc[$var],'colspan'=>$coldisplay)); - } - ?> +showOptionals($extrafieldsline, 'edit', array('style'=>$bc[$var],'colspan'=>$coldisplay), '', '', empty($conf->global->MAIN_EXTRAFIELDS_IN_ONE_TD)?0:1); +} +?> + service->enabled) && $line->product_type == 1 && $dateSelector) { ?> > + global->MAIN_VIEW_LINE_NUMBER)) { ?> + + trans('ServiceLimitedDuration').' '.$langs->trans('From').' '; ?> global->MAIN_USE_HOURMIN_IN_DATE_RANGE)?$conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE:''); @@ -264,7 +282,8 @@ $coldisplay=-1; // We remove first td ?> - + '; - $text = $tmp.$langs->transnoentities("InvoiceAvoirAsk") . ' '; - // $text.=''; - $text .= ''; - $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc"), 1, 'help', '', 0, 3); - print $desc; - - print '
    '; - print '    0 ? 'checked':'').' /> "; - print '
        0 ? 'checked':'').' /> "; - print '
    '; - - print '
    '; - } - } - else + // Credit note + if (empty($conf->global->INVOICE_DISABLE_CREDIT_NOTE)) { print '
    '; - $tmp=' '; - $text = $tmp.$langs->trans("InvoiceAvoir") . ' '; - $text.= '('.$langs->trans("YouMustCreateInvoiceFromSupplierThird").') '; + $tmp=' + jQuery(document).ready(function() { + if (! jQuery("#radio_creditnote").is(":checked")) + { + jQuery("#credit_note_options").hide(); + } + jQuery("#radio_creditnote").click(function() { + jQuery("#credit_note_options").show(); + }); + jQuery("#radio_standard, #radio_replacement, #radio_deposit").click(function() { + jQuery("#credit_note_options").hide(); + }); + }); + '; + $text = $tmp.$langs->transnoentities("InvoiceAvoirAsk") . ' '; + // $text.=''; + $text .= ''; $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc"), 1, 'help', '', 0, 3); print $desc; - print '
    ' . "\n"; + + print '
    '; + print '    0 ? 'checked':'').' /> "; + print '
        0 ? 'checked':'').' /> "; + print '
    '; + + print '
    '; } } + else + { + print '
    '; + $tmp=' '; + $text = $tmp.$langs->trans("InvoiceAvoir") . ' '; + $text.= '('.$langs->trans("YouMustCreateInvoiceFromSupplierThird").') '; + $desc = $form->textwithpicto($text, $langs->transnoentities("InvoiceAvoirDesc"), 1, 'help', '', 0, 3); + print $desc; + print '
    ' . "\n"; + } } print '
    '; print ''; - if ($socid > 0) + if ($societe->id > 0) { // Discounts for third party print '' . $langs->trans('Discounts') . ''; - if ($soc->remise_percent) - print $langs->trans("CompanyHasRelativeDiscount", '' . $soc->remise_percent . ''); - else - print $langs->trans("CompanyHasNoRelativeDiscount"); - print ' (' . $langs->trans("EditRelativeDiscount") . ')'; - print '. '; - print '
    '; - if ($absolute_discount) - print $langs->trans("CompanyHasAbsoluteDiscount", '' . price($absolute_discount) . '', $langs->trans("Currency" . $conf->currency)); - else - print $langs->trans("CompanyHasNoAbsoluteDiscount"); - print ' (' . $langs->trans("EditGlobalDiscounts") . ')'; - print '.'; + + $thirdparty = $societe; + $discount_type = 1; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?socid=' . $societe->id . '&action=' . $action . '&origin=' . GETPOST('origin') . '&originid=' . GETPOST('originid')); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + print ''; } @@ -1944,6 +2134,18 @@ else } $resteapayeraffiche = $resteapayer; + if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { // Never use this + $filterabsolutediscount = "fk_invoice_supplier_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + $filtercreditnote = "fk_invoice_supplier_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + } else { + $filterabsolutediscount = "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')"; + $filtercreditnote = "fk_invoice_supplier_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS PAID)%')"; + } + + $absolute_discount = $societe->getAvailableDiscounts('', $filterabsolutediscount, 0, 1); + $absolute_creditnote = $societe->getAvailableDiscounts('', $filtercreditnote, 0, 1); + $absolute_discount = price2num($absolute_discount, 'MT'); + $absolute_creditnote = price2num($absolute_creditnote, 'MT'); /* * View card @@ -1953,6 +2155,17 @@ else dol_fiche_head($head, 'card', $titre, -1, 'bill'); + $formconfirm = ''; + + // Confirmation de la conversion de l'avoir en reduc + if ($action == 'converttoreduc') { + if($object->type == FactureFournisseur::TYPE_STANDARD) $type_fac = 'ExcessPaid'; + elseif($object->type == FactureFournisseur::TYPE_CREDIT_NOTE) $type_fac = 'CreditNote'; + elseif($object->type == FactureFournisseur::TYPE_DEPOSIT) $type_fac = 'Deposit'; + $text = $langs->trans('ConfirmConvertToReducSupplier', strtolower($langs->transnoentities($type_fac))); + $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?facid=' . $object->id, $langs->trans('ConvertToReduc'), $text, 'confirm_converttoreduc', '', "yes", 2); + } + // Clone confirmation if ($action == 'clone') { @@ -2188,8 +2401,27 @@ else $facthatreplace->fetch($facidnext); print ' ('.$langs->transnoentities("ReplacedByInvoice",$facthatreplace->getNomUrl(1)).')'; } + if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE || $object->type == FactureFournisseur::TYPE_DEPOSIT) { + $discount = new DiscountAbsolute($db); + $result = $discount->fetch(0, 0, $object->id); + if ($result > 0){ + print '. '.$langs->trans("CreditNoteConvertedIntoDiscount", $object->getLibType(), $discount->getNomUrl(1, 'discount')).'
    '; + } + } print ''; + + // Relative and absolute discounts + print '' . $langs->trans('Discounts'); + print ''; + + $thirdparty = $societe; + $discount_type = 1; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?facid=' . $object->id); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + + print ''; + // Label print ''; print ''.$form->editfieldkey("Label",'label',$object->label,$object,($user->rights->fournisseur->facture->creer)).''; @@ -2429,7 +2661,7 @@ else $sql.= ' FROM '.MAIN_DB_PREFIX.'paiementfourn as p'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON p.fk_bank = b.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank_account as ba ON b.fk_account = ba.rowid'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id AND c.entity IN ('.getEntity('c_paiement').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'paiementfourn_facturefourn as pf ON pf.fk_paiementfourn = p.rowid'; $sql.= ' WHERE pf.fk_facturefourn = '.$object->id; $sql.= ' ORDER BY p.datep, p.tms'; @@ -2539,18 +2771,20 @@ else print $langs->trans('AlreadyPaid'); print ' : 0)?' class="amountalreadypaid"':'').'>' . price($totalpaye) . ' '; - $resteapayer = $object->total_ttc - $totalpaye; + //$resteapayer = $object->total_ttc - $totalpaye; $resteapayeraffiche = $resteapayer; + $cssforamountpaymentcomplete = 'amountpaymentcomplete'; // Loop on each credit note or deposit amount applied $creditnoteamount = 0; $depositamount = 0; - /* + + $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,"; - $sql .= " re.description, re.fk_facture_source"; - $sql .= " FROM " . MAIN_DB_PREFIX . "societe_remise_except_supplier as re"; - $sql .= " WHERE fk_facture = " . $object->id; + $sql .= " re.description, re.fk_invoice_supplier_source"; + $sql .= " FROM " . MAIN_DB_PREFIX . "societe_remise_except as re"; + $sql .= " WHERE fk_invoice_supplier = " . $object->id; $resql = $db->query($sql); if ($resql) { $num = $db->num_rows($resql); @@ -2558,7 +2792,7 @@ else $invoice = new FactureFournisseur($db); while ($i < $num) { $obj = $db->fetch_object($resql); - $invoice->fetch($obj->fk_facture_source); + $invoice->fetch($obj->fk_invoice_supplier_source); print ''; if ($invoice->type == FactureFournisseur::TYPE_CREDIT_NOTE) print $langs->trans("CreditNote") . ' '; @@ -2579,7 +2813,6 @@ else } else { dol_print_error($db); } - */ // Paye partiellement 'escompte' if (($object->statut == FactureFournisseur::STATUS_CLOSED || $object->statut == FactureFournisseur::STATUS_ABANDONED) && $object->close_code == 'discount_vat') { @@ -2625,9 +2858,9 @@ else if ($resteapayeraffiche >= 0) print $langs->trans('RemainderToPay'); else - print $langs->trans('ExcessReceived'); + print $langs->trans('ExcessPaid'); print ' :'; - print '' . price($resteapayeraffiche) . ''; + print '' . price($resteapayeraffiche) . ''; print ' '; } else // Credit note @@ -2649,7 +2882,7 @@ else else print $langs->trans('ExcessPaydBack'); print ' :'; - print '' . price($sign * $resteapayeraffiche) . ''; + print '' . price($sign * $resteapayeraffiche) . ''; print ' '; // Sold credit note @@ -2771,9 +3004,9 @@ else { if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->fournisseur->supplier_invoice_advance->send) { - print ''; + print ''; } - else print '
    '.$langs->trans('SendByMail').'
    '; + else print '
    '.$langs->trans('SendMail').'
    '; } // Make payments @@ -2792,7 +3025,7 @@ else } // Reverse back money or convert to reduction - if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE || $object->type == FactureFournisseur::TYPE_DEPOSIT) { + if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE || $object->type == FactureFournisseur::TYPE_DEPOSIT || $object->type == FactureFournisseur::TYPE_STANDARD) { // For credit note only if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0) { @@ -2806,6 +3039,11 @@ else } } + // For standard invoice with excess paid + if ($object->type == FactureFournisseur::TYPE_STANDARD && empty($object->paye) && ($object->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits) < 0 && $user->rights->fournisseur->facture->creer && empty($discount->id)) + { + print ''; + } // For credit note if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE && $object->statut == 1 && $object->paye == 0 && $user->rights->fournisseur->facture->creer && $object->getSommePaiement() == 0) { print ''; @@ -2928,7 +3166,7 @@ else } // Presend form - $modelmail='supplier_order_send'; + $modelmail='invoice_supplier_send'; $defaulttopic='SendBillRef'; $diroutput = $conf->fournisseur->facture->dir_output; $trackid = 'sin'.$object->id; diff --git a/htdocs/fourn/facture/list.php b/htdocs/fourn/facture/list.php index 1e056292517..d3fdcdcba01 100644 --- a/htdocs/fourn/facture/list.php +++ b/htdocs/fourn/facture/list.php @@ -55,6 +55,7 @@ $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); $optioncss = GETPOST('optioncss','alpha'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'supplierinvoicelist'; $socid = GETPOST('socid','int'); @@ -76,6 +77,7 @@ $search_amount_all_tax = GETPOST("search_amount_all_tax","alpha"); $search_product_category=GETPOST('search_product_category','int'); $search_ref=GETPOST('sf_ref')?GETPOST('sf_ref','alpha'):GETPOST('search_ref','alpha'); $search_refsupplier=GETPOST('search_refsupplier','alpha'); +$search_type=GETPOST('search_type','int'); $search_project=GETPOST('search_project','alpha'); $search_societe=GETPOST('search_societe','alpha'); $search_montant_ht=GETPOST('search_montant_ht','alpha'); @@ -117,16 +119,12 @@ $pagenext = $page + 1; if (! $sortorder) $sortorder="DESC"; if (! $sortfield) $sortfield="f.datef,f.rowid"; -// Initialize technical object to manage context to save list fields -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'supplierinvoicelist'; - $diroutputmassaction=$conf->fournisseur->facture->dir_output . '/temp/massgeneration/'.$user->id; -$object=new FactureFournisseur($db); - $now=dol_now(); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$object = new FactureFournisseur($db); $hookmanager->initHooks(array('supplierinvoicelist')); $extrafields = new ExtraFields($db); @@ -148,6 +146,7 @@ $checkedtypetiers=0; $arrayfields=array( 'f.ref'=>array('label'=>$langs->trans("Ref"), 'checked'=>1), 'f.ref_supplier'=>array('label'=>$langs->trans("RefSupplier"), 'checked'=>1), + 'f.type'=>array('label'=>$langs->trans("Type"), 'checked'=>0), 'f.label'=>array('label'=>$langs->trans("Label"), 'checked'=>0), 'f.datef'=>array('label'=>$langs->trans("DateInvoice"), 'checked'=>1), 'f.date_lim_reglement'=>array('label'=>$langs->trans("DateDue"), 'checked'=>1), @@ -203,6 +202,7 @@ if (empty($reshook)) $search_product_category=''; $search_ref=""; $search_refsupplier=""; + $search_type=""; $search_label=""; $search_project=''; $search_societe=""; @@ -261,14 +261,14 @@ llxHeader('',$langs->trans("SuppliersInvoices"),'EN:Suppliers_Invoices|FR:Factur $sql = "SELECT"; if ($search_all || $search_product_category > 0) $sql = 'SELECT DISTINCT'; -$sql.= " f.rowid as facid, f.ref, f.ref_supplier, f.datef, f.date_lim_reglement as datelimite, f.fk_mode_reglement,"; +$sql.= " f.rowid as facid, f.ref, f.ref_supplier, f.type, f.datef, f.date_lim_reglement as datelimite, f.fk_mode_reglement,"; $sql.= " f.total_ht, f.total_ttc, f.total_tva as total_vat, f.paye as paye, f.fk_statut as fk_statut, f.libelle as label, f.datec as date_creation, f.tms as date_update,"; $sql.= " f.localtax1 as total_localtax1, f.localtax2 as total_localtax2,"; $sql.= " s.rowid as socid, s.nom as name, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta as code_compta_client, s.code_compta_fournisseur,"; $sql.= " typent.code as typent_code,"; $sql.= " state.code_departement as state_code, state.nom as state_name,"; $sql.= " country.code as country_code,"; -$sql.= " p.rowid as project_id, p.ref as project_ref"; +$sql.= " p.rowid as project_id, p.ref as project_ref, p.title as project_label"; // We need dynamount_payed to be able to sort on status (value is surely wrong because we can count several lines several times due to other left join or link with contacts. But what we need is just 0 or > 0) // TODO Better solution to be able to sort on already payed or remain to pay is to store amount_payed in a denormalized field. if (! $search_all) $sql.= ', SUM(pf.amount) as dynamount_payed'; @@ -307,6 +307,15 @@ if ($search_ref) } if ($search_ref) $sql .= natural_search('f.ref', $search_ref); if ($search_refsupplier) $sql .= natural_search('f.ref_supplier', $search_refsupplier); +if ($search_type != '' && $search_type >= 0) +{ + if ($search_type == '0') $sql.=" AND f.type = 0"; // standard + if ($search_type == '1') $sql.=" AND f.type = 1"; // replacement + if ($search_type == '2') $sql.=" AND f.type = 2"; // credit note + if ($search_type == '3') $sql.=" AND f.type = 3"; // deposit + //if ($search_type == '4') $sql.=" AND f.type = 4"; // proforma + //if ($search_type == '5') $sql.=" AND f.type = 5"; // situation +} if ($search_project) $sql .= natural_search('p.ref', $search_project); if ($search_societe) $sql .= natural_search('s.nom', $search_societe); if ($search_town) $sql.= natural_search('s.town', $search_town); @@ -377,7 +386,7 @@ $sql.=$hookmanager->resPrint; if (! $search_all) { - $sql.= " GROUP BY f.rowid, f.ref, f.ref_supplier, f.datef, f.date_lim_reglement, f.fk_mode_reglement,"; + $sql.= " GROUP BY f.rowid, f.ref, f.ref_supplier, f.type, f.datef, f.date_lim_reglement, f.fk_mode_reglement,"; $sql.= " f.total_ht, f.total_ttc, f.total_tva, f.paye, f.fk_statut, f.libelle, f.datec, f.tms,"; $sql.= " f.localtax1, f.localtax2,"; $sql.= ' s.rowid, s.nom, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta, s.code_compta_fournisseur,'; @@ -434,6 +443,7 @@ if ($resql) if ($year_lim) $param.='&year_lim=' .urlencode($year_lim); if ($search_ref) $param.='&search_ref='.urlencode($search_ref); if ($search_refsupplier) $param.='&search_refsupplier='.urlencode($search_refsupplier); + if ($search_type != '') $param.='&search_type='.urlencode($search_type); if ($search_label) $param.='&search_label='.urlencode($search_label); if ($search_company) $param.='&search_company='.urlencode($search_company); if ($search_montant_ht != '') $param.='&search_montant_ht='.urlencode($search_montant_ht); @@ -461,6 +471,12 @@ if ($resql) if (in_array($massaction, array('presend','predelete','createbills'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); + $newcardbutton=''; + if ($user->rights->fournisseur->facture->creer) + { + $newcardbutton=''.$langs->trans('NewBill').''; + } + $i = 0; print '
    '."\n"; if ($optioncss != '') print ''; @@ -473,7 +489,7 @@ if ($resql) print ''; print ''; - print_barre_liste($langs->trans("BillsSuppliers").($socid?' '.$soc->name:''), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_accountancy', 0, '', '', $limit); + print_barre_liste($langs->trans("BillsSuppliers").($socid?' '.$soc->name:''), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_accountancy', 0, $newcardbutton, '', $limit); $topicmail="SendBillRef"; $modelmail="supplier_invoice_send"; @@ -590,6 +606,26 @@ if ($resql) print ''; print ''; } + // Type + if (! empty($arrayfields['f.type']['checked'])) + { + print ''; + $listtype=array( + FactureFournisseur::TYPE_STANDARD=>$langs->trans("InvoiceStandard"), + FactureFournisseur::TYPE_REPLACEMENT=>$langs->trans("InvoiceReplacement"), + FactureFournisseur::TYPE_CREDIT_NOTE=>$langs->trans("InvoiceAvoir"), + FactureFournisseur::TYPE_DEPOSIT=>$langs->trans("InvoiceDeposit"), + ); +/* + if (! empty($conf->global->INVOICE_USE_SITUATION)) + { + $listtype[Facture::TYPE_SITUATION] = $langs->trans("InvoiceSituation"); + } +*/ + //$listtype[Facture::TYPE_PROFORMA]=$langs->trans("InvoiceProForma"); // A proformat invoice is not an invoice but must be an order. + print $form->selectarray('search_type', $listtype, $search_type, 1, 0, 0, '', 0, 0, 0, 'ASC', 'maxwidth100'); + print ''; + } // Label if (! empty($arrayfields['f.label']['checked'])) { @@ -655,7 +691,7 @@ if ($resql) if (! empty($arrayfields['f.fk_mode_reglement']['checked'])) { print ''; - $form->select_types_paiements($search_paymentmode, 'search_paymentmode', '', 0, 0, 1, 10); + $form->select_types_paiements($search_paymentmode, 'search_paymentmode', '', 0, 1, 1, 10); print ''; } if (! empty($arrayfields['f.total_ht']['checked'])) @@ -741,6 +777,7 @@ if ($resql) print ''; if (! empty($arrayfields['f.ref']['checked'])) print_liste_field_titre($arrayfields['f.ref']['label'],$_SERVER['PHP_SELF'],'f.ref,f.rowid','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['f.ref_supplier']['checked'])) print_liste_field_titre($arrayfields['f.ref_supplier']['label'],$_SERVER["PHP_SELF"],'f.ref_supplier','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['f.type']['checked'])) print_liste_field_titre($arrayfields['f.type']['label'],$_SERVER["PHP_SELF"],'f.type','',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['f.label']['checked'])) print_liste_field_titre($arrayfields['f.label']['label'],$_SERVER['PHP_SELF'],"f.libelle,f.rowid",'',$param,'',$sortfield,$sortorder); if (! empty($arrayfields['f.datef']['checked'])) print_liste_field_titre($arrayfields['f.datef']['label'],$_SERVER['PHP_SELF'],'f.datef,f.rowid','',$param,'align="center"',$sortfield,$sortorder); if (! empty($arrayfields['f.date_lim_reglement']['checked'])) print_liste_field_titre($arrayfields['f.date_lim_reglement']['label'],$_SERVER['PHP_SELF'],"f.date_lim_reglement",'',$param,'align="center"',$sortfield,$sortorder); @@ -762,12 +799,12 @@ if ($resql) // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields - $parameters=array('arrayfields'=>$arrayfields); + $parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['f.datec']['checked'])) print_liste_field_titre($arrayfields['f.datec']['label'],$_SERVER["PHP_SELF"],"f.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); if (! empty($arrayfields['f.tms']['checked'])) print_liste_field_titre($arrayfields['f.tms']['label'],$_SERVER["PHP_SELF"],"f.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); - if (! empty($arrayfields['f.fk_statut']['checked'])) print_liste_field_titre($arrayfields['f.fk_statut']['label'],$_SERVER["PHP_SELF"],"fk_statut,paye","",$param,'align="right"',$sortfield,$sortorder); + if (! empty($arrayfields['f.fk_statut']['checked'])) print_liste_field_titre($arrayfields['f.fk_statut']['label'],$_SERVER["PHP_SELF"],"fk_statut,paye,type","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch '); print "\n"; @@ -844,6 +881,15 @@ if ($resql) if (! $i) $totalarray['nbfield']++; } + // Type + if (! empty($arrayfields['f.type']['checked'])) + { + print ''; + print $facturestatic->getLibType(); + print ""; + if (! $i) $totalarray['nbfield']++; + } + // Label if (! empty($arrayfields['f.label']['checked'])) { @@ -882,6 +928,7 @@ if ($resql) { $projectstatic->id=$obj->project_id; $projectstatic->ref=$obj->project_ref; + $projectstatic->title=$obj->project_label; print $projectstatic->getNomUrl(1); } print ''; diff --git a/htdocs/fourn/facture/paiement.php b/htdocs/fourn/facture/paiement.php index 64223d4b4e9..cc636718bb7 100644 --- a/htdocs/fourn/facture/paiement.php +++ b/htdocs/fourn/facture/paiement.php @@ -745,7 +745,7 @@ if (empty($action)) $sql.= ' FROM '.MAIN_DB_PREFIX.'paiementfourn AS p'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'paiementfourn_facturefourn AS pf ON p.rowid=pf.fk_paiementfourn'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'facture_fourn AS f ON f.rowid=pf.fk_facturefourn'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement AS c ON p.fk_paiement = c.id AND c.entity IN ('.getEntity('c_paiement').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement AS c ON p.fk_paiement = c.id'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe AS s ON s.rowid = f.fk_soc'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON p.fk_bank = b.rowid'; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank_account as ba ON b.fk_account = ba.rowid'; diff --git a/htdocs/fourn/facture/tpl/linkedobjectblock.tpl.php b/htdocs/fourn/facture/tpl/linkedobjectblock.tpl.php index ce72f5be42e..9f57fc35c8d 100644 --- a/htdocs/fourn/facture/tpl/linkedobjectblock.tpl.php +++ b/htdocs/fourn/facture/tpl/linkedobjectblock.tpl.php @@ -68,7 +68,7 @@ foreach($linkedObjectBlock as $key => $objectlink) } } ?> getLibStatut(3); ?> - ">transnoentitiesnoconv("RemoveLink")); ?> + ">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?> '.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("FTPClientSetup"), $linkback, 'title_setup'); print '
    '; diff --git a/htdocs/holiday/card.php b/htdocs/holiday/card.php index e626f950a2b..c653c756bba 100644 --- a/htdocs/holiday/card.php +++ b/htdocs/holiday/card.php @@ -50,6 +50,18 @@ $now=dol_now(); $langs->load("holiday"); +$childids = $user->getAllChildIds(1); + +$cancreate = 0; +if (! empty($user->rights->holiday->write_all)) $cancreate=1; +if (! empty($user->rights->holiday->write) && in_array($fuserid, $childids)) $cancreate=1; + +$candelete = 0; +if (! empty($user->rights->holiday->delete)) $candelete=1; + +$morefilter = 'AND employee = 1'; +if (! empty($conf->global->HOLIDAY_FOR_NON_SALARIES_TOO)) $morefilter = ''; + /* * Actions @@ -61,8 +73,7 @@ if ($action == 'create') $object = new Holiday($db); // If no right to create a request - $fuserid = GETPOST('fuserid','int'); - if (($fuserid == $user->id && empty($user->rights->holiday->write)) || ($fuserid != $user->id && empty($user->rights->holiday->write_all))) + if (! $cancreate) { $error++; setEventMessages($langs->trans('CantCreateCP'), null, 'errors'); @@ -201,13 +212,11 @@ if ($action == 'update') $object = new Holiday($db); $object->fetch($id); - $canedit=(($user->id == $object->fk_user && $user->rights->holiday->write) || ($user->id != $object->fk_user && $user->rights->holiday->write_all)); - // If under validation if ($object->statut == 1) { // If this is the requestor or has read/write rights - if ($canedit) + if ($cancreate) { $valideur = $_POST['valideur']; $description = trim($_POST['description']); @@ -252,21 +261,28 @@ if ($action == 'update') // Update $verif = $object->update($user); - if ($verif > 0) - { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); - exit; - } - else - { - // Otherwise we display the request form with the SQL error message - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit&error=SQL_Create&msg='.$object->error); - exit; - } + + if ($verif <= 0) + { + setEventMessages($object->error, $object->errors, 'warnings'); + $action='edit'; + } + else + { + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); + exit; + } } - } else { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); - exit; + else + { + setEventMessages($langs->trans("NotEnoughPermissions"), null, 'errors'); + $action=''; + } + } + else + { + setEventMessages($langs->trans("ErrorBadStatus"), null, 'errors'); + $action=''; } } @@ -280,26 +296,26 @@ if ($action == 'confirm_delete' && GETPOST('confirm') == 'yes' && $user->rights- $object = new Holiday($db); $object->fetch($id); - $canedit=(($user->id == $object->fk_user && $user->rights->holiday->write) || ($user->id != $object->fk_user && $user->rights->holiday->write_all)); - - // If this is a rough draft - if ($object->statut == 1 || $object->statut == 3) + // If this is a rough draft, approved, canceled or refused + if ($object->statut == 1 || $object->statut == 4 || $object->statut == 5) { // Si l'utilisateur à le droit de lire cette demande, il peut la supprimer - if ($canedit) + if ($candelete) { $result=$object->delete($user); } else { - $error = $langs->trans('ErrorCantDeleteCP'); + $error++; + setEventMessages($langs->trans('ErrorCantDeleteCP'), null, 'errors'); + $action=''; } } if (! $error) { $db->commit(); - header('Location: list.php'); + header('Location: list.php?restore_lastsearch_values=1'); exit; } else @@ -314,10 +330,8 @@ if ($action == 'confirm_send') $object = new Holiday($db); $object->fetch($id); - $canedit=(($user->id == $object->fk_user && $user->rights->holiday->write) || ($user->id != $object->fk_user && $user->rights->holiday->write_all)); - // Si brouillon et créateur - if($object->statut == 1 && $canedit) + if($object->statut == 1 && $cancreate) { $object->statut = 2; @@ -388,24 +402,26 @@ if ($action == 'confirm_send') $trackid='leav'.$object->id; - $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, null, null, null, '', '', 0, 0, '', '', $trackid); + $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, array(), array(), array(), '', '', 0, 0, '', '', $trackid); // Envoi du mail $result=$mail->sendfile(); if (!$result) { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=mail&error_content='.$mail->error); - exit; + setEventMessages($mail->error, $mail->errors, 'warnings'); + $action=''; + } + else + { + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); + exit; } - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); - exit; } else { - // Sinon on affiche le formulaire de demande avec le message d'erreur SQL - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=SQL_Create&msg='.$object->error); - exit; + setEventMessages($object->error, $object->errors, 'errors'); + $action=''; } } } @@ -476,31 +492,33 @@ if ($action == 'confirm_valid') $trackid='leav'.$object->id; - $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, null, null, null, '', '', 0, 0, '', '', $trackid); + $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, array(), array(), array(), '', '', 0, 0, '', '', $trackid); // Envoi du mail $result=$mail->sendfile(); - if (!$result) { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=mail&error_content='.$mail->error); - exit; + if (!$result) + { + setEventMessages($mail->error, $mail->errors, 'warnings'); + $action=''; + } + else + { + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); + exit; } - - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); - exit; - } else { - // Sinon on affiche le formulaire de demande avec le message d'erreur SQL - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=SQL_Create&msg='.$object->error); - exit; } - + else + { + setEventMessages($object->error, $object->errors, 'errors'); + $action=''; + } } - } -if ($action == 'confirm_refuse') +if ($action == 'confirm_refuse' && GETPOST('confirm','alpha') == 'yes') { - if (! empty($_POST['detail_refuse'])) + if (! empty($_POST['detail_refuse'])) { $object = new Holiday($db); $object->fetch($id); @@ -508,10 +526,10 @@ if ($action == 'confirm_refuse') // Si statut en attente de validation et valideur = utilisateur if ($object->statut == 2 && $user->id == $object->fk_validator) { - $object->date_refuse = date('Y-m-d H:i:s', time()); + $object->date_refuse = dol_print_date('dayhour', dol_now()); $object->fk_user_refuse = $user->id; $object->statut = 5; - $object->detail_refuse = $_POST['detail_refuse']; + $object->detail_refuse = GETPOST('detail_refuse','alphanohtml'); $verif = $object->update($user); @@ -553,29 +571,31 @@ if ($action == 'confirm_refuse') $trackid='leav'.$object->id; - $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, null, null, null, '', '', 0, 0, '', '', $trackid); + $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, array(), array(), array(), '', '', 0, 0, '', '', $trackid); // Envoi du mail $result=$mail->sendfile(); - if(!$result) { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=mail&error_content='.$mail->error); - exit; + if (!$result) + { + setEventMessages($mail->error, $mail->errors, 'warnings'); + $action=''; + } + else + { + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); + exit; } - - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); - exit; - } else { - // Sinon on affiche le formulaire de demande avec le message d'erreur SQL - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=SQL_Create&msg='.$object->error); - exit; } - + else + { + setEventMessages($object->error, $object->errors, 'errors'); + $action=''; + } } - } else { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=NoMotifRefuse'); - exit; + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("DetailRefusCP")), null, 'errors'); + $action='refuse'; } } @@ -583,6 +603,8 @@ if ($action == 'confirm_refuse') // Si Validation de la demande if ($action == 'confirm_draft' && GETPOST('confirm') == 'yes') { + $error = 0; + $object = new Holiday($db); $object->fetch($id); @@ -592,7 +614,8 @@ if ($action == 'confirm_draft' && GETPOST('confirm') == 'yes') $result = $object->update($user); if ($result < 0) { - $error = $langs->trans('ErrorBackToDraft'); + $error++; + setEventMessages($langs->trans('ErrorBackToDraft').' '.$object->error, $object->errors, 'errors'); } if (! $error) @@ -611,11 +634,13 @@ if ($action == 'confirm_draft' && GETPOST('confirm') == 'yes') // Si Validation de la demande if ($action == 'confirm_cancel' && GETPOST('confirm') == 'yes') { + $error = 0; + $object = new Holiday($db); $object->fetch($id); // Si statut en attente de validation et valideur = valideur ou utilisateur, ou droits de faire pour les autres - if (($object->statut == 2 || $object->statut == 3) && ($user->id == $object->fk_validator || $user->id == $object->fk_user || ! empty($user->rights->holiday->write_all))) + if (($object->statut == 2 || $object->statut == 3) && ($user->id == $object->fk_validator || in_array($object->fk_user, $childids) || ! empty($user->rights->holiday->write_all))) { $db->begin(); @@ -642,7 +667,8 @@ if ($action == 'confirm_cancel' && GETPOST('confirm') == 'yes') if ($result1 < 0 || $result2 < 0) { - $error = $langs->trans('ErrorCantDeleteCP'); + $error++; + setEventMessages($langs->trans('ErrorCantDeleteCP').' '.$object->error, $object->errors, 'errors'); } } @@ -692,27 +718,22 @@ if ($action == 'confirm_cancel' && GETPOST('confirm') == 'yes') $trackid='leav'.$object->id; - $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, null, null, null, '', '', 0, 0, '', '', $trackid); + $mail = new CMailFile($subject, $emailTo, $emailFrom, $message, array(), array(), array(), '', '', 0, 0, '', '', $trackid); // Envoi du mail $result=$mail->sendfile(); - if(!$result) + if (!$result) { - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=mail&error_content='.$mail->error); - exit; + setEventMessages($mail->error, $mail->errors, 'warnings'); + $action=''; + } + else + { + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); + exit; } - - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); - exit; } - else - { - // Sinon on affiche le formulaire de demande avec le message d'erreur SQL - header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id.'&error=SQL_Create&msg='.$object->error); - exit; - } - } } @@ -814,21 +835,27 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create print ''."\n"; print ''."\n"; - dol_fiche_head('', '', '', -1); + if (empty($conf->global->HOLIDAY_HIDE_BALANCE)) + { + dol_fiche_head('', '', '', -1); - $out=''; - $typeleaves=$object->getTypes(1,1); - foreach($typeleaves as $key => $val) - { - $nb_type = $object->getCPforUser($user->id, $val['rowid']); - $nb_holiday += $nb_type; - $out .= ' - '.$val['label'].': '.($nb_type?price2num($nb_type):0).'
    '; - } - print $langs->trans('SoldeCPUser', round($nb_holiday,5)).'
    '; - print $out; - - dol_fiche_end(); + $out=''; + $typeleaves=$object->getTypes(1,1); + foreach($typeleaves as $key => $val) + { + $nb_type = $object->getCPforUser($user->id, $val['rowid']); + $nb_holiday += $nb_type; + $out .= ' - '.$val['label'].': '.($nb_type?price2num($nb_type):0).'
    '; + } + print $langs->trans('SoldeCPUser', round($nb_holiday,5)).'
    '; + print $out; + dol_fiche_end(); + } + elseif(! is_numeric($conf->global->HOLIDAY_HIDE_BALANCE)) + { + print $langs->trans($conf->global->HOLIDAY_HIDE_BALANCE).'
    '; + } dol_fiche_head(); @@ -843,10 +870,10 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create print ''; if (empty($user->rights->holiday->write_all)) { - print $form->select_dolusers($fuserid, 'useridbis', 0, '', 1, '', '', 0, 0, 0, '', 0, '', 'maxwidth300'); - print ''; + print $form->select_dolusers(($fuserid?$fuserid:$user->id), 'fuserid', 0, '', 0, 'hierarchyme', '', 0, 0, 0, $morefilter, 0, '', 'maxwidth300'); + //print ''; } - else print $form->select_dolusers(GETPOST('fuserid','int')?GETPOST('fuserid','int'):$user->id,'fuserid',0,'',0); + else print $form->select_dolusers(GETPOST('fuserid','int')?GETPOST('fuserid','int'):$user->id, 'fuserid', 0, '', 0, '', '', 0, 0, 0, $morefilter, 0, '', 'maxwidth300'); print ''; print ''; @@ -858,11 +885,11 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create $arraytypeleaves=array(); foreach($typeleaves as $key => $val) { - $labeltoshow = $val['label']; + $labeltoshow = ($langs->trans($val['code'])!=$val['code'] ? $langs->trans($val['code']) : $val['label']); $labeltoshow .= ($val['delay'] > 0 ? ' ('.$langs->trans("NoticePeriod").': '.$val['delay'].' '.$langs->trans("days").')':''); $arraytypeleaves[$val['rowid']]=$labeltoshow; } - print $form->selectarray('type', $arraytypeleaves, (GETPOST('type')?GETPOST('type'):''), 1); + print $form->selectarray('type', $arraytypeleaves, (GETPOST('type','alpha')?GETPOST('type','alpha'):''), 1); if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1); print ''; print ''; @@ -878,11 +905,11 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create if (! GETPOST('date_debut_')) { $form->select_date(-1, 'date_debut_', 0, 0, 0, '', 1, 1); } else { - $tmpdate = dol_mktime(0, 0, 0, GETPOST('date_debut_month'), GETPOST('date_debut_day'), GETPOST('date_debut_year')); + $tmpdate = dol_mktime(0, 0, 0, GETPOST('date_debut_month','int'), GETPOST('date_debut_day','int'), GETPOST('date_debut_year','int')); $form->select_date($tmpdate, 'date_debut_', 0, 0, 0, '', 1, 1); } print '     '; - print $form->selectarray('starthalfday', $listhalfday, (GETPOST('starthalfday')?GETPOST('starthalfday'):'morning')); + print $form->selectarray('starthalfday', $listhalfday, (GETPOST('starthalfday','alpha')?GETPOST('starthalfday','alpha'):'morning')); print ''; print ''; @@ -897,11 +924,11 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create if (! GETPOST('date_fin_')) { $form->select_date(-1,'date_fin_', 0, 0, 0, '', 1, 1); } else { - $tmpdate = dol_mktime(0, 0, 0, GETPOST('date_fin_month'), GETPOST('date_fin_day'), GETPOST('date_fin_year')); + $tmpdate = dol_mktime(0, 0, 0, GETPOST('date_fin_month','int'), GETPOST('date_fin_day','int'), GETPOST('date_fin_year','int')); $form->select_date($tmpdate,'date_fin_', 0, 0, 0, '', 1, 1); } print '     '; - print $form->selectarray('endhalfday', $listhalfday, (GETPOST('endhalfday')?GETPOST('endhalfday'):'afternoon')); + print $form->selectarray('endhalfday', $listhalfday, (GETPOST('endhalfday','alpha')?GETPOST('endhalfday','alpha'):'afternoon')); print ''; print ''; @@ -909,7 +936,7 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create print ''; print ''.$langs->trans("ReviewedByCP").''; print ''; - print $form->select_dolusers((GETPOST('valideur')>0?GETPOST('valideur'):$user->fk_user), "valideur", 1, ($user->admin ? '' : array($user->id)), 0, '', 0, 0, 0, 0, '', 0, '', '', 1); // By default, hierarchical parent + print $form->select_dolusers((GETPOST('valideur','int')>0?GETPOST('valideur','int'):$user->fk_user), "valideur", 1, ($user->admin ? '' : array($user->id)), 0, '', 0, 0, 0, 0, '', 0, '', '', 1); // By default, hierarchical parent print ''; print ''; @@ -917,7 +944,7 @@ if (empty($id) || $action == 'add' || $action == 'request' || $action == 'create print ''; print ''.$langs->trans("DescCP").''; print ''; - $doleditor = new DolEditor('description', GETPOST('description'), '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('description', GETPOST('description','none'), '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); print $doleditor->Create(1); print ''; @@ -951,8 +978,6 @@ else { $object->fetch($id); - $canedit=(($user->id == $object->fk_user && $user->rights->holiday->write) || ($user->id != $object->fk_user && $user->rights->holiday->write_all)); - $valideur = new User($db); $valideur->fetch($object->fk_validator); @@ -999,50 +1024,10 @@ else } // On vérifie si l'utilisateur à le droit de lire cette demande - if ($canedit) + if ($cancreate) { - if ($action == 'delete') - { - if ($user->rights->holiday->delete) - { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleDeleteCP"),$langs->trans("ConfirmDeleteCP"),"confirm_delete", '', 0, 1); - } - } - - // Si envoi en validation - if ($action == 'sendToValidate' && $object->statut == 1) - { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleToValidCP"),$langs->trans("ConfirmToValidCP"),"confirm_send", '', 1, 1); - } - - // Si validation de la demande - if ($action == 'valid') - { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleValidCP"),$langs->trans("ConfirmValidCP"),"confirm_valid", '', 1, 1); - } - - // Si refus de la demande - if ($action == 'refuse') - { - $array_input = array(array('type'=>"text",'label'=> $langs->trans('DetailRefusCP'),'name'=>"detail_refuse",'size'=>"50",'value'=>"")); - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id."&action=confirm_refuse", $langs->trans("TitleRefuseCP"), $langs->trans('ConfirmRefuseCP'), "confirm_refuse", $array_input, 1, 0); - } - - // Si annulation de la demande - if ($action == 'cancel') - { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleCancelCP"),$langs->trans("ConfirmCancelCP"),"confirm_cancel", '', 1, 1); - } - - // Si back to draft - if ($action == 'backtodraft') - { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleSetToDraft"),$langs->trans("ConfirmSetToDraft"),"confirm_draft", '', 1, 1); - } - $head=holiday_prepare_head($object); - if ($action == 'edit' && $object->statut == 1) { $edit = true; @@ -1065,6 +1050,7 @@ else print ''; print ''; + // User print ''; print ''; print ''; print ''; print ''; @@ -1086,7 +1073,7 @@ else if(!$edit) { print ''; - print ''; + print ''; print ''; - print ''; + print ''; print ''; - print ''; + print ''; print ''; - print ''; + print ''; print ''; print ''; } + // Nb of days print ''; print ''; print ''; @@ -1183,11 +1171,14 @@ else // Validator if (!$edit) { print ''; - print ''; + print ''; print ''; print ''; } else { - print ''; + print ''; print ''; print ''; print ''; print ''; - if ($object->statut == 3) { + if ($object->statut == 3 || $object->statut == 4) { print ''; print ''; - print ''; + print ''; // warning: date_valid is approval date on holiday module print ''; } if ($object->statut == 4) { @@ -1229,10 +1220,51 @@ else dol_fiche_end(); + // Confirmation messages + if ($action == 'delete') + { + if ($user->rights->holiday->delete) + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleDeleteCP"),$langs->trans("ConfirmDeleteCP"),"confirm_delete", '', 0, 1); + } + } + + // Si envoi en validation + if ($action == 'sendToValidate' && $object->statut == 1) + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleToValidCP"),$langs->trans("ConfirmToValidCP"),"confirm_send", '', 1, 1); + } + + // Si validation de la demande + if ($action == 'valid') + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleValidCP"),$langs->trans("ConfirmValidCP"),"confirm_valid", '', 1, 1); + } + + // Si refus de la demande + if ($action == 'refuse') + { + $array_input = array(array('type'=>"text",'label'=> $langs->trans('DetailRefusCP'),'name'=>"detail_refuse",'size'=>"50",'value'=>"")); + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id."&action=confirm_refuse", $langs->trans("TitleRefuseCP"), $langs->trans('ConfirmRefuseCP'), "confirm_refuse", $array_input, 1, 0); + } + + // Si annulation de la demande + if ($action == 'cancel') + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleCancelCP"),$langs->trans("ConfirmCancelCP"),"confirm_cancel", '', 1, 1); + } + + // Si back to draft + if ($action == 'backtodraft') + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id,$langs->trans("TitleSetToDraft"),$langs->trans("ConfirmSetToDraft"),"confirm_draft", '', 1, 1); + } + + if ($action == 'edit' && $object->statut == 1) { print '
    '; - if ($canedit && $object->statut == 1) + if ($cancreate && $object->statut == 1) { print ''; } @@ -1247,20 +1279,20 @@ else print '
    '; // Boutons d'actions - if ($canedit && $object->statut == 1) + if ($cancreate && $object->statut == 1) { print ''.$langs->trans("EditCP").''; } - if ($canedit && $object->statut == 1) + if ($cancreate && $object->statut == 1) // If draft { print ''.$langs->trans("Validate").''; } - if ($user->rights->holiday->delete && $object->statut == 1) // If draft + if ($user->rights->holiday->delete && ($object->statut == 1 || $object->statut == 4 || $object->statut == 5)) // If draft or canceled or refused { print ''.$langs->trans("DeleteCP").''; } - if ($object->statut == 2) + if ($object->statut == 2) // If validated { if ($user->id == $object->fk_validator) { @@ -1274,13 +1306,13 @@ else } } - if (($user->id == $object->fk_validator || $user->id == $object->fk_user || ! empty($user->rights->holiday->write_all)) && ($object->statut == 2 || $object->statut == 3)) // Status validated or approved + if (($user->id == $object->fk_validator || in_array($object->fk_user, $childids) || ! empty($user->rights->holiday->write_all)) && ($object->statut == 2 || $object->statut == 3)) // Status validated or approved { if (($object->date_debut > dol_now()) || $user->admin) print ''.$langs->trans("ActionCancelCP").''; else print ''.$langs->trans("ActionCancelCP").''; } - if ($canedit && $object->statut == 4) + if ($cancreate && $object->statut == 4) { print ''.$langs->trans("SetToDraft").''; } diff --git a/htdocs/holiday/class/holiday.class.php b/htdocs/holiday/class/holiday.class.php index 26551847772..950c297fb3b 100644 --- a/htdocs/holiday/class/holiday.class.php +++ b/htdocs/holiday/class/holiday.class.php @@ -50,7 +50,7 @@ class Holiday extends CommonObject var $date_fin=''; // Date end in PHP server TZ var $date_debut_gmt=''; // Date start in GMT var $date_fin_gmt=''; // Date end in GMT - var $halfday=''; + var $halfday=''; // 0:Full days, 2:Start afternoon end morning, -1:Start afternoon end afternoon, 1:Start morning end morning var $statut=''; // 1=draft, 2=validated, 3=approved var $fk_validator; var $date_valid=''; @@ -276,12 +276,12 @@ class Holiday extends CommonObject } /** - * List holidays for a particular user + * List holidays for a particular user or list of users * - * @param int $user_id ID of user to list - * @param string $order Sort order - * @param string $filter SQL Filter - * @return int -1 if KO, 1 if OK, 2 if no result + * @param int|string $user_id ID of user to list, or comma separated list of IDs of users to list + * @param string $order Sort order + * @param string $filter SQL Filter + * @return int -1 if KO, 1 if OK, 2 if no result */ function fetchByUser($user_id, $order='', $filter='') { @@ -321,8 +321,8 @@ class Holiday extends CommonObject $sql.= " FROM ".MAIN_DB_PREFIX."holiday as cp, ".MAIN_DB_PREFIX."user as uu, ".MAIN_DB_PREFIX."user as ua"; $sql.= " WHERE cp.entity IN (".getEntity('holiday').")"; - $sql.= " AND cp.fk_user = uu.rowid AND cp.fk_validator = ua.rowid "; // Hack pour la recherche sur le tableau - $sql.= " AND cp.fk_user = ".$user_id; + $sql.= " AND cp.fk_user = uu.rowid AND cp.fk_validator = ua.rowid"; // Hack pour la recherche sur le tableau + $sql.= " AND cp.fk_user IN (".$user_id.")"; // Filtre de séléction if(!empty($filter)) { @@ -553,7 +553,7 @@ class Holiday extends CommonObject } else { $error++; } - $sql.= " halfday = ".$this->halfday.","; + $sql.= " halfday = ".$this->halfday.","; if(!empty($this->statut) && is_numeric($this->statut)) { $sql.= " statut = ".$this->statut.","; } else { @@ -714,10 +714,10 @@ class Holiday extends CommonObject if ($infos_CP['statut'] == 4) continue; // ignore not validated holidays if ($infos_CP['statut'] == 5) continue; // ignore not validated holidays /* - var_dump("--"); - var_dump("old: ".dol_print_date($infos_CP['date_debut'],'dayhour').' '.dol_print_date($infos_CP['date_fin'],'dayhour').' '.$infos_CP['halfday']); - var_dump("new: ".dol_print_date($dateStart,'dayhour').' '.dol_print_date($dateEnd,'dayhour').' '.$halfday); - */ + var_dump("--"); + var_dump("old: ".dol_print_date($infos_CP['date_debut'],'dayhour').' '.dol_print_date($infos_CP['date_fin'],'dayhour').' '.$infos_CP['halfday']); + var_dump("new: ".dol_print_date($dateStart,'dayhour').' '.dol_print_date($dateEnd,'dayhour').' '.$halfday); + */ if ($halfday == 0) { @@ -797,7 +797,7 @@ class Holiday extends CommonObject $sql.= " FROM ".MAIN_DB_PREFIX."holiday as cp"; $sql.= " WHERE cp.entity IN (".getEntity('holiday').")"; $sql.= " AND cp.fk_user = ".(int) $fk_user; - $sql.= " AND date_debut <= '".$this->db->idate($timestamp)."' AND date_fin >= '".$this->db->idate($timestamp)."'"; + $sql.= " AND date_debut <= '".$this->db->idate($timestamp)."' AND date_fin >= '".$this->db->idate($timestamp)."'"; $resql = $this->db->query($sql); if ($resql) @@ -837,7 +837,7 @@ class Holiday extends CommonObject } else dol_print_error($this->db); - return array('morning'=>$isavailablemorning, 'afternoon'=>$isavailableafternoon); + return array('morning'=>$isavailablemorning, 'afternoon'=>$isavailableafternoon); } @@ -860,10 +860,10 @@ class Holiday extends CommonObject //if ($option != 'nolink') //{ - // Add param to save lastsearch_values or not - $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); - if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; - if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; //} $linkstart = ''; @@ -957,10 +957,11 @@ class Holiday extends CommonObject /** * Affiche un select HTML des statuts de congés payés * - * @param int $selected int du statut séléctionné par défaut - * @return string affiche le select des statuts + * @param int $selected Id of preselected status + * @param string $htmlname Name of HTML select field + * @return string Show select of status */ - function selectStatutCP($selected='') { + function selectStatutCP($selected='', $htmlname='select_statut') { global $langs; @@ -969,7 +970,7 @@ class Holiday extends CommonObject $nb = count($name)+1; // Select HTML - $statut = ''."\n"; $statut.= ''."\n"; // Boucle des statuts @@ -1146,15 +1147,15 @@ class Holiday extends CommonObject } if ($result) - { + { $this->db->commit(); return 1; - } - else - { - $this->db->rollback(); + } + else + { + $this->db->rollback(); return -1; - } + } } return 0; @@ -1209,7 +1210,7 @@ class Holiday extends CommonObject return 1; } else - { + { return -1; } } @@ -1394,7 +1395,7 @@ class Holiday extends CommonObject } else { - // We want only list of vacation balance for user ids + // We want only list of vacation balance for user ids $sql = "SELECT DISTINCT cpu.fk_user"; $sql.= " FROM ".MAIN_DB_PREFIX."holiday_users as cpu, ".MAIN_DB_PREFIX."user as u"; $sql.= " WHERE cpu.fk_user = u.user"; @@ -1426,7 +1427,7 @@ class Holiday extends CommonObject return $stringlist; } else - { + { // Erreur SQL $this->error="Error ".$this->db->lasterror(); return -1; @@ -1453,46 +1454,46 @@ class Holiday extends CommonObject else $sql.= " WHERE u.entity IN (0,".$conf->entity.")"; - $sql.= " AND u.statut > 0"; - if ($filters) $sql.=$filters; + $sql.= " AND u.statut > 0"; + if ($filters) $sql.=$filters; - $resql=$this->db->query($sql); + $resql=$this->db->query($sql); - // Si pas d'erreur SQL - if ($resql) - { - $i = 0; - $tab_result = $this->holiday; - $num = $this->db->num_rows($resql); + // Si pas d'erreur SQL + if ($resql) + { + $i = 0; + $tab_result = $this->holiday; + $num = $this->db->num_rows($resql); - // Boucles du listage des utilisateurs - while($i < $num) { + // Boucles du listage des utilisateurs + while($i < $num) { - $obj = $this->db->fetch_object($resql); + $obj = $this->db->fetch_object($resql); - $tab_result[$i]['rowid'] = $obj->rowid; - $tab_result[$i]['name'] = $obj->lastname; // deprecated - $tab_result[$i]['lastname'] = $obj->lastname; - $tab_result[$i]['firstname'] = $obj->firstname; - $tab_result[$i]['gender'] = $obj->gender; - $tab_result[$i]['status'] = $obj->statut; - $tab_result[$i]['employee'] = $obj->employee; - $tab_result[$i]['photo'] = $obj->photo; - $tab_result[$i]['fk_user'] = $obj->fk_user; - //$tab_result[$i]['type'] = $obj->type; - //$tab_result[$i]['nb_holiday'] = $obj->nb_holiday; + $tab_result[$i]['rowid'] = $obj->rowid; + $tab_result[$i]['name'] = $obj->lastname; // deprecated + $tab_result[$i]['lastname'] = $obj->lastname; + $tab_result[$i]['firstname'] = $obj->firstname; + $tab_result[$i]['gender'] = $obj->gender; + $tab_result[$i]['status'] = $obj->statut; + $tab_result[$i]['employee'] = $obj->employee; + $tab_result[$i]['photo'] = $obj->photo; + $tab_result[$i]['fk_user'] = $obj->fk_user; + //$tab_result[$i]['type'] = $obj->type; + //$tab_result[$i]['nb_holiday'] = $obj->nb_holiday; - $i++; + $i++; + } + // Retoune le tableau des utilisateurs + return $tab_result; + } + else + { + // Erreur SQL + $this->errors[]="Error ".$this->db->lasterror(); + return -1; } - // Retoune le tableau des utilisateurs - return $tab_result; - } - else - { - // Erreur SQL - $this->errors[]="Error ".$this->db->lasterror(); - return -1; - } } else { @@ -1636,32 +1637,32 @@ class Holiday extends CommonObject $sql.= ")"; $resql=$this->db->query($sql); - if (! $resql) - { - $error++; $this->errors[]="Error ".$this->db->lasterror(); - } + if (! $resql) + { + $error++; $this->errors[]="Error ".$this->db->lasterror(); + } - if (! $error) - { - $this->optRowid = $this->db->last_insert_id(MAIN_DB_PREFIX."holiday_logs"); - } + if (! $error) + { + $this->optRowid = $this->db->last_insert_id(MAIN_DB_PREFIX."holiday_logs"); + } - // Commit or rollback - if ($error) - { - foreach($this->errors as $errmsg) - { - dol_syslog(get_class($this)."::addLogCP ".$errmsg, LOG_ERR); - $this->error.=($this->error?', '.$errmsg:$errmsg); - } - $this->db->rollback(); - return -1*$error; - } - else - { - $this->db->commit(); + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::addLogCP ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); return $this->optRowid; - } + } } /** @@ -1701,43 +1702,43 @@ class Holiday extends CommonObject $resql=$this->db->query($sql); // Si pas d'erreur SQL - if ($resql) { + if ($resql) { - $i = 0; - $tab_result = $this->logs; - $num = $this->db->num_rows($resql); + $i = 0; + $tab_result = $this->logs; + $num = $this->db->num_rows($resql); - // Si pas d'enregistrement - if(!$num) { + // Si pas d'enregistrement + if(!$num) { return 2; - } + } - // On liste les résultats et on les ajoutent dans le tableau - while($i < $num) { + // On liste les résultats et on les ajoutent dans le tableau + while($i < $num) { - $obj = $this->db->fetch_object($resql); + $obj = $this->db->fetch_object($resql); - $tab_result[$i]['rowid'] = $obj->rowid; - $tab_result[$i]['date_action'] = $obj->date_action; - $tab_result[$i]['fk_user_action'] = $obj->fk_user_action; - $tab_result[$i]['fk_user_update'] = $obj->fk_user_update; - $tab_result[$i]['type_action'] = $obj->type_action; - $tab_result[$i]['prev_solde'] = $obj->prev_solde; - $tab_result[$i]['new_solde'] = $obj->new_solde; - $tab_result[$i]['fk_type'] = $obj->fk_type; + $tab_result[$i]['rowid'] = $obj->rowid; + $tab_result[$i]['date_action'] = $obj->date_action; + $tab_result[$i]['fk_user_action'] = $obj->fk_user_action; + $tab_result[$i]['fk_user_update'] = $obj->fk_user_update; + $tab_result[$i]['type_action'] = $obj->type_action; + $tab_result[$i]['prev_solde'] = $obj->prev_solde; + $tab_result[$i]['new_solde'] = $obj->new_solde; + $tab_result[$i]['fk_type'] = $obj->fk_type; - $i++; - } - // Retourne 1 et ajoute le tableau à la variable - $this->logs = $tab_result; - return 1; - } - else - { - // Erreur SQL - $this->error="Error ".$this->db->lasterror(); - return -1; - } + $i++; + } + // Retourne 1 et ajoute le tableau à la variable + $this->logs = $tab_result; + return 1; + } + else + { + // Erreur SQL + $this->error="Error ".$this->db->lasterror(); + return -1; + } } diff --git a/htdocs/holiday/define_holiday.php b/htdocs/holiday/define_holiday.php index b4440700be7..4dc1e92268e 100644 --- a/htdocs/holiday/define_holiday.php +++ b/htdocs/holiday/define_holiday.php @@ -34,6 +34,7 @@ $langs->load('users'); $langs->load('hrm'); $action=GETPOST('action','aZ09'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'defineholidaylist'; $search_name=GETPOST('search_name', 'alpha'); $search_supervisor=GETPOST('search_supervisor', 'int'); @@ -58,9 +59,6 @@ if ($user->societe_id > 0) accessforbidden(); if (!$user->rights->holiday->read) accessforbidden(); -// Initialize technical object to manage context to save list fields -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'defineholidaylist'; - // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array $hookmanager->initHooks(array('defineholidaylist')); $extrafields = new ExtraFields($db); @@ -118,7 +116,7 @@ if (empty($reshook)) $userValue = $_POST['nb_holiday_'.$val['rowid']]; $userValue = $userValue[$userID]; - if (!empty($userValue)) + if (!empty($userValue) || (string) $userValue == '0') { $userValue = price2num($userValue,5); } else { @@ -128,7 +126,7 @@ if (empty($reshook)) //If the user set a comment, we add it to the log comment $comment = ((isset($_POST['note_holiday'][$userID]) && !empty($_POST['note_holiday'][$userID])) ? ' ('.$_POST['note_holiday'][$userID].')' : ''); - //print 'eee'.$val['rowid'].'-'.$userValue; + //print 'holiday: '.$val['rowid'].'-'.$userValue; if ($userValue != '') { // We add the modification to the log (must be before update of sold because we read current value of sold) @@ -174,11 +172,14 @@ $userstatic=new User($db); llxHeader('', $langs->trans('CPTitreMenu')); +$typeleaves=$holiday->getTypes(1,1); + + print ''; if ($optioncss != '') print ''; print ''; print ''; -print ''; +print ''; print ''; print ''; print ''; @@ -213,9 +214,11 @@ if (empty($user->rights->holiday->read_all)) $userchilds=$user->getAllChildIds(1); $filters.=' AND u.rowid IN ('.join(', ',$userchilds).')'; } - -$filters.=natural_search(array('u.firstname','u.lastname'), $search_name); +if (!empty($search_name)) { + $filters.=natural_search(array('u.firstname','u.lastname'), $search_name); +} if ($search_supervisor > 0) $filters.=natural_search(array('u.fk_user'), $search_supervisor, 2); +$filters.= ' AND employee = 1'; // Only employee users are visible $listUsers = $holiday->fetchUsers(false, true, $filters); if (is_numeric($listUsers) && $listUsers < 0) @@ -223,10 +226,8 @@ if (is_numeric($listUsers) && $listUsers < 0) setEventMessages($holiday->error, $holiday->errors, 'errors'); } -$var=true; $i = 0; -$typeleaves=$holiday->getTypes(1,1); if (count($typeleaves) == 0) { @@ -240,8 +241,6 @@ else $canedit=0; if (! empty($user->rights->holiday->define_holiday)) $canedit=1; - print ''; - $moreforfilter=''; print '
    '; @@ -286,7 +285,8 @@ else { foreach($typeleaves as $key => $val) { - print_liste_field_titre($val['label'], $_SERVER["PHP_SELF"], '', '', '', 'align="center"'); + $labeltype = ($langs->trans($val['code'])!=$val['code']) ? $langs->trans($val['code']) : $langs->trans($val['label']); + print_liste_field_titre($labeltype, $_SERVER["PHP_SELF"], '', '', '', 'align="center"'); } } else @@ -355,8 +355,10 @@ else print '
    '; + + // Button modify print ''; // Create date print ''; + +$morefilter = 'AND employee = 1'; +if (! empty($conf->global->HOLIDAY_FOR_NON_SALARIES_TOO)) $morefilter = ''; + // User -if ($user->rights->holiday->write_all) +$disabled=0; +// If into the tab holiday of a user ($id is set in such a case) +if ($id && ! GETPOSTISSET('search_employee')) { - print ''; } else { - //print ''; + if (GETPOSTISSET('search_employee')) $search_employee=GETPOST('search_employee','int'); print ''; } // Approve -if($user->rights->holiday->write_all) +if ($user->rights->holiday->read_all) { print ''; } else @@ -423,7 +465,7 @@ $typeleaves=$holidaystatic->getTypes(1,-1); $arraytypeleaves=array(); foreach($typeleaves as $key => $val) { - $labeltoshow = $val['label']; + $labeltoshow = ($langs->trans($val['code'])!=$val['code'] ? $langs->trans($val['code']) : $val['label']); //$labeltoshow .= ($val['delay'] > 0 ? ' ('.$langs->trans("NoticePeriod").': '.$val['delay'].' '.$langs->trans("days").')':''); $arraytypeleaves[$val['rowid']]=$labeltoshow; } @@ -435,19 +477,19 @@ print ''; // Start date print ''; // End date print ''; // Status print ''; // Actions @@ -464,17 +506,25 @@ print_liste_field_titre("DateCreateCP",$_SERVER["PHP_SELF"],"cp.date_create","", print_liste_field_titre("Employee",$_SERVER["PHP_SELF"],"cp.fk_user","",$param,'',$sortfield,$sortorder); print_liste_field_titre("ValidatorCP",$_SERVER["PHP_SELF"],"cp.fk_validator","",$param,'',$sortfield,$sortorder); print_liste_field_titre("Type",$_SERVER["PHP_SELF"],'','',$param,'',$sortfield,$sortorder); -print_liste_field_titre("Duration",$_SERVER["PHP_SELF"],'','',$pram,'align="right"',$sortfield,$sortorder); +print_liste_field_titre("NbUseDaysCPShort",$_SERVER["PHP_SELF"],'','',$pram,'align="right"',$sortfield,$sortorder); print_liste_field_titre("DateDebCP",$_SERVER["PHP_SELF"],"cp.date_debut","",$param,'align="center"',$sortfield,$sortorder); print_liste_field_titre("DateFinCP",$_SERVER["PHP_SELF"],"cp.date_fin","",$param,'align="center"',$sortfield,$sortorder); print_liste_field_titre("Status",$_SERVER["PHP_SELF"],"cp.statut","",$param,'align="right"',$sortfield,$sortorder); -print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; +print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],"",'',$param,'align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; print "\n"; $listhalfday=array('morning'=>$langs->trans("Morning"),"afternoon"=>$langs->trans("Afternoon")); + +// If we ask a dedicated card and not allow to see it, we forc on user. +if ($id && empty($user->rights->holiday->read_all) && ! in_array($id, $childids)) +{ + $langs->load("errors"); + print ''; + $result = 0; +} // Lines -if (! empty($holiday->holiday)) +elseif (! empty($holiday->holiday)) { $userstatic = new User($db); $approbatorstatic = new User($db); @@ -510,13 +560,14 @@ if (! empty($holiday->holiday)) print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; diff --git a/htdocs/hrm/admin/admin_establishment.php b/htdocs/hrm/admin/admin_establishment.php index 077d76d0d32..23e92b83004 100644 --- a/htdocs/hrm/admin/admin_establishment.php +++ b/htdocs/hrm/admin/admin_establishment.php @@ -69,7 +69,7 @@ $establishmenttmp=new Establishment($db); dol_htmloutput_mesg($mesg); // Subheader -$linkback = '' . $langs->trans("BackToModuleList") . ''; +$linkback = '' . $langs->trans("BackToModuleList") . ''; print load_fiche_titre($langs->trans("HRMSetup"), $linkback); // Configuration header diff --git a/htdocs/hrm/admin/admin_hrm.php b/htdocs/hrm/admin/admin_hrm.php index f488eb562e5..05fccafbda8 100644 --- a/htdocs/hrm/admin/admin_hrm.php +++ b/htdocs/hrm/admin/admin_hrm.php @@ -68,7 +68,7 @@ $form = new Form($db); dol_htmloutput_mesg($mesg); // Subheader -$linkback = '' . $langs->trans("BackToModuleList") . ''; +$linkback = '' . $langs->trans("BackToModuleList") . ''; print load_fiche_titre($langs->trans("HRMSetup"), $linkback); // Configuration header diff --git a/htdocs/hrm/index.php b/htdocs/hrm/index.php index f0e279bb848..2f360dce5c5 100644 --- a/htdocs/hrm/index.php +++ b/htdocs/hrm/index.php @@ -130,27 +130,34 @@ if (! empty($conf->global->MAIN_SEARCH_FORM_ON_HOME_AREAS)) // This is usele if (! empty($conf->holiday->enabled)) { - $user_id = $user->id; + if (empty($conf->global->HOLIDAY_HIDE_BALANCE)) + { + $user_id = $user->id; - print '
    '.$langs->trans("User").''; @@ -1076,7 +1062,8 @@ else print ''.$langs->trans("Type").''; $typeleaves=$object->getTypes(1,-1); - print empty($typeleaves[$object->fk_type]['label']) ? $langs->trans("TypeWasDisabledOrRemoved",$object->fk_type) : $typeleaves[$object->fk_type]['label']; + $labeltoshow = (($typeleaves[$object->fk_type]['code'] && $langs->trans($typeleaves[$object->fk_type]['code'])!=$typeleaves[$object->fk_type]['code']) ? $langs->trans($typeleaves[$object->fk_type]['code']) : $typeleaves[$object->fk_type]['label']); + print empty($labeltoshow) ? $langs->trans("TypeWasDisabledOrRemoved",$object->fk_type) : $labeltoshow; print '
    '.$langs->trans('DateDebCP').' ('.$langs->trans("FirstDayOfHoliday").')'.$langs->trans('DateDebCP').' ('.$langs->trans("FirstDayOfHoliday").')'.dol_print_date($object->date_debut,'day'); print '     '; print ''.$langs->trans($listhalfday[$starthalfday]).''; @@ -1096,7 +1083,7 @@ else else { print '
    '.$langs->trans('DateDebCP').' ('.$langs->trans("FirstDayOfHoliday").')'.$langs->trans('DateDebCP').' ('.$langs->trans("FirstDayOfHoliday").')'; $form->select_date($object->date_debut,'date_debut_'); print '     '; @@ -1108,7 +1095,7 @@ else if (!$edit) { print '
    '.$langs->trans('DateFinCP').' ('.$langs->trans("LastDayOfHoliday").')'.$langs->trans('DateFinCP').' ('.$langs->trans("LastDayOfHoliday").')'.dol_print_date($object->date_fin,'day'); print '     '; print ''.$langs->trans($listhalfday[$endhalfday]).''; @@ -1118,7 +1105,7 @@ else else { print '
    '.$langs->trans('DateFinCP').' ('.$langs->trans("LastDayOfHoliday").')'.$langs->trans('DateFinCP').' ('.$langs->trans("LastDayOfHoliday").')'; $form->select_date($object->date_fin,'date_fin_'); print '     '; @@ -1126,6 +1113,7 @@ else print '
    '.$langs->trans('NbUseDaysCP').''.num_open_day($object->date_debut_gmt, $object->date_fin_gmt, 0, 1, $object->halfday).'
    '.$langs->trans('ReviewedByCP').''; + if ($object->statut == 3 || $object->statut == 4) print $langs->trans('ApprovedBy'); + else print $langs->trans('ReviewedByCP'); + print ''.$valideur->getNomUrl(-1).'
    '.$langs->trans('ReviewedByCP').''; print $form->select_dolusers($object->fk_validator, "valideur", 1, ($user->admin ? '' : array($user->id))); // By default, hierarchical parent @@ -1199,10 +1190,10 @@ else print ''.$langs->trans('DateCreateCP').''.dol_print_date($object->date_create,'dayhour').'
    '.$langs->trans('DateValidCP').''.dol_print_date($object->date_valid,'dayhour').''.dol_print_date($object->date_valid,'dayhour').'
    '; if ($canedit) print ''; print ''; - if (! empty($user->rights->holiday->define_holiday)) + if (! empty($user->rights->holiday->define_holiday)) // Allowed to set the balance of any user { print ''; } diff --git a/htdocs/holiday/list.php b/htdocs/holiday/list.php index cf81d0a98fa..3397e6fced8 100644 --- a/htdocs/holiday/list.php +++ b/htdocs/holiday/list.php @@ -48,6 +48,18 @@ $contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'myobjectl $backtopage = GETPOST('backtopage','alpha'); // Go back to a dedicated page $optioncss = GETPOST('optioncss','aZ'); // Option for the css output (always '' except when 'print') +$childids = $user->getAllChildIds(1); + +// Security check +$socid=0; +if ($user->societe_id > 0) // Protection if external user +{ + //$socid = $user->societe_id; + accessforbidden(); +} +$result = restrictedArea($user, 'holiday', $id, ''); +$id = GETPOST('id','int'); + // Load variable for pagination $limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit; $sortfield = GETPOST('sortfield','alpha'); @@ -71,20 +83,22 @@ $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search if (! $sortfield) $sortfield="cp.rowid"; if (! $sortorder) $sortorder="DESC"; -$id = GETPOST('id','int'); -$sall = trim((GETPOST('search_all', 'alphanohtml')!='')?GETPOST('search_all', 'alphanohtml'):GETPOST('sall', 'alphanohtml')); -$search_ref = GETPOST('search_ref'); -$month_create = GETPOST('month_create'); -$year_create = GETPOST('year_create'); -$month_start = GETPOST('month_start'); -$year_start = GETPOST('year_start'); -$month_end = GETPOST('month_end'); -$year_end = GETPOST('year_end'); -$search_employee = GETPOST('search_employee'); -$search_valideur = GETPOST('search_valideur'); -$search_statut = GETPOST('select_statut'); -$search_type = GETPOST('search_type','int'); +$sall = trim((GETPOST('search_all', 'alphanohtml')!='')?GETPOST('search_all', 'alphanohtml'):GETPOST('sall', 'alphanohtml')); +$search_ref = GETPOST('search_ref','alphanohtml'); +$search_day_create = GETPOST('search_day_create','int'); +$search_month_create = GETPOST('search_month_create','int'); +$search_year_create = GETPOST('search_year_create','int'); +$search_day_start = GETPOST('search_day_start','int'); +$search_month_start = GETPOST('search_month_start','int'); +$search_year_start = GETPOST('search_year_start','int'); +$search_day_end = GETPOST('search_day_end','int'); +$search_month_end = GETPOST('search_month_end','int'); +$search_year_end = GETPOST('search_year_end','int'); +$search_employee = GETPOST('search_employee','int'); +$search_valideur = GETPOST('search_valideur','int'); +$search_statut = GETPOST('search_statut','int'); +$search_type = GETPOST('search_type','int'); // List of fields to search into when doing a "search in all" $fieldstosearchall = array( @@ -115,12 +129,12 @@ if (empty($reshook)) if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers { $search_ref=""; - $month_create=""; - $year_create=""; - $month_start=""; - $year_start=""; - $month_end=""; - $year_end=""; + $search_month_create=""; + $search_year_create=""; + $search_month_start=""; + $search_year_start=""; + $search_month_end=""; + $search_year_end=""; $search_employee=""; $search_valideur=""; $search_statut=""; @@ -157,9 +171,6 @@ $holiday = new Holiday($db); $holidaystatic=new Holiday($db); $fuser = new User($db); -$childids = $user->getAllChildIds(); -$childids[]=$user->id; - // Update sold $result = $holiday->updateBalance(); @@ -174,51 +185,51 @@ $order = $db->order($sortfield,$sortorder).$db->plimit($limit + 1, $offset); // Ref if(!empty($search_ref)) { - $filter.= " AND cp.rowid = ".$db->escape($search_ref); + $filter.= " AND cp.rowid = ".(int) $db->escape($search_ref); } // Start date -if($year_start > 0) { - if($month_start > 0) { - $filter .= " AND (cp.date_debut BETWEEN '".$db->idate(dol_get_first_day($year_start,$month_start,1))."' AND '".$db->idate(dol_get_last_day($year_start,$month_start,1))."')"; - //$filter.= " AND date_format(cp.date_debut, '%Y-%m') = '$year_start-$month_start'"; +if($search_year_start > 0) { + if($search_month_start > 0) { + $filter .= " AND (cp.date_debut BETWEEN '".$db->idate(dol_get_first_day($search_year_start,$search_month_start,1))."' AND '".$db->idate(dol_get_last_day($search_year_start,$search_month_start,1))."')"; + //$filter.= " AND date_format(cp.date_debut, '%Y-%m') = '$search_year_start-$search_month_start'"; } else { - $filter .= " AND (cp.date_debut BETWEEN '".$db->idate(dol_get_first_day($year_start,1,1))."' AND '".$db->idate(dol_get_last_day($year_start,12,1))."')"; - //$filter.= " AND date_format(cp.date_debut, '%Y') = '$year_start'"; + $filter .= " AND (cp.date_debut BETWEEN '".$db->idate(dol_get_first_day($search_year_start,1,1))."' AND '".$db->idate(dol_get_last_day($search_year_start,12,1))."')"; + //$filter.= " AND date_format(cp.date_debut, '%Y') = '$search_year_start'"; } } else { - if($month_start > 0) { - $filter.= " AND date_format(cp.date_debut, '%m') = '".$db->escape($month_start)."'"; + if($search_month_start > 0) { + $filter.= " AND date_format(cp.date_debut, '%m') = '".$db->escape($search_month_start)."'"; } } // End date -if($year_end > 0) { - if($month_end > 0) { - $filter .= " AND (cp.date_fin BETWEEN '".$db->idate(dol_get_first_day($year_end,$month_end,1))."' AND '".$db->idate(dol_get_last_day($year_end,$month_end,1))."')"; - //$filter.= " AND date_format(cp.date_fin, '%Y-%m') = '$year_end-$month_end'"; +if($search_year_end > 0) { + if($search_month_end > 0) { + $filter .= " AND (cp.date_fin BETWEEN '".$db->idate(dol_get_first_day($search_year_end,$search_month_end,1))."' AND '".$db->idate(dol_get_last_day($search_year_end,$search_month_end,1))."')"; + //$filter.= " AND date_format(cp.date_fin, '%Y-%m') = '$search_year_end-$search_month_end'"; } else { - $filter .= " AND (cp.date_fin BETWEEN '".$db->idate(dol_get_first_day($year_end,1,1))."' AND '".$db->idate(dol_get_last_day($year_end,12,1))."')"; - //$filter.= " AND date_format(cp.date_fin, '%Y') = '$year_end'"; + $filter .= " AND (cp.date_fin BETWEEN '".$db->idate(dol_get_first_day($search_year_end,1,1))."' AND '".$db->idate(dol_get_last_day($search_year_end,12,1))."')"; + //$filter.= " AND date_format(cp.date_fin, '%Y') = '$search_year_end'"; } } else { - if($month_end > 0) { - $filter.= " AND date_format(cp.date_fin, '%m') = '".$db->escape($month_end)."'"; + if($search_month_end > 0) { + $filter.= " AND date_format(cp.date_fin, '%m') = '".$db->escape($search_month_end)."'"; } } // Create date -if($year_create > 0) { - if($month_create > 0) { - $filter .= " AND (cp.date_create BETWEEN '".$db->idate(dol_get_first_day($year_create,$month_create,1))."' AND '".$db->idate(dol_get_last_day($year_create,$month_create,1))."')"; - //$filter.= " AND date_format(cp.date_create, '%Y-%m') = '$year_create-$month_create'"; +if($search_year_create > 0) { + if($search_month_create > 0) { + $filter .= " AND (cp.date_create BETWEEN '".$db->idate(dol_get_first_day($search_year_create,$search_month_create,1))."' AND '".$db->idate(dol_get_last_day($search_year_create,$search_month_create,1))."')"; + //$filter.= " AND date_format(cp.date_create, '%Y-%m') = '$search_year_create-$search_month_create'"; } else { - $filter .= " AND (cp.date_create BETWEEN '".$db->idate(dol_get_first_day($year_create,1,1))."' AND '".$db->idate(dol_get_last_day($year_create,12,1))."')"; - //$filter.= " AND date_format(cp.date_create, '%Y') = '$year_create'"; + $filter .= " AND (cp.date_create BETWEEN '".$db->idate(dol_get_first_day($search_year_create,1,1))."' AND '".$db->idate(dol_get_last_day($search_year_create,12,1))."')"; + //$filter.= " AND date_format(cp.date_create, '%Y') = '$search_year_create'"; } } else { - if($month_create > 0) { - $filter.= " AND date_format(cp.date_create, '%m') = '".$db->escape($month_create)."'"; + if($search_month_create > 0) { + $filter.= " AND date_format(cp.date_create, '%m') = '".$db->escape($search_month_create)."'"; } } @@ -263,14 +274,16 @@ if ($id > 0) $search_employee = $user_id; } -// Récupération des congés payés de l'utilisateur ou de tous les users +// Récupération des congés payés de l'utilisateur ou de tous les users de sa hierarchy +// Load array $holiday->holiday if (empty($user->rights->holiday->read_all) || $id > 0) { - $result = $holiday->fetchByUser($user_id,$order,$filter); // Load array $holiday->holiday + if ($id > 0) $result = $holiday->fetchByUser($id, $order, $filter); + else $result = $holiday->fetchByUser(join(',',$childids), $order, $filter); } else { - $result = $holiday->fetchAll($order,$filter); // Load array $holiday->holiday + $result = $holiday->fetchAll($order, $filter); } // Si erreur SQL if ($result == '-1') @@ -289,9 +302,23 @@ $num = count($holiday->holiday); $arrayofselected=is_array($toselect)?$toselect:array(); $param=''; -if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; -if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; -if ($optioncss != '') $param.='&optioncss='.$optioncss; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +if ($search_ref) $param.='&search_ref='.urlencode($search_ref); +if ($search_day_create) $param.='&search_day_create='.urlencode($search_day_create); +if ($search_month_create) $param.='&search_month_create='.urlencode($search_month_create); +if ($search_year_create) $param.='&search_year_create='.urlencode($search_year_create); +if ($search_search_day_start) $param.='&search_day_start='.urlencode($search_day_start); +if ($search_month_start) $param.='&search_month_start='.urlencode($search_month_start); +if ($search_year_start) $param.='&search_year_start='.urlencode($search_year_start); +if ($search_day_end) $param.='&search_day_end='.urlencode($search_day_end); +if ($search_month_end) $param.='&search_month_end='.urlencode($search_month_end); +if ($search_year_end) $param.='&search_year_end='.urlencode($search_year_end); +if ($search_employee > 0) $param.='&search_employee='.urlencode($search_employee); +if ($search_valideur > 0) $param.='&search_valideur='.urlencode($search_valideur); +if ($search_type > 0) $param.='&search_type='.urlencode($search_type); +if ($search_statut > 0) $param.='&search_statut='.urlencode($search_statut); // List of mass actions available $arrayofmassactions = array( @@ -315,7 +342,7 @@ if ($id > 0) print ''; if ($id > 0) // For user tab { $title = $langs->trans("User"); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; $head = user_prepare_head($fuser); dol_fiche_head($head, 'paidholidays', $title, -1, 'user'); @@ -350,7 +377,10 @@ else $nbtotalofrecords = count($holiday->holiday); //print $num; //print count($holiday->holiday); - print_barre_liste($langs->trans("ListeCP"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_hrm.png', 0, '', '', $limit); + + $newcardbutton=''.$langs->trans('MenuAddCP').''; + + print_barre_liste($langs->trans("ListeCP"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_hrm.png', 0, $newcardbutton, '', $limit); $topicmail="Information"; $modelmail="leaverequest"; @@ -380,27 +410,39 @@ print ''; -print ''; -$formother->select_year($year_create,'year_create',1, $min_year, 0); +print ''; +$formother->select_year($search_year_create,'search_year_create',1, $min_year, 0); print ''; - print $form->select_dolusers($search_employee,"search_employee",1,"",0,'','',0,0,0,'',0,'','maxwidth200'); + $search_employee=$id; + $disabled=1; +} +if (! empty($user->rights->holiday->read_all)) // Can see all +{ + if (GETPOSTISSET('search_employee')) $search_employee=GETPOST('search_employee','int'); + print ''; + print $form->select_dolusers($search_employee, "search_employee", 1, "", $disabled, '', '', 0, 0, 0, $morefilter, 0, '', 'maxwidth200'); print ' '; - print $form->select_dolusers($user->id,"search_employee",1,"",1,'','',0,0,0,'',0,'','maxwidth200'); + print $form->select_dolusers($search_employee, "search_employee", 1, "", $disabled, 'hierarchyme', '', 0, 0, 0, $morefilter, 0, '', 'maxwidth200'); print ''; @@ -409,7 +451,7 @@ if($user->rights->holiday->write_all) $valideurobjects = $validator->listUsersForGroup($excludefilter); $valideurarray = array(); foreach($valideurobjects as $val) $valideurarray[$val->id]=$val->id; - print $form->select_dolusers($search_valideur,"search_valideur",1,"",0,$valideurarray,'', 0, 0, 0, '', 0, '', 'maxwidth200'); + print $form->select_dolusers($search_valideur, "search_valideur", 1, "", 0, $valideurarray, '', 0, 0, 0, $morefilter, 0, '', 'maxwidth200'); print ' '; -print ''; -$formother->select_year($year_start,'year_start',1, $min_year, $max_year); +print ''; +$formother->select_year($search_year_start,'search_year_start',1, $min_year, $max_year); print ''; -print ''; -$formother->select_year($year_end,'year_end',1, $min_year, $max_year); +print ''; +$formother->select_year($search_year_end,'search_year_end',1, $min_year, $max_year); print ''; -$holiday->selectStatutCP($search_statut); +$holiday->selectStatutCP($search_statut, 'search_statut'); print '
    '.$langs->trans("NotEnoughPermissions").'
    '; - print $holidaystatic->getNomUrl(1); + print $holidaystatic->getNomUrl(1, 1); print ''.dol_print_date($date,'day').''.$userstatic->getNomUrl(-1, 'leave').''.$approbatorstatic->getNomUrl(-1).''; - print empty($typeleaves[$infos_CP['fk_type']]['label']) ? $langs->trans("TypeWasDisabledOrRemoved",$infos_CP['fk_type']) : $typeleaves[$infos_CP['fk_type']]['label']; + $labeltypeleavetoshow = ($langs->trans($typeleaves[$infos_CP['fk_type']]['code'])!=$typeleaves[$infos_CP['fk_type']]['code'] ? $langs->trans($typeleaves[$infos_CP['fk_type']]['code']) : $typeleaves[$infos_CP['fk_type']]['label']); + print empty($typeleaves[$infos_CP['fk_type']]['label']) ? $langs->trans("TypeWasDisabledOrRemoved",$infos_CP['fk_type']) : $labeltypeleavetoshow; print ''; $nbopenedday=num_open_day($infos_CP['date_debut_gmt'], $infos_CP['date_fin_gmt'], 0, 1, $infos_CP['halfday']); diff --git a/htdocs/holiday/month_report.php b/htdocs/holiday/month_report.php new file mode 100644 index 00000000000..cc094046316 --- /dev/null +++ b/htdocs/holiday/month_report.php @@ -0,0 +1,198 @@ + + * Copyright (C) 2011 François Legastelois + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file month_report.php + * \ingroup holiday + * \brief Monthly report of leave requests. + */ + +require('../main.inc.php'); +require_once(DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php'); +require_once(DOL_DOCUMENT_ROOT.'/user/class/user.class.php'); +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; + +$langs->loadLangs(array("holiday")); + +// Security check +$socid=0; +if ($user->societe_id > 0) // Protection if external user +{ + //$socid = $user->societe_id; + accessforbidden(); +} +$result = restrictedArea($user, 'holiday', $id, ''); + + + +/* + * View + */ + +$holidaystatic = new Holiday($db); + +$listhalfday=array('morning'=>$langs->trans("Morning"),"afternoon"=>$langs->trans("Afternoon")); + + +llxHeader('', $langs->trans('CPTitreMenu')); + +print_fiche_titre($langs->trans('MenuReportMonth')); + +$html = new Form($db); +$formother = new FormOther($db); + + +// Selection filter +print '
    '; + +print '' . "\n"; + +$search_month = GETPOST("remonth",'int')?GETPOST("remonth",'int'):date("m", time()); +$search_year = GETPOST("reyear",'int')?GETPOST("reyear",'int'):date("Y", time()); + +$month_year = sprintf("%02d",$search_month).'-'.sprintf("%04d",$search_year); +$year_month = sprintf("%04d",$search_year).'-'.sprintf("%02d",$search_month); + +print $formother->select_month($search_month,'remonth'); + +print $formother->select_year($search_year,'reyear'); + +print ''; + +print ''; + + +$sql = "SELECT cp.rowid, cp.fk_user, cp.date_debut, cp.date_fin, ct.label, cp.description, cp.halfday"; +$sql .= " FROM ".MAIN_DB_PREFIX."holiday cp"; +$sql .= " LEFT JOIN llx_user u ON cp.fk_user = u.rowid"; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_holiday_types ct ON cp.fk_type = ct.rowid"; +$sql .= " WHERE cp.rowid > 0"; +$sql .= " AND cp.statut = 3"; // Approved +$sql .= " AND (date_format(cp.date_debut, '%Y-%m') = '".$db->escape($year_month)."' OR date_format(cp.date_fin, '%Y-%m') = '".$db->escape($year_month)."')"; +$sql .= " ORDER BY u.lastname, cp.date_debut"; + +$resql = $db->query($sql); +if (empty($resql)) +{ + dol_print_error($db); + exit; +} + +$num = $db->num_rows($resql); + +print '
    '; + + +print '
    '; + +print '
    '; +print ''; + +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; + +if ($num == 0) +{ + print ''; +} +else +{ + while ($obj = $db->fetch_object($resql)) + { + $user = new User($db); + $user->fetch($obj->fk_user); + + $date_start = $db->jdate($obj->date_debut, true); + $date_end = $db->jdate($obj->date_fin, true); + + $tmpstart = dol_getdate($date_start); + $tmpend = dol_getdate($date_end); + + $starthalfday=($obj->halfday == -1 || $obj->halfday == 2)?'afternoon':'morning'; + $endhalfday=($obj->halfday == 1 || $obj->halfday == 2)?'morning':'afternoon'; + + $halfdayinmonth = $obj->halfday; + $starthalfdayinmonth = $starthalfday; + $endhalfdayinmonth = $endhalfday; + + //0:Full days, 2:Start afternoon end morning, -1:Start afternoon end afternoon, 1:Start morning end morning + + // Set date_start_gmt and date_end_gmt that are date to show for the selected month + $date_start_inmonth = $db->jdate($obj->date_debut, true); + $date_end_inmonth = $db->jdate($obj->date_fin, true); + if ($tmpstart['year'] < $search_year || $tmpstart['mon'] < $search_month) + { + $date_start_inmonth = dol_get_first_day($search_year, $search_month, true); + $starthalfdayinmonth = 'morning'; + if ($halfdayinmonth == 2) $halfdayinmonth=1; + if ($halfdayinmonth == -1) $halfdayinmonth=0; + } + if ($tmpend['year'] > $search_year || $tmpend['mon'] > $search_month) + { + $date_end_inmonth = dol_get_last_day($search_year, $search_month, true) - ((24 * 3600) - 1); + $endhalfdayinmonth = 'afternoon'; + if ($halfdayinmonth == 2) $halfdayinmonth=-1; + if ($halfdayinmonth == 1) $halfdayinmonth=0; + } + + // Leave request + $holidaystatic->id=$obj->rowid; + $holidaystatic->ref=$obj->rowid; + + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + } + +} +print '
    ' . $langs->trans('Ref') . '' . $langs->trans('Employee') . '' . $langs->trans('Type') . '' . $langs->trans('DateDebCP') . '' . $langs->trans('DateFinCP') . '' . $langs->trans('NbUseDaysCPShort') . '' . $langs->trans('DateStartInMonth') . '' . $langs->trans('DateEndInMonth') . '' . $langs->trans('NbUseDaysCPShortInMonth') . '' . $langs->trans('DescCP') . '
    '.$langs->trans('None').'
    '; + print $holidaystatic->getNomUrl(1, 1); + print '' . $user->getFullName($langs) . '' . $obj->label . '' . dol_print_date($obj->date_debut, 'day'); + print ' ('.$langs->trans($listhalfday[$starthalfday]).')'; + print '' . dol_print_date($obj->date_fin, 'day'); + print ' ('.$langs->trans($listhalfday[$endhalfday]).')'; + print '' . num_open_day($date_start, $date_end, 0, 1, $obj->halfday) . '' . dol_print_date($date_start_inmonth, 'day'); + print ' ('.$langs->trans($listhalfday[$starthalfdayinmonth]).')'; + print '' . dol_print_date($date_end_inmonth, 'day'); + print ' ('.$langs->trans($listhalfday[$endhalfdayinmonth]).')'; + print '' . num_open_day($date_start_inmonth, $date_end_inmonth, 0, 1, $halfdayinmonth) . '' . dol_escape_htmltag(dolGetFirstLineOfText($obj->description)) . '
    '; +print '
    '; + +// Fin de page +$db->close(); +llxFooter(); diff --git a/htdocs/holiday/view_log.php b/htdocs/holiday/view_log.php index 69bd6b6a190..d67db73d437 100644 --- a/htdocs/holiday/view_log.php +++ b/htdocs/holiday/view_log.php @@ -117,7 +117,7 @@ foreach($cp->logs as $logs_CP) print '
    '.$user_update->getNomUrl(-1).''.$logs_CP['type_action'].''; - $label=$alltypeleaves[$logs_CP['fk_type']]['label']; + $label = (($alltypeleaves[$logs_CP['fk_type']]['code'] && $langs->trans($alltypeleaves[$logs_CP['fk_type']]['code'])!=$alltypeleaves[$logs_CP['fk_type']]['code']) ? $langs->trans($alltypeleaves[$logs_CP['fk_type']]['code']) : $alltypeleaves[$logs_CP['fk_type']]['label']); print $label?$label:$logs_CP['fk_type']; print ''.price2num($logs_CP['prev_solde'],5).' '.$langs->trans('days').'
    '; - print ''; - print ""; - print ''; + print ''; + print '
    '.$langs->trans("Holidays").'
    '; + print ''; + print ''; + print ""; + print ''; - print ''; - print '
    '.$langs->trans("Holidays").'
    '; - $out=''; - $typeleaves=$holiday->getTypes(1,1); - foreach($typeleaves as $key => $val) - { - $nb_type = $holiday->getCPforUser($user->id, $val['rowid']); - $nb_holiday += $nb_type; - $out .= ' - '.$val['label'].': '.($nb_type?price2num($nb_type):0).'
    '; - } - print $langs->trans('SoldeCPUser', round($nb_holiday,5)).'
    '; - print $out; + $out=''; + $typeleaves=$holiday->getTypes(1,1); + foreach($typeleaves as $key => $val) + { + $nb_type = $holiday->getCPforUser($user->id, $val['rowid']); + $nb_holiday += $nb_type; + $out .= ' - '.$val['label'].': '.($nb_type?price2num($nb_type):0).'
    '; + } + print $langs->trans('SoldeCPUser', round($nb_holiday,5)).'
    '; + print $out; - print '

    '; + print '

    '; + } + elseif (! is_numeric($conf->global->HOLIDAY_HIDE_BALANCE)) + { + print $langs->trans($conf->global->HOLIDAY_HIDE_BALANCE).'
    '; + } } diff --git a/htdocs/imports/import.php b/htdocs/imports/import.php index b5dbc72fba0..08d3495c33c 100644 --- a/htdocs/imports/import.php +++ b/htdocs/imports/import.php @@ -1475,10 +1475,11 @@ if ($step == 5 && $datatoimport) $sourcelinenb=0; $endoffile=0; // Loop on each input file record - while ($sourcelinenb < $nboflines && ! $endoffile) + while (($sourcelinenb < $nboflines) && ! $endoffile) { $sourcelinenb++; // Read line and store it into $arrayrecord + //dol_syslog("line ".$sourcelinenb.' - '.$nboflines.' - '.$excludefirstline.' - '.$endatlinenb); $arrayrecord=$obj->import_read_record(); if ($arrayrecord === false) { @@ -1487,7 +1488,7 @@ if ($step == 5 && $datatoimport) continue; } if ($excludefirstline && ($sourcelinenb < $excludefirstline)) continue; - if ($endatlinenb && ($sourcelinenb > $endatlinenb)) continue; + if ($endatlinenb && ($sourcelinenb > $endatlinenb)) break; // Run import $result=$obj->import_insert($arrayrecord,$array_match_file_to_database,$objimport,count($fieldssource),$importid,$updatekeys); @@ -1857,7 +1858,7 @@ if ($step == 6 && $datatoimport) continue; } if ($excludefirstline && ($sourcelinenb < $excludefirstline)) continue; - if ($endatlinenb && ($sourcelinenb > $endatlinenb)) continue; + if ($endatlinenb && ($sourcelinenb > $endatlinenb)) break; // Run import $result=$obj->import_insert($arrayrecord,$array_match_file_to_database,$objimport,count($fieldssource),$importid,$updatekeys); diff --git a/htdocs/includes/evalmath/evalmath.class.php b/htdocs/includes/evalmath/evalmath.class.php index bd1755b0a20..9426e82a8ff 100644 --- a/htdocs/includes/evalmath/evalmath.class.php +++ b/htdocs/includes/evalmath/evalmath.class.php @@ -1,5 +1,4 @@ NAME EvalMath - safely evaluate math expressions - + SYNOPSIS include('evalmath.class.php'); $m = new EvalMath; @@ -24,9 +23,9 @@ SYNOPSIS $m->evaluate('f(x,y) = x^2 + y^2 - 2x*y + 1'); // and then use them $result = $m->evaluate('3*f(42,a)'); - + DESCRIPTION - Use the EvalMath class when you want to evaluate mathematical expressions + Use the EvalMath class when you want to evaluate mathematical expressions from untrusted sources. You can define your own variables and functions, which are stored in the object. Try it, it's fun! @@ -35,13 +34,13 @@ METHODS Evaluates the expression and returns the result. If an error occurs, prints a warning and returns false. If $expr is a function assignment, returns true on success. - + $m->e($expr) A synonym for $m->evaluate(). - + $m->vars() Returns an associative array of all user-defined variables and values. - + $m->funcs() Returns an array of all user-defined functions. @@ -63,7 +62,7 @@ LICENSE Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + 1 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright @@ -72,7 +71,7 @@ LICENSE 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. - + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -86,13 +85,13 @@ LICENSE POSSIBILITY OF SUCH DAMAGE. */ -class EvalMath { - +class EvalMath +{ var $suppress_errors = false; var $last_error = null; var $last_error_code = null; - - var $v = array('e'=>2.71,'pi'=>3.14); // variables (and constants) + + var $v = array('e'=>2.71,'pi'=>3.14159); // variables (and constants) var $f = array(); // user-defined functions var $vb = array('e', 'pi'); // constants var $fb = array( // built-in functions @@ -100,17 +99,20 @@ class EvalMath { 'cos','cosh','arccos','acos','arccosh','acosh', 'tan','tanh','arctan','atan','arctanh','atanh', 'sqrt','abs','ln','log'); - - function EvalMath() { + + /** + * Constructor + */ + function __construct() { // make the variables a little more accurate $this->v['pi'] = pi(); $this->v['e'] = exp(1); } - + function e($expr) { return $this->evaluate($expr); } - + function evaluate($expr) { $this->last_error = null; $this->last_error_code = null; @@ -134,7 +136,8 @@ class EvalMath { } $args = explode(",", preg_replace("/\s+/", "", $matches[2])); // get the arguments if (($stack = $this->nfx($matches[3])) === false) return false; // see if it can be converted to postfix - for ($i = 0; $iv)) { @@ -151,14 +154,14 @@ class EvalMath { return $this->pfx($this->nfx($expr)); // straight up evaluation, woo } } - + function vars() { $output = $this->v; unset($output['pi']); unset($output['e']); return $output; } - + function funcs() { $output = array(); foreach ($this->f as $fnn=>$dat) @@ -170,23 +173,23 @@ class EvalMath { // Convert infix to postfix notation function nfx($expr) { - + $index = 0; - $stack = new EvalMathStack; + $stack = new EvalMathStack(); $output = array(); // postfix form of expression, to be passed to pfx() $expr = trim(strtolower($expr)); - + $ops = array('+', '-', '*', '/', '^', '_'); - $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1); // right-associative operator? + $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1); // right-associative operator? $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2); // operator precedence - + $expecting_op = false; // we use this in syntax-checking the expression // and determining when a - is a negation - + if (preg_match("/[^\w\s+*^\/()\.,-]/", $expr, $matches)) { // make sure the characters are all good return $this->trigger(4, "illegal character '{$matches[0]}'", $matches[0]); } - + while(1) { // 1 Infinite Loop ;) $op = substr($expr, $index, 1); // get the first character at the current index // find out if we're currently at the beginning of a number/variable/function/parenthesis/operand @@ -195,7 +198,7 @@ class EvalMath { if ($op == '-' and !$expecting_op) { // is it a negation instead of a minus? $stack->push('_'); // put a negation on the stack $index++; - } elseif ($op == '_') { // we have to explicitly deny this, because it's legal on the stack + } elseif ($op == '_') { // we have to explicitly deny this, because it's legal on the stack return $this->trigger(4, "illegal character '_'", "_"); // but not in the input expression //=============== } elseif ((in_array($op, $ops) or $ex) and $expecting_op) { // are we putting an operator on the stack? @@ -233,7 +236,7 @@ class EvalMath { $index++; //=============== } elseif ($op == ',' and $expecting_op) { // did we just finish a function argument? - while (($o2 = $stack->pop()) != '(') { + while (($o2 = $stack->pop()) != '(') { if (is_null($o2)) return $this->trigger(5, "unexpected ','", ","); // oops, never had a ( else $output[] = $o2; // pop the argument expression stuff and push onto the output } @@ -282,11 +285,11 @@ class EvalMath { break; } } - while (substr($expr, $index, 1) == ' ') { // step the index past whitespace (pretty much turns whitespace + while (substr($expr, $index, 1) == ' ') { // step the index past whitespace (pretty much turns whitespace $index++; // into implicit multiplication if no operator is there) } - - } + + } while (!is_null($op = $stack->pop())) { // pop everything off the stack and push onto output if ($op == '(') return $this->trigger(11, "expecting ')'", ")"); // if there are (s on the stack, ()s were unbalanced $output[] = $op; @@ -296,11 +299,11 @@ class EvalMath { // evaluate postfix notation function pfx($tokens, $vars = array()) { - + if ($tokens == false) return false; - - $stack = new EvalMathStack; - + + $stack = new EvalMathStack(); + foreach ($tokens as $token) { // nice and easy // if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on if (in_array($token, array('+', '-', '*', '/', '^'))) { @@ -355,7 +358,7 @@ class EvalMath { if ($stack->count != 1) return $this->trigger(18, "internal error"); return $stack->pop(); } - + // trigger an error, but nicely, if need be function trigger($code, $msg, $info = null) { $this->last_error = $msg; @@ -365,17 +368,19 @@ class EvalMath { } } -// for internal use -class EvalMathStack { - +/** + * Class for internal use + */ +class EvalMathStack +{ var $stack = array(); var $count = 0; - + function push($val) { $this->stack[$this->count] = $val; $this->count++; } - + function pop() { if ($this->count > 0) { $this->count--; @@ -383,7 +388,7 @@ class EvalMathStack { } return null; } - + function last($n=1) { if (isset($this->stack[$this->count-$n])) { return $this->stack[$this->count-$n]; diff --git a/htdocs/includes/sabre/autoload.php b/htdocs/includes/sabre/autoload.php new file mode 100644 index 00000000000..d8ccc5f35c3 --- /dev/null +++ b/htdocs/includes/sabre/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/htdocs/includes/sabre/composer/LICENSE b/htdocs/includes/sabre/composer/LICENSE new file mode 100644 index 00000000000..1a28124886d --- /dev/null +++ b/htdocs/includes/sabre/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) 2016 Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/htdocs/includes/sabre/composer/autoload_classmap.php b/htdocs/includes/sabre/composer/autoload_classmap.php new file mode 100644 index 00000000000..7a91153b0d8 --- /dev/null +++ b/htdocs/includes/sabre/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/sabre/uri/lib/functions.php', + '3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php', + '93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php', + '2b9d0f43f9552984cfa82fee95491826' => $vendorDir . '/sabre/event/lib/coroutine.php', + 'd81bab31d3feb45bfe2f283ea3c8fdf7' => $vendorDir . '/sabre/event/lib/Loop/functions.php', + 'a1cce3d26cc15c00fcd0b3354bd72c88' => $vendorDir . '/sabre/event/lib/Promise/functions.php', + 'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php', +); diff --git a/htdocs/includes/sabre/composer/autoload_namespaces.php b/htdocs/includes/sabre/composer/autoload_namespaces.php new file mode 100644 index 00000000000..b7fc0125dbc --- /dev/null +++ b/htdocs/includes/sabre/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/sabre/xml/lib'), + 'Sabre\\VObject\\' => array($vendorDir . '/sabre/vobject/lib'), + 'Sabre\\Uri\\' => array($vendorDir . '/sabre/uri/lib'), + 'Sabre\\HTTP\\' => array($vendorDir . '/sabre/http/lib'), + 'Sabre\\Event\\' => array($vendorDir . '/sabre/event/lib'), + 'Sabre\\DAV\\' => array($vendorDir . '/sabre/dav/lib/DAV'), + 'Sabre\\DAVACL\\' => array($vendorDir . '/sabre/dav/lib/DAVACL'), + 'Sabre\\CardDAV\\' => array($vendorDir . '/sabre/dav/lib/CardDAV'), + 'Sabre\\CalDAV\\' => array($vendorDir . '/sabre/dav/lib/CalDAV'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), +); diff --git a/htdocs/includes/sabre/composer/autoload_real.php b/htdocs/includes/sabre/composer/autoload_real.php new file mode 100644 index 00000000000..bb9949a91c4 --- /dev/null +++ b/htdocs/includes/sabre/composer/autoload_real.php @@ -0,0 +1,70 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit60b9ac98a8448ede6c445b0fd4bd31e0::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit60b9ac98a8448ede6c445b0fd4bd31e0::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire60b9ac98a8448ede6c445b0fd4bd31e0($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire60b9ac98a8448ede6c445b0fd4bd31e0($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/htdocs/includes/sabre/composer/autoload_static.php b/htdocs/includes/sabre/composer/autoload_static.php new file mode 100644 index 00000000000..d76d2c4c34b --- /dev/null +++ b/htdocs/includes/sabre/composer/autoload_static.php @@ -0,0 +1,89 @@ + __DIR__ . '/..' . '/sabre/uri/lib/functions.php', + '3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php', + '93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php', + '2b9d0f43f9552984cfa82fee95491826' => __DIR__ . '/..' . '/sabre/event/lib/coroutine.php', + 'd81bab31d3feb45bfe2f283ea3c8fdf7' => __DIR__ . '/..' . '/sabre/event/lib/Loop/functions.php', + 'a1cce3d26cc15c00fcd0b3354bd72c88' => __DIR__ . '/..' . '/sabre/event/lib/Promise/functions.php', + 'ebdb698ed4152ae445614b69b5e4bb6a' => __DIR__ . '/..' . '/sabre/http/lib/functions.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'Sabre\\Xml\\' => 10, + 'Sabre\\VObject\\' => 14, + 'Sabre\\Uri\\' => 10, + 'Sabre\\HTTP\\' => 11, + 'Sabre\\Event\\' => 12, + 'Sabre\\DAV\\' => 10, + 'Sabre\\DAVACL\\' => 13, + 'Sabre\\CardDAV\\' => 14, + 'Sabre\\CalDAV\\' => 13, + ), + 'P' => + array ( + 'Psr\\Log\\' => 8, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Sabre\\Xml\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/xml/lib', + ), + 'Sabre\\VObject\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/vobject/lib', + ), + 'Sabre\\Uri\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/uri/lib', + ), + 'Sabre\\HTTP\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/http/lib', + ), + 'Sabre\\Event\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/event/lib', + ), + 'Sabre\\DAV\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/dav/lib/DAV', + ), + 'Sabre\\DAVACL\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/dav/lib/DAVACL', + ), + 'Sabre\\CardDAV\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/dav/lib/CardDAV', + ), + 'Sabre\\CalDAV\\' => + array ( + 0 => __DIR__ . '/..' . '/sabre/dav/lib/CalDAV', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit60b9ac98a8448ede6c445b0fd4bd31e0::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit60b9ac98a8448ede6c445b0fd4bd31e0::$prefixDirsPsr4; + + }, null, ClassLoader::class); + } +} diff --git a/htdocs/includes/sabre/composer/installed.json b/htdocs/includes/sabre/composer/installed.json new file mode 100644 index 00000000000..c226d3ef4a6 --- /dev/null +++ b/htdocs/includes/sabre/composer/installed.json @@ -0,0 +1,470 @@ +[ + { + "name": "sabre/uri", + "version": "1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-uri.git", + "reference": "8545a3335f741d4b7700bb14efb41b4c03775dcd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/8545a3335f741d4b7700bb14efb41b4c03775dcd", + "reference": "8545a3335f741d4b7700bb14efb41b4c03775dcd", + "shasum": "" + }, + "require": { + "php": ">=5.4.7" + }, + "require-dev": { + "phpunit/phpunit": "*", + "sabre/cs": "~1.0.0" + }, + "time": "2016-12-07T01:17:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Sabre\\Uri\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "Functions for making sense out of URIs.", + "homepage": "http://sabre.io/uri/", + "keywords": [ + "rfc3986", + "uri", + "url" + ] + }, + { + "name": "sabre/xml", + "version": "1.5.0", + "version_normalized": "1.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-xml.git", + "reference": "59b20e5bbace9912607481634f97d05a776ffca7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-xml/zipball/59b20e5bbace9912607481634f97d05a776ffca7", + "reference": "59b20e5bbace9912607481634f97d05a776ffca7", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "lib-libxml": ">=2.6.20", + "php": ">=5.5.5", + "sabre/uri": ">=1.0,<3.0.0" + }, + "require-dev": { + "phpunit/phpunit": "*", + "sabre/cs": "~1.0.0" + }, + "time": "2016-10-09T22:57:52+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sabre\\Xml\\": "lib/" + }, + "files": [ + "lib/Deserializer/functions.php", + "lib/Serializer/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + }, + { + "name": "Markus Staab", + "email": "markus.staab@redaxo.de", + "role": "Developer" + } + ], + "description": "sabre/xml is an XML library that you may not hate.", + "homepage": "https://sabre.io/xml/", + "keywords": [ + "XMLReader", + "XMLWriter", + "dom", + "xml" + ] + }, + { + "name": "sabre/vobject", + "version": "4.1.2", + "version_normalized": "4.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-vobject.git", + "reference": "d0fde2fafa2a3dad1f559c2d1c2591d4fd75ae3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-vobject/zipball/d0fde2fafa2a3dad1f559c2d1c2591d4fd75ae3c", + "reference": "d0fde2fafa2a3dad1f559c2d1c2591d4fd75ae3c", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.5", + "sabre/xml": ">=1.5 <3.0" + }, + "require-dev": { + "phpunit/phpunit": "*", + "sabre/cs": "^1.0.0" + }, + "suggest": { + "hoa/bench": "If you would like to run the benchmark scripts" + }, + "time": "2016-12-06T04:14:09+00:00", + "bin": [ + "bin/vobject", + "bin/generate_vcards" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sabre\\VObject\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + }, + { + "name": "Dominik Tobschall", + "email": "dominik@fruux.com", + "homepage": "http://tobschall.de/", + "role": "Developer" + }, + { + "name": "Ivan Enderlin", + "email": "ivan.enderlin@hoa-project.net", + "homepage": "http://mnt.io/", + "role": "Developer" + } + ], + "description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects", + "homepage": "http://sabre.io/vobject/", + "keywords": [ + "availability", + "freebusy", + "iCalendar", + "ical", + "ics", + "jCal", + "jCard", + "recurrence", + "rfc2425", + "rfc2426", + "rfc2739", + "rfc4770", + "rfc5545", + "rfc5546", + "rfc6321", + "rfc6350", + "rfc6351", + "rfc6474", + "rfc6638", + "rfc6715", + "rfc6868", + "vCalendar", + "vCard", + "vcf", + "xCal", + "xCard" + ] + }, + { + "name": "sabre/event", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-event.git", + "reference": "831d586f5a442dceacdcf5e9c4c36a4db99a3534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-event/zipball/831d586f5a442dceacdcf5e9c4c36a4db99a3534", + "reference": "831d586f5a442dceacdcf5e9c4c36a4db99a3534", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "*", + "sabre/cs": "~0.0.4" + }, + "time": "2015-11-05T20:14:39+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sabre\\Event\\": "lib/" + }, + "files": [ + "lib/coroutine.php", + "lib/Loop/functions.php", + "lib/Promise/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "sabre/event is a library for lightweight event-based programming", + "homepage": "http://sabre.io/event/", + "keywords": [ + "EventEmitter", + "async", + "events", + "hooks", + "plugin", + "promise", + "signal" + ] + }, + { + "name": "sabre/http", + "version": "4.2.2", + "version_normalized": "4.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-http.git", + "reference": "dd50e7260356f4599d40270826f9548b23efa204" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-http/zipball/dd50e7260356f4599d40270826f9548b23efa204", + "reference": "dd50e7260356f4599d40270826f9548b23efa204", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-mbstring": "*", + "php": ">=5.4", + "sabre/event": ">=1.0.0,<4.0.0", + "sabre/uri": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.3", + "sabre/cs": "~0.0.1" + }, + "suggest": { + "ext-curl": " to make http requests with the Client class" + }, + "time": "2017-01-02T19:38:42+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Sabre\\HTTP\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "The sabre/http library provides utilities for dealing with http requests and responses. ", + "homepage": "https://github.com/fruux/sabre-http", + "keywords": [ + "http" + ] + }, + { + "name": "psr/log", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-10-10T12:19:37+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ] + }, + { + "name": "sabre/dav", + "version": "3.2.2", + "version_normalized": "3.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-dav.git", + "reference": "e987775e619728f12205606c9cc3ee565ffb1516" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-dav/zipball/e987775e619728f12205606c9cc3ee565ffb1516", + "reference": "e987775e619728f12205606c9cc3ee565ffb1516", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-dom": "*", + "ext-iconv": "*", + "ext-mbstring": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "ext-spl": "*", + "lib-libxml": ">=2.7.0", + "php": ">=5.5.0", + "psr/log": "^1.0", + "sabre/event": ">=2.0.0, <4.0.0", + "sabre/http": "^4.2.1", + "sabre/uri": "^1.0.1", + "sabre/vobject": "^4.1.0", + "sabre/xml": "^1.4.0" + }, + "require-dev": { + "evert/phpdoc-md": "~0.1.0", + "monolog/monolog": "^1.18", + "phpunit/phpunit": "> 4.8, <6.0.0", + "sabre/cs": "^1.0.0" + }, + "suggest": { + "ext-curl": "*", + "ext-pdo": "*" + }, + "time": "2017-02-15T03:06:08+00:00", + "bin": [ + "bin/sabredav", + "bin/naturalselection" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sabre\\DAV\\": "lib/DAV/", + "Sabre\\DAVACL\\": "lib/DAVACL/", + "Sabre\\CalDAV\\": "lib/CalDAV/", + "Sabre\\CardDAV\\": "lib/CardDAV/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "WebDAV Framework for PHP", + "homepage": "http://sabre.io/", + "keywords": [ + "CalDAV", + "CardDAV", + "WebDAV", + "framework", + "iCalendar" + ] + } +] diff --git a/htdocs/includes/sabre/psr/log/.gitignore b/htdocs/includes/sabre/psr/log/.gitignore new file mode 100644 index 00000000000..22d0d82f809 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/htdocs/includes/sabre/psr/log/LICENSE b/htdocs/includes/sabre/psr/log/LICENSE new file mode 100644 index 00000000000..474c952b4b5 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/htdocs/includes/sabre/psr/log/Psr/Log/AbstractLogger.php b/htdocs/includes/sabre/psr/log/Psr/Log/AbstractLogger.php new file mode 100644 index 00000000000..90e721af2d3 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/Psr/Log/AbstractLogger.php @@ -0,0 +1,128 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/htdocs/includes/sabre/psr/log/Psr/Log/InvalidArgumentException.php b/htdocs/includes/sabre/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 00000000000..67f852d1dbc --- /dev/null +++ b/htdocs/includes/sabre/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/htdocs/includes/sabre/psr/log/Psr/Log/LoggerInterface.php b/htdocs/includes/sabre/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 00000000000..5ea72438b56 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,123 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/htdocs/includes/sabre/psr/log/Psr/Log/NullLogger.php b/htdocs/includes/sabre/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 00000000000..d8cd682c8f9 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,28 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/htdocs/includes/sabre/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/htdocs/includes/sabre/psr/log/Psr/Log/Test/LoggerInterfaceTest.php new file mode 100644 index 00000000000..a0391a52b8f --- /dev/null +++ b/htdocs/includes/sabre/psr/log/Psr/Log/Test/LoggerInterfaceTest.php @@ -0,0 +1,140 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + $level.' message of level '.$level.' with context: Bob', + $level.' message of level '.$level.' with context: Bob', + ); + $this->assertEquals($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + } + $dummy->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->getLogger()->warning($dummy); + + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest), + 'object' => new \DateTime, + 'resource' => fopen('php://memory', 'r'), + ); + + $this->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + 'warning Random message', + 'critical Uncaught Exception!' + ); + $this->assertEquals($expected, $this->getLogs()); + } +} + +class DummyTest +{ + public function __toString() + { + } +} diff --git a/htdocs/includes/sabre/psr/log/README.md b/htdocs/includes/sabre/psr/log/README.md new file mode 100644 index 00000000000..574bc1cb2a8 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/README.md @@ -0,0 +1,45 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/htdocs/includes/sabre/psr/log/composer.json b/htdocs/includes/sabre/psr/log/composer.json new file mode 100644 index 00000000000..87934d707e7 --- /dev/null +++ b/htdocs/includes/sabre/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/htdocs/includes/sabre/sabre/dav/.gitignore b/htdocs/includes/sabre/sabre/dav/.gitignore new file mode 100644 index 00000000000..6cf24588320 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/.gitignore @@ -0,0 +1,43 @@ +# Unit tests +tests/temp +tests/.sabredav +tests/cov + +# Custom settings for tests +tests/config.user.php + +# ViM +*.swp + +# Composer +composer.lock +vendor + +# Composer binaries +bin/phing +bin/phpunit +bin/vobject +bin/generate_vcards +bin/phpdocmd +bin/phpunit +bin/php-cs-fixer +bin/sabre-cs-fixer + +# Assuming every .php file in the root is for testing +/*.php + +# Other testing stuff +/tmpdata +/data +/public + +# Build +build +build.properties + +# Docs +docs/api +docs/wikidocs + +# Mac +.DS_Store diff --git a/htdocs/includes/sabre/sabre/dav/.travis.yml b/htdocs/includes/sabre/sabre/dav/.travis.yml new file mode 100644 index 00000000000..85637048ad4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/.travis.yml @@ -0,0 +1,36 @@ +language: php +php: + - 5.5 + - 5.6 + - 7.0 + - 7.1 + + +env: + matrix: + - LOWEST_DEPS="" TEST_DEPS="" + - LOWEST_DEPS="--prefer-lowest" TEST_DEPS="tests/Sabre/" + +services: + - mysql + - postgresql + +sudo: false + +before_script: + - mysql -e 'create database sabredav_test' + - psql -c "create database sabredav_test" -U postgres + - psql -c "create user sabredav with PASSWORD 'sabredav';GRANT ALL PRIVILEGES ON DATABASE sabredav_test TO sabredav" -U postgres + # - composer self-update + - composer update --prefer-dist $LOWEST_DEPS + +# addons: +# postgresql: "9.5" + +script: + - ./bin/phpunit --configuration tests/phpunit.xml.dist $TEST_DEPS + - ./bin/sabre-cs-fixer fix . --dry-run --diff + +cache: + directories: + - $HOME/.composer/cache diff --git a/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md b/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md new file mode 100644 index 00000000000..425ee19ba85 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md @@ -0,0 +1,87 @@ +Contributing to sabre projects +============================== + +Want to contribute to sabre/dav? Here are some guidelines to ensure your patch +gets accepted. + + +Building a new feature? Contact us first +---------------------------------------- + +We may not want to accept every feature that comes our way. Sometimes +features are out of scope for our projects. + +We don't want to waste your time, so by having a quick chat with us first, +you may find out quickly if the feature makes sense to us, and we can give +some tips on how to best build the feature. + +If we don't accept the feature, it could be for a number of reasons. For +instance, we've rejected features in the past because we felt uncomfortable +assuming responsibility for maintaining the feature. + +In those cases, it's often possible to keep the feature separate from the +sabre projects. sabre/dav for instance has a plugin system, and there's no +reason the feature can't live in a project you own. + +In that case, definitely let us know about your plugin as well, so we can +feature it on [sabre.io][4]. + +We are often on [IRC][5], in the #sabredav channel on freenode. If there's +no one there, post a message on the [mailing list][6]. + + +Coding standards +---------------- + +sabre projects follow: + +1. [PSR-1][1] +2. [PSR-4][2] + +sabre projects don't follow [PSR-2][3]. + +In addition to that, here's a list of basic rules: + +1. PHP 5.4 array syntax must be used every where. This means you use `[` and + `]` instead of `array(` and `)`. +2. Use PHP namespaces everywhere. +3. Use 4 spaces for indentation. +4. Try to keep your lines under 80 characters. This is not a hard rule, as + there are many places in the source where it felt more sensibile to not + do so. In particular, function declarations are never split over multiple + lines. +5. Opening braces (`{`) are _always_ on the same line as the `class`, `if`, + `function`, etc. they belong to. +6. `public` must be omitted from method declarations. It must also be omitted + for static properties. +7. All files should use unix-line endings (`\n`). +8. Files must omit the closing php tag (`?>`). +9. `true`, `false` and `null` are always lower-case. +10. Constants are always upper-case. +11. Any of the rules stated before may be broken where this is the pragmatic + thing to do. + + +Unit test requirements +---------------------- + +Any new feature or change requires unittests. We use [PHPUnit][7] for all our +tests. + +Adding unittests will greatly increase the likelyhood of us quickly accepting +your pull request. If unittests are not included though for whatever reason, +we'd still _love_ your pull request. + +We may have to write the tests ourselves, which can increase the time it takes +to accept the patch, but we'd still really like your contribution! + +To run the testsuite jump into the directory `cd tests` and trigger `phpunit`. +Make sure you did a `composer install` beforehand. + +[1]: http://www.php-fig.org/psr/psr-1/ +[2]: http://www.php-fig.org/psr/psr-4/ +[3]: http://www.php-fig.org/psr/psr-2/ +[4]: http://sabre.io/ +[5]: irc://freenode.net/#sabredav +[6]: http://groups.google.com/group/sabredav-discuss +[7]: http://phpunit.de/ diff --git a/htdocs/includes/sabre/sabre/dav/bin/build.php b/htdocs/includes/sabre/sabre/dav/bin/build.php new file mode 100644 index 00000000000..c4ba2094161 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/build.php @@ -0,0 +1,177 @@ +#!/usr/bin/env php + [ + 'init', 'test', 'clean', + ], + 'markrelease' => [ + 'init', 'test', 'clean', + ], + 'clean' => [], + 'test' => [ + 'composerupdate', + ], + 'init' => [], + 'composerupdate' => [], + ]; + +$default = 'buildzip'; + +$baseDir = __DIR__ . '/../'; +chdir($baseDir); + +$currentTask = $default; +if ($argc > 1) $currentTask = $argv[1]; +$version = null; +if ($argc > 2) $version = $argv[2]; + +if (!isset($tasks[$currentTask])) { + echo "Task not found: ", $currentTask, "\n"; + die(1); +} + +// Creating the dependency graph +$newTaskList = []; +$oldTaskList = [$currentTask => true]; + +while (count($oldTaskList) > 0) { + + foreach ($oldTaskList as $task => $foo) { + + if (!isset($tasks[$task])) { + echo "Dependency not found: " . $task, "\n"; + die(1); + } + $dependencies = $tasks[$task]; + + $fullFilled = true; + foreach ($dependencies as $dependency) { + if (isset($newTaskList[$dependency])) { + // Already in the fulfilled task list. + continue; + } else { + $oldTaskList[$dependency] = true; + $fullFilled = false; + } + + } + if ($fullFilled) { + unset($oldTaskList[$task]); + $newTaskList[$task] = 1; + } + + } + +} + +foreach (array_keys($newTaskList) as $task) { + + echo "task: " . $task, "\n"; + call_user_func($task); + echo "\n"; + +} + +function init() { + + global $version; + if (!$version) { + include __DIR__ . '/../vendor/autoload.php'; + $version = Sabre\DAV\Version::VERSION; + } + + echo " Building sabre/dav " . $version, "\n"; + +} + +function clean() { + + global $baseDir; + echo " Removing build files\n"; + $outputDir = $baseDir . '/build/SabreDAV'; + if (is_dir($outputDir)) { + system('rm -r ' . $baseDir . '/build/SabreDAV'); + } + +} + +function composerupdate() { + + global $baseDir; + echo " Updating composer packages to latest version\n\n"; + system('cd ' . $baseDir . '; composer update'); +} + +function test() { + + global $baseDir; + + echo " Running all unittests.\n"; + echo " This may take a while.\n\n"; + system(__DIR__ . '/phpunit --configuration ' . $baseDir . '/tests/phpunit.xml.dist --stop-on-failure', $code); + if ($code != 0) { + echo "PHPUnit reported error code $code\n"; + die(1); + } + +} + +function buildzip() { + + global $baseDir, $version; + echo " Generating composer.json\n"; + + $input = json_decode(file_get_contents(__DIR__ . '/../composer.json'), true); + $newComposer = [ + "require" => $input['require'], + "config" => [ + "bin-dir" => "./bin", + ], + "prefer-stable" => true, + "minimum-stability" => "alpha", + ]; + unset( + $newComposer['require']['sabre/vobject'], + $newComposer['require']['sabre/http'], + $newComposer['require']['sabre/uri'], + $newComposer['require']['sabre/event'] + ); + $newComposer['require']['sabre/dav'] = $version; + mkdir('build/SabreDAV'); + file_put_contents('build/SabreDAV/composer.json', json_encode($newComposer, JSON_PRETTY_PRINT)); + + echo " Downloading dependencies\n"; + system("cd build/SabreDAV; composer install -n", $code); + if ($code !== 0) { + echo "Composer reported error code $code\n"; + die(1); + } + + echo " Removing pointless files\n"; + unlink('build/SabreDAV/composer.json'); + unlink('build/SabreDAV/composer.lock'); + + echo " Moving important files to the root of the project\n"; + + $fileNames = [ + 'CHANGELOG.md', + 'LICENSE', + 'README.md', + 'examples', + ]; + foreach ($fileNames as $fileName) { + echo " $fileName\n"; + rename('build/SabreDAV/vendor/sabre/dav/' . $fileName, 'build/SabreDAV/' . $fileName); + } + + // + + echo "\n"; + echo "Zipping the sabredav distribution\n\n"; + system('cd build; zip -qr sabredav-' . $version . '.zip SabreDAV'); + + echo "Done."; + +} diff --git a/htdocs/includes/sabre/sabre/dav/bin/googlecode_upload.py b/htdocs/includes/sabre/sabre/dav/bin/googlecode_upload.py new file mode 100644 index 00000000000..caafd5dedac --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/googlecode_upload.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# +# Copyright 2006, 2007 Google Inc. All Rights Reserved. +# Author: danderson@google.com (David Anderson) +# +# Script for uploading files to a Google Code project. +# +# This is intended to be both a useful script for people who want to +# streamline project uploads and a reference implementation for +# uploading files to Google Code projects. +# +# To upload a file to Google Code, you need to provide a path to the +# file on your local machine, a small summary of what the file is, a +# project name, and a valid account that is a member or owner of that +# project. You can optionally provide a list of labels that apply to +# the file. The file will be uploaded under the same name that it has +# in your local filesystem (that is, the "basename" or last path +# component). Run the script with '--help' to get the exact syntax +# and available options. +# +# Note that the upload script requests that you enter your +# googlecode.com password. This is NOT your Gmail account password! +# This is the password you use on googlecode.com for committing to +# Subversion and uploading files. You can find your password by going +# to http://code.google.com/hosting/settings when logged in with your +# Gmail account. If you have already committed to your project's +# Subversion repository, the script will automatically retrieve your +# credentials from there (unless disabled, see the output of '--help' +# for details). +# +# If you are looking at this script as a reference for implementing +# your own Google Code file uploader, then you should take a look at +# the upload() function, which is the meat of the uploader. You +# basically need to build a multipart/form-data POST request with the +# right fields and send it to https://PROJECT.googlecode.com/files . +# Authenticate the request using HTTP Basic authentication, as is +# shown below. +# +# Licensed under the terms of the Apache Software License 2.0: +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Questions, comments, feature requests and patches are most welcome. +# Please direct all of these to the Google Code users group: +# http://groups.google.com/group/google-code-hosting + +"""Google Code file uploader script. +""" + +__author__ = 'danderson@google.com (David Anderson)' + +import httplib +import os.path +import optparse +import getpass +import base64 +import sys + + +def upload(file, project_name, user_name, password, summary, labels=None): + """Upload a file to a Google Code project's file server. + + Args: + file: The local path to the file. + project_name: The name of your project on Google Code. + user_name: Your Google account name. + password: The googlecode.com password for your account. + Note that this is NOT your global Google Account password! + summary: A small description for the file. + labels: an optional list of label strings with which to tag the file. + + Returns: a tuple: + http_status: 201 if the upload succeeded, something else if an + error occurred. + http_reason: The human-readable string associated with http_status + file_url: If the upload succeeded, the URL of the file on Google + Code, None otherwise. + """ + # The login is the user part of user@gmail.com. If the login provided + # is in the full user@domain form, strip it down. + if user_name.endswith('@gmail.com'): + user_name = user_name[:user_name.index('@gmail.com')] + + form_fields = [('summary', summary)] + if labels is not None: + form_fields.extend([('label', l.strip()) for l in labels]) + + content_type, body = encode_upload_request(form_fields, file) + + upload_host = '%s.googlecode.com' % project_name + upload_uri = '/files' + auth_token = base64.b64encode('%s:%s'% (user_name, password)) + headers = { + 'Authorization': 'Basic %s' % auth_token, + 'User-Agent': 'Googlecode.com uploader v0.9.4', + 'Content-Type': content_type, + } + + server = httplib.HTTPSConnection(upload_host) + server.request('POST', upload_uri, body, headers) + resp = server.getresponse() + server.close() + + if resp.status == 201: + location = resp.getheader('Location', None) + else: + location = None + return resp.status, resp.reason, location + + +def encode_upload_request(fields, file_path): + """Encode the given fields and file into a multipart form body. + + fields is a sequence of (name, value) pairs. file is the path of + the file to upload. The file will be uploaded to Google Code with + the same file name. + + Returns: (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla' + CRLF = '\r\n' + + body = [] + + # Add the metadata about the upload first + for key, value in fields: + body.extend( + ['--' + BOUNDARY, + 'Content-Disposition: form-data; name="%s"' % key, + '', + value, + ]) + + # Now add the file itself + file_name = os.path.basename(file_path) + f = open(file_path, 'rb') + file_content = f.read() + f.close() + + body.extend( + ['--' + BOUNDARY, + 'Content-Disposition: form-data; name="filename"; filename="%s"' + % file_name, + # The upload server determines the mime-type, no need to set it. + 'Content-Type: application/octet-stream', + '', + file_content, + ]) + + # Finalize the form body + body.extend(['--' + BOUNDARY + '--', '']) + + return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body) + + +def upload_find_auth(file_path, project_name, summary, labels=None, + user_name=None, password=None, tries=3): + """Find credentials and upload a file to a Google Code project's file server. + + file_path, project_name, summary, and labels are passed as-is to upload. + + Args: + file_path: The local path to the file. + project_name: The name of your project on Google Code. + summary: A small description for the file. + labels: an optional list of label strings with which to tag the file. + config_dir: Path to Subversion configuration directory, 'none', or None. + user_name: Your Google account name. + tries: How many attempts to make. + """ + + while tries > 0: + if user_name is None: + # Read username if not specified or loaded from svn config, or on + # subsequent tries. + sys.stdout.write('Please enter your googlecode.com username: ') + sys.stdout.flush() + user_name = sys.stdin.readline().rstrip() + if password is None: + # Read password if not loaded from svn config, or on subsequent tries. + print 'Please enter your googlecode.com password.' + print '** Note that this is NOT your Gmail account password! **' + print 'It is the password you use to access Subversion repositories,' + print 'and can be found here: http://code.google.com/hosting/settings' + password = getpass.getpass() + + status, reason, url = upload(file_path, project_name, user_name, password, + summary, labels) + # Returns 403 Forbidden instead of 401 Unauthorized for bad + # credentials as of 2007-07-17. + if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]: + # Rest for another try. + user_name = password = None + tries = tries - 1 + else: + # We're done. + break + + return status, reason, url + + +def main(): + parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY ' + '-p PROJECT [options] FILE') + parser.add_option('-s', '--summary', dest='summary', + help='Short description of the file') + parser.add_option('-p', '--project', dest='project', + help='Google Code project name') + parser.add_option('-u', '--user', dest='user', + help='Your Google Code username') + parser.add_option('-w', '--password', dest='password', + help='Your Google Code password') + parser.add_option('-l', '--labels', dest='labels', + help='An optional list of comma-separated labels to attach ' + 'to the file') + + options, args = parser.parse_args() + + if not options.summary: + parser.error('File summary is missing.') + elif not options.project: + parser.error('Project name is missing.') + elif len(args) < 1: + parser.error('File to upload not provided.') + elif len(args) > 1: + parser.error('Only one file may be specified.') + + file_path = args[0] + + if options.labels: + labels = options.labels.split(',') + else: + labels = None + + status, reason, url = upload_find_auth(file_path, options.project, + options.summary, labels, + options.user, options.password) + if url: + print 'The file was uploaded successfully.' + print 'URL: %s' % url + return 0 + else: + print 'An error occurred. Your file was not uploaded.' + print 'Google Code upload server said: %s (%s)' % (reason, status) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php new file mode 100644 index 00000000000..77236804f35 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php @@ -0,0 +1,453 @@ +#!/usr/bin/env php +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + +switch ($driver) { + + case 'mysql' : + echo "Detected MySQL.\n"; + break; + case 'sqlite' : + echo "Detected SQLite.\n"; + break; + default : + echo "Error: unsupported driver: " . $driver . "\n"; + die(-1); +} + +foreach (['calendar', 'addressbook'] as $itemType) { + + $tableName = $itemType . 's'; + $tableNameOld = $tableName . '_old'; + $changesTable = $itemType . 'changes'; + + echo "Upgrading '$tableName'\n"; + + // The only cross-db way to do this, is to just fetch a single record. + $row = $pdo->query("SELECT * FROM $tableName LIMIT 1")->fetch(); + + if (!$row) { + + echo "No records were found in the '$tableName' table.\n"; + echo "\n"; + echo "We're going to rename the old table to $tableNameOld (just in case).\n"; + echo "and re-create the new table.\n"; + + switch ($driver) { + + case 'mysql' : + $pdo->exec("RENAME TABLE $tableName TO $tableNameOld"); + switch ($itemType) { + case 'calendar' : + $pdo->exec(" + CREATE TABLE calendars ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(100), + displayname VARCHAR(100), + uri VARCHAR(200), + synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1', + description TEXT, + calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0', + calendarcolor VARCHAR(10), + timezone TEXT, + components VARCHAR(20), + transparent TINYINT(1) NOT NULL DEFAULT '0', + UNIQUE(principaluri, uri) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + "); + break; + case 'addressbook' : + $pdo->exec(" + CREATE TABLE addressbooks ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(255), + displayname VARCHAR(255), + uri VARCHAR(200), + description TEXT, + synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1', + UNIQUE(principaluri, uri) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + "); + break; + } + break; + + case 'sqlite' : + + $pdo->exec("ALTER TABLE $tableName RENAME TO $tableNameOld"); + + switch ($itemType) { + case 'calendar' : + $pdo->exec(" + CREATE TABLE calendars ( + id integer primary key asc, + principaluri text, + displayname text, + uri text, + synctoken integer, + description text, + calendarorder integer, + calendarcolor text, + timezone text, + components text, + transparent bool + ); + "); + break; + case 'addressbook' : + $pdo->exec(" + CREATE TABLE addressbooks ( + id integer primary key asc, + principaluri text, + displayname text, + uri text, + description text, + synctoken integer + ); + "); + + break; + } + break; + + } + echo "Creation of 2.0 $tableName table is complete\n"; + + } else { + + // Checking if there's a synctoken field already. + if (array_key_exists('synctoken', $row)) { + echo "The 'synctoken' field already exists in the $tableName table.\n"; + echo "It's likely you already upgraded, so we're simply leaving\n"; + echo "the $tableName table alone\n"; + } else { + + echo "1.8 table schema detected\n"; + switch ($driver) { + + case 'mysql' : + $pdo->exec("ALTER TABLE $tableName ADD synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1'"); + $pdo->exec("ALTER TABLE $tableName DROP ctag"); + $pdo->exec("UPDATE $tableName SET synctoken = '1'"); + break; + case 'sqlite' : + $pdo->exec("ALTER TABLE $tableName ADD synctoken integer"); + $pdo->exec("UPDATE $tableName SET synctoken = '1'"); + echo "Note: there's no easy way to remove fields in sqlite.\n"; + echo "The ctag field is no longer used, but it's kept in place\n"; + break; + + } + + echo "Upgraded '$tableName' to 2.0 schema.\n"; + + } + + } + + try { + $pdo->query("SELECT * FROM $changesTable LIMIT 1"); + + echo "'$changesTable' already exists. Assuming that this part of the\n"; + echo "upgrade was already completed.\n"; + + } catch (Exception $e) { + echo "Creating '$changesTable' table.\n"; + + switch ($driver) { + + case 'mysql' : + $pdo->exec(" + CREATE TABLE $changesTable ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + uri VARCHAR(200) NOT NULL, + synctoken INT(11) UNSIGNED NOT NULL, + {$itemType}id INT(11) UNSIGNED NOT NULL, + operation TINYINT(1) NOT NULL, + INDEX {$itemType}id_synctoken ({$itemType}id, synctoken) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + + "); + break; + case 'sqlite' : + $pdo->exec(" + + CREATE TABLE $changesTable ( + id integer primary key asc, + uri text, + synctoken integer, + {$itemType}id integer, + operation bool + ); + + "); + $pdo->exec("CREATE INDEX {$itemType}id_synctoken ON $changesTable ({$itemType}id, synctoken);"); + break; + + } + + } + +} + +try { + $pdo->query("SELECT * FROM calendarsubscriptions LIMIT 1"); + + echo "'calendarsubscriptions' already exists. Assuming that this part of the\n"; + echo "upgrade was already completed.\n"; + +} catch (Exception $e) { + echo "Creating calendarsubscriptions table.\n"; + + switch ($driver) { + + case 'mysql' : + $pdo->exec(" +CREATE TABLE calendarsubscriptions ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + uri VARCHAR(200) NOT NULL, + principaluri VARCHAR(100) NOT NULL, + source TEXT, + displayname VARCHAR(100), + refreshrate VARCHAR(10), + calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0', + calendarcolor VARCHAR(10), + striptodos TINYINT(1) NULL, + stripalarms TINYINT(1) NULL, + stripattachments TINYINT(1) NULL, + lastmodified INT(11) UNSIGNED, + UNIQUE(principaluri, uri) +); + "); + break; + case 'sqlite' : + $pdo->exec(" + +CREATE TABLE calendarsubscriptions ( + id integer primary key asc, + uri text, + principaluri text, + source text, + displayname text, + refreshrate text, + calendarorder integer, + calendarcolor text, + striptodos bool, + stripalarms bool, + stripattachments bool, + lastmodified int +); + "); + + $pdo->exec("CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);"); + break; + + } + +} + +try { + $pdo->query("SELECT * FROM propertystorage LIMIT 1"); + + echo "'propertystorage' already exists. Assuming that this part of the\n"; + echo "upgrade was already completed.\n"; + +} catch (Exception $e) { + echo "Creating propertystorage table.\n"; + + switch ($driver) { + + case 'mysql' : + $pdo->exec(" +CREATE TABLE propertystorage ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + path VARBINARY(1024) NOT NULL, + name VARBINARY(100) NOT NULL, + value MEDIUMBLOB +); + "); + $pdo->exec(" +CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100)); + "); + break; + case 'sqlite' : + $pdo->exec(" +CREATE TABLE propertystorage ( + id integer primary key asc, + path TEXT, + name TEXT, + value TEXT +); + "); + $pdo->exec(" +CREATE UNIQUE INDEX path_property ON propertystorage (path, name); + "); + + break; + + } + +} + +echo "Upgrading cards table to 2.0 schema\n"; + +try { + + $create = false; + $row = $pdo->query("SELECT * FROM cards LIMIT 1")->fetch(); + if (!$row) { + $random = mt_rand(1000, 9999); + echo "There was no data in the cards table, so we're re-creating it\n"; + echo "The old table will be renamed to cards_old$random, just in case.\n"; + + $create = true; + + switch ($driver) { + case 'mysql' : + $pdo->exec("RENAME TABLE cards TO cards_old$random"); + break; + case 'sqlite' : + $pdo->exec("ALTER TABLE cards RENAME TO cards_old$random"); + break; + + } + } + +} catch (Exception $e) { + + echo "Exception while checking cards table. Assuming that the table does not yet exist.\n"; + echo "Debug: ", $e->getMessage(), "\n"; + $create = true; + +} + +if ($create) { + switch ($driver) { + case 'mysql' : + $pdo->exec(" +CREATE TABLE cards ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + addressbookid INT(11) UNSIGNED NOT NULL, + carddata MEDIUMBLOB, + uri VARCHAR(200), + lastmodified INT(11) UNSIGNED, + etag VARBINARY(32), + size INT(11) UNSIGNED NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + + "); + break; + + case 'sqlite' : + + $pdo->exec(" +CREATE TABLE cards ( + id integer primary key asc, + addressbookid integer, + carddata blob, + uri text, + lastmodified integer, + etag text, + size integer +); + "); + break; + + } +} else { + switch ($driver) { + case 'mysql' : + $pdo->exec(" + ALTER TABLE cards + ADD etag VARBINARY(32), + ADD size INT(11) UNSIGNED NOT NULL; + "); + break; + + case 'sqlite' : + + $pdo->exec(" + ALTER TABLE cards ADD etag text; + ALTER TABLE cards ADD size integer; + "); + break; + + } + echo "Reading all old vcards and populating etag and size fields.\n"; + $result = $pdo->query('SELECT id, carddata FROM cards'); + $stmt = $pdo->prepare('UPDATE cards SET etag = ?, size = ? WHERE id = ?'); + while ($row = $result->fetch(\PDO::FETCH_ASSOC)) { + $stmt->execute([ + md5($row['carddata']), + strlen($row['carddata']), + $row['id'] + ]); + } + + +} + +echo "Upgrade to 2.0 schema completed.\n"; diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php new file mode 100644 index 00000000000..c81ee5cca1a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php @@ -0,0 +1,176 @@ +#!/usr/bin/env php +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + +switch ($driver) { + + case 'mysql' : + echo "Detected MySQL.\n"; + break; + case 'sqlite' : + echo "Detected SQLite.\n"; + break; + default : + echo "Error: unsupported driver: " . $driver . "\n"; + die(-1); +} + +echo "Upgrading 'calendarobjects'\n"; +$addUid = false; +try { + $result = $pdo->query('SELECT * FROM calendarobjects LIMIT 1'); + $row = $result->fetch(\PDO::FETCH_ASSOC); + + if (!$row) { + echo "No data in table. Going to try to add the uid field anyway.\n"; + $addUid = true; + } elseif (array_key_exists('uid', $row)) { + echo "uid field exists. Assuming that this part of the migration has\n"; + echo "Already been completed.\n"; + } else { + echo "2.0 schema detected.\n"; + $addUid = true; + } + +} catch (Exception $e) { + echo "Could not find a calendarobjects table. Skipping this part of the\n"; + echo "upgrade.\n"; +} + +if ($addUid) { + + switch ($driver) { + case 'mysql' : + $pdo->exec('ALTER TABLE calendarobjects ADD uid VARCHAR(200)'); + break; + case 'sqlite' : + $pdo->exec('ALTER TABLE calendarobjects ADD uid TEXT'); + break; + } + + $result = $pdo->query('SELECT id, calendardata FROM calendarobjects'); + $stmt = $pdo->prepare('UPDATE calendarobjects SET uid = ? WHERE id = ?'); + $counter = 0; + + while ($row = $result->fetch(\PDO::FETCH_ASSOC)) { + + try { + $vobj = \Sabre\VObject\Reader::read($row['calendardata']); + } catch (\Exception $e) { + echo "Warning! Item with id $row[id] could not be parsed!\n"; + continue; + } + $uid = null; + $item = $vobj->getBaseComponent(); + if (!isset($item->UID)) { + echo "Warning! Item with id $item[id] does NOT have a UID property and this is required.\n"; + continue; + } + $uid = (string)$item->UID; + $stmt->execute([$uid, $row['id']]); + $counter++; + + } + +} + +echo "Creating 'schedulingobjects'\n"; + +switch ($driver) { + + case 'mysql' : + $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects +( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(255), + calendardata MEDIUMBLOB, + uri VARCHAR(200), + lastmodified INT(11) UNSIGNED, + etag VARCHAR(32), + size INT(11) UNSIGNED NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + '); + break; + + + case 'sqlite' : + $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects ( + id integer primary key asc, + principaluri text, + calendardata blob, + uri text, + lastmodified integer, + etag text, + size integer +) +'); + break; +} + +echo "Done.\n"; + +echo "Upgrade to 2.1 schema completed.\n"; diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php new file mode 100644 index 00000000000..9ca77c13c3c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php @@ -0,0 +1,171 @@ +#!/usr/bin/env php +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + +switch ($driver) { + + case 'mysql' : + echo "Detected MySQL.\n"; + break; + case 'sqlite' : + echo "Detected SQLite.\n"; + break; + default : + echo "Error: unsupported driver: " . $driver . "\n"; + die(-1); +} + +echo "Upgrading 'propertystorage'\n"; +$addValueType = false; +try { + $result = $pdo->query('SELECT * FROM propertystorage LIMIT 1'); + $row = $result->fetch(\PDO::FETCH_ASSOC); + + if (!$row) { + echo "No data in table. Going to re-create the table.\n"; + $random = mt_rand(1000, 9999); + echo "Renaming propertystorage -> propertystorage_old$random and creating new table.\n"; + + switch ($driver) { + + case 'mysql' : + $pdo->exec('RENAME TABLE propertystorage TO propertystorage_old' . $random); + $pdo->exec(' + CREATE TABLE propertystorage ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + path VARBINARY(1024) NOT NULL, + name VARBINARY(100) NOT NULL, + valuetype INT UNSIGNED, + value MEDIUMBLOB + ); + '); + $pdo->exec('CREATE UNIQUE INDEX path_property_' . $random . ' ON propertystorage (path(600), name(100));'); + break; + case 'sqlite' : + $pdo->exec('ALTER TABLE propertystorage RENAME TO propertystorage_old' . $random); + $pdo->exec(' +CREATE TABLE propertystorage ( + id integer primary key asc, + path text, + name text, + valuetype integer, + value blob +);'); + + $pdo->exec('CREATE UNIQUE INDEX path_property_' . $random . ' ON propertystorage (path, name);'); + break; + + } + } elseif (array_key_exists('valuetype', $row)) { + echo "valuetype field exists. Assuming that this part of the migration has\n"; + echo "Already been completed.\n"; + } else { + echo "2.1 schema detected. Going to perform upgrade.\n"; + $addValueType = true; + } + +} catch (Exception $e) { + echo "Could not find a propertystorage table. Skipping this part of the\n"; + echo "upgrade.\n"; + echo $e->getMessage(), "\n"; +} + +if ($addValueType) { + + switch ($driver) { + case 'mysql' : + $pdo->exec('ALTER TABLE propertystorage ADD valuetype INT UNSIGNED'); + break; + case 'sqlite' : + $pdo->exec('ALTER TABLE propertystorage ADD valuetype INT'); + + break; + } + + $pdo->exec('UPDATE propertystorage SET valuetype = 1 WHERE valuetype IS NULL '); + +} + +echo "Migrating vcardurl\n"; + +$result = $pdo->query('SELECT id, uri, vcardurl FROM principals WHERE vcardurl IS NOT NULL'); +$stmt1 = $pdo->prepare('INSERT INTO propertystorage (path, name, valuetype, value) VALUES (?, ?, 3, ?)'); + +while ($row = $result->fetch(\PDO::FETCH_ASSOC)) { + + // Inserting the new record + $stmt1->execute([ + 'addressbooks/' . basename($row['uri']), + '{http://calendarserver.org/ns/}me-card', + serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl'])) + ]); + + echo serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl'])); + +} + +echo "Done.\n"; +echo "Upgrade to 3.0 schema completed.\n"; diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php new file mode 100644 index 00000000000..7567aeb60f7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php @@ -0,0 +1,268 @@ +#!/usr/bin/env php +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + +switch ($driver) { + + case 'mysql' : + echo "Detected MySQL.\n"; + break; + case 'sqlite' : + echo "Detected SQLite.\n"; + break; + default : + echo "Error: unsupported driver: " . $driver . "\n"; + die(-1); +} + +echo "Creating 'calendarinstances'\n"; +$addValueType = false; +try { + $result = $pdo->query('SELECT * FROM calendarinstances LIMIT 1'); + $result->fetch(\PDO::FETCH_ASSOC); + echo "calendarinstances exists. Assuming this part of the migration has already been done.\n"; +} catch (Exception $e) { + echo "calendarinstances does not yet exist. Creating table and migrating data.\n"; + + switch ($driver) { + case 'mysql' : + $pdo->exec(<<exec(" +INSERT INTO calendarinstances + ( + calendarid, + principaluri, + access, + displayname, + uri, + description, + calendarorder, + calendarcolor, + transparent + ) +SELECT + id, + principaluri, + 1, + displayname, + uri, + description, + calendarorder, + calendarcolor, + transparent +FROM calendars +"); + break; + case 'sqlite' : + $pdo->exec(<<exec(" +INSERT INTO calendarinstances + ( + calendarid, + principaluri, + access, + displayname, + uri, + description, + calendarorder, + calendarcolor, + transparent + ) +SELECT + id, + principaluri, + 1, + displayname, + uri, + description, + calendarorder, + calendarcolor, + transparent +FROM calendars +"); + break; + } + +} +try { + $result = $pdo->query('SELECT * FROM calendars LIMIT 1'); + $row = $result->fetch(\PDO::FETCH_ASSOC); + + if (!$row) { + echo "Source table is empty.\n"; + $migrateCalendars = true; + } + + $columnCount = count($row); + if ($columnCount === 3) { + echo "The calendars table has 3 columns already. Assuming this part of the migration was already done.\n"; + $migrateCalendars = false; + } else { + echo "The calendars table has " . $columnCount . " columns.\n"; + $migrateCalendars = true; + } + +} catch (Exception $e) { + echo "calendars table does not exist. This is a major problem. Exiting.\n"; + exit(-1); +} + +if ($migrateCalendars) { + + $calendarBackup = 'calendars_3_1_' . $backupPostfix; + echo "Backing up 'calendars' to '", $calendarBackup, "'\n"; + + switch ($driver) { + case 'mysql' : + $pdo->exec('RENAME TABLE calendars TO ' . $calendarBackup); + break; + case 'sqlite' : + $pdo->exec('ALTER TABLE calendars RENAME TO ' . $calendarBackup); + break; + + } + + echo "Creating new calendars table.\n"; + switch ($driver) { + case 'mysql' : + $pdo->exec(<<exec(<<exec(<<0): + print "Bytes to go before we hit threshold:", bytes + else: + print "Threshold exceeded with:", -bytes, "bytes" + dir = os.listdir(cacheDir) + dir2 = [] + for file in dir: + path = cacheDir + '/' + file + dir2.append({ + "path" : path, + "atime": os.stat(path).st_atime, + "size" : os.stat(path).st_size + }) + + dir2.sort(lambda x,y: int(x["atime"]-y["atime"])) + + filesunlinked = 0 + gainedspace = 0 + + # Left is the amount of bytes that need to be freed up + # The default is the 'min_erase setting' + left = min_erase + + # If the min_erase setting is lower than the amount of bytes over + # the threshold, we use that number instead. + if left < -bytes : + left = -bytes + + print "Need to delete at least:", left; + + for file in dir2: + + # Only deleting files if we're not simulating + if not simulate: os.unlink(file["path"]) + left = int(left - file["size"]) + gainedspace = gainedspace + file["size"] + filesunlinked = filesunlinked + 1 + + if(left<0): + break + + print "%d files deleted (%d bytes)" % (filesunlinked, gainedspace) + + + time.sleep(sleep) + + + +def main(): + parser = OptionParser( + version="naturalselection v0.3", + description="Cache directory manager. Deletes cache entries based on accesstime and free space thresholds.\n" + + "This utility is distributed alongside SabreDAV.", + usage="usage: %prog [options] cacheDirectory", + ) + parser.add_option( + '-s', + dest="simulate", + action="store_true", + help="Don't actually make changes, but just simulate the behaviour", + ) + parser.add_option( + '-r','--runs', + help="How many times to check before exiting. -1 is infinite, which is the default", + type="int", + dest="runs", + default=-1 + ) + parser.add_option( + '-n','--interval', + help="Sleep time in seconds (default = 5)", + type="int", + dest="sleep", + default=5 + ) + parser.add_option( + '-l','--threshold', + help="Threshold in bytes (default = 10737418240, which is 10GB)", + type="int", + dest="threshold", + default=10737418240 + ) + parser.add_option( + '-m', '--min-erase', + help="Minimum number of bytes to erase when the threshold is reached. " + + "Setting this option higher will reduce the number of times the cache directory will need to be scanned. " + + "(the default is 1073741824, which is 1GB.)", + type="int", + dest="min_erase", + default=1073741824 + ) + + options,args = parser.parse_args() + if len(args)<1: + parser.error("This utility requires at least 1 argument") + cacheDir = args[0] + + print "Natural Selection" + print "Cache directory:", cacheDir + free = getfreespace(cacheDir); + print "Current free disk space:", free + + runs = options.runs; + while runs!=0 : + run( + cacheDir, + sleep=options.sleep, + simulate=options.simulate, + threshold=options.threshold, + min_erase=options.min_erase + ) + if runs>0: + runs = runs - 1 + +if __name__ == '__main__' : + main() diff --git a/htdocs/includes/sabre/sabre/dav/bin/sabredav b/htdocs/includes/sabre/sabre/dav/bin/sabredav new file mode 100644 index 00000000000..032371ba8bc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/sabredav @@ -0,0 +1,2 @@ +#!/bin/sh +php -S 0.0.0.0:8080 `dirname $0`/sabredav.php diff --git a/htdocs/includes/sabre/sabre/dav/bin/sabredav.php b/htdocs/includes/sabre/sabre/dav/bin/sabredav.php new file mode 100644 index 00000000000..950075d1af7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/bin/sabredav.php @@ -0,0 +1,53 @@ +stream = fopen('php://stdout', 'w'); + + } + + function log($msg) { + fwrite($this->stream, $msg . "\n"); + } + +} + +$log = new CliLog(); + +if (php_sapi_name() !== 'cli-server') { + die("This script is intended to run on the built-in php webserver"); +} + +// Finding composer + + +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +use Sabre\DAV; + +// Root +$root = new DAV\FS\Directory(getcwd()); + +// Setting up server. +$server = new DAV\Server($root); + +// Browser plugin +$server->addPlugin(new DAV\Browser\Plugin()); + +$server->exec(); diff --git a/htdocs/includes/sabre/sabre/dav/composer.json b/htdocs/includes/sabre/sabre/dav/composer.json new file mode 100644 index 00000000000..fca0e07fbd4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/composer.json @@ -0,0 +1,68 @@ +{ + "name": "sabre/dav", + "type": "library", + "description": "WebDAV Framework for PHP", + "keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"], + "homepage": "http://sabre.io/", + "license" : "BSD-3-Clause", + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage" : "http://evertpot.com/", + "role" : "Developer" + } + ], + "require": { + "php": ">=5.5.0", + "sabre/vobject": "^4.1.0", + "sabre/event" : ">=2.0.0, <4.0.0", + "sabre/xml" : "^1.4.0", + "sabre/http" : "^4.2.1", + "sabre/uri" : "^1.0.1", + "ext-dom": "*", + "ext-pcre": "*", + "ext-spl": "*", + "ext-simplexml": "*", + "ext-mbstring" : "*", + "ext-ctype" : "*", + "ext-date" : "*", + "ext-iconv" : "*", + "lib-libxml" : ">=2.7.0", + "psr/log": "^1.0" + }, + "require-dev" : { + "phpunit/phpunit" : "> 4.8, <6.0.0", + "evert/phpdoc-md" : "~0.1.0", + "sabre/cs" : "^1.0.0", + "monolog/monolog": "^1.18" + }, + "suggest" : { + "ext-curl" : "*", + "ext-pdo" : "*" + }, + "autoload": { + "psr-4" : { + "Sabre\\DAV\\" : "lib/DAV/", + "Sabre\\DAVACL\\" : "lib/DAVACL/", + "Sabre\\CalDAV\\" : "lib/CalDAV/", + "Sabre\\CardDAV\\" : "lib/CardDAV/" + } + }, + "support" : { + "forum" : "https://groups.google.com/group/sabredav-discuss", + "source" : "https://github.com/fruux/sabre-dav" + }, + "bin" : [ + "bin/sabredav", + "bin/naturalselection" + ], + "config" : { + "bin-dir" : "./bin" + }, + "extra" : { + "branch-alias": { + "dev-master": "3.1.0-dev" + } + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php new file mode 100644 index 00000000000..311b1c41509 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php @@ -0,0 +1,226 @@ +getCalendarObject($calendarId, $uri); + }, $uris); + + } + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by \Sabre\CalDAV\CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. + * + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. + * + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on either VEVENT or VTODO. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interprete all these filters can also simply + * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * @param mixed $calendarId + * @param array $filters + * @return array + */ + function calendarQuery($calendarId, array $filters) { + + $result = []; + $objects = $this->getCalendarObjects($calendarId); + + foreach ($objects as $object) { + + if ($this->validateFilterForObject($object, $filters)) { + $result[] = $object['uri']; + } + + } + + return $result; + + } + + /** + * This method validates if a filter (as passed to calendarQuery) matches + * the given object. + * + * @param array $object + * @param array $filters + * @return bool + */ + protected function validateFilterForObject(array $object, array $filters) { + + // Unfortunately, setting the 'calendardata' here is optional. If + // it was excluded, we actually need another call to get this as + // well. + if (!isset($object['calendardata'])) { + $object = $this->getCalendarObject($object['calendarid'], $object['uri']); + } + + $vObject = VObject\Reader::read($object['calendardata']); + + $validator = new CalDAV\CalendarQueryValidator(); + $result = $validator->validate($vObject, $filters); + + // Destroy circular references so PHP will GC the object. + $vObject->destroy(); + + return $result; + + } + + /** + * Searches through all of a users calendars and calendar objects to find + * an object with a specific UID. + * + * This method should return the path to this object, relative to the + * calendar home, so this path usually only contains two parts: + * + * calendarpath/objectpath.ics + * + * If the uid is not found, return null. + * + * This method should only consider * objects that the principal owns, so + * any calendars owned by other principals that also appear in this + * collection should be ignored. + * + * @param string $principalUri + * @param string $uid + * @return string|null + */ + function getCalendarObjectByUID($principalUri, $uid) { + + // Note: this is a super slow naive implementation of this method. You + // are highly recommended to optimize it, if your backend allows it. + foreach ($this->getCalendarsForUser($principalUri) as $calendar) { + + // We must ignore calendars owned by other principals. + if ($calendar['principaluri'] !== $principalUri) { + continue; + } + + // Ignore calendars that are shared. + if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) { + continue; + } + + $results = $this->calendarQuery( + $calendar['id'], + [ + 'name' => 'VCALENDAR', + 'prop-filters' => [], + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'time-range' => null, + 'comp-filters' => [], + 'prop-filters' => [ + [ + 'name' => 'UID', + 'is-not-defined' => false, + 'time-range' => null, + 'text-match' => [ + 'value' => $uid, + 'negate-condition' => false, + 'collation' => 'i;octet', + ], + 'param-filters' => [], + ], + ] + ] + ], + ] + ); + if ($results) { + // We have a match + return $calendar['uri'] . '/' . $results[0]; + } + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php new file mode 100644 index 00000000000..bd8ee760228 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php @@ -0,0 +1,270 @@ + 'displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + ]; + + /** + * List of subscription properties, and how they map to database fieldnames. + * + * @var array + */ + public $subscriptionPropertyMap = [ + '{DAV:}displayname' => 'displayname', + '{http://apple.com/ns/ical/}refreshrate' => 'refreshrate', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + '{http://calendarserver.org/ns/}subscribed-strip-todos' => 'striptodos', + '{http://calendarserver.org/ns/}subscribed-strip-alarms' => 'stripalarms', + '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments', + ]; + + /** + * Creates the backend + * + * @param \PDO $pdo + */ + function __construct(\PDO $pdo) { + + $this->pdo = $pdo; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri. This is just the 'base uri' or 'filename' of the calendar. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * Many clients also require: + * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set + * For this property, you can just return an instance of + * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet. + * + * If you return {http://sabredav.org/ns}read-only and set the value to 1, + * ACL will automatically be put in read-only mode. + * + * @param string $principalUri + * @return array + */ + function getCalendarsForUser($principalUri) { + + $fields = array_values($this->propertyMap); + $fields[] = 'calendarid'; + $fields[] = 'uri'; + $fields[] = 'synctoken'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + $fields[] = 'transparent'; + $fields[] = 'access'; + + // Making fields a comma-delimited list + $fields = implode(', ', $fields); + $stmt = $this->pdo->prepare(<<calendarInstancesTableName}.id as id, $fields FROM {$this->calendarInstancesTableName} + LEFT JOIN {$this->calendarTableName} ON + {$this->calendarInstancesTableName}.calendarid = {$this->calendarTableName}.id +WHERE principaluri = ? ORDER BY calendarorder ASC +SQL + ); + $stmt->execute([$principalUri]); + + $calendars = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $components = []; + if ($row['components']) { + $components = explode(',', $row['components']); + } + + $calendar = [ + 'id' => [(int)$row['calendarid'], (int)$row['id']], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken'] ? $row['synctoken'] : '0'), + '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0', + '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components), + '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'), + 'share-resource-uri' => '/ns/share/' . $row['calendarid'], + ]; + + $calendar['share-access'] = (int)$row['access']; + // 1 = owner, 2 = readonly, 3 = readwrite + if ($row['access'] > 1) { + // We need to find more information about the original owner. + //$stmt2 = $this->pdo->prepare('SELECT principaluri FROM ' . $this->calendarInstancesTableName . ' WHERE access = 1 AND id = ?'); + //$stmt2->execute([$row['id']]); + + // read-only is for backwards compatbility. Might go away in + // the future. + $calendar['read-only'] = (int)$row['access'] === \Sabre\DAV\Sharing\Plugin::ACCESS_READ; + } + + foreach ($this->propertyMap as $xmlName => $dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + $calendars[] = $calendar; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used + * to reference this calendar in other methods, such as updateCalendar. + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string + */ + function createCalendar($principalUri, $calendarUri, array $properties) { + + $fieldNames = [ + 'principaluri', + 'uri', + 'transparent', + 'calendarid', + ]; + $values = [ + ':principaluri' => $principalUri, + ':uri' => $calendarUri, + ':transparent' => 0, + ]; + + + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + if (!isset($properties[$sccs])) { + // Default value + $components = 'VEVENT,VTODO'; + } else { + if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) { + throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet'); + } + $components = implode(',', $properties[$sccs]->getValue()); + } + $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; + if (isset($properties[$transp])) { + $values[':transparent'] = $properties[$transp]->getValue() === 'transparent' ? 1 : 0; + } + $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (synctoken, components) VALUES (1, ?)"); + $stmt->execute([$components]); + + $calendarId = $this->pdo->lastInsertId( + $this->calendarTableName . '_id_seq' + ); + + $values[':calendarid'] = $calendarId; + + foreach ($this->propertyMap as $xmlName => $dbName) { + if (isset($properties[$xmlName])) { + + $values[':' . $dbName] = $properties[$xmlName]; + $fieldNames[] = $dbName; + } + } + + $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarInstancesTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); + + $stmt->execute($values); + + return [ + $calendarId, + $this->pdo->lastInsertId($this->calendarInstancesTableName . '_id_seq') + ]; + + } + + /** + * Updates properties for a calendar. + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param mixed $calendarId + * @param \Sabre\DAV\PropPatch $propPatch + * @return void + */ + function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $supportedProperties = array_keys($this->propertyMap); + $supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; + + $propPatch->handle($supportedProperties, function($mutations) use ($calendarId, $instanceId) { + $newValues = []; + foreach ($mutations as $propertyName => $propertyValue) { + + switch ($propertyName) { + case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' : + $fieldName = 'transparent'; + $newValues[$fieldName] = $propertyValue->getValue() === 'transparent'; + break; + default : + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + break; + } + + } + $valuesSql = []; + foreach ($newValues as $fieldName => $value) { + $valuesSql[] = $fieldName . ' = ?'; + } + + $stmt = $this->pdo->prepare("UPDATE " . $this->calendarInstancesTableName . " SET " . implode(', ', $valuesSql) . " WHERE id = ?"); + $newValues['id'] = $instanceId; + $stmt->execute(array_values($newValues)); + + $this->addChange($calendarId, "", 2); + + return true; + + }); + + } + + /** + * Delete a calendar and all it's objects + * + * @param mixed $calendarId + * @return void + */ + function deleteCalendar($calendarId) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $stmt = $this->pdo->prepare('SELECT access FROM ' . $this->calendarInstancesTableName . ' where id = ?'); + $stmt->execute([$instanceId]); + $access = (int)$stmt->fetchColumn(); + + if ($access === \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER) { + + /** + * If the user is the owner of the calendar, we delete all data and all + * instances. + **/ + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarChangesTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarInstancesTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarTableName . ' WHERE id = ?'); + $stmt->execute([$calendarId]); + + } else { + + /** + * If it was an instance of a shared calendar, we only delete that + * instance. + */ + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarInstancesTableName . ' WHERE id = ?'); + $stmt->execute([$instanceId]); + + } + + + } + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can + * be any arbitrary string, but making sure it ends with '.ics' is a + * good idea. This is only the basename, or filename, not the full + * path. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * size - The size of the calendar objects, in bytes. + * * component - optional, a string containing the type of object, such + * as 'vevent' or 'vtodo'. If specified, this will be used to populate + * the Content-Type header. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param mixed $calendarId + * @return array + */ + function getCalendarObjects($calendarId) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $result = []; + foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $result[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => (int)$row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'size' => (int)$row['size'], + 'component' => strtolower($row['componenttype']), + ]; + } + + return $result; + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * This method must return null if the object did not exist. + * + * @param mixed $calendarId + * @param string $objectUri + * @return array|null + */ + function getCalendarObject($calendarId, $objectUri) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarId, $objectUri]); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if (!$row) return null; + + return [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => (int)$row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'size' => (int)$row['size'], + 'calendardata' => $row['calendardata'], + 'component' => strtolower($row['componenttype']), + ]; + + } + + /** + * Returns a list of calendar objects. + * + * This method should work identical to getCalendarObject, but instead + * return all the calendar objects in the list as an array. + * + * If the backend supports this, it may allow for some speed-ups. + * + * @param mixed $calendarId + * @param array $uris + * @return array + */ + function getMultipleCalendarObjects($calendarId, array $uris) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $result = []; + foreach (array_chunk($uris, 900) as $chunk) { + $query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri IN ('; + // Inserting a whole bunch of question marks + $query .= implode(',', array_fill(0, count($chunk), '?')); + $query .= ')'; + + $stmt = $this->pdo->prepare($query); + $stmt->execute(array_merge([$calendarId], $chunk)); + + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $result[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => (int)$row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'size' => (int)$row['size'], + 'calendardata' => $row['calendardata'], + 'component' => strtolower($row['componenttype']), + ]; + + } + } + return $result; + + } + + + /** + * Creates a new calendar object. + * + * The object uri is only the basename, or filename and not a full path. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + function createCalendarObject($calendarId, $objectUri, $calendarData) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $extraData = $this->getDenormalizedData($calendarData); + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarObjectTableName . ' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)'); + $stmt->execute([ + $calendarId, + $objectUri, + $calendarData, + time(), + $extraData['etag'], + $extraData['size'], + $extraData['componentType'], + $extraData['firstOccurence'], + $extraData['lastOccurence'], + $extraData['uid'], + ]); + $this->addChange($calendarId, $objectUri, 1); + + return '"' . $extraData['etag'] . '"'; + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + function updateCalendarObject($calendarId, $objectUri, $calendarData) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $extraData = $this->getDenormalizedData($calendarData); + + $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarObjectTableName . ' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]); + + $this->addChange($calendarId, $objectUri, 2); + + return '"' . $extraData['etag'] . '"'; + + } + + /** + * Parses some information from calendar objects, used for optimized + * calendar-queries. + * + * Returns an array with the following keys: + * * etag - An md5 checksum of the object without the quotes. + * * size - Size of the object in bytes + * * componentType - VEVENT, VTODO or VJOURNAL + * * firstOccurence + * * lastOccurence + * * uid - value of the UID property + * + * @param string $calendarData + * @return array + */ + protected function getDenormalizedData($calendarData) { + + $vObject = VObject\Reader::read($calendarData); + $componentType = null; + $component = null; + $firstOccurence = null; + $lastOccurence = null; + $uid = null; + foreach ($vObject->getComponents() as $component) { + if ($component->name !== 'VTIMEZONE') { + $componentType = $component->name; + $uid = (string)$component->UID; + break; + } + } + if (!$componentType) { + throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); + } + if ($componentType === 'VEVENT') { + $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); + // Finding the last occurence is a bit harder + if (!isset($component->RRULE)) { + if (isset($component->DTEND)) { + $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); + } elseif (isset($component->DURATION)) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate = $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue())); + $lastOccurence = $endDate->getTimeStamp(); + } elseif (!$component->DTSTART->hasTime()) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate = $endDate->modify('+1 day'); + $lastOccurence = $endDate->getTimeStamp(); + } else { + $lastOccurence = $firstOccurence; + } + } else { + $it = new VObject\Recur\EventIterator($vObject, (string)$component->UID); + $maxDate = new \DateTime(self::MAX_DATE); + if ($it->isInfinite()) { + $lastOccurence = $maxDate->getTimeStamp(); + } else { + $end = $it->getDtEnd(); + while ($it->valid() && $end < $maxDate) { + $end = $it->getDtEnd(); + $it->next(); + + } + $lastOccurence = $end->getTimeStamp(); + } + + } + + // Ensure Occurence values are positive + if ($firstOccurence < 0) $firstOccurence = 0; + if ($lastOccurence < 0) $lastOccurence = 0; + } + + // Destroy circular references to PHP will GC the object. + $vObject->destroy(); + + return [ + 'etag' => md5($calendarData), + 'size' => strlen($calendarData), + 'componentType' => $componentType, + 'firstOccurence' => $firstOccurence, + 'lastOccurence' => $lastOccurence, + 'uid' => $uid, + ]; + + } + + /** + * Deletes an existing calendar object. + * + * The object uri is only the basename, or filename and not a full path. + * + * @param mixed $calendarId + * @param string $objectUri + * @return void + */ + function deleteCalendarObject($calendarId, $objectUri) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarId, $objectUri]); + + $this->addChange($calendarId, $objectUri, 3); + + } + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by \Sabre\CalDAV\CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. + * + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. + * + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on a VEVENT. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interpret all these filters can also simply + * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * This specific implementation (for the PDO) backend optimizes filters on + * specific components, and VEVENT time-ranges. + * + * @param mixed $calendarId + * @param array $filters + * @return array + */ + function calendarQuery($calendarId, array $filters) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $componentType = null; + $requirePostFilter = true; + $timeRange = null; + + // if no filters were specified, we don't need to filter after a query + if (!$filters['prop-filters'] && !$filters['comp-filters']) { + $requirePostFilter = false; + } + + // Figuring out if there's a component filter + if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { + $componentType = $filters['comp-filters'][0]['name']; + + // Checking if we need post-filters + if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { + $requirePostFilter = false; + } + // There was a time-range filter + if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { + $timeRange = $filters['comp-filters'][0]['time-range']; + + // If start time OR the end time is not specified, we can do a + // 100% accurate mysql query. + if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { + $requirePostFilter = false; + } + } + + } + + if ($requirePostFilter) { + $query = "SELECT uri, calendardata FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid"; + } else { + $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid"; + } + + $values = [ + 'calendarid' => $calendarId, + ]; + + if ($componentType) { + $query .= " AND componenttype = :componenttype"; + $values['componenttype'] = $componentType; + } + + if ($timeRange && $timeRange['start']) { + $query .= " AND lastoccurence > :startdate"; + $values['startdate'] = $timeRange['start']->getTimeStamp(); + } + if ($timeRange && $timeRange['end']) { + $query .= " AND firstoccurence < :enddate"; + $values['enddate'] = $timeRange['end']->getTimeStamp(); + } + + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($requirePostFilter) { + if (!$this->validateFilterForObject($row, $filters)) { + continue; + } + } + $result[] = $row['uri']; + + } + + return $result; + + } + + /** + * Searches through all of a users calendars and calendar objects to find + * an object with a specific UID. + * + * This method should return the path to this object, relative to the + * calendar home, so this path usually only contains two parts: + * + * calendarpath/objectpath.ics + * + * If the uid is not found, return null. + * + * This method should only consider * objects that the principal owns, so + * any calendars owned by other principals that also appear in this + * collection should be ignored. + * + * @param string $principalUri + * @param string $uid + * @return string|null + */ + function getCalendarObjectByUID($principalUri, $uid) { + + $query = <<calendarObjectTableName AS calendarobjects +LEFT JOIN + $this->calendarInstancesTableName AS calendar_instances + ON calendarobjects.calendarid = calendar_instances.calendarid +WHERE + calendar_instances.principaluri = ? + AND + calendarobjects.uid = ? +SQL; + + $stmt = $this->pdo->prepare($query); + $stmt->execute([$principalUri, $uid]); + + if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return $row['calendaruri'] . '/' . $row['objecturi']; + } + + } + + /** + * The getChanges method returns all the changes that have happened, since + * the specified syncToken in the specified calendar. + * + * This function should return an array, such as the following: + * + * [ + * 'syncToken' => 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'modified.txt', + * ], + * 'deleted' => [ + * 'foo.php.bak', + * 'old.txt' + * ] + * ]; + * + * The returned syncToken property should reflect the *current* syncToken + * of the calendar, as reported in the {http://sabredav.org/ns}sync-token + * property this is needed here too, to ensure the operation is atomic. + * + * If the $syncToken argument is specified as null, this is an initial + * sync, and all members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The $syncLevel argument is basically the 'depth' of the report. If it's + * 1, you only have to report changes that happened only directly in + * immediate descendants. If it's 2, it should also include changes from + * the nodes below the child collections. (grandchildren) + * + * The $limit argument allows a client to specify how many results should + * be returned at most. If the limit is not specified, it should be treated + * as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param mixed $calendarId + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + // Current synctoken + $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->calendarTableName . ' WHERE id = ?'); + $stmt->execute([$calendarId]); + $currentToken = $stmt->fetchColumn(0); + + if (is_null($currentToken)) return null; + + $result = [ + 'syncToken' => $currentToken, + 'added' => [], + 'modified' => [], + 'deleted' => [], + ]; + + if ($syncToken) { + + $query = "SELECT uri, operation FROM " . $this->calendarChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; + if ($limit > 0) $query .= " LIMIT " . (int)$limit; + + // Fetching all changes + $stmt = $this->pdo->prepare($query); + $stmt->execute([$syncToken, $currentToken, $calendarId]); + + $changes = []; + + // This loop ensures that any duplicates are overwritten, only the + // last change on a node is relevant. + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $changes[$row['uri']] = $row['operation']; + + } + + foreach ($changes as $uri => $operation) { + + switch ($operation) { + case 1 : + $result['added'][] = $uri; + break; + case 2 : + $result['modified'][] = $uri; + break; + case 3 : + $result['deleted'][] = $uri; + break; + } + + } + } else { + // No synctoken supplied, this is the initial sync. + $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = ?"; + $stmt = $this->pdo->prepare($query); + $stmt->execute([$calendarId]); + + $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); + } + return $result; + + } + + /** + * Adds a change record to the calendarchanges table. + * + * @param mixed $calendarId + * @param string $objectUri + * @param int $operation 1 = add, 2 = modify, 3 = delete. + * @return void + */ + protected function addChange($calendarId, $objectUri, $operation) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarChangesTableName . ' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->calendarTableName . ' WHERE id = ?'); + $stmt->execute([ + $objectUri, + $calendarId, + $operation, + $calendarId + ]); + $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarTableName . ' SET synctoken = synctoken + 1 WHERE id = ?'); + $stmt->execute([ + $calendarId + ]); + + } + + /** + * Returns a list of subscriptions for a principal. + * + * Every subscription is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * subscription. This can be the same as the uri or a database key. + * * uri. This is just the 'base uri' or 'filename' of the subscription. + * * principaluri. The owner of the subscription. Almost always the same as + * principalUri passed to this method. + * * source. Url to the actual feed + * + * Furthermore, all the subscription info must be returned too: + * + * 1. {DAV:}displayname + * 2. {http://apple.com/ns/ical/}refreshrate + * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos + * should not be stripped). + * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms + * should not be stripped). + * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if + * attachments should not be stripped). + * 7. {http://apple.com/ns/ical/}calendar-color + * 8. {http://apple.com/ns/ical/}calendar-order + * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set + * (should just be an instance of + * Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of + * default components). + * + * @param string $principalUri + * @return array + */ + function getSubscriptionsForUser($principalUri) { + + $fields = array_values($this->subscriptionPropertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'source'; + $fields[] = 'principaluri'; + $fields[] = 'lastmodified'; + + // Making fields a comma-delimited list + $fields = implode(', ', $fields); + $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM " . $this->calendarSubscriptionsTableName . " WHERE principaluri = ? ORDER BY calendarorder ASC"); + $stmt->execute([$principalUri]); + + $subscriptions = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $subscription = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + 'source' => $row['source'], + 'lastmodified' => $row['lastmodified'], + + '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']), + ]; + + foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) { + if (!is_null($row[$dbName])) { + $subscription[$xmlName] = $row[$dbName]; + } + } + + $subscriptions[] = $subscription; + + } + + return $subscriptions; + + } + + /** + * Creates a new subscription for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this subscription in other methods, such as updateSubscription. + * + * @param string $principalUri + * @param string $uri + * @param array $properties + * @return mixed + */ + function createSubscription($principalUri, $uri, array $properties) { + + $fieldNames = [ + 'principaluri', + 'uri', + 'source', + 'lastmodified', + ]; + + if (!isset($properties['{http://calendarserver.org/ns/}source'])) { + throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions'); + } + + $values = [ + ':principaluri' => $principalUri, + ':uri' => $uri, + ':source' => $properties['{http://calendarserver.org/ns/}source']->getHref(), + ':lastmodified' => time(), + ]; + + foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) { + if (isset($properties[$xmlName])) { + + $values[':' . $dbName] = $properties[$xmlName]; + $fieldNames[] = $dbName; + } + } + + $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); + $stmt->execute($values); + + return $this->pdo->lastInsertId( + $this->calendarSubscriptionsTableName . '_id_seq' + ); + + } + + /** + * Updates a subscription + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param mixed $subscriptionId + * @param \Sabre\DAV\PropPatch $propPatch + * @return void + */ + function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) { + + $supportedProperties = array_keys($this->subscriptionPropertyMap); + $supportedProperties[] = '{http://calendarserver.org/ns/}source'; + + $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) { + + $newValues = []; + + foreach ($mutations as $propertyName => $propertyValue) { + + if ($propertyName === '{http://calendarserver.org/ns/}source') { + $newValues['source'] = $propertyValue->getHref(); + } else { + $fieldName = $this->subscriptionPropertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + } + + } + + // Now we're generating the sql query. + $valuesSql = []; + foreach ($newValues as $fieldName => $value) { + $valuesSql[] = $fieldName . ' = ?'; + } + + $stmt = $this->pdo->prepare("UPDATE " . $this->calendarSubscriptionsTableName . " SET " . implode(', ', $valuesSql) . ", lastmodified = ? WHERE id = ?"); + $newValues['lastmodified'] = time(); + $newValues['id'] = $subscriptionId; + $stmt->execute(array_values($newValues)); + + return true; + + }); + + } + + /** + * Deletes a subscription + * + * @param mixed $subscriptionId + * @return void + */ + function deleteSubscription($subscriptionId) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarSubscriptionsTableName . ' WHERE id = ?'); + $stmt->execute([$subscriptionId]); + + } + + /** + * Returns a single scheduling object. + * + * The returned array should contain the following elements: + * * uri - A unique basename for the object. This will be used to + * construct a full uri. + * * calendardata - The iCalendar object + * * lastmodified - The last modification date. Can be an int for a unix + * timestamp, or a PHP DateTime object. + * * etag - A unique token that must change if the object changed. + * * size - The size of the object, in bytes. + * + * @param string $principalUri + * @param string $objectUri + * @return array + */ + function getSchedulingObject($principalUri, $objectUri) { + + $stmt = $this->pdo->prepare('SELECT uri, calendardata, lastmodified, etag, size FROM ' . $this->schedulingObjectTableName . ' WHERE principaluri = ? AND uri = ?'); + $stmt->execute([$principalUri, $objectUri]); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if (!$row) return null; + + return [ + 'uri' => $row['uri'], + 'calendardata' => $row['calendardata'], + 'lastmodified' => $row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'size' => (int)$row['size'], + ]; + + } + + /** + * Returns all scheduling objects for the inbox collection. + * + * These objects should be returned as an array. Every item in the array + * should follow the same structure as returned from getSchedulingObject. + * + * The main difference is that 'calendardata' is optional. + * + * @param string $principalUri + * @return array + */ + function getSchedulingObjects($principalUri) { + + $stmt = $this->pdo->prepare('SELECT id, calendardata, uri, lastmodified, etag, size FROM ' . $this->schedulingObjectTableName . ' WHERE principaluri = ?'); + $stmt->execute([$principalUri]); + + $result = []; + foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $result[] = [ + 'calendardata' => $row['calendardata'], + 'uri' => $row['uri'], + 'lastmodified' => $row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'size' => (int)$row['size'], + ]; + } + + return $result; + + } + + /** + * Deletes a scheduling object + * + * @param string $principalUri + * @param string $objectUri + * @return void + */ + function deleteSchedulingObject($principalUri, $objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->schedulingObjectTableName . ' WHERE principaluri = ? AND uri = ?'); + $stmt->execute([$principalUri, $objectUri]); + + } + + /** + * Creates a new scheduling object. This should land in a users' inbox. + * + * @param string $principalUri + * @param string $objectUri + * @param string $objectData + * @return void + */ + function createSchedulingObject($principalUri, $objectUri, $objectData) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->schedulingObjectTableName . ' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)'); + $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData)]); + + } + + /** + * Updates the list of shares. + * + * @param mixed $calendarId + * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees + * @return void + */ + function updateInvites($calendarId, array $sharees) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + $currentInvites = $this->getInvites($calendarId); + list($calendarId, $instanceId) = $calendarId; + + $removeStmt = $this->pdo->prepare("DELETE FROM " . $this->calendarInstancesTableName . " WHERE calendarid = ? AND share_href = ? AND access IN (2,3)"); + $updateStmt = $this->pdo->prepare("UPDATE " . $this->calendarInstancesTableName . " SET access = ?, share_displayname = ?, share_invitestatus = ? WHERE calendarid = ? AND share_href = ?"); + + $insertStmt = $this->pdo->prepare(' +INSERT INTO ' . $this->calendarInstancesTableName . ' + ( + calendarid, + principaluri, + access, + displayname, + uri, + description, + calendarorder, + calendarcolor, + timezone, + transparent, + share_href, + share_displayname, + share_invitestatus + ) + SELECT + ?, + ?, + ?, + displayname, + ?, + description, + calendarorder, + calendarcolor, + timezone, + 1, + ?, + ?, + ? + FROM ' . $this->calendarInstancesTableName . ' WHERE id = ?'); + + foreach ($sharees as $sharee) { + + if ($sharee->access === \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS) { + // if access was set no NOACCESS, it means access for an + // existing sharee was removed. + $removeStmt->execute([$calendarId, $sharee->href]); + continue; + } + + if (is_null($sharee->principal)) { + // If the server could not determine the principal automatically, + // we will mark the invite status as invalid. + $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_INVALID; + } else { + // Because sabre/dav does not yet have an invitation system, + // every invite is automatically accepted for now. + $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED; + } + + foreach ($currentInvites as $oldSharee) { + + if ($oldSharee->href === $sharee->href) { + // This is an update + $sharee->properties = array_merge( + $oldSharee->properties, + $sharee->properties + ); + $updateStmt->execute([ + $sharee->access, + isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null, + $sharee->inviteStatus ?: $oldSharee->inviteStatus, + $calendarId, + $sharee->href + ]); + continue 2; + } + + } + // If we got here, it means it was a new sharee + $insertStmt->execute([ + $calendarId, + $sharee->principal, + $sharee->access, + \Sabre\DAV\UUIDUtil::getUUID(), + $sharee->href, + isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null, + $sharee->inviteStatus ?: \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE, + $instanceId + ]); + + } + + } + + /** + * Returns the list of people whom a calendar is shared with. + * + * Every item in the returned list must be a Sharee object with at + * least the following properties set: + * $href + * $shareAccess + * $inviteStatus + * + * and optionally: + * $properties + * + * @param mixed $calendarId + * @return \Sabre\DAV\Xml\Element\Sharee[] + */ + function getInvites($calendarId) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to getInvites() is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $query = <<calendarInstancesTableName} +WHERE + calendarid = ? +SQL; + + $stmt = $this->pdo->prepare($query); + $stmt->execute([$calendarId]); + + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $result[] = new Sharee([ + 'href' => isset($row['share_href']) ? $row['share_href'] : \Sabre\HTTP\encodePath($row['principaluri']), + 'access' => (int)$row['access'], + /// Everyone is always immediately accepted, for now. + 'inviteStatus' => (int)$row['share_invitestatus'], + 'properties' => + !empty($row['share_displayname']) + ? ['{DAV:}displayname' => $row['share_displayname']] + : [], + 'principal' => $row['principaluri'], + ]); + + } + return $result; + + } + + /** + * Publishes a calendar + * + * @param mixed $calendarId + * @param bool $value + * @return void + */ + function setPublishStatus($calendarId, $value) { + + throw new DAV\Exception\NotImplemented('Not implemented'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php new file mode 100644 index 00000000000..6ec0bf06b9f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php @@ -0,0 +1,65 @@ +pdo = $pdo; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri. This is just the 'base uri' or 'filename' of the calendar. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * Many clients also require: + * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set + * For this property, you can just return an instance of + * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet. + * + * If you return {http://sabredav.org/ns}read-only and set the value to 1, + * ACL will automatically be put in read-only mode. + * + * @param string $principalUri + * @return array + */ + function getCalendarsForUser($principalUri) { + + // Making fields a comma-delimited list + $stmt = $this->pdo->prepare("SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC"); + $stmt->execute([$principalUri]); + + $calendars = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $calendars[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $principalUri, + ]; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used + * to reference this calendar in other methods, such as updateCalendar. + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string + */ + function createCalendar($principalUri, $calendarUri, array $properties) { + + $stmt = $this->pdo->prepare("INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)"); + $stmt->execute([$principalUri, $calendarUri]); + + return $this->pdo->lastInsertId(); + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + function deleteCalendar($calendarId) { + + $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?'); + $stmt->execute([$calendarId]); + + } + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can + * be any arbitrary string, but making sure it ends with '.ics' is a + * good idea. This is only the basename, or filename, not the full + * path. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * size - The size of the calendar objects, in bytes. + * * component - optional, a string containing the type of object, such + * as 'vevent' or 'vtodo'. If specified, this will be used to populate + * the Content-Type header. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array + */ + function getCalendarObjects($calendarId) { + + $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $result = []; + foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $result[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'etag' => '"' . md5($row['calendardata']) . '"', + 'calendarid' => $calendarId, + 'size' => strlen($row['calendardata']), + 'calendardata' => $row['calendardata'], + ]; + } + + return $result; + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * This method must return null if the object did not exist. + * + * @param string $calendarId + * @param string $objectUri + * @return array|null + */ + function getCalendarObject($calendarId, $objectUri) { + + $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarId, $objectUri]); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if (!$row) return null; + + return [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'etag' => '"' . md5($row['calendardata']) . '"', + 'calendarid' => $calendarId, + 'size' => strlen($row['calendardata']), + 'calendardata' => $row['calendardata'], + ]; + + } + + /** + * Creates a new calendar object. + * + * The object uri is only the basename, or filename and not a full path. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + function createCalendarObject($calendarId, $objectUri, $calendarData) { + + $stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)'); + $stmt->execute([ + $calendarId, + $objectUri, + $calendarData + ]); + + return '"' . md5($calendarData) . '"'; + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + function updateCalendarObject($calendarId, $objectUri, $calendarData) { + + $stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarData, $calendarId, $objectUri]); + + return '"' . md5($calendarData) . '"'; + + } + + /** + * Deletes an existing calendar object. + * + * The object uri is only the basename, or filename and not a full path. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + function deleteCalendarObject($calendarId, $objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarId, $objectUri]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php new file mode 100644 index 00000000000..d77a2fe0fd5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php @@ -0,0 +1,89 @@ + 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'modified.txt', + * ], + * 'deleted' => [ + * 'foo.php.bak', + * 'old.txt' + * ] + * ); + * + * The returned syncToken property should reflect the *current* syncToken + * of the calendar, as reported in the {http://sabredav.org/ns}sync-token + * property This is * needed here too, to ensure the operation is atomic. + * + * If the $syncToken argument is specified as null, this is an initial + * sync, and all members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The $syncLevel argument is basically the 'depth' of the report. If it's + * 1, you only have to report changes that happened only directly in + * immediate descendants. If it's 2, it should also include changes from + * the nodes below the child collections. (grandchildren) + * + * The $limit argument allows a client to specify how many results should + * be returned at most. If the limit is not specified, it should be treated + * as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $calendarId + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null); + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Calendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Calendar.php new file mode 100644 index 00000000000..7467900ccdc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Calendar.php @@ -0,0 +1,472 @@ +caldavBackend = $caldavBackend; + $this->calendarInfo = $calendarInfo; + + } + + /** + * Returns the name of the calendar + * + * @return string + */ + function getName() { + + return $this->calendarInfo['uri']; + + } + + /** + * Updates properties on this node. + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * To update specific properties, call the 'handle' method on this object. + * Read the PropPatch documentation for more information. + * + * @param PropPatch $propPatch + * @return void + */ + function propPatch(PropPatch $propPatch) { + + return $this->caldavBackend->updateCalendar($this->calendarInfo['id'], $propPatch); + + } + + /** + * Returns the list of properties + * + * @param array $requestedProperties + * @return array + */ + function getProperties($requestedProperties) { + + $response = []; + + foreach ($this->calendarInfo as $propName => $propValue) { + + if (!is_null($propValue) && $propName[0] === '{') + $response[$propName] = $this->calendarInfo[$propName]; + + } + return $response; + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return \Sabre\CalDAV\ICalendarObject + */ + function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); + + if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found'); + + $obj['acl'] = $this->getChildACL(); + + return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = []; + foreach ($objs as $obj) { + $obj['acl'] = $this->getChildACL(); + $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + } + return $children; + + } + + /** + * This method receives a list of paths in it's first argument. + * It must return an array with Node objects. + * + * If any children are not found, you do not have to return them. + * + * @param string[] $paths + * @return array + */ + function getMultipleChildren(array $paths) { + + $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); + $children = []; + foreach ($objs as $obj) { + $obj['acl'] = $this->getChildACL(); + $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + } + return $children; + + } + + /** + * Checks if a child-node exists. + * + * @param string $name + * @return bool + */ + function childExists($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); + if (!$obj) + return false; + else + return true; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in calendars. + * + * @param string $name + * @return void + */ + function createDirectory($name) { + + throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid ICalendar string. + * + * @param string $name + * @param resource $calendarData + * @return string|null + */ + function createFile($name, $calendarData = null) { + + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); + } + return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $calendarData); + + } + + /** + * Deletes the calendar. + * + * @return void + */ + function delete() { + + $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); + + } + + /** + * Renames the calendar. Note that most calendars use the + * {DAV:}displayname to display a name to display a name. + * + * @param string $newName + * @return void + */ + function setName($newName) { + + throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return null + */ + function getLastModified() { + + return null; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->calendarInfo['principaluri']; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + $acl = [ + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + + ]; + if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->getOwner(), + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ]; + } + + return $acl; + + } + + /** + * This method returns the ACL's for calendar objects in this calendar. + * The result of this method automatically gets passed to the + * calendar-object nodes in the calendar. + * + * @return array + */ + function getChildACL() { + + $acl = [ + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'protected' => true, + ], + + ]; + if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->getOwner(), + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ]; + + } + return $acl; + + } + + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre\CalDAV\CalendarQueryParser. + * + * @param array $filters + * @return array + */ + function calendarQuery(array $filters) { + + return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); + + } + + /** + * This method returns the current sync-token for this collection. + * This can be any string. + * + * If null is returned from this function, the plugin assumes there's no + * sync information available. + * + * @return string|null + */ + function getSyncToken() { + + if ( + $this->caldavBackend instanceof Backend\SyncSupport && + isset($this->calendarInfo['{DAV:}sync-token']) + ) { + return $this->calendarInfo['{DAV:}sync-token']; + } + if ( + $this->caldavBackend instanceof Backend\SyncSupport && + isset($this->calendarInfo['{http://sabredav.org/ns}sync-token']) + ) { + return $this->calendarInfo['{http://sabredav.org/ns}sync-token']; + } + + } + + /** + * The getChanges method returns all the changes that have happened, since + * the specified syncToken and the current collection. + * + * This function should return an array, such as the following: + * + * [ + * 'syncToken' => 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'modified.txt', + * ], + * 'deleted' => [ + * 'foo.php.bak', + * 'old.txt' + * ] + * ]; + * + * The syncToken property should reflect the *current* syncToken of the + * collection, as reported getSyncToken(). This is needed here too, to + * ensure the operation is atomic. + * + * If the syncToken is specified as null, this is an initial sync, and all + * members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The second argument is basically the 'depth' of the report. If it's 1, + * you only have to report changes that happened only directly in immediate + * descendants. If it's 2, it should also include changes from the nodes + * below the child collections. (grandchildren) + * + * The third (optional) argument allows a client to specify how many + * results should be returned at most. If the limit is not specified, it + * should be treated as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChanges($syncToken, $syncLevel, $limit = null) { + + if (!$this->caldavBackend instanceof Backend\SyncSupport) { + return null; + } + + return $this->caldavBackend->getChangesForCalendar( + $this->calendarInfo['id'], + $syncToken, + $syncLevel, + $limit + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php new file mode 100644 index 00000000000..ffd7f72fb69 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php @@ -0,0 +1,378 @@ +caldavBackend = $caldavBackend; + $this->principalInfo = $principalInfo; + + } + + /** + * Returns the name of this object + * + * @return string + */ + function getName() { + + list(, $name) = URLUtil::splitPath($this->principalInfo['uri']); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + function setName($name) { + + throw new DAV\Exception\Forbidden(); + + } + + /** + * Deletes this object + * + * @return void + */ + function delete() { + + throw new DAV\Exception\Forbidden(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + function createFile($filename, $data = null) { + + throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + function createDirectory($filename) { + + throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @return Calendar + */ + function getChild($name) { + + // Special nodes + if ($name === 'inbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { + return new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); + } + if ($name === 'outbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { + return new Schedule\Outbox($this->principalInfo['uri']); + } + if ($name === 'notifications' && $this->caldavBackend instanceof Backend\NotificationSupport) { + return new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); + } + + // Calendars + foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) { + if ($calendar['uri'] === $name) { + if ($this->caldavBackend instanceof Backend\SharingSupport) { + return new SharedCalendar($this->caldavBackend, $calendar); + } else { + return new Calendar($this->caldavBackend, $calendar); + } + } + } + + if ($this->caldavBackend instanceof Backend\SubscriptionSupport) { + foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { + if ($subscription['uri'] === $name) { + return new Subscriptions\Subscription($this->caldavBackend, $subscription); + } + } + + } + + throw new NotFound('Node with name \'' . $name . '\' could not be found'); + + } + + /** + * Checks if a calendar exists. + * + * @param string $name + * @return bool + */ + function childExists($name) { + + try { + return !!$this->getChild($name); + } catch (NotFound $e) { + return false; + } + + } + + /** + * Returns a list of calendars + * + * @return array + */ + function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); + $objs = []; + foreach ($calendars as $calendar) { + if ($this->caldavBackend instanceof Backend\SharingSupport) { + $objs[] = new SharedCalendar($this->caldavBackend, $calendar); + } else { + $objs[] = new Calendar($this->caldavBackend, $calendar); + } + } + + if ($this->caldavBackend instanceof Backend\SchedulingSupport) { + $objs[] = new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); + $objs[] = new Schedule\Outbox($this->principalInfo['uri']); + } + + // We're adding a notifications node, if it's supported by the backend. + if ($this->caldavBackend instanceof Backend\NotificationSupport) { + $objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); + } + + // If the backend supports subscriptions, we'll add those as well, + if ($this->caldavBackend instanceof Backend\SubscriptionSupport) { + foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { + $objs[] = new Subscriptions\Subscription($this->caldavBackend, $subscription); + } + } + + return $objs; + + } + + /** + * Creates a new calendar or subscription. + * + * @param string $name + * @param MkCol $mkCol + * @throws DAV\Exception\InvalidResourceType + * @return void + */ + function createExtendedCollection($name, MkCol $mkCol) { + + $isCalendar = false; + $isSubscription = false; + foreach ($mkCol->getResourceType() as $rt) { + switch ($rt) { + case '{DAV:}collection' : + case '{http://calendarserver.org/ns/}shared-owner' : + // ignore + break; + case '{urn:ietf:params:xml:ns:caldav}calendar' : + $isCalendar = true; + break; + case '{http://calendarserver.org/ns/}subscribed' : + $isSubscription = true; + break; + default : + throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt); + } + } + + $properties = $mkCol->getRemainingValues(); + $mkCol->setRemainingResultCode(201); + + if ($isSubscription) { + if (!$this->caldavBackend instanceof Backend\SubscriptionSupport) { + throw new DAV\Exception\InvalidResourceType('This backend does not support subscriptions'); + } + $this->caldavBackend->createSubscription($this->principalInfo['uri'], $name, $properties); + + } elseif ($isCalendar) { + $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); + + } else { + throw new DAV\Exception\InvalidResourceType('You can only create calendars and subscriptions in this collection'); + + } + + } + + /** + * Returns the owner of the calendar home. + * + * @return string + */ + function getOwner() { + + return $this->principalInfo['uri']; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return [ + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', + 'protected' => true, + ], + + ]; + + } + + + /** + * This method is called when a user replied to a request to share. + * + * This method should return the url of the newly created calendar if the + * share was accepted. + * + * @param string $href The sharee who is replying (often a mailto: address) + * @param int $status One of the SharingPlugin::STATUS_* constants + * @param string $calendarUri The url to the calendar thats being shared + * @param string $inReplyTo The unique id this message is a response to + * @param string $summary A description of the reply + * @return null|string + */ + function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { + + if (!$this->caldavBackend instanceof Backend\SharingSupport) { + throw new DAV\Exception\NotImplemented('Sharing support is not implemented by this backend.'); + } + + return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary); + + } + + /** + * Searches through all of a users calendars and calendar objects to find + * an object with a specific UID. + * + * This method should return the path to this object, relative to the + * calendar home, so this path usually only contains two parts: + * + * calendarpath/objectpath.ics + * + * If the uid is not found, return null. + * + * This method should only consider * objects that the principal owns, so + * any calendars owned by other principals that also appear in this + * collection should be ignored. + * + * @param string $uid + * @return string|null + */ + function getCalendarObjectByUID($uid) { + + return $this->caldavBackend->getCalendarObjectByUID($this->principalInfo['uri'], $uid); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php new file mode 100644 index 00000000000..9d6532a35b1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php @@ -0,0 +1,237 @@ +caldavBackend = $caldavBackend; + + if (!isset($objectData['uri'])) { + throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); + } + + $this->calendarInfo = $calendarInfo; + $this->objectData = $objectData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + function getName() { + + return $this->objectData['uri']; + + } + + /** + * Returns the ICalendar-formatted object + * + * @return string + */ + function get() { + + // Pre-populating the 'calendardata' is optional, if we don't have it + // already we fetch it from the backend. + if (!isset($this->objectData['calendardata'])) { + $this->objectData = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $this->objectData['uri']); + } + return $this->objectData['calendardata']; + + } + + /** + * Updates the ICalendar-formatted object + * + * @param string|resource $calendarData + * @return string + */ + function put($calendarData) { + + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); + } + $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'], $this->objectData['uri'], $calendarData); + $this->objectData['calendardata'] = $calendarData; + $this->objectData['etag'] = $etag; + + return $etag; + + } + + /** + * Deletes the calendar object + * + * @return void + */ + function delete() { + + $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'], $this->objectData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + function getContentType() { + + $mime = 'text/calendar; charset=utf-8'; + if (isset($this->objectData['component']) && $this->objectData['component']) { + $mime .= '; component=' . $this->objectData['component']; + } + return $mime; + + } + + /** + * Returns an ETag for this object. + * + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * @return string + */ + function getETag() { + + if (isset($this->objectData['etag'])) { + return $this->objectData['etag']; + } else { + return '"' . md5($this->get()) . '"'; + } + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return int + */ + function getLastModified() { + + return $this->objectData['lastmodified']; + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + function getSize() { + + if (array_key_exists('size', $this->objectData)) { + return $this->objectData['size']; + } else { + return strlen($this->get()); + } + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->calendarInfo['principaluri']; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + // An alternative acl may be specified in the object data. + if (isset($this->objectData['acl'])) { + return $this->objectData['acl']; + } + + // The default ACL + return [ + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ], + + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php new file mode 100644 index 00000000000..df8008fe276 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php @@ -0,0 +1,375 @@ +name !== $filters['name']) { + return false; + } + + return + $this->validateCompFilters($vObject, $filters['comp-filters']) && + $this->validatePropFilters($vObject, $filters['prop-filters']); + + + } + + /** + * This method checks the validity of comp-filters. + * + * A list of comp-filters needs to be specified. Also the parent of the + * component we're checking should be specified, not the component to check + * itself. + * + * @param VObject\Component $parent + * @param array $filters + * @return bool + */ + protected function validateCompFilters(VObject\Component $parent, array $filters) { + + foreach ($filters as $filter) { + + $isDefined = isset($parent->{$filter['name']}); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach ($parent->{$filter['name']} as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['comp-filters'] && !$filter['prop-filters']) { + continue; + } + + // If there are sub-filters, we need to find at least one component + // for which the subfilters hold true. + foreach ($parent->{$filter['name']} as $subComponent) { + + if ( + $this->validateCompFilters($subComponent, $filter['comp-filters']) && + $this->validatePropFilters($subComponent, $filter['prop-filters'])) { + // We had a match, so this comp-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-comp-filters or + // sub-prop-filters and there was no match. This means this filter + // needs to return false. + return false; + + } + + // If we got here it means we got through all comp-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of prop-filters. + * + * A list of prop-filters needs to be specified. Also the parent of the + * property we're checking should be specified, not the property to check + * itself. + * + * @param VObject\Component $parent + * @param array $filters + * @return bool + */ + protected function validatePropFilters(VObject\Component $parent, array $filters) { + + foreach ($filters as $filter) { + + $isDefined = isset($parent->{$filter['name']}); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach ($parent->{$filter['name']} as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['param-filters'] && !$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one property + // for which the subfilters hold true. + foreach ($parent->{$filter['name']} as $subComponent) { + + if ( + $this->validateParamFilters($subComponent, $filter['param-filters']) && + (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match'])) + ) { + // We had a match, so this prop-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-param-filters or + // text-match filters and there was no match. This means the + // filter needs to return false. + return false; + + } + + // If we got here it means we got through all prop-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of param-filters. + * + * A list of param-filters needs to be specified. Also the parent of the + * parameter we're checking should be specified, not the parameter to check + * itself. + * + * @param VObject\Property $parent + * @param array $filters + * @return bool + */ + protected function validateParamFilters(VObject\Property $parent, array $filters) { + + foreach ($filters as $filter) { + + $isDefined = isset($parent[$filter['name']]); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if (!$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one parameter + // for which the subfilters hold true. + foreach ($parent[$filter['name']]->getParts() as $paramPart) { + + if ($this->validateTextMatch($paramPart, $filter['text-match'])) { + // We had a match, so this param-filter succeeds + continue 2; + } + + } + + // If we got here it means there was a text-match filter and there + // were no matches. This means the filter needs to return false. + return false; + + } + + // If we got here it means we got through all param-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of a text-match. + * + * A single text-match should be specified as well as the specific property + * or parameter we need to validate. + * + * @param VObject\Node|string $check Value to check against. + * @param array $textMatch + * @return bool + */ + protected function validateTextMatch($check, array $textMatch) { + + if ($check instanceof VObject\Node) { + $check = $check->getValue(); + } + + $isMatching = \Sabre\DAV\StringUtil::textMatch($check, $textMatch['value'], $textMatch['collation']); + + return ($textMatch['negate-condition'] xor $isMatching); + + } + + /** + * Validates if a component matches the given time range. + * + * This is all based on the rules specified in rfc4791, which are quite + * complex. + * + * @param VObject\Node $component + * @param DateTime $start + * @param DateTime $end + * @return bool + */ + protected function validateTimeRange(VObject\Node $component, $start, $end) { + + if (is_null($start)) { + $start = new DateTime('1900-01-01'); + } + if (is_null($end)) { + $end = new DateTime('3000-01-01'); + } + + switch ($component->name) { + + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + + return $component->isInTimeRange($start, $end); + + case 'VALARM' : + + // If the valarm is wrapped in a recurring event, we need to + // expand the recursions, and validate each. + // + // Our datamodel doesn't easily allow us to do this straight + // in the VALARM component code, so this is a hack, and an + // expensive one too. + if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { + + // Fire up the iterator! + $it = new VObject\Recur\EventIterator($component->parent->parent, (string)$component->parent->UID); + while ($it->valid()) { + $expandedEvent = $it->getEventObject(); + + // We need to check from these expanded alarms, which + // one is the first to trigger. Based on this, we can + // determine if we can 'give up' expanding events. + $firstAlarm = null; + if ($expandedEvent->VALARM !== null) { + foreach ($expandedEvent->VALARM as $expandedAlarm) { + + $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); + if ($expandedAlarm->isInTimeRange($start, $end)) { + return true; + } + + if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { + // This is an alarm with a non-relative trigger + // time, likely created by a buggy client. The + // implication is that every alarm in this + // recurring event trigger at the exact same + // time. It doesn't make sense to traverse + // further. + } else { + // We store the first alarm as a means to + // figure out when we can stop traversing. + if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { + $firstAlarm = $effectiveTrigger; + } + } + } + } + if (is_null($firstAlarm)) { + // No alarm was found. + // + // Or technically: No alarm that will change for + // every instance of the recurrence was found, + // which means we can assume there was no match. + return false; + } + if ($firstAlarm > $end) { + return false; + } + $it->next(); + } + return false; + } else { + return $component->isInTimeRange($start, $end); + } + + case 'VFREEBUSY' : + throw new \Sabre\DAV\Exception\NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); + + case 'COMPLETED' : + case 'CREATED' : + case 'DTEND' : + case 'DTSTAMP' : + case 'DTSTART' : + case 'DUE' : + case 'LAST-MODIFIED' : + return ($start <= $component->getDateTime() && $end >= $component->getDateTime()); + + + + default : + throw new \Sabre\DAV\Exception\BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php new file mode 100644 index 00000000000..1d6b2ac9ff8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php @@ -0,0 +1,80 @@ +caldavBackend = $caldavBackend; + + } + + /** + * Returns the nodename + * + * We're overriding this, because the default will be the 'principalPrefix', + * and we want it to be Sabre\CalDAV\Plugin::CALENDAR_ROOT + * + * @return string + */ + function getName() { + + return Plugin::CALENDAR_ROOT; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return \Sabre\DAV\INode + */ + function getChildForPrincipal(array $principal) { + + return new CalendarHome($this->caldavBackend, $principal); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php new file mode 100644 index 00000000000..7aff2edab55 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php @@ -0,0 +1,35 @@ +ownerDocument; + + $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV, 'cal:supported-calendar-component'); + $errorNode->appendChild($np); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php new file mode 100644 index 00000000000..fc8b971f3b4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php @@ -0,0 +1,378 @@ +server = $server; + $server->on('method:GET', [$this, 'httpGet'], 90); + $server->on('browserButtonActions', function($path, $node, &$actions) { + if ($node instanceof ICalendar) { + $actions .= ''; + } + }); + + } + + /** + * Intercepts GET requests on calendar urls ending with ?export. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $queryParams = $request->getQueryParameters(); + if (!array_key_exists('export', $queryParams)) return; + + $path = $request->getPath(); + + $node = $this->server->getProperties($path, [ + '{DAV:}resourcetype', + '{DAV:}displayname', + '{http://sabredav.org/ns}sync-token', + '{DAV:}sync-token', + '{http://apple.com/ns/ical/}calendar-color', + ]); + + if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{' . Plugin::NS_CALDAV . '}calendar')) { + return; + } + // Marking the transactionType, for logging purposes. + $this->server->transactionType = 'get-calendar-export'; + + $properties = $node; + + $start = null; + $end = null; + $expand = false; + $componentType = false; + if (isset($queryParams['start'])) { + if (!ctype_digit($queryParams['start'])) { + throw new BadRequest('The start= parameter must contain a unix timestamp'); + } + $start = DateTime::createFromFormat('U', $queryParams['start']); + } + if (isset($queryParams['end'])) { + if (!ctype_digit($queryParams['end'])) { + throw new BadRequest('The end= parameter must contain a unix timestamp'); + } + $end = DateTime::createFromFormat('U', $queryParams['end']); + } + if (isset($queryParams['expand']) && !!$queryParams['expand']) { + if (!$start || !$end) { + throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.'); + } + $expand = true; + $componentType = 'VEVENT'; + } + if (isset($queryParams['componentType'])) { + if (!in_array($queryParams['componentType'], ['VEVENT', 'VTODO', 'VJOURNAL'])) { + throw new BadRequest('You are not allowed to search for components of type: ' . $queryParams['componentType'] . ' here'); + } + $componentType = $queryParams['componentType']; + } + + $format = \Sabre\HTTP\Util::Negotiate( + $request->getHeader('Accept'), + [ + 'text/calendar', + 'application/calendar+json', + ] + ); + + if (isset($queryParams['accept'])) { + if ($queryParams['accept'] === 'application/calendar+json' || $queryParams['accept'] === 'jcal') { + $format = 'application/calendar+json'; + } + } + if (!$format) { + $format = 'text/calendar'; + } + + $this->generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, $response); + + // Returning false to break the event chain + return false; + + } + + /** + * This method is responsible for generating the actual, full response. + * + * @param string $path + * @param DateTime|null $start + * @param DateTime|null $end + * @param bool $expand + * @param string $componentType + * @param string $format + * @param array $properties + * @param ResponseInterface $response + */ + protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response) { + + $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data'; + $calendarNode = $this->server->tree->getNodeForPath($path); + + $blobs = []; + if ($start || $end || $componentType) { + + // If there was a start or end filter, we need to enlist + // calendarQuery for speed. + $queryResult = $calendarNode->calendarQuery([ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => $componentType, + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => $start, + 'end' => $end, + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]); + + // queryResult is just a list of base urls. We need to prefix the + // calendar path. + $queryResult = array_map( + function($item) use ($path) { + return $path . '/' . $item; + }, + $queryResult + ); + $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]); + unset($queryResult); + + } else { + $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1); + } + + // Flattening the arrays + foreach ($nodes as $node) { + if (isset($node[200][$calDataProp])) { + $blobs[$node['href']] = $node[200][$calDataProp]; + } + } + unset($nodes); + + $mergedCalendar = $this->mergeObjects( + $properties, + $blobs + ); + + if ($expand) { + $calendarTimeZone = null; + // We're expanding, and for that we need to figure out the + // calendar's timezone. + $tzProp = '{' . Plugin::NS_CALDAV . '}calendar-timezone'; + $tzResult = $this->server->getProperties($path, [$tzProp]); + if (isset($tzResult[$tzProp])) { + // This property contains a VCALENDAR with a single + // VTIMEZONE. + $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]); + $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); + // Destroy circular references to PHP will GC the object. + $vtimezoneObj->destroy(); + unset($vtimezoneObj); + } else { + // Defaulting to UTC. + $calendarTimeZone = new DateTimeZone('UTC'); + } + + $mergedCalendar = $mergedCalendar->expand($start, $end, $calendarTimeZone); + } + + $filenameExtension = '.ics'; + + switch ($format) { + case 'text/calendar' : + $mergedCalendar = $mergedCalendar->serialize(); + $filenameExtension = '.ics'; + break; + case 'application/calendar+json' : + $mergedCalendar = json_encode($mergedCalendar->jsonSerialize()); + $filenameExtension = '.json'; + break; + } + + $filename = preg_replace( + '/[^a-zA-Z0-9-_ ]/um', + '', + $calendarNode->getName() + ); + $filename .= '-' . date('Y-m-d') . $filenameExtension; + + $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); + $response->setHeader('Content-Type', $format); + + $response->setStatus(200); + $response->setBody($mergedCalendar); + + } + + /** + * Merges all calendar objects, and builds one big iCalendar blob. + * + * @param array $properties Some CalDAV properties + * @param array $inputObjects + * @return VObject\Component\VCalendar + */ + function mergeObjects(array $properties, array $inputObjects) { + + $calendar = new VObject\Component\VCalendar(); + $calendar->VERSION = '2.0'; + if (DAV\Server::$exposeVersion) { + $calendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; + } else { + $calendar->PRODID = '-//SabreDAV//SabreDAV//EN'; + } + if (isset($properties['{DAV:}displayname'])) { + $calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname']; + } + if (isset($properties['{http://apple.com/ns/ical/}calendar-color'])) { + $calendar->{'X-APPLE-CALENDAR-COLOR'} = $properties['{http://apple.com/ns/ical/}calendar-color']; + } + + $collectedTimezones = []; + + $timezones = []; + $objects = []; + + foreach ($inputObjects as $href => $inputObject) { + + $nodeComp = VObject\Reader::read($inputObject); + + foreach ($nodeComp->children() as $child) { + + switch ($child->name) { + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + $objects[] = clone $child; + break; + + // VTIMEZONE is special, because we need to filter out the duplicates + case 'VTIMEZONE' : + // Naively just checking tzid. + if (in_array((string)$child->TZID, $collectedTimezones)) continue; + + $timezones[] = clone $child; + $collectedTimezones[] = $child->TZID; + break; + + } + + } + // Destroy circular references to PHP will GC the object. + $nodeComp->destroy(); + unset($nodeComp); + + } + + foreach ($timezones as $tz) $calendar->add($tz); + foreach ($objects as $obj) $calendar->add($obj); + + return $calendar; + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using \Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'ics-export'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds the ability to export CalDAV calendars as a single iCalendar file.', + 'link' => 'http://sabre.io/dav/ics-export-plugin/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php new file mode 100644 index 00000000000..7cf4b12561a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php @@ -0,0 +1,18 @@ +caldavBackend = $caldavBackend; + $this->principalUri = $principalUri; + + } + + /** + * Returns all notifications for a principal + * + * @return array + */ + function getChildren() { + + $children = []; + $notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri); + + foreach ($notifications as $notification) { + + $children[] = new Node( + $this->caldavBackend, + $this->principalUri, + $notification + ); + } + + return $children; + + } + + /** + * Returns the name of this object + * + * @return string + */ + function getName() { + + return 'notifications'; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->principalUri; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php new file mode 100644 index 00000000000..008e87435a0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php @@ -0,0 +1,23 @@ +caldavBackend = $caldavBackend; + $this->principalUri = $principalUri; + $this->notification = $notification; + + } + + /** + * Returns the path name for this notification + * + * @return string + */ + function getName() { + + return $this->notification->getId() . '.xml'; + + } + + /** + * Returns the etag for the notification. + * + * The etag must be surrounded by litteral double-quotes. + * + * @return string + */ + function getETag() { + + return $this->notification->getETag(); + + } + + /** + * This method must return an xml element, using the + * Sabre\CalDAV\Xml\Notification\NotificationInterface classes. + * + * @return NotificationInterface + */ + function getNotificationType() { + + return $this->notification; + + } + + /** + * Deletes this notification + * + * @return void + */ + function delete() { + + $this->caldavBackend->deleteNotification($this->getOwner(), $this->notification); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->principalUri; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php new file mode 100644 index 00000000000..e742351f5be --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php @@ -0,0 +1,180 @@ +server = $server; + $server->on('method:GET', [$this, 'httpGet'], 90); + $server->on('propFind', [$this, 'propFind']); + + $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; + + array_push($server->protectedProperties, + '{' . self::NS_CALENDARSERVER . '}notification-URL', + '{' . self::NS_CALENDARSERVER . '}notificationtype' + ); + + } + + /** + * PropFind + * + * @param PropFind $propFind + * @param BaseINode $node + * @return void + */ + function propFind(PropFind $propFind, BaseINode $node) { + + $caldavPlugin = $this->server->getPlugin('caldav'); + + if ($node instanceof DAVACL\IPrincipal) { + + $principalUrl = $node->getPrincipalUrl(); + + // notification-URL property + $propFind->handle('{' . self::NS_CALENDARSERVER . '}notification-URL', function() use ($principalUrl, $caldavPlugin) { + + $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl) . '/notifications/'; + return new DAV\Xml\Property\Href($notificationPath); + + }); + + } + + if ($node instanceof INode) { + + $propFind->handle( + '{' . self::NS_CALENDARSERVER . '}notificationtype', + [$node, 'getNotificationType'] + ); + + } + + } + + /** + * This event is triggered before the usual GET request handler. + * + * We use this to intercept GET calls to notification nodes, and return the + * proper response. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + try { + $node = $this->server->tree->getNodeForPath($path); + } catch (DAV\Exception\NotFound $e) { + return; + } + + if (!$node instanceof INode) + return; + + $writer = $this->server->xml->getWriter(); + $writer->contextUri = $this->server->getBaseUri(); + $writer->openMemory(); + $writer->startDocument('1.0', 'UTF-8'); + $writer->startElement('{http://calendarserver.org/ns/}notification'); + $node->getNotificationType()->xmlSerializeFull($writer); + $writer->endElement(); + + $response->setHeader('Content-Type', 'application/xml'); + $response->setHeader('ETag', $node->getETag()); + $response->setStatus(200); + $response->setBody($writer->outputMemory()); + + // Return false to break the event chain. + return false; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds support for caldav-notifications, which is required to enable caldav-sharing.', + 'link' => 'http://sabre.io/dav/caldav-sharing/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php new file mode 100644 index 00000000000..def11d52dff --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php @@ -0,0 +1,1068 @@ +server->tree->getNodeForPath($parent); + + if ($node instanceof DAV\IExtendedCollection) { + try { + $node->getChild($name); + } catch (DAV\Exception\NotFound $e) { + return ['MKCALENDAR']; + } + } + return []; + + } + + /** + * Returns the path to a principal's calendar home. + * + * The return url must not end with a slash. + * This function should return null in case a principal did not have + * a calendar home. + * + * @param string $principalUrl + * @return string + */ + function getCalendarHomeForPrincipal($principalUrl) { + + // The default behavior for most sabre/dav servers is that there is a + // principals root node, which contains users directly under it. + // + // This function assumes that there are two components in a principal + // path. If there's more, we don't return a calendar home. This + // excludes things like the calendar-proxy-read principal (which it + // should). + $parts = explode('/', trim($principalUrl, '/')); + if (count($parts) !== 2) return; + if ($parts[0] !== 'principals') return; + + return self::CALENDAR_ROOT . '/' . $parts[1]; + + } + + /** + * Returns a list of features for the DAV: HTTP header. + * + * @return array + */ + function getFeatures() { + + return ['calendar-access', 'calendar-proxy']; + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'caldav'; + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + function getSupportedReportSet($uri) { + + $node = $this->server->tree->getNodeForPath($uri); + + $reports = []; + if ($node instanceof ICalendarObjectContainer || $node instanceof ICalendarObject) { + $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; + $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; + } + if ($node instanceof ICalendar) { + $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; + } + // iCal has a bug where it assumes that sync support is enabled, only + // if we say we support it on the calendar-home, even though this is + // not actually the case. + if ($node instanceof CalendarHome && $this->server->getPlugin('sync')) { + $reports[] = '{DAV:}sync-collection'; + } + return $reports; + + } + + /** + * Initializes the plugin + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + $this->server = $server; + + $server->on('method:MKCALENDAR', [$this, 'httpMkCalendar']); + $server->on('report', [$this, 'report']); + $server->on('propFind', [$this, 'propFind']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); + $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); + $server->on('afterMethod:GET', [$this, 'httpAfterGET']); + $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']); + + $server->xml->namespaceMap[self::NS_CALDAV] = 'cal'; + $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs'; + + $server->xml->elementMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; + $server->xml->elementMap['{' . self::NS_CALDAV . '}calendar-query'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport'; + $server->xml->elementMap['{' . self::NS_CALDAV . '}calendar-multiget'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport'; + $server->xml->elementMap['{' . self::NS_CALDAV . '}free-busy-query'] = 'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport'; + $server->xml->elementMap['{' . self::NS_CALDAV . '}mkcalendar'] = 'Sabre\\CalDAV\\Xml\\Request\\MkCalendar'; + $server->xml->elementMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp'; + $server->xml->elementMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; + + $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; + + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; + + array_push($server->protectedProperties, + + '{' . self::NS_CALDAV . '}supported-calendar-component-set', + '{' . self::NS_CALDAV . '}supported-calendar-data', + '{' . self::NS_CALDAV . '}max-resource-size', + '{' . self::NS_CALDAV . '}min-date-time', + '{' . self::NS_CALDAV . '}max-date-time', + '{' . self::NS_CALDAV . '}max-instances', + '{' . self::NS_CALDAV . '}max-attendees-per-instance', + '{' . self::NS_CALDAV . '}calendar-home-set', + '{' . self::NS_CALDAV . '}supported-collation-set', + '{' . self::NS_CALDAV . '}calendar-data', + + // CalendarServer extensions + '{' . self::NS_CALENDARSERVER . '}getctag', + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for' + + ); + + if ($aclPlugin = $server->getPlugin('acl')) { + $aclPlugin->principalSearchPropertySet['{' . self::NS_CALDAV . '}calendar-user-address-set'] = 'Calendar address'; + } + } + + /** + * This functions handles REPORT requests specific to CalDAV + * + * @param string $reportName + * @param mixed $report + * @param mixed $path + * @return bool + */ + function report($reportName, $report, $path) { + + switch ($reportName) { + case '{' . self::NS_CALDAV . '}calendar-multiget' : + $this->server->transactionType = 'report-calendar-multiget'; + $this->calendarMultiGetReport($report); + return false; + case '{' . self::NS_CALDAV . '}calendar-query' : + $this->server->transactionType = 'report-calendar-query'; + $this->calendarQueryReport($report); + return false; + case '{' . self::NS_CALDAV . '}free-busy-query' : + $this->server->transactionType = 'report-free-busy-query'; + $this->freeBusyQueryReport($report); + return false; + + } + + + } + + /** + * This function handles the MKCALENDAR HTTP method, which creates + * a new calendar. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpMkCalendar(RequestInterface $request, ResponseInterface $response) { + + $body = $request->getBodyAsString(); + $path = $request->getPath(); + + $properties = []; + + if ($body) { + + try { + $mkcalendar = $this->server->xml->expect( + '{urn:ietf:params:xml:ns:caldav}mkcalendar', + $body + ); + } catch (\Sabre\Xml\ParseException $e) { + throw new BadRequest($e->getMessage(), null, $e); + } + $properties = $mkcalendar->getProperties(); + + } + + // iCal abuses MKCALENDAR since iCal 10.9.2 to create server-stored + // subscriptions. Before that it used MKCOL which was the correct way + // to do this. + // + // If the body had a {DAV:}resourcetype, it means we stumbled upon this + // request, and we simply use it instead of the pre-defined list. + if (isset($properties['{DAV:}resourcetype'])) { + $resourceType = $properties['{DAV:}resourcetype']->getValue(); + } else { + $resourceType = ['{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar']; + } + + $this->server->createCollection($path, new MkCol($resourceType, $properties)); + + $response->setStatus(201); + $response->setHeader('Content-Length', 0); + + // This breaks the method chain. + return false; + } + + /** + * PropFind + * + * This method handler is invoked before any after properties for a + * resource are fetched. This allows us to add in any CalDAV specific + * properties. + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFind(DAV\PropFind $propFind, DAV\INode $node) { + + $ns = '{' . self::NS_CALDAV . '}'; + + if ($node instanceof ICalendarObjectContainer) { + + $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); + $propFind->handle($ns . 'supported-calendar-data', function() { + return new Xml\Property\SupportedCalendarData(); + }); + $propFind->handle($ns . 'supported-collation-set', function() { + return new Xml\Property\SupportedCollationSet(); + }); + + } + + if ($node instanceof DAVACL\IPrincipal) { + + $principalUrl = $node->getPrincipalUrl(); + + $propFind->handle('{' . self::NS_CALDAV . '}calendar-home-set', function() use ($principalUrl) { + + $calendarHomePath = $this->getCalendarHomeForPrincipal($principalUrl); + if (is_null($calendarHomePath)) return null; + return new LocalHref($calendarHomePath . '/'); + + }); + // The calendar-user-address-set property is basically mapped to + // the {DAV:}alternate-URI-set property. + $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-address-set', function() use ($node) { + $addresses = $node->getAlternateUriSet(); + $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/'; + return new LocalHref($addresses); + }); + // For some reason somebody thought it was a good idea to add + // another one of these properties. We're supporting it too. + $propFind->handle('{' . self::NS_CALENDARSERVER . '}email-address-set', function() use ($node) { + $addresses = $node->getAlternateUriSet(); + $emails = []; + foreach ($addresses as $address) { + if (substr($address, 0, 7) === 'mailto:') { + $emails[] = substr($address, 7); + } + } + return new Xml\Property\EmailAddressSet($emails); + }); + + // These two properties are shortcuts for ical to easily find + // other principals this principal has access to. + $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; + $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; + + if ($propFind->getStatus($propRead) === 404 || $propFind->getStatus($propWrite) === 404) { + + $aclPlugin = $this->server->getPlugin('acl'); + $membership = $aclPlugin->getPrincipalMembership($propFind->getPath()); + $readList = []; + $writeList = []; + + foreach ($membership as $group) { + + $groupNode = $this->server->tree->getNodeForPath($group); + + $listItem = Uri\split($group)[0] . '/'; + + // If the node is either ap proxy-read or proxy-write + // group, we grab the parent principal and add it to the + // list. + if ($groupNode instanceof Principal\IProxyRead) { + $readList[] = $listItem; + } + if ($groupNode instanceof Principal\IProxyWrite) { + $writeList[] = $listItem; + } + + } + + $propFind->set($propRead, new LocalHref($readList)); + $propFind->set($propWrite, new LocalHref($writeList)); + + } + + } // instanceof IPrincipal + + if ($node instanceof ICalendarObject) { + + // The calendar-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. + // Therefore we simply expose it as a property. + $propFind->handle('{' . self::NS_CALDAV . '}calendar-data', function() use ($node) { + $val = $node->get(); + if (is_resource($val)) + $val = stream_get_contents($val); + + // Taking out \r to not screw up the xml output + return str_replace("\r", "", $val); + + }); + + } + + } + + /** + * This function handles the calendar-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param CalendarMultiGetReport $report + * @return void + */ + function calendarMultiGetReport($report) { + + $needsJson = $report->contentType === 'application/calendar+json'; + + $timeZones = []; + $propertyList = []; + + $paths = array_map( + [$this->server, 'calculateUri'], + $report->hrefs + ); + + foreach ($this->server->getPropertiesForMultiplePaths($paths, $report->properties) as $uri => $objProps) { + + if (($needsJson || $report->expand) && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { + $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); + + if ($report->expand) { + // We're expanding, and for that we need to figure out the + // calendar's timezone. + list($calendarPath) = Uri\split($uri); + if (!isset($timeZones[$calendarPath])) { + // Checking the calendar-timezone property. + $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; + $tzResult = $this->server->getProperties($calendarPath, [$tzProp]); + if (isset($tzResult[$tzProp])) { + // This property contains a VCALENDAR with a single + // VTIMEZONE. + $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]); + $timeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); + } else { + // Defaulting to UTC. + $timeZone = new DateTimeZone('UTC'); + } + $timeZones[$calendarPath] = $timeZone; + } + + $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $timeZones[$calendarPath]); + } + if ($needsJson) { + $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); + } else { + $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + // Destroy circular references so PHP will garbage collect the + // object. + $vObject->destroy(); + } + + $propertyList[] = $objProps; + + } + + $prefer = $this->server->getHTTPPrefer(); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); + $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return'] === 'minimal')); + + } + + /** + * This function handles the calendar-query REPORT + * + * This report is used by clients to request calendar objects based on + * complex conditions. + * + * @param Xml\Request\CalendarQueryReport $report + * @return void + */ + function calendarQueryReport($report) { + + $path = $this->server->getRequestUri(); + + $needsJson = $report->contentType === 'application/calendar+json'; + + $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); + $depth = $this->server->getHTTPDepth(0); + + // The default result is an empty array + $result = []; + + $calendarTimeZone = null; + if ($report->expand) { + // We're expanding, and for that we need to figure out the + // calendar's timezone. + $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; + $tzResult = $this->server->getProperties($path, [$tzProp]); + if (isset($tzResult[$tzProp])) { + // This property contains a VCALENDAR with a single + // VTIMEZONE. + $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]); + $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); + + // Destroy circular references so PHP will garbage collect the + // object. + $vtimezoneObj->destroy(); + } else { + // Defaulting to UTC. + $calendarTimeZone = new DateTimeZone('UTC'); + } + } + + // The calendarobject was requested directly. In this case we handle + // this locally. + if ($depth == 0 && $node instanceof ICalendarObject) { + + $requestedCalendarData = true; + $requestedProperties = $report->properties; + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } + + $properties = $this->server->getPropertiesForPath( + $path, + $requestedProperties, + 0 + ); + + // This array should have only 1 element, the first calendar + // object. + $properties = current($properties); + + // If there wasn't any calendar-data returned somehow, we ignore + // this. + if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { + + $validator = new CalendarQueryValidator(); + + $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + if ($validator->validate($vObject, $report->filters)) { + + // If the client didn't require the calendar-data property, + // we won't give it back. + if (!$requestedCalendarData) { + unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + } else { + + + if ($report->expand) { + $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $calendarTimeZone); + } + if ($needsJson) { + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); + } elseif ($report->expand) { + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + } + + $result = [$properties]; + + } + // Destroy circular references so PHP will garbage collect the + // object. + $vObject->destroy(); + + } + + } + + if ($node instanceof ICalendarObjectContainer && $depth === 0) { + + if (strpos($this->server->httpRequest->getHeader('User-Agent'), 'MSFT-') === 0) { + // Microsoft clients incorrectly supplied depth as 0, when it actually + // should have set depth to 1. We're implementing a workaround here + // to deal with this. + // + // This targets at least the following clients: + // Windows 10 + // Windows Phone 8, 10 + $depth = 1; + } else { + throw new BadRequest('A calendar-query REPORT on a calendar with a Depth: 0 is undefined. Set Depth to 1'); + } + + } + + // If we're dealing with a calendar, the calendar itself is responsible + // for the calendar-query. + if ($node instanceof ICalendarObjectContainer && $depth == 1) { + + $nodePaths = $node->calendarQuery($report->filters); + + foreach ($nodePaths as $path) { + + list($properties) = + $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $report->properties); + + if (($needsJson || $report->expand)) { + $vObject = VObject\Reader::read($properties[200]['{' . self::NS_CALDAV . '}calendar-data']); + + if ($report->expand) { + $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $calendarTimeZone); + } + + if ($needsJson) { + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); + } else { + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + + // Destroy circular references so PHP will garbage collect the + // object. + $vObject->destroy(); + } + $result[] = $properties; + + } + + } + + $prefer = $this->server->getHTTPPrefer(); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); + $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal')); + + } + + /** + * This method is responsible for parsing the request and generating the + * response for the CALDAV:free-busy-query REPORT. + * + * @param Xml\Request\FreeBusyQueryReport $report + * @return void + */ + protected function freeBusyQueryReport(Xml\Request\FreeBusyQueryReport $report) { + + $uri = $this->server->getRequestUri(); + + $acl = $this->server->getPlugin('acl'); + if ($acl) { + $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy'); + } + + $calendar = $this->server->tree->getNodeForPath($uri); + if (!$calendar instanceof ICalendar) { + throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); + } + + $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; + + // Figuring out the default timezone for the calendar, for floating + // times. + $calendarProps = $this->server->getProperties($uri, [$tzProp]); + + if (isset($calendarProps[$tzProp])) { + $vtimezoneObj = VObject\Reader::read($calendarProps[$tzProp]); + $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); + // Destroy circular references so PHP will garbage collect the object. + $vtimezoneObj->destroy(); + } else { + $calendarTimeZone = new DateTimeZone('UTC'); + } + + // Doing a calendar-query first, to make sure we get the most + // performance. + $urls = $calendar->calendarQuery([ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => $report->start, + 'end' => $report->end, + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]); + + $objects = array_map(function($url) use ($calendar) { + $obj = $calendar->getChild($url)->get(); + return $obj; + }, $urls); + + $generator = new VObject\FreeBusyGenerator(); + $generator->setObjects($objects); + $generator->setTimeRange($report->start, $report->end); + $generator->setTimeZone($calendarTimeZone); + $result = $generator->getResult(); + $result = $result->serialize(); + + $this->server->httpResponse->setStatus(200); + $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); + $this->server->httpResponse->setHeader('Content-Length', strlen($result)); + $this->server->httpResponse->setBody($result); + + } + + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that CalDAV objects receive + * valid calendar data. + * + * @param string $path + * @param DAV\IFile $node + * @param resource $data + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @return void + */ + function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) { + + if (!$node instanceof ICalendarObject) + return; + + // We're onyl interested in ICalendarObject nodes that are inside of a + // real calendar. This is to avoid triggering validation and scheduling + // for non-calendars (such as an inbox). + list($parent) = Uri\split($path); + $parentNode = $this->server->tree->getNodeForPath($parent); + + if (!$parentNode instanceof ICalendar) + return; + + $this->validateICalendar( + $data, + $path, + $modified, + $this->server->httpRequest, + $this->server->httpResponse, + false + ); + + } + + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that newly created calendar + * objects contain valid calendar data. + * + * @param string $path + * @param resource $data + * @param DAV\ICollection $parentNode + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @return void + */ + function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) { + + if (!$parentNode instanceof ICalendar) + return; + + $this->validateICalendar( + $data, + $path, + $modified, + $this->server->httpRequest, + $this->server->httpResponse, + true + ); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @param string $path + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @param RequestInterface $request The http request. + * @param ResponseInterface $response The http response. + * @param bool $isNew Is the item a new one, or an update. + * @return void + */ + protected function validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + $before = $data; + + try { + + // If the data starts with a [, we can reasonably assume we're dealing + // with a jCal object. + if (substr($data, 0, 1) === '[') { + $vobj = VObject\Reader::readJson($data); + + // Converting $data back to iCalendar, as that's what we + // technically support everywhere. + $data = $vobj->serialize(); + $modified = true; + } else { + $vobj = VObject\Reader::read($data); + } + + } catch (VObject\ParseException $e) { + + throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); + + } + + if ($vobj->name !== 'VCALENDAR') { + throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.'); + } + + $sCCS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + + // Get the Supported Components for the target calendar + list($parentPath) = Uri\split($path); + $calendarProperties = $this->server->getProperties($parentPath, [$sCCS]); + + if (isset($calendarProperties[$sCCS])) { + $supportedComponents = $calendarProperties[$sCCS]->getValue(); + } else { + $supportedComponents = ['VJOURNAL', 'VTODO', 'VEVENT']; + } + + $foundType = null; + + foreach ($vobj->getComponents() as $component) { + switch ($component->name) { + case 'VTIMEZONE' : + continue 2; + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + $foundType = $component->name; + break; + } + + } + + if (!$foundType || !in_array($foundType, $supportedComponents)) { + throw new Exception\InvalidComponentType('iCalendar objects must at least have a component of type ' . implode(', ', $supportedComponents)); + } + + $options = VObject\Node::PROFILE_CALDAV; + $prefer = $this->server->getHTTPPrefer(); + + if ($prefer['handling'] !== 'strict') { + $options |= VObject\Node::REPAIR; + } + + $messages = $vobj->validate($options); + + $highestLevel = 0; + $warningMessage = null; + + // $messages contains a list of problems with the vcard, along with + // their severity. + foreach ($messages as $message) { + + if ($message['level'] > $highestLevel) { + // Recording the highest reported error level. + $highestLevel = $message['level']; + $warningMessage = $message['message']; + } + switch ($message['level']) { + + case 1 : + // Level 1 means that there was a problem, but it was repaired. + $modified = true; + break; + case 2 : + // Level 2 means a warning, but not critical + break; + case 3 : + // Level 3 means a critical error + throw new DAV\Exception\UnsupportedMediaType('Validation error in iCalendar: ' . $message['message']); + + } + + } + if ($warningMessage) { + $response->setHeader( + 'X-Sabre-Ew-Gross', + 'iCalendar validation warning: ' . $warningMessage + ); + } + + // We use an extra variable to allow event handles to tell us whether + // the object was modified or not. + // + // This helps us determine if we need to re-serialize the object. + $subModified = false; + + $this->server->emit( + 'calendarObjectChange', + [ + $request, + $response, + $vobj, + $parentPath, + &$subModified, + $isNew + ] + ); + + if ($modified || $subModified) { + // An event handler told us that it modified the object. + $data = $vobj->serialize(); + + // Using md5 to figure out if there was an *actual* change. + if (!$modified && strcmp($data, $before) !== 0) { + $modified = true; + } + + } + + // Destroy circular references so PHP will garbage collect the object. + $vobj->destroy(); + + } + + /** + * This method is triggered whenever a subsystem reqeuests the privileges + * that are supported on a particular node. + * + * @param INode $node + * @param array $supportedPrivilegeSet + */ + function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) { + + if ($node instanceof ICalendar) { + $supportedPrivilegeSet['{DAV:}read']['aggregates']['{' . self::NS_CALDAV . '}read-free-busy'] = [ + 'abstract' => false, + 'aggregates' => [], + ]; + } + } + + /** + * This method is used to generate HTML output for the + * DAV\Browser\Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param DAV\INode $node + * @param string $output + * @return bool + */ + function htmlActionsPanel(DAV\INode $node, &$output) { + + if (!$node instanceof CalendarHome) + return; + + $output .= '
    +

    Create new calendar

    + + +
    +
    + +
    + '; + + return false; + + } + + /** + * This event is triggered after GET requests. + * + * This is used to transform data into jCal, if this was requested. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpAfterGet(RequestInterface $request, ResponseInterface $response) { + + if (strpos($response->getHeader('Content-Type'), 'text/calendar') === false) { + return; + } + + $result = HTTP\Util::negotiate( + $request->getHeader('Accept'), + ['text/calendar', 'application/calendar+json'] + ); + + if ($result !== 'application/calendar+json') { + // Do nothing + return; + } + + // Transforming. + $vobj = VObject\Reader::read($response->getBody()); + + $jsonBody = json_encode($vobj->jsonSerialize()); + $response->setBody($jsonBody); + + // Destroy circular references so PHP will garbage collect the object. + $vobj->destroy(); + + $response->setHeader('Content-Type', 'application/calendar+json'); + $response->setHeader('Content-Length', strlen($jsonBody)); + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds support for CalDAV (rfc4791)', + 'link' => 'http://sabre.io/dav/caldav/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php new file mode 100644 index 00000000000..e19719a76d8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php @@ -0,0 +1,33 @@ +principalBackend, $principalInfo); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php new file mode 100644 index 00000000000..7dd3759329c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php @@ -0,0 +1,19 @@ +principalInfo = $principalInfo; + $this->principalBackend = $principalBackend; + + } + + /** + * Returns this principals name. + * + * @return string + */ + function getName() { + + return 'calendar-proxy-read'; + + } + + /** + * Returns the last modification time + * + * @return null + */ + function getLastModified() { + + return null; + + } + + /** + * Deletes the current node + * + * @throws DAV\Exception\Forbidden + * @return void + */ + function delete() { + + throw new DAV\Exception\Forbidden('Permission denied to delete node'); + + } + + /** + * Renames the node + * + * @param string $name The new name + * @throws DAV\Exception\Forbidden + * @return void + */ + function setName($name) { + + throw new DAV\Exception\Forbidden('Permission denied to rename file'); + + } + + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + function getAlternateUriSet() { + + return []; + + } + + /** + * Returns the full principal url + * + * @return string + */ + function getPrincipalUrl() { + + return $this->principalInfo['uri'] . '/' . $this->getName(); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + function getGroupMembership() { + + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void + */ + function setGroupMemberSet(array $principals) { + + $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); + + } + + /** + * Returns the displayname + * + * This should be a human readable name for the principal. + * If none is available, return the nodename. + * + * @return string + */ + function getDisplayName() { + + return $this->getName(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php new file mode 100644 index 00000000000..43dd9bf07bf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php @@ -0,0 +1,181 @@ +principalInfo = $principalInfo; + $this->principalBackend = $principalBackend; + + } + + /** + * Returns this principals name. + * + * @return string + */ + function getName() { + + return 'calendar-proxy-write'; + + } + + /** + * Returns the last modification time + * + * @return null + */ + function getLastModified() { + + return null; + + } + + /** + * Deletes the current node + * + * @throws DAV\Exception\Forbidden + * @return void + */ + function delete() { + + throw new DAV\Exception\Forbidden('Permission denied to delete node'); + + } + + /** + * Renames the node + * + * @param string $name The new name + * @throws DAV\Exception\Forbidden + * @return void + */ + function setName($name) { + + throw new DAV\Exception\Forbidden('Permission denied to rename file'); + + } + + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + function getAlternateUriSet() { + + return []; + + } + + /** + * Returns the full principal url + * + * @return string + */ + function getPrincipalUrl() { + + return $this->principalInfo['uri'] . '/' . $this->getName(); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + function getGroupMembership() { + + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void + */ + function setGroupMemberSet(array $principals) { + + $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); + + } + + /** + * Returns the displayname + * + * This should be a human readable name for the principal. + * If none is available, return the nodename. + * + * @return string + */ + function getDisplayName() { + + return $this->getName(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php new file mode 100644 index 00000000000..6e97e7cca56 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php @@ -0,0 +1,135 @@ +principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); + if (!$principal) { + throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); + } + if ($name === 'calendar-proxy-read') + return new ProxyRead($this->principalBackend, $this->principalProperties); + + if ($name === 'calendar-proxy-write') + return new ProxyWrite($this->principalBackend, $this->principalProperties); + + throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); + + } + + /** + * Returns an array with all the child nodes + * + * @return DAV\INode[] + */ + function getChildren() { + + $r = []; + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { + $r[] = new ProxyRead($this->principalBackend, $this->principalProperties); + } + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { + $r[] = new ProxyWrite($this->principalBackend, $this->principalProperties); + } + + return $r; + + } + + /** + * Returns whether or not the child node exists + * + * @param string $name + * @return bool + */ + function childExists($name) { + + try { + $this->getChild($name); + return true; + } catch (DAV\Exception\NotFound $e) { + return false; + } + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + $acl = parent::getACL(); + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + return $acl; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php new file mode 100644 index 00000000000..c9fd77d9353 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php @@ -0,0 +1,15 @@ +senderEmail = $senderEmail; + + } + + /* + * This initializes the plugin. + * + * This function is called by Sabre\DAV\Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + $server->on('schedule', [$this, 'schedule'], 120); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using \Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'imip'; + + } + + /** + * Event handler for the 'schedule' event. + * + * @param ITip\Message $iTipMessage + * @return void + */ + function schedule(ITip\Message $iTipMessage) { + + // Not sending any emails if the system considers the update + // insignificant. + if (!$iTipMessage->significantChange) { + if (!$iTipMessage->scheduleStatus) { + $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; + } + return; + } + + $summary = $iTipMessage->message->VEVENT->SUMMARY; + + if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') + return; + + if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') + return; + + $sender = substr($iTipMessage->sender, 7); + $recipient = substr($iTipMessage->recipient, 7); + + if ($iTipMessage->senderName) { + $sender = $iTipMessage->senderName . ' <' . $sender . '>'; + } + if ($iTipMessage->recipientName) { + $recipient = $iTipMessage->recipientName . ' <' . $recipient . '>'; + } + + $subject = 'SabreDAV iTIP message'; + switch (strtoupper($iTipMessage->method)) { + case 'REPLY' : + $subject = 'Re: ' . $summary; + break; + case 'REQUEST' : + $subject = $summary; + break; + case 'CANCEL' : + $subject = 'Cancelled: ' . $summary; + break; + } + + $headers = [ + 'Reply-To: ' . $sender, + 'From: ' . $this->senderEmail, + 'Content-Type: text/calendar; charset=UTF-8; method=' . $iTipMessage->method, + ]; + if (DAV\Server::$exposeVersion) { + $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION; + } + $this->mail( + $recipient, + $subject, + $iTipMessage->message->serialize(), + $headers + ); + $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; + + } + + // @codeCoverageIgnoreStart + // This is deemed untestable in a reasonable manner + + /** + * This function is responsible for sending the actual email. + * + * @param string $to Recipient email address + * @param string $subject Subject of the email + * @param string $body iCalendar body + * @param array $headers List of headers + * @return void + */ + protected function mail($to, $subject, $body, array $headers) { + + mail($to, $subject, $body, implode("\r\n", $headers)); + + } + + // @codeCoverageIgnoreEnd + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Email delivery (rfc6047) for CalDAV scheduling', + 'link' => 'http://sabre.io/dav/scheduling/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php new file mode 100644 index 00000000000..88fbdc4114b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php @@ -0,0 +1,15 @@ +caldavBackend = $caldavBackend; + $this->principalUri = $principalUri; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + + return 'inbox'; + + } + + /** + * Returns an array with all the child nodes + * + * @return \Sabre\DAV\INode[] + */ + function getChildren() { + + $objs = $this->caldavBackend->getSchedulingObjects($this->principalUri); + $children = []; + foreach ($objs as $obj) { + //$obj['acl'] = $this->getACL(); + $obj['principaluri'] = $this->principalUri; + $children[] = new SchedulingObject($this->caldavBackend, $obj); + } + return $children; + + } + + /** + * Creates a new file in the directory + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After successful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string + */ + function createFile($name, $data = null) { + + $this->caldavBackend->createSchedulingObject($this->principalUri, $name, $data); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return [ + [ + 'privilege' => '{DAV:}read', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write-properties', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}unbind', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}unbind', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + ]; + + } + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by \Sabre\CalDAV\CalendarQueryParser. + * + * @param array $filters + * @return array + */ + function calendarQuery(array $filters) { + + $result = []; + $validator = new CalDAV\CalendarQueryValidator(); + + $objects = $this->caldavBackend->getSchedulingObjects($this->principalUri); + foreach ($objects as $object) { + $vObject = VObject\Reader::read($object['calendardata']); + if ($validator->validate($vObject, $filters)) { + $result[] = $object['uri']; + } + + // Destroy circular references to PHP will GC the object. + $vObject->destroy(); + } + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php new file mode 100644 index 00000000000..888ea308626 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php @@ -0,0 +1,123 @@ +principalUri = $principalUri; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + + return 'outbox'; + + } + + /** + * Returns an array with all the child nodes + * + * @return \Sabre\DAV\INode[] + */ + function getChildren() { + + return []; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return [ + [ + 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-send', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-send', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php new file mode 100644 index 00000000000..0b991e61979 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php @@ -0,0 +1,1066 @@ +server = $server; + $server->on('method:POST', [$this, 'httpPost']); + $server->on('propFind', [$this, 'propFind']); + $server->on('propPatch', [$this, 'propPatch']); + $server->on('calendarObjectChange', [$this, 'calendarObjectChange']); + $server->on('beforeUnbind', [$this, 'beforeUnbind']); + $server->on('schedule', [$this, 'scheduleLocalDelivery']); + $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']); + + $ns = '{' . self::NS_CALDAV . '}'; + + /** + * This information ensures that the {DAV:}resourcetype property has + * the correct values. + */ + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = $ns . 'schedule-outbox'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IInbox'] = $ns . 'schedule-inbox'; + + /** + * Properties we protect are made read-only by the server. + */ + array_push($server->protectedProperties, + $ns . 'schedule-inbox-URL', + $ns . 'schedule-outbox-URL', + $ns . 'calendar-user-address-set', + $ns . 'calendar-user-type', + $ns . 'schedule-default-calendar-URL' + ); + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + function getHTTPMethods($uri) { + + try { + $node = $this->server->tree->getNodeForPath($uri); + } catch (NotFound $e) { + return []; + } + + if ($node instanceof IOutbox) { + return ['POST']; + } + + return []; + + } + + /** + * This method handles POST request for the outbox. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpPost(RequestInterface $request, ResponseInterface $response) { + + // Checking if this is a text/calendar content type + $contentType = $request->getHeader('Content-Type'); + if (strpos($contentType, 'text/calendar') !== 0) { + return; + } + + $path = $request->getPath(); + + // Checking if we're talking to an outbox + try { + $node = $this->server->tree->getNodeForPath($path); + } catch (NotFound $e) { + return; + } + if (!$node instanceof IOutbox) + return; + + $this->server->transactionType = 'post-caldav-outbox'; + $this->outboxRequest($node, $request, $response); + + // Returning false breaks the event chain and tells the server we've + // handled the request. + return false; + + } + + /** + * This method handler is invoked during fetching of properties. + * + * We use this event to add calendar-auto-schedule-specific properties. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + if ($node instanceof DAVACL\IPrincipal) { + + $caldavPlugin = $this->server->getPlugin('caldav'); + $principalUrl = $node->getPrincipalUrl(); + + // schedule-outbox-URL property + $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function() use ($principalUrl, $caldavPlugin) { + + $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); + if (!$calendarHomePath) { + return null; + } + $outboxPath = $calendarHomePath . '/outbox/'; + + return new LocalHref($outboxPath); + + }); + // schedule-inbox-URL property + $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function() use ($principalUrl, $caldavPlugin) { + + $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); + if (!$calendarHomePath) { + return null; + } + $inboxPath = $calendarHomePath . '/inbox/'; + + return new LocalHref($inboxPath); + + }); + + $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function() use ($principalUrl, $caldavPlugin) { + + // We don't support customizing this property yet, so in the + // meantime we just grab the first calendar in the home-set. + $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); + + if (!$calendarHomePath) { + return null; + } + + $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set'; + + $result = $this->server->getPropertiesForPath($calendarHomePath, [ + '{DAV:}resourcetype', + '{DAV:}share-access', + $sccs, + ], 1); + + foreach ($result as $child) { + if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar')) { + // Node is either not a calendar + continue; + } + if (isset($child[200]['{DAV:}share-access'])) { + $shareAccess = $child[200]['{DAV:}share-access']->getValue(); + if ($shareAccess !== Sharing\Plugin::ACCESS_NOTSHARED && $shareAccess !== Sharing\Plugin::ACCESS_SHAREDOWNER) { + // Node is a shared node, not owned by the relevant + // user. + continue; + } + + } + if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) { + // Either there is no supported-calendar-component-set + // (which is fine) or we found one that supports VEVENT. + return new LocalHref($child['href']); + } + } + + }); + + // The server currently reports every principal to be of type + // 'INDIVIDUAL' + $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function() { + + return 'INDIVIDUAL'; + + }); + + } + + // Mapping the old property to the new property. + $propFind->handle('{http://calendarserver.org/ns/}calendar-availability', function() use ($propFind, $node) { + + // In case it wasn't clear, the only difference is that we map the + // old property to a different namespace. + $availProp = '{' . self::NS_CALDAV . '}calendar-availability'; + $subPropFind = new PropFind( + $propFind->getPath(), + [$availProp] + ); + + $this->server->getPropertiesByNode( + $subPropFind, + $node + ); + + $propFind->set( + '{http://calendarserver.org/ns/}calendar-availability', + $subPropFind->get($availProp), + $subPropFind->getStatus($availProp) + ); + + }); + + } + + /** + * This method is called during property updates. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch) { + + // Mapping the old property to the new property. + $propPatch->handle('{http://calendarserver.org/ns/}calendar-availability', function($value) use ($path) { + + $availProp = '{' . self::NS_CALDAV . '}calendar-availability'; + $subPropPatch = new PropPatch([$availProp => $value]); + $this->server->emit('propPatch', [$path, $subPropPatch]); + $subPropPatch->commit(); + + return $subPropPatch->getResult()[$availProp]; + + }); + + } + + /** + * This method is triggered whenever there was a calendar object gets + * created or updated. + * + * @param RequestInterface $request HTTP request + * @param ResponseInterface $response HTTP Response + * @param VCalendar $vCal Parsed iCalendar object + * @param mixed $calendarPath Path to calendar collection + * @param mixed $modified The iCalendar object has been touched. + * @param mixed $isNew Whether this was a new item or we're updating one + * @return void + */ + function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) { + + if (!$this->scheduleReply($this->server->httpRequest)) { + return; + } + + $calendarNode = $this->server->tree->getNodeForPath($calendarPath); + + $addresses = $this->getAddressesForPrincipal( + $calendarNode->getOwner() + ); + + if (!$isNew) { + $node = $this->server->tree->getNodeForPath($request->getPath()); + $oldObj = Reader::read($node->get()); + } else { + $oldObj = null; + } + + $this->processICalendarChange($oldObj, $vCal, $addresses, [], $modified); + + if ($oldObj) { + // Destroy circular references so PHP will GC the object. + $oldObj->destroy(); + } + + } + + /** + * This method is responsible for delivering the ITip message. + * + * @param ITip\Message $iTipMessage + * @return void + */ + function deliver(ITip\Message $iTipMessage) { + + $this->server->emit('schedule', [$iTipMessage]); + if (!$iTipMessage->scheduleStatus) { + $iTipMessage->scheduleStatus = '5.2;There was no system capable of delivering the scheduling message'; + } + // In case the change was considered 'insignificant', we are going to + // remove any error statuses, if any. See ticket #525. + list($baseCode) = explode('.', $iTipMessage->scheduleStatus); + if (!$iTipMessage->significantChange && in_array($baseCode, ['3', '5'])) { + $iTipMessage->scheduleStatus = null; + } + + } + + /** + * This method is triggered before a file gets deleted. + * + * We use this event to make sure that when this happens, attendees get + * cancellations, and organizers get 'DECLINED' statuses. + * + * @param string $path + * @return void + */ + function beforeUnbind($path) { + + // FIXME: We shouldn't trigger this functionality when we're issuing a + // MOVE. This is a hack. + if ($this->server->httpRequest->getMethod() === 'MOVE') return; + + $node = $this->server->tree->getNodeForPath($path); + + if (!$node instanceof ICalendarObject || $node instanceof ISchedulingObject) { + return; + } + + if (!$this->scheduleReply($this->server->httpRequest)) { + return; + } + + $addresses = $this->getAddressesForPrincipal( + $node->getOwner() + ); + + $broker = new ITip\Broker(); + $messages = $broker->parseEvent(null, $addresses, $node->get()); + + foreach ($messages as $message) { + $this->deliver($message); + } + + } + + /** + * Event handler for the 'schedule' event. + * + * This handler attempts to look at local accounts to deliver the + * scheduling object. + * + * @param ITip\Message $iTipMessage + * @return void + */ + function scheduleLocalDelivery(ITip\Message $iTipMessage) { + + $aclPlugin = $this->server->getPlugin('acl'); + + // Local delivery is not available if the ACL plugin is not loaded. + if (!$aclPlugin) { + return; + } + + $caldavNS = '{' . self::NS_CALDAV . '}'; + + $principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient); + if (!$principalUri) { + $iTipMessage->scheduleStatus = '3.7;Could not find principal.'; + return; + } + + // We found a principal URL, now we need to find its inbox. + // Unfortunately we may not have sufficient privileges to find this, so + // we are temporarily turning off ACL to let this come through. + // + // Once we support PHP 5.5, this should be wrapped in a try..finally + // block so we can ensure that this privilege gets added again after. + $this->server->removeListener('propFind', [$aclPlugin, 'propFind']); + + $result = $this->server->getProperties( + $principalUri, + [ + '{DAV:}principal-URL', + $caldavNS . 'calendar-home-set', + $caldavNS . 'schedule-inbox-URL', + $caldavNS . 'schedule-default-calendar-URL', + '{http://sabredav.org/ns}email-address', + ] + ); + + // Re-registering the ACL event + $this->server->on('propFind', [$aclPlugin, 'propFind'], 20); + + if (!isset($result[$caldavNS . 'schedule-inbox-URL'])) { + $iTipMessage->scheduleStatus = '5.2;Could not find local inbox'; + return; + } + if (!isset($result[$caldavNS . 'calendar-home-set'])) { + $iTipMessage->scheduleStatus = '5.2;Could not locate a calendar-home-set'; + return; + } + if (!isset($result[$caldavNS . 'schedule-default-calendar-URL'])) { + $iTipMessage->scheduleStatus = '5.2;Could not find a schedule-default-calendar-URL property'; + return; + } + + $calendarPath = $result[$caldavNS . 'schedule-default-calendar-URL']->getHref(); + $homePath = $result[$caldavNS . 'calendar-home-set']->getHref(); + $inboxPath = $result[$caldavNS . 'schedule-inbox-URL']->getHref(); + + if ($iTipMessage->method === 'REPLY') { + $privilege = 'schedule-deliver-reply'; + } else { + $privilege = 'schedule-deliver-invite'; + } + + if (!$aclPlugin->checkPrivileges($inboxPath, $caldavNS . $privilege, DAVACL\Plugin::R_PARENT, false)) { + $iTipMessage->scheduleStatus = '3.8;insufficient privileges: ' . $privilege . ' is required on the recipient schedule inbox.'; + return; + } + + // Next, we're going to find out if the item already exits in one of + // the users' calendars. + $uid = $iTipMessage->uid; + + $newFileName = 'sabredav-' . \Sabre\DAV\UUIDUtil::getUUID() . '.ics'; + + $home = $this->server->tree->getNodeForPath($homePath); + $inbox = $this->server->tree->getNodeForPath($inboxPath); + + $currentObject = null; + $objectNode = null; + $isNewNode = false; + + $result = $home->getCalendarObjectByUID($uid); + if ($result) { + // There was an existing object, we need to update probably. + $objectPath = $homePath . '/' . $result; + $objectNode = $this->server->tree->getNodeForPath($objectPath); + $oldICalendarData = $objectNode->get(); + $currentObject = Reader::read($oldICalendarData); + } else { + $isNewNode = true; + } + + $broker = new ITip\Broker(); + $newObject = $broker->processMessage($iTipMessage, $currentObject); + + $inbox->createFile($newFileName, $iTipMessage->message->serialize()); + + if (!$newObject) { + // We received an iTip message referring to a UID that we don't + // have in any calendars yet, and processMessage did not give us a + // calendarobject back. + // + // The implication is that processMessage did not understand the + // iTip message. + $iTipMessage->scheduleStatus = '5.0;iTip message was not processed by the server, likely because we didn\'t understand it.'; + return; + } + + // Note that we are bypassing ACL on purpose by calling this directly. + // We may need to look a bit deeper into this later. Supporting ACL + // here would be nice. + if ($isNewNode) { + $calendar = $this->server->tree->getNodeForPath($calendarPath); + $calendar->createFile($newFileName, $newObject->serialize()); + } else { + // If the message was a reply, we may have to inform other + // attendees of this attendees status. Therefore we're shooting off + // another itipMessage. + if ($iTipMessage->method === 'REPLY') { + $this->processICalendarChange( + $oldICalendarData, + $newObject, + [$iTipMessage->recipient], + [$iTipMessage->sender] + ); + } + $objectNode->put($newObject->serialize()); + } + $iTipMessage->scheduleStatus = '1.2;Message delivered locally'; + + } + + /** + * This method is triggered whenever a subsystem requests the privileges + * that are supported on a particular node. + * + * We need to add a number of privileges for scheduling purposes. + * + * @param INode $node + * @param array $supportedPrivilegeSet + */ + function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) { + + $ns = '{' . self::NS_CALDAV . '}'; + if ($node instanceof IOutbox) { + $supportedPrivilegeSet[$ns . 'schedule-send'] = [ + 'abstract' => false, + 'aggregates' => [ + $ns . 'schedule-send-invite' => [ + 'abstract' => false, + 'aggregates' => [], + ], + $ns . 'schedule-send-reply' => [ + 'abstract' => false, + 'aggregates' => [], + ], + $ns . 'schedule-send-freebusy' => [ + 'abstract' => false, + 'aggregates' => [], + ], + // Privilege from an earlier scheduling draft, but still + // used by some clients. + $ns . 'schedule-post-vevent' => [ + 'abstract' => false, + 'aggregates' => [], + ], + ] + ]; + } + if ($node instanceof IInbox) { + $supportedPrivilegeSet[$ns . 'schedule-deliver'] = [ + 'abstract' => false, + 'aggregates' => [ + $ns . 'schedule-deliver-invite' => [ + 'abstract' => false, + 'aggregates' => [], + ], + $ns . 'schedule-deliver-reply' => [ + 'abstract' => false, + 'aggregates' => [], + ], + $ns . 'schedule-query-freebusy' => [ + 'abstract' => false, + 'aggregates' => [], + ], + ] + ]; + } + + } + + /** + * This method looks at an old iCalendar object, a new iCalendar object and + * starts sending scheduling messages based on the changes. + * + * A list of addresses needs to be specified, so the system knows who made + * the update, because the behavior may be different based on if it's an + * attendee or an organizer. + * + * This method may update $newObject to add any status changes. + * + * @param VCalendar|string $oldObject + * @param VCalendar $newObject + * @param array $addresses + * @param array $ignore Any addresses to not send messages to. + * @param bool $modified A marker to indicate that the original object + * modified by this process. + * @return void + */ + protected function processICalendarChange($oldObject = null, VCalendar $newObject, array $addresses, array $ignore = [], &$modified = false) { + + $broker = new ITip\Broker(); + $messages = $broker->parseEvent($newObject, $addresses, $oldObject); + + if ($messages) $modified = true; + + foreach ($messages as $message) { + + if (in_array($message->recipient, $ignore)) { + continue; + } + + $this->deliver($message); + + if (isset($newObject->VEVENT->ORGANIZER) && ($newObject->VEVENT->ORGANIZER->getNormalizedValue() === $message->recipient)) { + if ($message->scheduleStatus) { + $newObject->VEVENT->ORGANIZER['SCHEDULE-STATUS'] = $message->getScheduleStatus(); + } + unset($newObject->VEVENT->ORGANIZER['SCHEDULE-FORCE-SEND']); + + } else { + + if (isset($newObject->VEVENT->ATTENDEE)) foreach ($newObject->VEVENT->ATTENDEE as $attendee) { + + if ($attendee->getNormalizedValue() === $message->recipient) { + if ($message->scheduleStatus) { + $attendee['SCHEDULE-STATUS'] = $message->getScheduleStatus(); + } + unset($attendee['SCHEDULE-FORCE-SEND']); + break; + } + + } + + } + + } + + } + + /** + * Returns a list of addresses that are associated with a principal. + * + * @param string $principal + * @return array + */ + protected function getAddressesForPrincipal($principal) { + + $CUAS = '{' . self::NS_CALDAV . '}calendar-user-address-set'; + + $properties = $this->server->getProperties( + $principal, + [$CUAS] + ); + + // If we can't find this information, we'll stop processing + if (!isset($properties[$CUAS])) { + return; + } + + $addresses = $properties[$CUAS]->getHrefs(); + return $addresses; + + } + + /** + * This method handles POST requests to the schedule-outbox. + * + * Currently, two types of requests are supported: + * * FREEBUSY requests from RFC 6638 + * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04 + * + * The latter is from an expired early draft of the CalDAV scheduling + * extensions, but iCal depends on a feature from that spec, so we + * implement it. + * + * @param IOutbox $outboxNode + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function outboxRequest(IOutbox $outboxNode, RequestInterface $request, ResponseInterface $response) { + + $outboxPath = $request->getPath(); + + // Parsing the request body + try { + $vObject = VObject\Reader::read($request->getBody()); + } catch (VObject\ParseException $e) { + throw new BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); + } + + // The incoming iCalendar object must have a METHOD property, and a + // component. The combination of both determines what type of request + // this is. + $componentType = null; + foreach ($vObject->getComponents() as $component) { + if ($component->name !== 'VTIMEZONE') { + $componentType = $component->name; + break; + } + } + if (is_null($componentType)) { + throw new BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); + } + + // Validating the METHOD + $method = strtoupper((string)$vObject->METHOD); + if (!$method) { + throw new BadRequest('A METHOD property must be specified in iTIP messages'); + } + + // So we support one type of request: + // + // REQUEST with a VFREEBUSY component + + $acl = $this->server->getPlugin('acl'); + + if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') { + + $acl && $acl->checkPrivileges($outboxPath, '{' . self::NS_CALDAV . '}schedule-send-freebusy'); + $this->handleFreeBusyRequest($outboxNode, $vObject, $request, $response); + + // Destroy circular references so PHP can GC the object. + $vObject->destroy(); + unset($vObject); + + } else { + + throw new NotImplemented('We only support VFREEBUSY (REQUEST) on this endpoint'); + + } + + } + + /** + * This method is responsible for parsing a free-busy query request and + * returning it's result. + * + * @param IOutbox $outbox + * @param VObject\Component $vObject + * @param RequestInterface $request + * @param ResponseInterface $response + * @return string + */ + protected function handleFreeBusyRequest(IOutbox $outbox, VObject\Component $vObject, RequestInterface $request, ResponseInterface $response) { + + $vFreeBusy = $vObject->VFREEBUSY; + $organizer = $vFreeBusy->ORGANIZER; + + $organizer = (string)$organizer; + + // Validating if the organizer matches the owner of the inbox. + $owner = $outbox->getOwner(); + + $caldavNS = '{' . self::NS_CALDAV . '}'; + + $uas = $caldavNS . 'calendar-user-address-set'; + $props = $this->server->getProperties($owner, [$uas]); + + if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) { + throw new Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox'); + } + + if (!isset($vFreeBusy->ATTENDEE)) { + throw new BadRequest('You must at least specify 1 attendee'); + } + + $attendees = []; + foreach ($vFreeBusy->ATTENDEE as $attendee) { + $attendees[] = (string)$attendee; + } + + + if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) { + throw new BadRequest('DTSTART and DTEND must both be specified'); + } + + $startRange = $vFreeBusy->DTSTART->getDateTime(); + $endRange = $vFreeBusy->DTEND->getDateTime(); + + $results = []; + foreach ($attendees as $attendee) { + $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject); + } + + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->formatOutput = true; + $scheduleResponse = $dom->createElement('cal:schedule-response'); + foreach ($this->server->xml->namespaceMap as $namespace => $prefix) { + + $scheduleResponse->setAttribute('xmlns:' . $prefix, $namespace); + + } + $dom->appendChild($scheduleResponse); + + foreach ($results as $result) { + $xresponse = $dom->createElement('cal:response'); + + $recipient = $dom->createElement('cal:recipient'); + $recipientHref = $dom->createElement('d:href'); + + $recipientHref->appendChild($dom->createTextNode($result['href'])); + $recipient->appendChild($recipientHref); + $xresponse->appendChild($recipient); + + $reqStatus = $dom->createElement('cal:request-status'); + $reqStatus->appendChild($dom->createTextNode($result['request-status'])); + $xresponse->appendChild($reqStatus); + + if (isset($result['calendar-data'])) { + + $calendardata = $dom->createElement('cal:calendar-data'); + $calendardata->appendChild($dom->createTextNode(str_replace("\r\n", "\n", $result['calendar-data']->serialize()))); + $xresponse->appendChild($calendardata); + + } + $scheduleResponse->appendChild($xresponse); + } + + $response->setStatus(200); + $response->setHeader('Content-Type', 'application/xml'); + $response->setBody($dom->saveXML()); + + } + + /** + * Returns free-busy information for a specific address. The returned + * data is an array containing the following properties: + * + * calendar-data : A VFREEBUSY VObject + * request-status : an iTip status code. + * href: The principal's email address, as requested + * + * The following request status codes may be returned: + * * 2.0;description + * * 3.7;description + * + * @param string $email address + * @param \DateTimeInterface $start + * @param \DateTimeInterface $end + * @param VObject\Component $request + * @return array + */ + protected function getFreeBusyForEmail($email, \DateTimeInterface $start, \DateTimeInterface $end, VObject\Component $request) { + + $caldavNS = '{' . self::NS_CALDAV . '}'; + + $aclPlugin = $this->server->getPlugin('acl'); + if (substr($email, 0, 7) === 'mailto:') $email = substr($email, 7); + + $result = $aclPlugin->principalSearch( + ['{http://sabredav.org/ns}email-address' => $email], + [ + '{DAV:}principal-URL', + $caldavNS . 'calendar-home-set', + $caldavNS . 'schedule-inbox-URL', + '{http://sabredav.org/ns}email-address', + + ] + ); + + if (!count($result)) { + return [ + 'request-status' => '3.7;Could not find principal', + 'href' => 'mailto:' . $email, + ]; + } + + if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { + return [ + 'request-status' => '3.7;No calendar-home-set property found', + 'href' => 'mailto:' . $email, + ]; + } + if (!isset($result[0][200][$caldavNS . 'schedule-inbox-URL'])) { + return [ + 'request-status' => '3.7;No schedule-inbox-URL property found', + 'href' => 'mailto:' . $email, + ]; + } + $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); + $inboxUrl = $result[0][200][$caldavNS . 'schedule-inbox-URL']->getHref(); + + // Do we have permission? + $aclPlugin->checkPrivileges($inboxUrl, $caldavNS . 'schedule-query-freebusy'); + + // Grabbing the calendar list + $objects = []; + $calendarTimeZone = new DateTimeZone('UTC'); + + foreach ($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { + if (!$node instanceof ICalendar) { + continue; + } + + $sct = $caldavNS . 'schedule-calendar-transp'; + $ctz = $caldavNS . 'calendar-timezone'; + $props = $node->getProperties([$sct, $ctz]); + + if (isset($props[$sct]) && $props[$sct]->getValue() == ScheduleCalendarTransp::TRANSPARENT) { + // If a calendar is marked as 'transparent', it means we must + // ignore it for free-busy purposes. + continue; + } + + if (isset($props[$ctz])) { + $vtimezoneObj = VObject\Reader::read($props[$ctz]); + $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); + + // Destroy circular references so PHP can garbage collect the object. + $vtimezoneObj->destroy(); + + } + + // Getting the list of object uris within the time-range + $urls = $node->calendarQuery([ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => $start, + 'end' => $end, + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]); + + $calObjects = array_map(function($url) use ($node) { + $obj = $node->getChild($url)->get(); + return $obj; + }, $urls); + + $objects = array_merge($objects, $calObjects); + + } + + $inboxProps = $this->server->getProperties( + $inboxUrl, + $caldavNS . 'calendar-availability' + ); + + $vcalendar = new VObject\Component\VCalendar(); + $vcalendar->METHOD = 'REPLY'; + + $generator = new VObject\FreeBusyGenerator(); + $generator->setObjects($objects); + $generator->setTimeRange($start, $end); + $generator->setBaseObject($vcalendar); + $generator->setTimeZone($calendarTimeZone); + + if ($inboxProps) { + $generator->setVAvailability( + VObject\Reader::read( + $inboxProps[$caldavNS . 'calendar-availability'] + ) + ); + } + + $result = $generator->getResult(); + + $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; + $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID; + $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; + + return [ + 'calendar-data' => $result, + 'request-status' => '2.0;Success', + 'href' => 'mailto:' . $email, + ]; + } + + /** + * This method checks the 'Schedule-Reply' header + * and returns false if it's 'F', otherwise true. + * + * @param RequestInterface $request + * @return bool + */ + private function scheduleReply(RequestInterface $request) { + + $scheduleReply = $request->getHeader('Schedule-Reply'); + return $scheduleReply !== 'F'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds calendar-auto-schedule, as defined in rfc6638', + 'link' => 'http://sabre.io/dav/scheduling/', + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php new file mode 100644 index 00000000000..0cd05a965c3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php @@ -0,0 +1,155 @@ +caldavBackend = $caldavBackend; + + if (!isset($objectData['uri'])) { + throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); + } + + $this->objectData = $objectData; + + } + + /** + * Returns the ICalendar-formatted object + * + * @return string + */ + function get() { + + // Pre-populating the 'calendardata' is optional, if we don't have it + // already we fetch it from the backend. + if (!isset($this->objectData['calendardata'])) { + $this->objectData = $this->caldavBackend->getSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']); + } + return $this->objectData['calendardata']; + + } + + /** + * Updates the ICalendar-formatted object + * + * @param string|resource $calendarData + * @return string + */ + function put($calendarData) { + + throw new MethodNotAllowed('Updating scheduling objects is not supported'); + + } + + /** + * Deletes the scheduling message + * + * @return void + */ + function delete() { + + $this->caldavBackend->deleteSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->objectData['principaluri']; + + } + + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + // An alternative acl may be specified in the object data. + // + + if (isset($this->objectData['acl'])) { + return $this->objectData['acl']; + } + + // The default ACL + return [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php new file mode 100644 index 00000000000..7a77616e353 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php @@ -0,0 +1,229 @@ +calendarInfo['share-access']) ? $this->calendarInfo['share-access'] : SPlugin::ACCESS_NOTSHARED; + + } + + /** + * This function must return a URI that uniquely identifies the shared + * resource. This URI should be identical across instances, and is + * also used in several other XML bodies to connect invites to + * resources. + * + * This may simply be a relative reference to the original shared instance, + * but it could also be a urn. As long as it's a valid URI and unique. + * + * @return string + */ + function getShareResourceUri() { + + return $this->calendarInfo['share-resource-uri']; + + } + + /** + * Updates the list of sharees. + * + * Every item must be a Sharee object. + * + * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees + * @return void + */ + function updateInvites(array $sharees) { + + $this->caldavBackend->updateInvites($this->calendarInfo['id'], $sharees); + + } + + /** + * Returns the list of people whom this resource is shared with. + * + * Every item in the returned array must be a Sharee object with + * at least the following properties set: + * + * * $href + * * $shareAccess + * * $inviteStatus + * + * and optionally: + * + * * $properties + * + * @return \Sabre\DAV\Xml\Element\Sharee[] + */ + function getInvites() { + + return $this->caldavBackend->getInvites($this->calendarInfo['id']); + + } + + /** + * Marks this calendar as published. + * + * Publishing a calendar should automatically create a read-only, public, + * subscribable calendar. + * + * @param bool $value + * @return void + */ + function setPublishStatus($value) { + + $this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value); + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + $acl = []; + + switch ($this->getShareAccess()) { + case SPlugin::ACCESS_NOTSHARED : + case SPlugin::ACCESS_SHAREDOWNER : + $acl[] = [ + 'privilege' => '{DAV:}share', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}share', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + // No break intentional! + case SPlugin::ACCESS_READWRITE : + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + // No break intentional! + case SPlugin::ACCESS_READ : + $acl[] = [ + 'privilege' => '{DAV:}write-properties', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write-properties', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ]; + break; + } + return $acl; + + } + + + /** + * This method returns the ACL's for calendar objects in this calendar. + * The result of this method automatically gets passed to the + * calendar-object nodes in the calendar. + * + * @return array + */ + function getChildACL() { + + $acl = []; + + switch ($this->getShareAccess()) { + case SPlugin::ACCESS_NOTSHARED : + // No break intentional + case SPlugin::ACCESS_SHAREDOWNER : + // No break intentional + case SPlugin::ACCESS_READWRITE: + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + // No break intentional + case SPlugin::ACCESS_READ: + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ]; + break; + } + + return $acl; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php new file mode 100644 index 00000000000..5cce79678be --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php @@ -0,0 +1,401 @@ +server = $server; + + if (is_null($this->server->getPlugin('sharing'))) { + throw new \LogicException('The generic "sharing" plugin must be loaded before the caldav sharing plugin. Call $server->addPlugin(new \Sabre\DAV\Sharing\Plugin()); before this one.'); + } + + array_push( + $this->server->protectedProperties, + '{' . Plugin::NS_CALENDARSERVER . '}invite', + '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', + '{' . Plugin::NS_CALENDARSERVER . '}shared-url' + ); + + $this->server->xml->elementMap['{' . Plugin::NS_CALENDARSERVER . '}share'] = 'Sabre\\CalDAV\\Xml\\Request\\Share'; + $this->server->xml->elementMap['{' . Plugin::NS_CALENDARSERVER . '}invite-reply'] = 'Sabre\\CalDAV\\Xml\\Request\\InviteReply'; + + $this->server->on('propFind', [$this, 'propFindEarly']); + $this->server->on('propFind', [$this, 'propFindLate'], 150); + $this->server->on('propPatch', [$this, 'propPatch'], 40); + $this->server->on('method:POST', [$this, 'httpPost']); + + } + + /** + * This event is triggered when properties are requested for a certain + * node. + * + * This allows us to inject any properties early. + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { + + if ($node instanceof ISharedCalendar) { + + $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}invite', function() use ($node) { + + // Fetching owner information + $props = $this->server->getPropertiesForPath($node->getOwner(), [ + '{http://sabredav.org/ns}email-address', + '{DAV:}displayname', + ], 0); + + $ownerInfo = [ + 'href' => $node->getOwner(), + ]; + + if (isset($props[0][200])) { + + // We're mapping the internal webdav properties to the + // elements caldav-sharing expects. + if (isset($props[0][200]['{http://sabredav.org/ns}email-address'])) { + $ownerInfo['href'] = 'mailto:' . $props[0][200]['{http://sabredav.org/ns}email-address']; + } + if (isset($props[0][200]['{DAV:}displayname'])) { + $ownerInfo['commonName'] = $props[0][200]['{DAV:}displayname']; + } + + } + + return new Xml\Property\Invite( + $node->getInvites(), + $ownerInfo + ); + + }); + + } + + } + + /** + * This method is triggered *after* all properties have been retrieved. + * This allows us to inject the correct resourcetype for calendars that + * have been shared. + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { + + if ($node instanceof ISharedCalendar) { + $shareAccess = $node->getShareAccess(); + if ($rt = $propFind->get('{DAV:}resourcetype')) { + switch ($shareAccess) { + case \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER : + $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner'); + break; + case \Sabre\DAV\Sharing\Plugin::ACCESS_READ : + case \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE : + $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared'); + break; + + } + } + $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function() { + return new Xml\Property\AllowedSharingModes(true, false); + }); + + } + + } + + /** + * This method is trigged when a user attempts to update a node's + * properties. + * + * A previous draft of the sharing spec stated that it was possible to use + * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing + * the calendar. + * + * Even though this is no longer in the current spec, we keep this around + * because OS X 10.7 may still make use of this feature. + * + * @param string $path + * @param DAV\PropPatch $propPatch + * @return void + */ + function propPatch($path, DAV\PropPatch $propPatch) { + + $node = $this->server->tree->getNodeForPath($path); + if (!$node instanceof ISharedCalendar) + return; + + if ($node->getShareAccess() === \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER || $node->getShareAccess() === \Sabre\DAV\Sharing\Plugin::ACCESS_NOTSHARED) { + + $propPatch->handle('{DAV:}resourcetype', function($value) use ($node) { + if ($value->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return false; + $shares = $node->getInvites(); + foreach ($shares as $share) { + $share->access = DAV\Sharing\Plugin::ACCESS_NOACCESS; + } + $node->updateInvites($shares); + + return true; + + }); + + } + + } + + /** + * We intercept this to handle POST requests on calendars. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return null|bool + */ + function httpPost(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + // Only handling xml + $contentType = $request->getHeader('Content-Type'); + if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) + return; + + // Making sure the node exists + try { + $node = $this->server->tree->getNodeForPath($path); + } catch (DAV\Exception\NotFound $e) { + return; + } + + $requestBody = $request->getBodyAsString(); + + // If this request handler could not deal with this POST request, it + // will return 'null' and other plugins get a chance to handle the + // request. + // + // However, we already requested the full body. This is a problem, + // because a body can only be read once. This is why we preemptively + // re-populated the request body with the existing data. + $request->setBody($requestBody); + + $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); + + switch ($documentType) { + + // Both the DAV:share-resource and CALENDARSERVER:share requests + // behave identically. + case '{' . Plugin::NS_CALENDARSERVER . '}share' : + + $sharingPlugin = $this->server->getPlugin('sharing'); + $sharingPlugin->shareResource($path, $message->sharees); + + $response->setStatus(200); + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $response->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + // The invite-reply document is sent when the user replies to an + // invitation of a calendar share. + case '{' . Plugin::NS_CALENDARSERVER . '}invite-reply' : + + // This only works on the calendar-home-root node. + if (!$node instanceof CalendarHome) { + return; + } + $this->server->transactionType = 'post-invite-reply'; + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($path, '{DAV:}write'); + } + + $url = $node->shareReply( + $message->href, + $message->status, + $message->calendarUri, + $message->inReplyTo, + $message->summary + ); + + $response->setStatus(200); + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $response->setHeader('X-Sabre-Status', 'everything-went-well'); + + if ($url) { + $writer = $this->server->xml->getWriter(); + $writer->openMemory(); + $writer->startDocument(); + $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}shared-as'); + $writer->write(new LocalHref($url)); + $writer->endElement(); + $response->setHeader('Content-Type', 'application/xml'); + $response->setBody($writer->outputMemory()); + + } + + // Breaking the event chain + return false; + + case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' : + + // We can only deal with IShareableCalendar objects + if (!$node instanceof ISharedCalendar) { + return; + } + $this->server->transactionType = 'post-publish-calendar'; + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($path, '{DAV:}share'); + } + + $node->setPublishStatus(true); + + // iCloud sends back the 202, so we will too. + $response->setStatus(202); + + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $response->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' : + + // We can only deal with IShareableCalendar objects + if (!$node instanceof ISharedCalendar) { + return; + } + $this->server->transactionType = 'post-unpublish-calendar'; + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($path, '{DAV:}share'); + } + + $node->setPublishStatus(false); + + $response->setStatus(200); + + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $response->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + } + + + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds support for caldav-sharing.', + 'link' => 'http://sabre.io/dav/caldav-sharing/', + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php new file mode 100644 index 00000000000..7ba259c7b68 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php @@ -0,0 +1,40 @@ +resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] = + '{http://calendarserver.org/ns/}subscribed'; + + $server->xml->elementMap['{http://calendarserver.org/ns/}source'] = + 'Sabre\\DAV\\Xml\\Property\\Href'; + + $server->on('propFind', [$this, 'propFind'], 150); + + } + + /** + * This method should return a list of server-features. + * + * This is for example 'versioning' and is added to the DAV: header + * in an OPTIONS response. + * + * @return array + */ + function getFeatures() { + + return ['calendarserver-subscribed']; + + } + + /** + * Triggered after properties have been fetched. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + // There's a bunch of properties that must appear as a self-closing + // xml-element. This event handler ensures that this will be the case. + $props = [ + '{http://calendarserver.org/ns/}subscribed-strip-alarms', + '{http://calendarserver.org/ns/}subscribed-strip-attachments', + '{http://calendarserver.org/ns/}subscribed-strip-todos', + ]; + + foreach ($props as $prop) { + + if ($propFind->getStatus($prop) === 200) { + $propFind->set($prop, '', 200); + } + + } + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using \Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'subscriptions'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'This plugin allows users to store iCalendar subscriptions in their calendar-home.', + 'link' => null, + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php new file mode 100644 index 00000000000..6a1851ed868 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php @@ -0,0 +1,221 @@ +caldavBackend = $caldavBackend; + $this->subscriptionInfo = $subscriptionInfo; + + $required = [ + 'id', + 'uri', + 'principaluri', + 'source', + ]; + + foreach ($required as $r) { + if (!isset($subscriptionInfo[$r])) { + throw new \InvalidArgumentException('The ' . $r . ' field is required when creating a subscription node'); + } + } + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + + return $this->subscriptionInfo['uri']; + + } + + /** + * Returns the last modification time + * + * @return int + */ + function getLastModified() { + + if (isset($this->subscriptionInfo['lastmodified'])) { + return $this->subscriptionInfo['lastmodified']; + } + + } + + /** + * Deletes the current node + * + * @return void + */ + function delete() { + + $this->caldavBackend->deleteSubscription( + $this->subscriptionInfo['id'] + ); + + } + + /** + * Returns an array with all the child nodes + * + * @return \Sabre\DAV\INode[] + */ + function getChildren() { + + return []; + + } + + /** + * Updates properties on this node. + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * To update specific properties, call the 'handle' method on this object. + * Read the PropPatch documentation for more information. + * + * @param PropPatch $propPatch + * @return void + */ + function propPatch(PropPatch $propPatch) { + + return $this->caldavBackend->updateSubscription( + $this->subscriptionInfo['id'], + $propPatch + ); + + } + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname. + * + * If the array is empty, it means 'all properties' were requested. + * + * Note that it's fine to liberally give properties back, instead of + * conforming to the list of requested properties. + * The Server class will filter out the extra. + * + * @param array $properties + * @return array + */ + function getProperties($properties) { + + $r = []; + + foreach ($properties as $prop) { + + switch ($prop) { + case '{http://calendarserver.org/ns/}source' : + $r[$prop] = new Href($this->subscriptionInfo['source']); + break; + default : + if (array_key_exists($prop, $this->subscriptionInfo)) { + $r[$prop] = $this->subscriptionInfo[$prop]; + } + break; + } + + } + + return $r; + + } + + /** + * Returns the owner principal. + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->subscriptionInfo['principaluri']; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return [ + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'protected' => true, + ] + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php new file mode 100644 index 00000000000..9669be304e1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php @@ -0,0 +1,84 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'contentType' => $reader->getAttribute('content-type') ?: 'text/calendar', + 'version' => $reader->getAttribute('version') ?: '2.0', + ]; + + $elems = (array)$reader->parseInnerTree(); + foreach ($elems as $elem) { + + switch ($elem['name']) { + case '{' . Plugin::NS_CALDAV . '}expand' : + + $result['expand'] = [ + 'start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, + 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, + ]; + + if (!$result['expand']['start'] || !$result['expand']['end']) { + throw new BadRequest('The "start" and "end" attributes are required when expanding calendar-data'); + } + if ($result['expand']['end'] <= $result['expand']['start']) { + throw new BadRequest('The end-date must be larger than the start-date when expanding calendar-data'); + } + break; + } + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php new file mode 100644 index 00000000000..c21ede66b5c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php @@ -0,0 +1,97 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'name' => null, + 'is-not-defined' => false, + 'comp-filters' => [], + 'prop-filters' => [], + 'time-range' => false, + ]; + + $att = $reader->parseAttributes(); + $result['name'] = $att['name']; + + $elems = $reader->parseInnerTree(); + + if (is_array($elems)) foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{' . Plugin::NS_CALDAV . '}comp-filter' : + $result['comp-filters'][] = $elem['value']; + break; + case '{' . Plugin::NS_CALDAV . '}prop-filter' : + $result['prop-filters'][] = $elem['value']; + break; + case '{' . Plugin::NS_CALDAV . '}is-not-defined' : + $result['is-not-defined'] = true; + break; + case '{' . Plugin::NS_CALDAV . '}time-range' : + if ($result['name'] === 'VCALENDAR') { + throw new BadRequest('You cannot add time-range filters on the VCALENDAR component'); + } + $result['time-range'] = [ + 'start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, + 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, + ]; + if ($result['time-range']['start'] && $result['time-range']['end'] && $result['time-range']['end'] <= $result['time-range']['start']) { + throw new BadRequest('The end-date must be larger than the start-date'); + } + break; + + } + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php new file mode 100644 index 00000000000..bf422cf0545 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php @@ -0,0 +1,82 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'name' => null, + 'is-not-defined' => false, + 'text-match' => null, + ]; + + $att = $reader->parseAttributes(); + $result['name'] = $att['name']; + + $elems = $reader->parseInnerTree(); + + if (is_array($elems)) foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{' . Plugin::NS_CALDAV . '}is-not-defined' : + $result['is-not-defined'] = true; + break; + case '{' . Plugin::NS_CALDAV . '}text-match' : + $result['text-match'] = [ + 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', + 'value' => $elem['value'], + ]; + break; + + } + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php new file mode 100644 index 00000000000..db9207295a2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php @@ -0,0 +1,98 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'name' => null, + 'is-not-defined' => false, + 'param-filters' => [], + 'text-match' => null, + 'time-range' => false, + ]; + + $att = $reader->parseAttributes(); + $result['name'] = $att['name']; + + $elems = $reader->parseInnerTree(); + + if (is_array($elems)) foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{' . Plugin::NS_CALDAV . '}param-filter' : + $result['param-filters'][] = $elem['value']; + break; + case '{' . Plugin::NS_CALDAV . '}is-not-defined' : + $result['is-not-defined'] = true; + break; + case '{' . Plugin::NS_CALDAV . '}time-range' : + $result['time-range'] = [ + 'start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, + 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, + ]; + if ($result['time-range']['start'] && $result['time-range']['end'] && $result['time-range']['end'] <= $result['time-range']['start']) { + throw new BadRequest('The end-date must be larger than the start-date'); + } + break; + case '{' . Plugin::NS_CALDAV . '}text-match' : + $result['text-match'] = [ + 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', + 'value' => $elem['value'], + ]; + break; + + } + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php new file mode 100644 index 00000000000..92a9ac7b715 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php @@ -0,0 +1,302 @@ + $value) { + if (!property_exists($this, $key)) { + throw new \InvalidArgumentException('Unknown option: ' . $key); + } + $this->$key = $value; + } + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + $writer->writeElement('{' . CalDAV\Plugin::NS_CALENDARSERVER . '}invite-notification'); + + } + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Writer $writer + * @return void + */ + function xmlSerializeFull(Writer $writer) { + + $cs = '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}'; + + $this->dtStamp->setTimezone(new \DateTimezone('GMT')); + $writer->writeElement($cs . 'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z')); + + $writer->startElement($cs . 'invite-notification'); + + $writer->writeElement($cs . 'uid', $this->id); + $writer->writeElement('{DAV:}href', $this->href); + + switch ($this->type) { + + case DAV\Sharing\Plugin::INVITE_ACCEPTED : + $writer->writeElement($cs . 'invite-accepted'); + break; + case DAV\Sharing\Plugin::INVITE_NORESPONSE : + $writer->writeElement($cs . 'invite-noresponse'); + break; + + } + + $writer->writeElement($cs . 'hosturl', [ + '{DAV:}href' => $writer->contextUri . $this->hostUrl + ]); + + if ($this->summary) { + $writer->writeElement($cs . 'summary', $this->summary); + } + + $writer->startElement($cs . 'access'); + if ($this->readOnly) { + $writer->writeElement($cs . 'read'); + } else { + $writer->writeElement($cs . 'read-write'); + } + $writer->endElement(); // access + + $writer->startElement($cs . 'organizer'); + // If the organizer contains a 'mailto:' part, it means it should be + // treated as absolute. + if (strtolower(substr($this->organizer, 0, 7)) === 'mailto:') { + $writer->writeElement('{DAV:}href', $this->organizer); + } else { + $writer->writeElement('{DAV:}href', $writer->contextUri . $this->organizer); + } + if ($this->commonName) { + $writer->writeElement($cs . 'common-name', $this->commonName); + } + if ($this->firstName) { + $writer->writeElement($cs . 'first-name', $this->firstName); + } + if ($this->lastName) { + $writer->writeElement($cs . 'last-name', $this->lastName); + } + $writer->endElement(); // organizer + + if ($this->commonName) { + $writer->writeElement($cs . 'organizer-cn', $this->commonName); + } + if ($this->firstName) { + $writer->writeElement($cs . 'organizer-first', $this->firstName); + } + if ($this->lastName) { + $writer->writeElement($cs . 'organizer-last', $this->lastName); + } + if ($this->supportedComponents) { + $writer->writeElement('{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set', $this->supportedComponents); + } + + $writer->endElement(); // invite-notification + + } + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + function getId() { + + return $this->id; + + } + + /** + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + function getETag() { + + return $this->etag; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php new file mode 100644 index 00000000000..f4b10a396bc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php @@ -0,0 +1,213 @@ + $value) { + if (!property_exists($this, $key)) { + throw new \InvalidArgumentException('Unknown option: ' . $key); + } + $this->$key = $value; + } + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + $writer->writeElement('{' . CalDAV\Plugin::NS_CALENDARSERVER . '}invite-reply'); + + } + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Writer $writer + * @return void + */ + function xmlSerializeFull(Writer $writer) { + + $cs = '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}'; + + $this->dtStamp->setTimezone(new \DateTimezone('GMT')); + $writer->writeElement($cs . 'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z')); + + $writer->startElement($cs . 'invite-reply'); + + $writer->writeElement($cs . 'uid', $this->id); + $writer->writeElement($cs . 'in-reply-to', $this->inReplyTo); + $writer->writeElement('{DAV:}href', $this->href); + + switch ($this->type) { + + case DAV\Sharing\Plugin::INVITE_ACCEPTED : + $writer->writeElement($cs . 'invite-accepted'); + break; + case DAV\Sharing\Plugin::INVITE_DECLINED : + $writer->writeElement($cs . 'invite-declined'); + break; + + } + + $writer->writeElement($cs . 'hosturl', [ + '{DAV:}href' => $writer->contextUri . $this->hostUrl + ]); + + if ($this->summary) { + $writer->writeElement($cs . 'summary', $this->summary); + } + $writer->endElement(); // invite-reply + + } + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + function getId() { + + return $this->id; + + } + + /** + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + function getETag() { + + return $this->etag; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php new file mode 100644 index 00000000000..b98f9c88889 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php @@ -0,0 +1,45 @@ +id = $id; + $this->type = $type; + $this->description = $description; + $this->href = $href; + $this->etag = $etag; + + } + + /** + * The serialize method is called during xml writing. + * + * It should use the $writer argument to encode this object into XML. + * + * Important note: it is not needed to create the parent element. The + * parent element is already created, and we only have to worry about + * attributes, child elements and text (if any). + * + * Important note 2: If you are writing any new elements, you are also + * responsible for closing them. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + switch ($this->type) { + case self::TYPE_LOW : + $type = 'low'; + break; + case self::TYPE_MEDIUM : + $type = 'medium'; + break; + default : + case self::TYPE_HIGH : + $type = 'high'; + break; + } + + $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}systemstatus'); + $writer->writeAttribute('type', $type); + $writer->endElement(); + + } + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Writer $writer + * @return void + */ + function xmlSerializeFull(Writer $writer) { + + $cs = '{' . Plugin::NS_CALENDARSERVER . '}'; + switch ($this->type) { + case self::TYPE_LOW : + $type = 'low'; + break; + case self::TYPE_MEDIUM : + $type = 'medium'; + break; + default : + case self::TYPE_HIGH : + $type = 'high'; + break; + } + + $writer->startElement($cs . 'systemstatus'); + $writer->writeAttribute('type', $type); + + + if ($this->description) { + $writer->writeElement($cs . 'description', $this->description); + } + if ($this->href) { + $writer->writeElement('{DAV:}href', $this->href); + } + + $writer->endElement(); // systemstatus + + } + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + function getId() { + + return $this->id; + + } + + /* + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + function getETag() { + + return $this->etag; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php new file mode 100644 index 00000000000..54e5a116a6a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php @@ -0,0 +1,87 @@ +canBeShared = $canBeShared; + $this->canBePublished = $canBePublished; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + if ($this->canBeShared) { + $writer->writeElement('{' . Plugin::NS_CALENDARSERVER . '}can-be-shared'); + } + if ($this->canBePublished) { + $writer->writeElement('{' . Plugin::NS_CALENDARSERVER . '}can-be-published'); + } + + } + + + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php new file mode 100644 index 00000000000..fc6f1d505fa --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php @@ -0,0 +1,80 @@ +emails = $emails; + + } + + /** + * Returns the email addresses + * + * @return array + */ + function getValue() { + + return $this->emails; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->emails as $email) { + + $writer->writeElement('{http://calendarserver.org/ns/}email-address', $email); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php new file mode 100644 index 00000000000..4f33c464cc3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php @@ -0,0 +1,128 @@ +sharees = $sharees; + + } + + /** + * Returns the list of users, as it was passed to the constructor. + * + * @return array + */ + function getValue() { + + return $this->sharees; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + $cs = '{' . Plugin::NS_CALENDARSERVER . '}'; + + foreach ($this->sharees as $sharee) { + + if ($sharee->access === DAV\Sharing\Plugin::ACCESS_SHAREDOWNER) { + $writer->startElement($cs . 'organizer'); + } else { + $writer->startElement($cs . 'user'); + + switch ($sharee->inviteStatus) { + case DAV\Sharing\Plugin::INVITE_ACCEPTED : + $writer->writeElement($cs . 'invite-accepted'); + break; + case DAV\Sharing\Plugin::INVITE_DECLINED : + $writer->writeElement($cs . 'invite-declined'); + break; + case DAV\Sharing\Plugin::INVITE_NORESPONSE : + $writer->writeElement($cs . 'invite-noresponse'); + break; + case DAV\Sharing\Plugin::INVITE_INVALID : + $writer->writeElement($cs . 'invite-invalid'); + break; + } + + $writer->startElement($cs . 'access'); + switch ($sharee->access) { + case DAV\Sharing\Plugin::ACCESS_READWRITE : + $writer->writeElement($cs . 'read-write'); + break; + case DAV\Sharing\Plugin::ACCESS_READ : + $writer->writeElement($cs . 'read'); + break; + + } + $writer->endElement(); // access + + } + + $href = new DAV\Xml\Property\Href($sharee->href); + $href->xmlSerialize($writer); + + if (isset($sharee->properties['{DAV:}displayname'])) { + $writer->writeElement($cs . 'common-name', $sharee->properties['{DAV:}displayname']); + } + if ($sharee->comment) { + $writer->writeElement($cs . 'summary', $sharee->comment); + } + $writer->endElement(); // organizer or user + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php new file mode 100644 index 00000000000..10c20be55c8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php @@ -0,0 +1,130 @@ +value = $value; + + } + + /** + * Returns the current value + * + * @return string + */ + function getValue() { + + return $this->value; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + switch ($this->value) { + case self::TRANSPARENT : + $writer->writeElement('{' . Plugin::NS_CALDAV . '}transparent'); + break; + case self::OPAQUE : + $writer->writeElement('{' . Plugin::NS_CALDAV . '}opaque'); + break; + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = Deserializer\enum($reader, Plugin::NS_CALDAV); + + if (in_array('transparent', $elems)) { + $value = self::TRANSPARENT; + } else { + $value = self::OPAQUE; + } + return new self($value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php new file mode 100644 index 00000000000..7fc25c5f0c2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php @@ -0,0 +1,129 @@ +components = $components; + + } + + /** + * Returns the list of supported components + * + * @return array + */ + function getValue() { + + return $this->components; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->components as $component) { + + $writer->startElement('{' . Plugin::NS_CALDAV . '}comp'); + $writer->writeAttributes(['name' => $component]); + $writer->endElement(); + + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree(); + + $components = []; + + foreach ((array)$elems as $elem) { + if ($elem['name'] === '{' . Plugin::NS_CALDAV . '}comp') { + $components[] = $elem['attributes']['name']; + } + } + + if (!$components) { + throw new ParseException('supported-calendar-component-set must have at least one CALDAV:comp element'); + } + + return new self($components); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php new file mode 100644 index 00000000000..d123ba4c0d2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php @@ -0,0 +1,60 @@ +startElement('{' . Plugin::NS_CALDAV . '}calendar-data'); + $writer->writeAttributes([ + 'content-type' => 'text/calendar', + 'version' => '2.0', + ]); + $writer->endElement(); // calendar-data + $writer->startElement('{' . Plugin::NS_CALDAV . '}calendar-data'); + $writer->writeAttributes([ + 'content-type' => 'application/calendar+json', + ]); + $writer->endElement(); // calendar-data + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php new file mode 100644 index 00000000000..af10860d077 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php @@ -0,0 +1,57 @@ +writeElement('{' . Plugin::NS_CALDAV . '}supported-collation', $collation); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php new file mode 100644 index 00000000000..6d3c5d5089c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php @@ -0,0 +1,124 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree([ + '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + ]); + + $newProps = [ + 'hrefs' => [], + 'properties' => [], + ]; + + foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{DAV:}prop' : + $newProps['properties'] = array_keys($elem['value']); + if (isset($elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data'])) { + $newProps += $elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data']; + } + break; + case '{DAV:}href' : + $newProps['hrefs'][] = Uri\resolve($reader->contextUri, $elem['value']); + break; + + } + + } + + $obj = new self(); + foreach ($newProps as $key => $value) { + $obj->$key = $value; + } + + return $obj; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php new file mode 100644 index 00000000000..e0b1c795032 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php @@ -0,0 +1,139 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree([ + '{urn:ietf:params:xml:ns:caldav}comp-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\CompFilter', + '{urn:ietf:params:xml:ns:caldav}prop-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\PropFilter', + '{urn:ietf:params:xml:ns:caldav}param-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\ParamFilter', + '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + ]); + + $newProps = [ + 'filters' => null, + 'properties' => [], + ]; + + if (!is_array($elems)) $elems = []; + + foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{DAV:}prop' : + $newProps['properties'] = array_keys($elem['value']); + if (isset($elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data'])) { + $newProps += $elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data']; + } + break; + case '{' . Plugin::NS_CALDAV . '}filter' : + foreach ($elem['value'] as $subElem) { + if ($subElem['name'] === '{' . Plugin::NS_CALDAV . '}comp-filter') { + if (!is_null($newProps['filters'])) { + throw new BadRequest('Only one top-level comp-filter may be defined'); + } + $newProps['filters'] = $subElem['value']; + } + } + break; + + } + + } + + if (is_null($newProps['filters'])) { + throw new BadRequest('The {' . Plugin::NS_CALDAV . '}filter element is required for this request'); + } + + $obj = new self(); + foreach ($newProps as $key => $value) { + $obj->$key = $value; + } + return $obj; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php new file mode 100644 index 00000000000..0f6c1e0741d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php @@ -0,0 +1,91 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $timeRange = '{' . Plugin::NS_CALDAV . '}time-range'; + + $start = null; + $end = null; + + foreach ((array)$reader->parseInnerTree([]) as $elem) { + + if ($elem['name'] !== $timeRange) continue; + + $start = empty($elem['attributes']['start']) ?: $elem['attributes']['start']; + $end = empty($elem['attributes']['end']) ?: $elem['attributes']['end']; + + } + if (!$start && !$end) { + throw new BadRequest('The freebusy report must have a time-range element'); + } + if ($start) { + $start = DateTimeParser::parseDateTime($start); + } + if ($end) { + $end = DateTimeParser::parseDateTime($end); + } + $result = new self(); + $result->start = $start; + $result->end = $end; + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php new file mode 100644 index 00000000000..db32cc6a59e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php @@ -0,0 +1,150 @@ +href = $href; + $this->calendarUri = $calendarUri; + $this->inReplyTo = $inReplyTo; + $this->summary = $summary; + $this->status = $status; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = KeyValue::xmlDeserialize($reader); + + $href = null; + $calendarUri = null; + $inReplyTo = null; + $summary = null; + $status = null; + + foreach ($elems as $name => $value) { + + switch ($name) { + + case '{' . Plugin::NS_CALENDARSERVER . '}hosturl' : + foreach ($value as $bla) { + if ($bla['name'] === '{DAV:}href') { + $calendarUri = $bla['value']; + } + } + break; + case '{' . Plugin::NS_CALENDARSERVER . '}invite-accepted' : + $status = DAV\Sharing\Plugin::INVITE_ACCEPTED; + break; + case '{' . Plugin::NS_CALENDARSERVER . '}invite-declined' : + $status = DAV\Sharing\Plugin::INVITE_DECLINED; + break; + case '{' . Plugin::NS_CALENDARSERVER . '}in-reply-to' : + $inReplyTo = $value; + break; + case '{' . Plugin::NS_CALENDARSERVER . '}summary' : + $summary = $value; + break; + case '{DAV:}href' : + $href = $value; + break; + } + + } + if (is_null($calendarUri)) { + throw new BadRequest('The {http://calendarserver.org/ns/}hosturl/{DAV:}href element must exist'); + } + + return new self($href, $calendarUri, $inReplyTo, $summary, $status); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php new file mode 100644 index 00000000000..ce7fafde910 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php @@ -0,0 +1,79 @@ +properties; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $self = new self(); + + $elementMap = $reader->elementMap; + $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop'; + $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue'; + $elems = $reader->parseInnerTree($elementMap); + + foreach ($elems as $elem) { + if ($elem['name'] === '{DAV:}set') { + $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']); + } + } + + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php new file mode 100644 index 00000000000..e0bd8e0af31 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php @@ -0,0 +1,111 @@ +sharees = $sharees; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseGetElements([ + '{' . Plugin::NS_CALENDARSERVER . '}set' => 'Sabre\\Xml\\Element\\KeyValue', + '{' . Plugin::NS_CALENDARSERVER . '}remove' => 'Sabre\\Xml\\Element\\KeyValue', + ]); + + $sharees = []; + + foreach ($elems as $elem) { + switch ($elem['name']) { + + case '{' . Plugin::NS_CALENDARSERVER . '}set' : + $sharee = $elem['value']; + + $sumElem = '{' . Plugin::NS_CALENDARSERVER . '}summary'; + $commonName = '{' . Plugin::NS_CALENDARSERVER . '}common-name'; + + $properties = []; + if (isset($sharee[$commonName])) { + $properties['{DAV:}displayname'] = $sharee[$commonName]; + } + + $access = array_key_exists('{' . Plugin::NS_CALENDARSERVER . '}read-write', $sharee) + ? \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE + : \Sabre\DAV\Sharing\Plugin::ACCESS_READ; + + $sharees[] = new Sharee([ + 'href' => $sharee['{DAV:}href'], + 'properties' => $properties, + 'access' => $access, + 'comment' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null + ]); + break; + + case '{' . Plugin::NS_CALENDARSERVER . '}remove' : + $sharees[] = new Sharee([ + 'href' => $elem['value']['{DAV:}href'], + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS + ]); + break; + + } + } + + return new self($sharees); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php new file mode 100644 index 00000000000..c9d28a091c8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php @@ -0,0 +1,357 @@ +carddavBackend = $carddavBackend; + $this->addressBookInfo = $addressBookInfo; + + } + + /** + * Returns the name of the addressbook + * + * @return string + */ + function getName() { + + return $this->addressBookInfo['uri']; + + } + + /** + * Returns a card + * + * @param string $name + * @return Card + */ + function getChild($name) { + + $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name); + if (!$obj) throw new DAV\Exception\NotFound('Card not found'); + return new Card($this->carddavBackend, $this->addressBookInfo, $obj); + + } + + /** + * Returns the full list of cards + * + * @return array + */ + function getChildren() { + + $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']); + $children = []; + foreach ($objs as $obj) { + $obj['acl'] = $this->getChildACL(); + $children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj); + } + return $children; + + } + + /** + * This method receives a list of paths in it's first argument. + * It must return an array with Node objects. + * + * If any children are not found, you do not have to return them. + * + * @param string[] $paths + * @return array + */ + function getMultipleChildren(array $paths) { + + $objs = $this->carddavBackend->getMultipleCards($this->addressBookInfo['id'], $paths); + $children = []; + foreach ($objs as $obj) { + $obj['acl'] = $this->getChildACL(); + $children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj); + } + return $children; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in addressbooks. + * + * @param string $name + * @return void + */ + function createDirectory($name) { + + throw new DAV\Exception\MethodNotAllowed('Creating collections in addressbooks is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid VCARD. + * + * This method may return an ETag. + * + * @param string $name + * @param resource $vcardData + * @return string|null + */ + function createFile($name, $vcardData = null) { + + if (is_resource($vcardData)) { + $vcardData = stream_get_contents($vcardData); + } + // Converting to UTF-8, if needed + $vcardData = DAV\StringUtil::ensureUTF8($vcardData); + + return $this->carddavBackend->createCard($this->addressBookInfo['id'], $name, $vcardData); + + } + + /** + * Deletes the entire addressbook. + * + * @return void + */ + function delete() { + + $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']); + + } + + /** + * Renames the addressbook + * + * @param string $newName + * @return void + */ + function setName($newName) { + + throw new DAV\Exception\MethodNotAllowed('Renaming addressbooks is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return void + */ + function getLastModified() { + + return null; + + } + + /** + * Updates properties on this node. + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * To update specific properties, call the 'handle' method on this object. + * Read the PropPatch documentation for more information. + * + * @param DAV\PropPatch $propPatch + * @return void + */ + function propPatch(DAV\PropPatch $propPatch) { + + return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $propPatch); + + } + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname + * + * If the array is empty, it means 'all properties' were requested. + * + * @param array $properties + * @return array + */ + function getProperties($properties) { + + $response = []; + foreach ($properties as $propertyName) { + + if (isset($this->addressBookInfo[$propertyName])) { + + $response[$propertyName] = $this->addressBookInfo[$propertyName]; + + } + + } + + return $response; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->addressBookInfo['principaluri']; + + } + + + /** + * This method returns the ACL's for card nodes in this address book. + * The result of this method automatically gets passed to the + * card nodes in this address book. + * + * @return array + */ + function getChildACL() { + + return [ + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->getOwner(), + 'protected' => true, + ], + ]; + + } + + + /** + * This method returns the current sync-token for this collection. + * This can be any string. + * + * If null is returned from this function, the plugin assumes there's no + * sync information available. + * + * @return string|null + */ + function getSyncToken() { + + if ( + $this->carddavBackend instanceof Backend\SyncSupport && + isset($this->addressBookInfo['{DAV:}sync-token']) + ) { + return $this->addressBookInfo['{DAV:}sync-token']; + } + if ( + $this->carddavBackend instanceof Backend\SyncSupport && + isset($this->addressBookInfo['{http://sabredav.org/ns}sync-token']) + ) { + return $this->addressBookInfo['{http://sabredav.org/ns}sync-token']; + } + + } + + /** + * The getChanges method returns all the changes that have happened, since + * the specified syncToken and the current collection. + * + * This function should return an array, such as the following: + * + * [ + * 'syncToken' => 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'modified.txt', + * ], + * 'deleted' => [ + * 'foo.php.bak', + * 'old.txt' + * ] + * ]; + * + * The syncToken property should reflect the *current* syncToken of the + * collection, as reported getSyncToken(). This is needed here too, to + * ensure the operation is atomic. + * + * If the syncToken is specified as null, this is an initial sync, and all + * members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The second argument is basically the 'depth' of the report. If it's 1, + * you only have to report changes that happened only directly in immediate + * descendants. If it's 2, it should also include changes from the nodes + * below the child collections. (grandchildren) + * + * The third (optional) argument allows a client to specify how many + * results should be returned at most. If the limit is not specified, it + * should be treated as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChanges($syncToken, $syncLevel, $limit = null) { + + if (!$this->carddavBackend instanceof Backend\SyncSupport) { + return null; + } + + return $this->carddavBackend->getChangesForAddressBook( + $this->addressBookInfo['id'], + $syncToken, + $syncLevel, + $limit + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php new file mode 100644 index 00000000000..d770c0ffe0f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php @@ -0,0 +1,191 @@ +carddavBackend = $carddavBackend; + $this->principalUri = $principalUri; + + } + + /** + * Returns the name of this object + * + * @return string + */ + function getName() { + + list(, $name) = Uri\split($this->principalUri); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + function setName($name) { + + throw new DAV\Exception\MethodNotAllowed(); + + } + + /** + * Deletes this object + * + * @return void + */ + function delete() { + + throw new DAV\Exception\MethodNotAllowed(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + function createFile($filename, $data = null) { + + throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + function createDirectory($filename) { + + throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single addressbook, by name + * + * @param string $name + * @todo needs optimizing + * @return AddressBook + */ + function getChild($name) { + + foreach ($this->getChildren() as $child) { + if ($name == $child->getName()) + return $child; + + } + throw new DAV\Exception\NotFound('Addressbook with name \'' . $name . '\' could not be found'); + + } + + /** + * Returns a list of addressbooks + * + * @return array + */ + function getChildren() { + + $addressbooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri); + $objs = []; + foreach ($addressbooks as $addressbook) { + $objs[] = new AddressBook($this->carddavBackend, $addressbook); + } + return $objs; + + } + + /** + * Creates a new address book. + * + * @param string $name + * @param MkCol $mkCol + * @throws DAV\Exception\InvalidResourceType + * @return void + */ + function createExtendedCollection($name, MkCol $mkCol) { + + if (!$mkCol->hasResourceType('{' . Plugin::NS_CARDDAV . '}addressbook')) { + throw new DAV\Exception\InvalidResourceType('Unknown resourceType for this collection'); + } + $properties = $mkCol->getRemainingValues(); + $mkCol->setRemainingResultCode(201); + $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->principalUri; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php new file mode 100644 index 00000000000..a9f1183da49 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php @@ -0,0 +1,80 @@ +carddavBackend = $carddavBackend; + parent::__construct($principalBackend, $principalPrefix); + + } + + /** + * Returns the name of the node + * + * @return string + */ + function getName() { + + return Plugin::ADDRESSBOOK_ROOT; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return \Sabre\DAV\INode + */ + function getChildForPrincipal(array $principal) { + + return new AddressBookHome($this->carddavBackend, $principal['uri']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php new file mode 100644 index 00000000000..03d2346da08 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php @@ -0,0 +1,38 @@ +getCard($addressBookId, $uri); + }, $uris); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php new file mode 100644 index 00000000000..18c0c0a9982 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php @@ -0,0 +1,190 @@ +pdo = $pdo; + + } + + /** + * Returns the list of addressbooks for a specific user. + * + * @param string $principalUri + * @return array + */ + function getAddressBooksForUser($principalUri) { + + $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM ' . $this->addressBooksTableName . ' WHERE principaluri = ?'); + $stmt->execute([$principalUri]); + + $addressBooks = []; + + foreach ($stmt->fetchAll() as $row) { + + $addressBooks[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{DAV:}displayname' => $row['displayname'], + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], + '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], + '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0', + ]; + + } + + return $addressBooks; + + } + + + /** + * Updates properties for an address book. + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param string $addressBookId + * @param \Sabre\DAV\PropPatch $propPatch + * @return void + */ + function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) { + + $supportedProperties = [ + '{DAV:}displayname', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description', + ]; + + $propPatch->handle($supportedProperties, function($mutations) use ($addressBookId) { + + $updates = []; + foreach ($mutations as $property => $newValue) { + + switch ($property) { + case '{DAV:}displayname' : + $updates['displayname'] = $newValue; + break; + case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : + $updates['description'] = $newValue; + break; + } + } + $query = 'UPDATE ' . $this->addressBooksTableName . ' SET '; + $first = true; + foreach ($updates as $key => $value) { + if ($first) { + $first = false; + } else { + $query .= ', '; + } + $query .= ' ' . $key . ' = :' . $key . ' '; + } + $query .= ' WHERE id = :addressbookid'; + + $stmt = $this->pdo->prepare($query); + $updates['addressbookid'] = $addressBookId; + + $stmt->execute($updates); + + $this->addChange($addressBookId, "", 2); + + return true; + + }); + + } + + /** + * Creates a new address book + * + * @param string $principalUri + * @param string $url Just the 'basename' of the url. + * @param array $properties + * @return int Last insert id + */ + function createAddressBook($principalUri, $url, array $properties) { + + $values = [ + 'displayname' => null, + 'description' => null, + 'principaluri' => $principalUri, + 'uri' => $url, + ]; + + foreach ($properties as $property => $newValue) { + + switch ($property) { + case '{DAV:}displayname' : + $values['displayname'] = $newValue; + break; + case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : + $values['description'] = $newValue; + break; + default : + throw new DAV\Exception\BadRequest('Unknown property: ' . $property); + } + + } + + $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, synctoken) VALUES (:uri, :displayname, :description, :principaluri, 1)'; + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + return $this->pdo->lastInsertId( + $this->addressBooksTableName . '_id_seq' + ); + + } + + /** + * Deletes an entire addressbook and all its contents + * + * @param int $addressBookId + * @return void + */ + function deleteAddressBook($addressBookId) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); + $stmt->execute([$addressBookId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); + $stmt->execute([$addressBookId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBookChangesTableName . ' WHERE addressbookid = ?'); + $stmt->execute([$addressBookId]); + + } + + /** + * Returns all cards for a specific addressbook id. + * + * This method should return the following properties for each card: + * * carddata - raw vcard data + * * uri - Some unique url + * * lastmodified - A unix timestamp + * + * It's recommended to also return the following properties: + * * etag - A unique etag. This must change every time the card changes. + * * size - The size of the card in bytes. + * + * If these last two properties are provided, less time will be spent + * calculating them. If they are specified, you can also ommit carddata. + * This may speed up certain requests, especially with large cards. + * + * @param mixed $addressbookId + * @return array + */ + function getCards($addressbookId) { + + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); + $stmt->execute([$addressbookId]); + + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $row['etag'] = '"' . $row['etag'] . '"'; + $row['lastmodified'] = (int)$row['lastmodified']; + $result[] = $row; + } + return $result; + + } + + /** + * Returns a specific card. + * + * The same set of properties must be returned as with getCards. The only + * exception is that 'carddata' is absolutely required. + * + * If the card does not exist, you must return false. + * + * @param mixed $addressBookId + * @param string $cardUri + * @return array + */ + function getCard($addressBookId, $cardUri) { + + $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1'); + $stmt->execute([$addressBookId, $cardUri]); + + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + if (!$result) return false; + + $result['etag'] = '"' . $result['etag'] . '"'; + $result['lastmodified'] = (int)$result['lastmodified']; + return $result; + + } + + /** + * Returns a list of cards. + * + * This method should work identical to getCard, but instead return all the + * cards in the list as an array. + * + * If the backend supports this, it may allow for some speed-ups. + * + * @param mixed $addressBookId + * @param array $uris + * @return array + */ + function getMultipleCards($addressBookId, array $uris) { + + $query = 'SELECT id, uri, lastmodified, etag, size, carddata FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri IN ('; + // Inserting a whole bunch of question marks + $query .= implode(',', array_fill(0, count($uris), '?')); + $query .= ')'; + + $stmt = $this->pdo->prepare($query); + $stmt->execute(array_merge([$addressBookId], $uris)); + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $row['etag'] = '"' . $row['etag'] . '"'; + $row['lastmodified'] = (int)$row['lastmodified']; + $result[] = $row; + } + return $result; + + } + + /** + * Creates a new card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressBooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag is for the + * newly created resource, and must be enclosed with double quotes (that + * is, the string itself must contain the double quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null + */ + function createCard($addressBookId, $cardUri, $cardData) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)'); + + $etag = md5($cardData); + + $stmt->execute([ + $cardData, + $cardUri, + time(), + $addressBookId, + strlen($cardData), + $etag, + ]); + + $this->addChange($addressBookId, $cardUri, 1); + + return '"' . $etag . '"'; + + } + + /** + * Updates a card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressBooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag should + * match that of the updated resource, and must be enclosed with double + * quotes (that is: the string itself must contain the actual quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null + */ + function updateCard($addressBookId, $cardUri, $cardData) { + + $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ? AND addressbookid =?'); + + $etag = md5($cardData); + $stmt->execute([ + $cardData, + time(), + strlen($cardData), + $etag, + $cardUri, + $addressBookId + ]); + + $this->addChange($addressBookId, $cardUri, 2); + + return '"' . $etag . '"'; + + } + + /** + * Deletes a card + * + * @param mixed $addressBookId + * @param string $cardUri + * @return bool + */ + function deleteCard($addressBookId, $cardUri) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); + $stmt->execute([$addressBookId, $cardUri]); + + $this->addChange($addressBookId, $cardUri, 3); + + return $stmt->rowCount() === 1; + + } + + /** + * The getChanges method returns all the changes that have happened, since + * the specified syncToken in the specified address book. + * + * This function should return an array, such as the following: + * + * [ + * 'syncToken' => 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'updated.txt', + * ], + * 'deleted' => [ + * 'foo.php.bak', + * 'old.txt' + * ] + * ]; + * + * The returned syncToken property should reflect the *current* syncToken + * of the addressbook, as reported in the {http://sabredav.org/ns}sync-token + * property. This is needed here too, to ensure the operation is atomic. + * + * If the $syncToken argument is specified as null, this is an initial + * sync, and all members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The $syncLevel argument is basically the 'depth' of the report. If it's + * 1, you only have to report changes that happened only directly in + * immediate descendants. If it's 2, it should also include changes from + * the nodes below the child collections. (grandchildren) + * + * The $limit argument allows a client to specify how many results should + * be returned at most. If the limit is not specified, it should be treated + * as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $addressBookId + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) { + + // Current synctoken + $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); + $stmt->execute([$addressBookId]); + $currentToken = $stmt->fetchColumn(0); + + if (is_null($currentToken)) return null; + + $result = [ + 'syncToken' => $currentToken, + 'added' => [], + 'modified' => [], + 'deleted' => [], + ]; + + if ($syncToken) { + + $query = "SELECT uri, operation FROM " . $this->addressBookChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND addressbookid = ? ORDER BY synctoken"; + if ($limit > 0) $query .= " LIMIT " . (int)$limit; + + // Fetching all changes + $stmt = $this->pdo->prepare($query); + $stmt->execute([$syncToken, $currentToken, $addressBookId]); + + $changes = []; + + // This loop ensures that any duplicates are overwritten, only the + // last change on a node is relevant. + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $changes[$row['uri']] = $row['operation']; + + } + + foreach ($changes as $uri => $operation) { + + switch ($operation) { + case 1: + $result['added'][] = $uri; + break; + case 2: + $result['modified'][] = $uri; + break; + case 3: + $result['deleted'][] = $uri; + break; + } + + } + } else { + // No synctoken supplied, this is the initial sync. + $query = "SELECT uri FROM " . $this->cardsTableName . " WHERE addressbookid = ?"; + $stmt = $this->pdo->prepare($query); + $stmt->execute([$addressBookId]); + + $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); + } + return $result; + + } + + /** + * Adds a change record to the addressbookchanges table. + * + * @param mixed $addressBookId + * @param string $objectUri + * @param int $operation 1 = add, 2 = modify, 3 = delete + * @return void + */ + protected function addChange($addressBookId, $objectUri, $operation) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->addressBookChangesTableName . ' (uri, synctoken, addressbookid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); + $stmt->execute([ + $objectUri, + $addressBookId, + $operation, + $addressBookId + ]); + $stmt = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET synctoken = synctoken + 1 WHERE id = ?'); + $stmt->execute([ + $addressBookId + ]); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php new file mode 100644 index 00000000000..f80618a8e14 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php @@ -0,0 +1,81 @@ + 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'modified.txt', + * ], + * 'deleted' => [ + * 'foo.php.bak', + * 'old.txt' + * ] + * ]; + * + * The returned syncToken property should reflect the *current* syncToken + * of the calendar, as reported in the {http://sabredav.org/ns}sync-token + * property. This is needed here too, to ensure the operation is atomic. + * + * If the $syncToken argument is specified as null, this is an initial + * sync, and all members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The $syncLevel argument is basically the 'depth' of the report. If it's + * 1, you only have to report changes that happened only directly in + * immediate descendants. If it's 2, it should also include changes from + * the nodes below the child collections. (grandchildren) + * + * The $limit argument allows a client to specify how many results should + * be returned at most. If the limit is not specified, it should be treated + * as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $addressBookId + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null); + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Card.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Card.php new file mode 100644 index 00000000000..42a2d7b6a11 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Card.php @@ -0,0 +1,216 @@ +carddavBackend = $carddavBackend; + $this->addressBookInfo = $addressBookInfo; + $this->cardData = $cardData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + function getName() { + + return $this->cardData['uri']; + + } + + /** + * Returns the VCard-formatted object + * + * @return string + */ + function get() { + + // Pre-populating 'carddata' is optional. If we don't yet have it + // already, we fetch it from the backend. + if (!isset($this->cardData['carddata'])) { + $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']); + } + return $this->cardData['carddata']; + + } + + /** + * Updates the VCard-formatted object + * + * @param string $cardData + * @return string|null + */ + function put($cardData) { + + if (is_resource($cardData)) + $cardData = stream_get_contents($cardData); + + // Converting to UTF-8, if needed + $cardData = DAV\StringUtil::ensureUTF8($cardData); + + $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'], $this->cardData['uri'], $cardData); + $this->cardData['carddata'] = $cardData; + $this->cardData['etag'] = $etag; + + return $etag; + + } + + /** + * Deletes the card + * + * @return void + */ + function delete() { + + $this->carddavBackend->deleteCard($this->addressBookInfo['id'], $this->cardData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + function getContentType() { + + return 'text/vcard; charset=utf-8'; + + } + + /** + * Returns an ETag for this object + * + * @return string + */ + function getETag() { + + if (isset($this->cardData['etag'])) { + return $this->cardData['etag']; + } else { + $data = $this->get(); + if (is_string($data)) { + return '"' . md5($data) . '"'; + } else { + // We refuse to calculate the md5 if it's a stream. + return null; + } + } + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return int + */ + function getLastModified() { + + return isset($this->cardData['lastmodified']) ? $this->cardData['lastmodified'] : null; + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + function getSize() { + + if (array_key_exists('size', $this->cardData)) { + return $this->cardData['size']; + } else { + return strlen($this->get()); + } + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->addressBookInfo['principaluri']; + + } + + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + // An alternative acl may be specified through the cardData array. + if (isset($this->cardData['acl'])) { + return $this->cardData['acl']; + } + + return [ + [ + 'privilege' => '{DAV:}all', + 'principal' => $this->addressBookInfo['principaluri'], + 'protected' => true, + ], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php new file mode 100644 index 00000000000..f80e0557554 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php @@ -0,0 +1,18 @@ +on('propFind', [$this, 'propFindEarly']); + $server->on('propFind', [$this, 'propFindLate'], 150); + $server->on('report', [$this, 'report']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); + $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); + $server->on('afterMethod:GET', [$this, 'httpAfterGet']); + + $server->xml->namespaceMap[self::NS_CARDDAV] = 'card'; + + $server->xml->elementMap['{' . self::NS_CARDDAV . '}addressbook-query'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport'; + $server->xml->elementMap['{' . self::NS_CARDDAV . '}addressbook-multiget'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport'; + + /* Mapping Interfaces to {DAV:}resourcetype values */ + $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; + $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; + + /* Adding properties that may never be changed */ + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; + + $server->xml->elementMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Xml\\Property\\Href'; + + $this->server = $server; + + } + + /** + * Returns a list of supported features. + * + * This is used in the DAV: header in the OPTIONS and PROPFIND requests. + * + * @return array + */ + function getFeatures() { + + return ['addressbook']; + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + function getSupportedReportSet($uri) { + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof IAddressBook || $node instanceof ICard) { + return [ + '{' . self::NS_CARDDAV . '}addressbook-multiget', + '{' . self::NS_CARDDAV . '}addressbook-query', + ]; + } + return []; + + } + + + /** + * Adds all CardDAV-specific properties + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { + + $ns = '{' . self::NS_CARDDAV . '}'; + + if ($node instanceof IAddressBook) { + + $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); + $propFind->handle($ns . 'supported-address-data', function() { + return new Xml\Property\SupportedAddressData(); + }); + $propFind->handle($ns . 'supported-collation-set', function() { + return new Xml\Property\SupportedCollationSet(); + }); + + } + if ($node instanceof DAVACL\IPrincipal) { + + $path = $propFind->getPath(); + + $propFind->handle('{' . self::NS_CARDDAV . '}addressbook-home-set', function() use ($path) { + return new LocalHref($this->getAddressBookHomeForPrincipal($path) . '/'); + }); + + if ($this->directories) $propFind->handle('{' . self::NS_CARDDAV . '}directory-gateway', function() { + return new LocalHref($this->directories); + }); + + } + + if ($node instanceof ICard) { + + // The address-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. + // Therefore we simply expose it as a property. + $propFind->handle('{' . self::NS_CARDDAV . '}address-data', function() use ($node) { + $val = $node->get(); + if (is_resource($val)) + $val = stream_get_contents($val); + + return $val; + + }); + + } + + } + + /** + * This functions handles REPORT requests specific to CardDAV + * + * @param string $reportName + * @param \DOMNode $dom + * @param mixed $path + * @return bool + */ + function report($reportName, $dom, $path) { + + switch ($reportName) { + case '{' . self::NS_CARDDAV . '}addressbook-multiget' : + $this->server->transactionType = 'report-addressbook-multiget'; + $this->addressbookMultiGetReport($dom); + return false; + case '{' . self::NS_CARDDAV . '}addressbook-query' : + $this->server->transactionType = 'report-addressbook-query'; + $this->addressBookQueryReport($dom); + return false; + default : + return; + + } + + + } + + /** + * Returns the addressbook home for a given principal + * + * @param string $principal + * @return string + */ + protected function getAddressbookHomeForPrincipal($principal) { + + list(, $principalId) = \Sabre\HTTP\URLUtil::splitPath($principal); + return self::ADDRESSBOOK_ROOT . '/' . $principalId; + + } + + + /** + * This function handles the addressbook-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param Xml\Request\AddressBookMultiGetReport $report + * @return void + */ + function addressbookMultiGetReport($report) { + + $contentType = $report->contentType; + $version = $report->version; + if ($version) { + $contentType .= '; version=' . $version; + } + + $vcardType = $this->negotiateVCard( + $contentType + ); + + $propertyList = []; + $paths = array_map( + [$this->server, 'calculateUri'], + $report->hrefs + ); + foreach ($this->server->getPropertiesForMultiplePaths($paths, $report->properties) as $props) { + + if (isset($props['200']['{' . self::NS_CARDDAV . '}address-data'])) { + + $props['200']['{' . self::NS_CARDDAV . '}address-data'] = $this->convertVCard( + $props[200]['{' . self::NS_CARDDAV . '}address-data'], + $vcardType + ); + + } + $propertyList[] = $props; + + } + + $prefer = $this->server->getHTTPPrefer(); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); + $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return'] === 'minimal')); + + } + + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that Card nodes receive valid + * vcard data. + * + * @param string $path + * @param DAV\IFile $node + * @param resource $data + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @return void + */ + function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) { + + if (!$node instanceof ICard) + return; + + $this->validateVCard($data, $modified); + + } + + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that Card nodes receive valid + * vcard data. + * + * @param string $path + * @param resource $data + * @param DAV\ICollection $parentNode + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @return void + */ + function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) { + + if (!$parentNode instanceof IAddressBook) + return; + + $this->validateVCard($data, $modified); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @return void + */ + protected function validateVCard(&$data, &$modified) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + $before = $data; + + try { + + // If the data starts with a [, we can reasonably assume we're dealing + // with a jCal object. + if (substr($data, 0, 1) === '[') { + $vobj = VObject\Reader::readJson($data); + + // Converting $data back to iCalendar, as that's what we + // technically support everywhere. + $data = $vobj->serialize(); + $modified = true; + } else { + $vobj = VObject\Reader::read($data); + } + + } catch (VObject\ParseException $e) { + + throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vCard or jCard data. Parse error: ' . $e->getMessage()); + + } + + if ($vobj->name !== 'VCARD') { + throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.'); + } + + $options = VObject\Node::PROFILE_CARDDAV; + $prefer = $this->server->getHTTPPrefer(); + + if ($prefer['handling'] !== 'strict') { + $options |= VObject\Node::REPAIR; + } + + $messages = $vobj->validate($options); + + $highestLevel = 0; + $warningMessage = null; + + // $messages contains a list of problems with the vcard, along with + // their severity. + foreach ($messages as $message) { + + if ($message['level'] > $highestLevel) { + // Recording the highest reported error level. + $highestLevel = $message['level']; + $warningMessage = $message['message']; + } + + switch ($message['level']) { + + case 1 : + // Level 1 means that there was a problem, but it was repaired. + $modified = true; + break; + case 2 : + // Level 2 means a warning, but not critical + break; + case 3 : + // Level 3 means a critical error + throw new DAV\Exception\UnsupportedMediaType('Validation error in vCard: ' . $message['message']); + + } + + } + if ($warningMessage) { + $this->server->httpResponse->setHeader( + 'X-Sabre-Ew-Gross', + 'vCard validation warning: ' . $warningMessage + ); + + // Re-serializing object. + $data = $vobj->serialize(); + if (!$modified && strcmp($data, $before) !== 0) { + // This ensures that the system does not send an ETag back. + $modified = true; + } + } + + // Destroy circular references to PHP will GC the object. + $vobj->destroy(); + } + + + /** + * This function handles the addressbook-query REPORT + * + * This report is used by the client to filter an addressbook based on a + * complex query. + * + * @param Xml\Request\AddressBookQueryReport $report + * @return void + */ + protected function addressbookQueryReport($report) { + + $depth = $this->server->getHTTPDepth(0); + + if ($depth == 0) { + $candidateNodes = [ + $this->server->tree->getNodeForPath($this->server->getRequestUri()) + ]; + if (!$candidateNodes[0] instanceof ICard) { + throw new ReportNotSupported('The addressbook-query report is not supported on this url with Depth: 0'); + } + } else { + $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); + } + + $contentType = $report->contentType; + if ($report->version) { + $contentType .= '; version=' . $report->version; + } + + $vcardType = $this->negotiateVCard( + $contentType + ); + + $validNodes = []; + foreach ($candidateNodes as $node) { + + if (!$node instanceof ICard) + continue; + + $blob = $node->get(); + if (is_resource($blob)) { + $blob = stream_get_contents($blob); + } + + if (!$this->validateFilters($blob, $report->filters, $report->test)) { + continue; + } + + $validNodes[] = $node; + + if ($report->limit && $report->limit <= count($validNodes)) { + // We hit the maximum number of items, we can stop now. + break; + } + + } + + $result = []; + foreach ($validNodes as $validNode) { + + if ($depth == 0) { + $href = $this->server->getRequestUri(); + } else { + $href = $this->server->getRequestUri() . '/' . $validNode->getName(); + } + + list($props) = $this->server->getPropertiesForPath($href, $report->properties, 0); + + if (isset($props[200]['{' . self::NS_CARDDAV . '}address-data'])) { + + $props[200]['{' . self::NS_CARDDAV . '}address-data'] = $this->convertVCard( + $props[200]['{' . self::NS_CARDDAV . '}address-data'], + $vcardType, + $report->addressDataProperties + ); + + } + $result[] = $props; + + } + + $prefer = $this->server->getHTTPPrefer(); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); + $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal')); + + } + + /** + * Validates if a vcard makes it throught a list of filters. + * + * @param string $vcardData + * @param array $filters + * @param string $test anyof or allof (which means OR or AND) + * @return bool + */ + function validateFilters($vcardData, array $filters, $test) { + + + if (!$filters) return true; + $vcard = VObject\Reader::read($vcardData); + + foreach ($filters as $filter) { + + $isDefined = isset($vcard->{$filter['name']}); + if ($filter['is-not-defined']) { + if ($isDefined) { + $success = false; + } else { + $success = true; + } + } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) { + + // We only need to check for existence + $success = $isDefined; + + } else { + + $vProperties = $vcard->select($filter['name']); + + $results = []; + if ($filter['param-filters']) { + $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']); + } + if ($filter['text-matches']) { + $texts = []; + foreach ($vProperties as $vProperty) + $texts[] = $vProperty->getValue(); + + $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']); + } + + if (count($results) === 1) { + $success = $results[0]; + } else { + if ($filter['test'] === 'anyof') { + $success = $results[0] || $results[1]; + } else { + $success = $results[0] && $results[1]; + } + } + + } // else + + // There are two conditions where we can already determine whether + // or not this filter succeeds. + if ($test === 'anyof' && $success) { + + // Destroy circular references to PHP will GC the object. + $vcard->destroy(); + + return true; + } + if ($test === 'allof' && !$success) { + + // Destroy circular references to PHP will GC the object. + $vcard->destroy(); + + return false; + } + + } // foreach + + + // Destroy circular references to PHP will GC the object. + $vcard->destroy(); + + // If we got all the way here, it means we haven't been able to + // determine early if the test failed or not. + // + // This implies for 'anyof' that the test failed, and for 'allof' that + // we succeeded. Sounds weird, but makes sense. + return $test === 'allof'; + + } + + /** + * Validates if a param-filter can be applied to a specific property. + * + * @todo currently we're only validating the first parameter of the passed + * property. Any subsequence parameters with the same name are + * ignored. + * @param array $vProperties + * @param array $filters + * @param string $test + * @return bool + */ + protected function validateParamFilters(array $vProperties, array $filters, $test) { + + foreach ($filters as $filter) { + + $isDefined = false; + foreach ($vProperties as $vProperty) { + $isDefined = isset($vProperty[$filter['name']]); + if ($isDefined) break; + } + + if ($filter['is-not-defined']) { + if ($isDefined) { + $success = false; + } else { + $success = true; + } + + // If there's no text-match, we can just check for existence + } elseif (!$filter['text-match'] || !$isDefined) { + + $success = $isDefined; + + } else { + + $success = false; + foreach ($vProperties as $vProperty) { + // If we got all the way here, we'll need to validate the + // text-match filter. + $success = DAV\StringUtil::textMatch($vProperty[$filter['name']]->getValue(), $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']); + if ($success) break; + } + if ($filter['text-match']['negate-condition']) { + $success = !$success; + } + + } // else + + // There are two conditions where we can already determine whether + // or not this filter succeeds. + if ($test === 'anyof' && $success) { + return true; + } + if ($test === 'allof' && !$success) { + return false; + } + + } + + // If we got all the way here, it means we haven't been able to + // determine early if the test failed or not. + // + // This implies for 'anyof' that the test failed, and for 'allof' that + // we succeeded. Sounds weird, but makes sense. + return $test === 'allof'; + + } + + /** + * Validates if a text-filter can be applied to a specific property. + * + * @param array $texts + * @param array $filters + * @param string $test + * @return bool + */ + protected function validateTextMatches(array $texts, array $filters, $test) { + + foreach ($filters as $filter) { + + $success = false; + foreach ($texts as $haystack) { + $success = DAV\StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']); + + // Breaking on the first match + if ($success) break; + } + if ($filter['negate-condition']) { + $success = !$success; + } + + if ($success && $test === 'anyof') + return true; + + if (!$success && $test == 'allof') + return false; + + + } + + // If we got all the way here, it means we haven't been able to + // determine early if the test failed or not. + // + // This implies for 'anyof' that the test failed, and for 'allof' that + // we succeeded. Sounds weird, but makes sense. + return $test === 'allof'; + + } + + /** + * This event is triggered when fetching properties. + * + * This event is scheduled late in the process, after most work for + * propfind has been done. + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { + + // If the request was made using the SOGO connector, we must rewrite + // the content-type property. By default SabreDAV will send back + // text/x-vcard; charset=utf-8, but for SOGO we must strip that last + // part. + if (strpos($this->server->httpRequest->getHeader('User-Agent'), 'Thunderbird') === false) { + return; + } + $contentType = $propFind->get('{DAV:}getcontenttype'); + list($part) = explode(';', $contentType); + if ($part === 'text/x-vcard' || $part === 'text/vcard') { + $propFind->set('{DAV:}getcontenttype', 'text/x-vcard'); + } + + } + + /** + * This method is used to generate HTML output for the + * Sabre\DAV\Browser\Plugin. This allows us to generate an interface users + * can use to create new addressbooks. + * + * @param DAV\INode $node + * @param string $output + * @return bool + */ + function htmlActionsPanel(DAV\INode $node, &$output) { + + if (!$node instanceof AddressBookHome) + return; + + $output .= '
    +

    Create new address book

    + + +
    +
    + +
    + '; + + return false; + + } + + /** + * This event is triggered after GET requests. + * + * This is used to transform data into jCal, if this was requested. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpAfterGet(RequestInterface $request, ResponseInterface $response) { + + if (strpos($response->getHeader('Content-Type'), 'text/vcard') === false) { + return; + } + + $target = $this->negotiateVCard($request->getHeader('Accept'), $mimeType); + + $newBody = $this->convertVCard( + $response->getBody(), + $target + ); + + $response->setBody($newBody); + $response->setHeader('Content-Type', $mimeType . '; charset=utf-8'); + $response->setHeader('Content-Length', strlen($newBody)); + + } + + /** + * This helper function performs the content-type negotiation for vcards. + * + * It will return one of the following strings: + * 1. vcard3 + * 2. vcard4 + * 3. jcard + * + * It defaults to vcard3. + * + * @param string $input + * @param string $mimeType + * @return string + */ + protected function negotiateVCard($input, &$mimeType = null) { + + $result = HTTP\Util::negotiate( + $input, + [ + // Most often used mime-type. Version 3 + 'text/x-vcard', + // The correct standard mime-type. Defaults to version 3 as + // well. + 'text/vcard', + // vCard 4 + 'text/vcard; version=4.0', + // vCard 3 + 'text/vcard; version=3.0', + // jCard + 'application/vcard+json', + ] + ); + + $mimeType = $result; + switch ($result) { + + default : + case 'text/x-vcard' : + case 'text/vcard' : + case 'text/vcard; version=3.0' : + $mimeType = 'text/vcard'; + return 'vcard3'; + case 'text/vcard; version=4.0' : + return 'vcard4'; + case 'application/vcard+json' : + return 'jcard'; + + // @codeCoverageIgnoreStart + } + // @codeCoverageIgnoreEnd + + } + + /** + * Converts a vcard blob to a different version, or jcard. + * + * @param string|resource $data + * @param string $target + * @param array $propertiesFilter + * @return string + */ + protected function convertVCard($data, $target, array $propertiesFilter = null) { + + if (is_resource($data)) { + $data = stream_get_contents($data); + } + $input = VObject\Reader::read($data); + if (!empty($propertiesFilter)) { + $propertiesFilter = array_merge(['UID', 'VERSION', 'FN'], $propertiesFilter); + $keys = array_unique(array_map(function($child) { + return $child->name; + }, $input->children())); + $keys = array_diff($keys, $propertiesFilter); + foreach ($keys as $key) { + unset($input->$key); + } + $data = $input->serialize(); + } + $output = null; + try { + + switch ($target) { + default : + case 'vcard3' : + if ($input->getDocumentType() === VObject\Document::VCARD30) { + // Do nothing + return $data; + } + $output = $input->convert(VObject\Document::VCARD30); + return $output->serialize(); + case 'vcard4' : + if ($input->getDocumentType() === VObject\Document::VCARD40) { + // Do nothing + return $data; + } + $output = $input->convert(VObject\Document::VCARD40); + return $output->serialize(); + case 'jcard' : + $output = $input->convert(VObject\Document::VCARD40); + return json_encode($output); + + } + + } finally { + + // Destroy circular references to PHP will GC the object. + $input->destroy(); + if (!is_null($output)) { + $output->destroy(); + } + } + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'carddav'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds support for CardDAV (rfc6352)', + 'link' => 'http://sabre.io/dav/carddav/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php new file mode 100644 index 00000000000..2d61db6ac3d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php @@ -0,0 +1,172 @@ +server = $server; + $this->server->on('method:GET', [$this, 'httpGet'], 90); + $server->on('browserButtonActions', function($path, $node, &$actions) { + if ($node instanceof IAddressBook) { + $actions .= ''; + } + }); + } + + /** + * Intercepts GET requests on addressbook urls ending with ?export. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $queryParams = $request->getQueryParameters(); + if (!array_key_exists('export', $queryParams)) return; + + $path = $request->getPath(); + + $node = $this->server->tree->getNodeForPath($path); + + if (!($node instanceof IAddressBook)) return; + + $this->server->transactionType = 'get-addressbook-export'; + + // Checking ACL, if available. + if ($aclPlugin = $this->server->getPlugin('acl')) { + $aclPlugin->checkPrivileges($path, '{DAV:}read'); + } + + $nodes = $this->server->getPropertiesForPath($path, [ + '{' . Plugin::NS_CARDDAV . '}address-data', + ], 1); + + $format = 'text/directory'; + + $output = null; + $filenameExtension = null; + + switch ($format) { + case 'text/directory': + $output = $this->generateVCF($nodes); + $filenameExtension = '.vcf'; + break; + } + + $filename = preg_replace( + '/[^a-zA-Z0-9-_ ]/um', + '', + $node->getName() + ); + $filename .= '-' . date('Y-m-d') . $filenameExtension; + + $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); + $response->setHeader('Content-Type', $format); + + $response->setStatus(200); + $response->setBody($output); + + // Returning false to break the event chain + return false; + + } + + /** + * Merges all vcard objects, and builds one big vcf export + * + * @param array $nodes + * @return string + */ + function generateVCF(array $nodes) { + + $output = ""; + + foreach ($nodes as $node) { + + if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) { + continue; + } + $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data']; + + // Parsing this node so VObject can clean up the output. + $vcard = VObject\Reader::read($nodeData); + $output .= $vcard->serialize(); + + // Destroy circular references to PHP will GC the object. + $vcard->destroy(); + + } + + return $output; + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using \Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'vcf-export'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.', + 'link' => 'http://sabre.io/dav/vcf-export-plugin/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php new file mode 100644 index 00000000000..a130cd61d2e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php @@ -0,0 +1,63 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'contentType' => $reader->getAttribute('content-type') ?: 'text/vcard', + 'version' => $reader->getAttribute('version') ?: '3.0', + ]; + + $elems = (array)$reader->parseInnerTree(); + $result['addressDataProperties'] = array_map(function($element) { + return $element['attributes']['name']; + }, $elems); + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php new file mode 100644 index 00000000000..936e26917ce --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php @@ -0,0 +1,89 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'name' => null, + 'is-not-defined' => false, + 'text-match' => null, + ]; + + $att = $reader->parseAttributes(); + $result['name'] = $att['name']; + + $elems = $reader->parseInnerTree(); + + if (is_array($elems)) foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{' . Plugin::NS_CARDDAV . '}is-not-defined' : + $result['is-not-defined'] = true; + break; + case '{' . Plugin::NS_CARDDAV . '}text-match' : + $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains'; + + if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) { + throw new BadRequest('Unknown match-type: ' . $matchType); + } + $result['text-match'] = [ + 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap', + 'value' => $elem['value'], + 'match-type' => $matchType, + ]; + break; + + } + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php new file mode 100644 index 00000000000..d7799429db4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php @@ -0,0 +1,98 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = [ + 'name' => null, + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => [], + 'text-matches' => [], + ]; + + $att = $reader->parseAttributes(); + $result['name'] = $att['name']; + + if (isset($att['test']) && $att['test'] === 'allof') { + $result['test'] = 'allof'; + } + + $elems = $reader->parseInnerTree(); + + if (is_array($elems)) foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{' . Plugin::NS_CARDDAV . '}param-filter' : + $result['param-filters'][] = $elem['value']; + break; + case '{' . Plugin::NS_CARDDAV . '}is-not-defined' : + $result['is-not-defined'] = true; + break; + case '{' . Plugin::NS_CARDDAV . '}text-match' : + $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains'; + + if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) { + throw new BadRequest('Unknown match-type: ' . $matchType); + } + $result['text-matches'][] = [ + 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap', + 'value' => $elem['value'], + 'match-type' => $matchType, + ]; + break; + + } + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php new file mode 100644 index 00000000000..aecd8a09fc8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php @@ -0,0 +1,83 @@ + 'text/vcard', 'version' => '3.0'], + ['contentType' => 'text/vcard', 'version' => '4.0'], + ['contentType' => 'application/vcard+json', 'version' => '4.0'], + ]; + } + + $this->supportedData = $supportedData; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->supportedData as $supported) { + $writer->startElement('{' . Plugin::NS_CARDDAV . '}address-data-type'); + $writer->writeAttributes([ + 'content-type' => $supported['contentType'], + 'version' => $supported['version'] + ]); + $writer->endElement(); // address-data-type + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php new file mode 100644 index 00000000000..778aa2b64e5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php @@ -0,0 +1,47 @@ +writeElement('{urn:ietf:params:xml:ns:carddav}supported-collation', $coll); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php new file mode 100644 index 00000000000..0115a010722 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php @@ -0,0 +1,113 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree([ + '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + ]); + + $newProps = [ + 'hrefs' => [], + 'properties' => [] + ]; + + foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{DAV:}prop' : + $newProps['properties'] = array_keys($elem['value']); + if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { + $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; + } + break; + case '{DAV:}href' : + $newProps['hrefs'][] = Uri\resolve($reader->contextUri, $elem['value']); + break; + + } + + } + + $obj = new self(); + foreach ($newProps as $key => $value) { + $obj->$key = $value; + } + return $obj; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php new file mode 100644 index 00000000000..09fad008adb --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php @@ -0,0 +1,199 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = (array)$reader->parseInnerTree([ + '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', + '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter', + '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + ]); + + $newProps = [ + 'filters' => null, + 'properties' => [], + 'test' => 'anyof', + 'limit' => null, + ]; + + if (!is_array($elems)) $elems = []; + + foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{DAV:}prop' : + $newProps['properties'] = array_keys($elem['value']); + if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { + $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; + } + break; + case '{' . Plugin::NS_CARDDAV . '}filter' : + + if (!is_null($newProps['filters'])) { + throw new BadRequest('You can only include 1 {' . Plugin::NS_CARDDAV . '}filter element'); + } + if (isset($elem['attributes']['test'])) { + $newProps['test'] = $elem['attributes']['test']; + if ($newProps['test'] !== 'allof' && $newProps['test'] !== 'anyof') { + throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"'); + } + } + + $newProps['filters'] = []; + foreach ((array)$elem['value'] as $subElem) { + if ($subElem['name'] === '{' . Plugin::NS_CARDDAV . '}prop-filter') { + $newProps['filters'][] = $subElem['value']; + } + } + break; + case '{' . Plugin::NS_CARDDAV . '}limit' : + foreach ($elem['value'] as $child) { + if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') { + $newProps['limit'] = (int)$child['value']; + } + } + break; + + } + + } + + if (is_null($newProps['filters'])) { + /* + * We are supposed to throw this error, but KDE sometimes does not + * include the filter element, and we need to treat it as if no + * filters are supplied + */ + //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request'); + $newProps['filters'] = []; + + } + + $obj = new self(); + foreach ($newProps as $key => $value) { + $obj->$key = $value; + } + + return $obj; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php new file mode 100644 index 00000000000..40a95f8bfb7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php @@ -0,0 +1,144 @@ +realm = $realm; + + } + + /** + * When this method is called, the backend must check if authentication was + * successful. + * + * The returned value must be one of the following + * + * [true, "principals/username"] + * [false, "reason for failure"] + * + * If authentication was successful, it's expected that the authentication + * backend returns a so-called principal url. + * + * Examples of a principal url: + * + * principals/admin + * principals/user1 + * principals/users/joe + * principals/uid/123457 + * + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply + * return a string such as: + * + * principals/users/[username] + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function check(RequestInterface $request, ResponseInterface $response) { + + $auth = new HTTP\Auth\Basic( + $this->realm, + $request, + $response + ); + + $userpass = $auth->getCredentials(); + if (!$userpass) { + return [false, "No 'Authorization: Basic' header found. Either the client didn't send one, or the server is misconfigured"]; + } + if (!$this->validateUserPass($userpass[0], $userpass[1])) { + return [false, "Username or password was incorrect"]; + } + return [true, $this->principalPrefix . $userpass[0]]; + + } + + /** + * This method is called when a user could not be authenticated, and + * authentication was required for the current request. + * + * This gives you the opportunity to set authentication headers. The 401 + * status code will already be set. + * + * In this case of Basic Auth, this would for example mean that the + * following header needs to be set: + * + * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); + * + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + + $auth = new HTTP\Auth\Basic( + $this->realm, + $request, + $response + ); + $auth->requireLogin(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php new file mode 100644 index 00000000000..ae7a8a12f77 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php @@ -0,0 +1,138 @@ +realm = $realm; + + } + + /** + * When this method is called, the backend must check if authentication was + * successful. + * + * The returned value must be one of the following + * + * [true, "principals/username"] + * [false, "reason for failure"] + * + * If authentication was successful, it's expected that the authentication + * backend returns a so-called principal url. + * + * Examples of a principal url: + * + * principals/admin + * principals/user1 + * principals/users/joe + * principals/uid/123457 + * + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply + * return a string such as: + * + * principals/users/[username] + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function check(RequestInterface $request, ResponseInterface $response) { + + $auth = new HTTP\Auth\Bearer( + $this->realm, + $request, + $response + ); + + $bearerToken = $auth->getToken($request); + if (!$bearerToken) { + return [false, "No 'Authorization: Bearer' header found. Either the client didn't send one, or the server is mis-configured"]; + } + $principalUrl = $this->validateBearerToken($bearerToken); + if (!$principalUrl) { + return [false, "Bearer token was incorrect"]; + } + return [true, $principalUrl]; + + } + + /** + * This method is called when a user could not be authenticated, and + * authentication was required for the current request. + * + * This gives you the opportunity to set authentication headers. The 401 + * status code will already be set. + * + * In this case of Bearer Auth, this would for example mean that the + * following header needs to be set: + * + * $response->addHeader('WWW-Authenticate', 'Bearer realm=SabreDAV'); + * + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + + $auth = new HTTP\Auth\Bearer( + $this->realm, + $request, + $response + ); + $auth->requireLogin(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php new file mode 100644 index 00000000000..4b47f56c9ff --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php @@ -0,0 +1,168 @@ +realm = $realm; + + } + + /** + * Returns a users digest hash based on the username and realm. + * + * If the user was not known, null must be returned. + * + * @param string $realm + * @param string $username + * @return string|null + */ + abstract function getDigestHash($realm, $username); + + /** + * When this method is called, the backend must check if authentication was + * successful. + * + * The returned value must be one of the following + * + * [true, "principals/username"] + * [false, "reason for failure"] + * + * If authentication was successful, it's expected that the authentication + * backend returns a so-called principal url. + * + * Examples of a principal url: + * + * principals/admin + * principals/user1 + * principals/users/joe + * principals/uid/123457 + * + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply + * return a string such as: + * + * principals/users/[username] + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function check(RequestInterface $request, ResponseInterface $response) { + + $digest = new HTTP\Auth\Digest( + $this->realm, + $request, + $response + ); + $digest->init(); + + $username = $digest->getUsername(); + + // No username was given + if (!$username) { + return [false, "No 'Authorization: Digest' header found. Either the client didn't send one, or the server is misconfigured"]; + } + + $hash = $this->getDigestHash($this->realm, $username); + // If this was false, the user account didn't exist + if ($hash === false || is_null($hash)) { + return [false, "Username or password was incorrect"]; + } + if (!is_string($hash)) { + throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); + } + + // If this was false, the password or part of the hash was incorrect. + if (!$digest->validateA1($hash)) { + return [false, "Username or password was incorrect"]; + } + + return [true, $this->principalPrefix . $username]; + + } + + /** + * This method is called when a user could not be authenticated, and + * authentication was required for the current request. + * + * This gives you the opportunity to set authentication headers. The 401 + * status code will already be set. + * + * In this case of Basic Auth, this would for example mean that the + * following header needs to be set: + * + * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); + * + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + + $auth = new HTTP\Auth\Digest( + $this->realm, + $request, + $response + ); + $auth->init(); + + $oldStatus = $response->getStatus() ?: 200; + $auth->requireLogin(); + + // Preventing the digest utility from modifying the http status code, + // this should be handled by the main plugin. + $response->setStatus($oldStatus); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php new file mode 100644 index 00000000000..e203d26855d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php @@ -0,0 +1,96 @@ +getRawServerValue('REMOTE_USER'); + if (is_null($remoteUser)) { + $remoteUser = $request->getRawServerValue('REDIRECT_REMOTE_USER'); + } + if (is_null($remoteUser)) { + return [false, 'No REMOTE_USER property was found in the PHP $_SERVER super-global. This likely means your server is not configured correctly']; + } + + return [true, $this->principalPrefix . $remoteUser]; + + } + + /** + * This method is called when a user could not be authenticated, and + * authentication was required for the current request. + * + * This gives you the opportunity to set authentication headers. The 401 + * status code will already be set. + * + * In this case of Basic Auth, this would for example mean that the + * following header needs to be set: + * + * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); + * + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php new file mode 100644 index 00000000000..0fb2210f488 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php @@ -0,0 +1,70 @@ +addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); + * + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function challenge(RequestInterface $request, ResponseInterface $response); + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php new file mode 100644 index 00000000000..7ad8f48b2e1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php @@ -0,0 +1,58 @@ +callBack = $callBack; + + } + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + protected function validateUserPass($username, $password) { + + $cb = $this->callBack; + return $cb($username, $password); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php new file mode 100644 index 00000000000..3a687d747b2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php @@ -0,0 +1,77 @@ +loadFile($filename); + + } + + /** + * Loads an htdigest-formatted file. This method can be called multiple times if + * more than 1 file is used. + * + * @param string $filename + * @return void + */ + function loadFile($filename) { + + foreach (file($filename, FILE_IGNORE_NEW_LINES) as $line) { + + if (substr_count($line, ":") !== 2) + throw new DAV\Exception('Malformed htdigest file. Every line should contain 2 colons'); + + list($username, $realm, $A1) = explode(':', $line); + + if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) + throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash'); + + $this->users[$realm . ':' . $username] = $A1; + + } + + } + + /** + * Returns a users' information + * + * @param string $realm + * @param string $username + * @return string + */ + function getDigestHash($realm, $username) { + + return isset($this->users[$realm . ':' . $username]) ? $this->users[$realm . ':' . $username] : false; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php new file mode 100644 index 00000000000..c2f6de97480 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php @@ -0,0 +1,57 @@ +pdo = $pdo; + + } + + /** + * Returns the digest hash for a user. + * + * @param string $realm + * @param string $username + * @return string|null + */ + function getDigestHash($realm, $username) { + + $stmt = $this->pdo->prepare('SELECT digesta1 FROM ' . $this->tableName . ' WHERE username = ?'); + $stmt->execute([$username]); + return $stmt->fetchColumn() ?: null; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php new file mode 100644 index 00000000000..bbb5d180d0c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php @@ -0,0 +1,285 @@ +addBackend($authBackend); + } + + } + + /** + * Adds an authentication backend to the plugin. + * + * @param Backend\BackendInterface $authBackend + * @return void + */ + function addBackend(Backend\BackendInterface $authBackend) { + + $this->backends[] = $authBackend; + + } + + /** + * Initializes the plugin. This function is automatically called by the server + * + * @param Server $server + * @return void + */ + function initialize(Server $server) { + + $server->on('beforeMethod', [$this, 'beforeMethod'], 10); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'auth'; + + } + + /** + * Returns the currently logged-in principal. + * + * This will return a string such as: + * + * principals/username + * principals/users/username + * + * This method will return null if nobody is logged in. + * + * @return string|null + */ + function getCurrentPrincipal() { + + return $this->currentPrincipal; + + } + + /** + * This method is called before any HTTP method and forces users to be authenticated + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function beforeMethod(RequestInterface $request, ResponseInterface $response) { + + if ($this->currentPrincipal) { + + // We already have authentication information. This means that the + // event has already fired earlier, and is now likely fired for a + // sub-request. + // + // We don't want to authenticate users twice, so we simply don't do + // anything here. See Issue #700 for additional reasoning. + // + // This is not a perfect solution, but will be fixed once the + // "currently authenticated principal" is information that's not + // not associated with the plugin, but rather per-request. + // + // See issue #580 for more information about that. + return; + + } + + $authResult = $this->check($request, $response); + + if ($authResult[0]) { + // Auth was successful + $this->currentPrincipal = $authResult[1]; + $this->loginFailedReasons = null; + return; + } + + + + // If we got here, it means that no authentication backend was + // successful in authenticating the user. + $this->currentPrincipal = null; + $this->loginFailedReasons = $authResult[1]; + + if ($this->autoRequireLogin) { + $this->challenge($request, $response); + throw new NotAuthenticated(implode(', ', $authResult[1])); + } + + } + + /** + * Checks authentication credentials, and logs the user in if possible. + * + * This method returns an array. The first item in the array is a boolean + * indicating if login was successful. + * + * If login was successful, the second item in the array will contain the + * current principal url/path of the logged in user. + * + * If login was not successful, the second item in the array will contain a + * an array with strings. The strings are a list of reasons why login was + * unsuccessful. For every auth backend there will be one reason, so usually + * there's just one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function check(RequestInterface $request, ResponseInterface $response) { + + if (!$this->backends) { + throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.'); + } + $reasons = []; + foreach ($this->backends as $backend) { + + $result = $backend->check( + $request, + $response + ); + + if (!is_array($result) || count($result) !== 2 || !is_bool($result[0]) || !is_string($result[1])) { + throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.'); + } + + if ($result[0]) { + $this->currentPrincipal = $result[1]; + // Exit early + return [true, $result[1]]; + } + $reasons[] = $result[1]; + + } + + return [false, $reasons]; + + } + + /** + * This method sends authentication challenges to the user. + * + * This method will for example cause a HTTP Basic backend to set a + * WWW-Authorization header, indicating to the client that it should + * authenticate. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + + foreach ($this->backends as $backend) { + $backend->challenge($request, $response); + } + + } + + /** + * List of reasons why login failed for the last login operation. + * + * @var string[]|null + */ + protected $loginFailedReasons; + + /** + * Returns a list of reasons why login was unsuccessful. + * + * This method will return the login failed reasons for the last login + * operation. One for each auth backend. + * + * This method returns null if the last authentication attempt was + * successful, or if there was no authentication attempt yet. + * + * @return string[]|null + */ + function getLoginFailedReasons() { + + return $this->loginFailedReasons; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Generic authentication plugin', + 'link' => 'http://sabre.io/dav/authentication/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php new file mode 100644 index 00000000000..3ba2aee25b2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php @@ -0,0 +1,101 @@ + 'image/jpeg', + 'gif' => 'image/gif', + 'png' => 'image/png', + + // groupware + 'ics' => 'text/calendar', + 'vcf' => 'text/vcard', + + // text + 'txt' => 'text/plain', + + ]; + + /** + * Initializes the plugin + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + // Using a relatively low priority (200) to allow other extensions + // to set the content-type first. + $server->on('propFind', [$this, 'propFind'], 200); + + } + + /** + * Our PROPFIND handler + * + * Here we set a contenttype, if the node didn't already have one. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + $propFind->handle('{DAV:}getcontenttype', function() use ($propFind) { + + list(, $fileName) = URLUtil::splitPath($propFind->getPath()); + return $this->getContentType($fileName); + + }); + + } + + /** + * Simple method to return the contenttype + * + * @param string $fileName + * @return string + */ + protected function getContentType($fileName) { + + // Just grabbing the extension + $extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1)); + if (isset($this->extensionMap[$extension])) { + return $this->extensionMap[$extension]; + } + return 'application/octet-stream'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php new file mode 100644 index 00000000000..f4be6b34841 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php @@ -0,0 +1,34 @@ +baseUri = $baseUri; + $this->namespaceMap = $namespaceMap; + + } + + /** + * Generates a 'full' url based on a relative one. + * + * For relative urls, the base of the application is taken as the reference + * url, not the 'current url of the current request'. + * + * Absolute urls are left alone. + * + * @param string $path + * @return string + */ + function fullUrl($path) { + + return Uri\resolve($this->baseUri, $path); + + } + + /** + * Escape string for HTML output. + * + * @param string $input + * @return string + */ + function h($input) { + + return htmlspecialchars($input, ENT_COMPAT, 'UTF-8'); + + } + + /** + * Generates a full -tag. + * + * Url is automatically expanded. If label is not specified, we re-use the + * url. + * + * @param string $url + * @param string $label + * @return string + */ + function link($url, $label = null) { + + $url = $this->h($this->fullUrl($url)); + return '' . ($label ? $this->h($label) : $url) . ''; + + } + + /** + * This method takes an xml element in clark-notation, and turns it into a + * shortened version with a prefix, if it was a known namespace. + * + * @param string $element + * @return string + */ + function xmlName($element) { + + list($ns, $localName) = XmlService::parseClarkNotation($element); + if (isset($this->namespaceMap[$ns])) { + $propName = $this->namespaceMap[$ns] . ':' . $localName; + } else { + $propName = $element; + } + return "h($element) . "\">" . $this->h($propName) . ""; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php new file mode 100644 index 00000000000..61327c49a0c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php @@ -0,0 +1,60 @@ +server = $server; + $this->server->on('method:GET', [$this, 'httpGet'], 90); + } + + /** + * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $node = $this->server->tree->getNodeForPath($request->getPath()); + if ($node instanceof DAV\IFile) return; + + $subRequest = clone $request; + $subRequest->setMethod('PROPFIND'); + + $this->server->invokeMethod($subRequest, $response); + return false; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php new file mode 100644 index 00000000000..545ad56337e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php @@ -0,0 +1,802 @@ +enablePost = $enablePost; + + } + + /** + * Initializes the plugin and subscribes to events + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + $this->server = $server; + $this->server->on('method:GET', [$this, 'httpGetEarly'], 90); + $this->server->on('method:GET', [$this, 'httpGet'], 200); + $this->server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel'], 200); + if ($this->enablePost) $this->server->on('method:POST', [$this, 'httpPOST']); + } + + /** + * This method intercepts GET requests that have ?sabreAction=info + * appended to the URL + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGetEarly(RequestInterface $request, ResponseInterface $response) { + + $params = $request->getQueryParameters(); + if (isset($params['sabreAction']) && $params['sabreAction'] === 'info') { + return $this->httpGet($request, $response); + } + + } + + /** + * This method intercepts GET requests to collections and returns the html + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + // We're not using straight-up $_GET, because we want everything to be + // unit testable. + $getVars = $request->getQueryParameters(); + + // CSP headers + $response->setHeader('Content-Security-Policy', "default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"); + + $sabreAction = isset($getVars['sabreAction']) ? $getVars['sabreAction'] : null; + + switch ($sabreAction) { + + case 'asset' : + // Asset handling, such as images + $this->serveAsset(isset($getVars['assetName']) ? $getVars['assetName'] : null); + return false; + default : + case 'info' : + try { + $this->server->tree->getNodeForPath($request->getPath()); + } catch (DAV\Exception\NotFound $e) { + // We're simply stopping when the file isn't found to not interfere + // with other plugins. + return; + } + + $response->setStatus(200); + $response->setHeader('Content-Type', 'text/html; charset=utf-8'); + + $response->setBody( + $this->generateDirectoryIndex($request->getPath()) + ); + + return false; + + case 'plugins' : + $response->setStatus(200); + $response->setHeader('Content-Type', 'text/html; charset=utf-8'); + + $response->setBody( + $this->generatePluginListing() + ); + + return false; + + } + + } + + /** + * Handles POST requests for tree operations. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpPOST(RequestInterface $request, ResponseInterface $response) { + + $contentType = $request->getHeader('Content-Type'); + list($contentType) = explode(';', $contentType); + if ($contentType !== 'application/x-www-form-urlencoded' && + $contentType !== 'multipart/form-data') { + return; + } + $postVars = $request->getPostData(); + + if (!isset($postVars['sabreAction'])) + return; + + $uri = $request->getPath(); + + if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) { + + switch ($postVars['sabreAction']) { + + case 'mkcol' : + if (isset($postVars['name']) && trim($postVars['name'])) { + // Using basename() because we won't allow slashes + list(, $folderName) = URLUtil::splitPath(trim($postVars['name'])); + + if (isset($postVars['resourceType'])) { + $resourceType = explode(',', $postVars['resourceType']); + } else { + $resourceType = ['{DAV:}collection']; + } + + $properties = []; + foreach ($postVars as $varName => $varValue) { + // Any _POST variable in clark notation is treated + // like a property. + if ($varName[0] === '{') { + // PHP will convert any dots to underscores. + // This leaves us with no way to differentiate + // the two. + // Therefore we replace the string *DOT* with a + // real dot. * is not allowed in uris so we + // should be good. + $varName = str_replace('*DOT*', '.', $varName); + $properties[$varName] = $varValue; + } + } + + $mkCol = new MkCol( + $resourceType, + $properties + ); + $this->server->createCollection($uri . '/' . $folderName, $mkCol); + } + break; + + // @codeCoverageIgnoreStart + case 'put' : + + if ($_FILES) $file = current($_FILES); + else break; + + list(, $newName) = URLUtil::splitPath(trim($file['name'])); + if (isset($postVars['name']) && trim($postVars['name'])) + $newName = trim($postVars['name']); + + // Making sure we only have a 'basename' component + list(, $newName) = URLUtil::splitPath($newName); + + if (is_uploaded_file($file['tmp_name'])) { + $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'], 'r')); + } + break; + // @codeCoverageIgnoreEnd + + } + + } + $response->setHeader('Location', $request->getUrl()); + $response->setStatus(302); + return false; + + } + + /** + * Escapes a string for html. + * + * @param string $value + * @return string + */ + function escapeHTML($value) { + + return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); + + } + + /** + * Generates the html directory index for a given url + * + * @param string $path + * @return string + */ + function generateDirectoryIndex($path) { + + $html = $this->generateHeader($path ? $path : '/', $path); + + $node = $this->server->tree->getNodeForPath($path); + if ($node instanceof DAV\ICollection) { + + $html .= "

    Nodes

    \n"; + $html .= ""; + + $subNodes = $this->server->getPropertiesForChildren($path, [ + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ]); + + foreach ($subNodes as $subPath => $subProps) { + + $subNode = $this->server->tree->getNodeForPath($subPath); + $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath); + list(, $displayPath) = URLUtil::splitPath($subPath); + + $subNodes[$subPath]['subNode'] = $subNode; + $subNodes[$subPath]['fullPath'] = $fullPath; + $subNodes[$subPath]['displayPath'] = $displayPath; + } + uasort($subNodes, [$this, 'compareNodes']); + + foreach ($subNodes as $subProps) { + $type = [ + 'string' => 'Unknown', + 'icon' => 'cog', + ]; + if (isset($subProps['{DAV:}resourcetype'])) { + $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); + } + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + + $buttonActions = ''; + if ($subProps['subNode'] instanceof DAV\IFile) { + $buttonActions = ''; + } + $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]); + + $html .= ''; + $html .= ''; + } + + $html .= '
    ' . $this->escapeHTML($subProps['displayPath']) . '' . $this->escapeHTML($type['string']) . ''; + if (isset($subProps['{DAV:}getcontentlength'])) { + $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes'); + } + $html .= ''; + if (isset($subProps['{DAV:}getlastmodified'])) { + $lastMod = $subProps['{DAV:}getlastmodified']->getTime(); + $html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a')); + } + $html .= '' . $buttonActions . '
    '; + + } + + $html .= "
    "; + $html .= "

    Properties

    "; + $html .= ""; + + // Allprops request + $propFind = new PropFindAll($path); + $properties = $this->server->getPropertiesByNode($propFind, $node); + + $properties = $propFind->getResultForMultiStatus()[200]; + + foreach ($properties as $propName => $propValue) { + if (!in_array($propName, $this->uninterestingProperties)) { + $html .= $this->drawPropertyRow($propName, $propValue); + } + + } + + + $html .= "
    "; + $html .= "
    "; + + /* Start of generating actions */ + + $output = ''; + if ($this->enablePost) { + $this->server->emit('onHTMLActionsPanel', [$node, &$output, $path]); + } + + if ($output) { + + $html .= "

    Actions

    "; + $html .= "
    \n"; + $html .= $output; + $html .= "
    \n"; + $html .= "
    \n"; + } + + $html .= $this->generateFooter(); + + $this->server->httpResponse->setHeader('Content-Security-Policy', "default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"); + + return $html; + + } + + /** + * Generates the 'plugins' page. + * + * @return string + */ + function generatePluginListing() { + + $html = $this->generateHeader('Plugins'); + + $html .= "

    Plugins

    "; + $html .= ""; + foreach ($this->server->getPlugins() as $plugin) { + $info = $plugin->getPluginInfo(); + $html .= ''; + $html .= ''; + $html .= ''; + } + $html .= "
    ' . $info['name'] . '' . $info['description'] . ''; + if (isset($info['link']) && $info['link']) { + $html .= ''; + } + $html .= '
    "; + $html .= "
    "; + + /* Start of generating actions */ + + $html .= $this->generateFooter(); + + return $html; + + } + + /** + * Generates the first block of HTML, including the tag and page + * header. + * + * Returns footer. + * + * @param string $title + * @param string $path + * @return string + */ + function generateHeader($title, $path = null) { + + $version = ''; + if (DAV\Server::$exposeVersion) { + $version = DAV\Version::VERSION; + } + + $vars = [ + 'title' => $this->escapeHTML($title), + 'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')), + 'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')), + 'iconstyle' => $this->escapeHTML($this->getAssetUrl('openiconic/open-iconic.css')), + 'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')), + 'baseUrl' => $this->server->getBaseUri(), + ]; + + $html = << + + + $vars[title] - sabre/dav $version + + + + + + +
    + +
    + + "; + + return $html; + + } + + /** + * Generates the page footer. + * + * Returns html. + * + * @return string + */ + function generateFooter() { + + $version = ''; + if (DAV\Server::$exposeVersion) { + $version = DAV\Version::VERSION; + } + return <<Generated by SabreDAV $version (c)2007-2016 http://sabre.io/ + + +HTML; + + } + + /** + * This method is used to generate the 'actions panel' output for + * collections. + * + * This specifically generates the interfaces for creating new files, and + * creating new directories. + * + * @param DAV\INode $node + * @param mixed $output + * @param string $path + * @return void + */ + function htmlActionsPanel(DAV\INode $node, &$output, $path) { + + if (!$node instanceof DAV\ICollection) + return; + + // We also know fairly certain that if an object is a non-extended + // SimpleCollection, we won't need to show the panel either. + if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') + return; + + $output .= << +

    Create new folder

    + +
    + + +
    +

    Upload file

    + +
    +
    + +
    +HTML; + + } + + /** + * This method takes a path/name of an asset and turns it into url + * suiteable for http access. + * + * @param string $assetName + * @return string + */ + protected function getAssetUrl($assetName) { + + return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); + + } + + /** + * This method returns a local pathname to an asset. + * + * @param string $assetName + * @throws DAV\Exception\NotFound + * @return string + */ + protected function getLocalAssetPath($assetName) { + + $assetDir = __DIR__ . '/assets/'; + $path = $assetDir . $assetName; + + // Making sure people aren't trying to escape from the base path. + $path = str_replace('\\', '/', $path); + if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { + throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); + } + if (strpos(realpath($path), realpath($assetDir)) === 0 && file_exists($path)) { + return $path; + } + throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); + } + + /** + * This method reads an asset from disk and generates a full http response. + * + * @param string $assetName + * @return void + */ + protected function serveAsset($assetName) { + + $assetPath = $this->getLocalAssetPath($assetName); + + // Rudimentary mime type detection + $mime = 'application/octet-stream'; + $map = [ + 'ico' => 'image/vnd.microsoft.icon', + 'png' => 'image/png', + 'css' => 'text/css', + ]; + + $ext = substr($assetName, strrpos($assetName, '.') + 1); + if (isset($map[$ext])) { + $mime = $map[$ext]; + } + + $this->server->httpResponse->setHeader('Content-Type', $mime); + $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); + $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); + $this->server->httpResponse->setStatus(200); + $this->server->httpResponse->setBody(fopen($assetPath, 'r')); + + } + + /** + * Sort helper function: compares two directory entries based on type and + * display name. Collections sort above other types. + * + * @param array $a + * @param array $b + * @return int + */ + protected function compareNodes($a, $b) { + + $typeA = (isset($a['{DAV:}resourcetype'])) + ? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue())) + : false; + + $typeB = (isset($b['{DAV:}resourcetype'])) + ? (in_array('{DAV:}collection', $b['{DAV:}resourcetype']->getValue())) + : false; + + // If same type, sort alphabetically by filename: + if ($typeA === $typeB) { + return strnatcasecmp($a['displayPath'], $b['displayPath']); + } + return (($typeA < $typeB) ? 1 : -1); + + } + + /** + * Maps a resource type to a human-readable string and icon. + * + * @param array $resourceTypes + * @param DAV\INode $node + * @return array + */ + private function mapResourceType(array $resourceTypes, $node) { + + if (!$resourceTypes) { + if ($node instanceof DAV\IFile) { + return [ + 'string' => 'File', + 'icon' => 'file', + ]; + } else { + return [ + 'string' => 'Unknown', + 'icon' => 'cog', + ]; + } + } + + $types = [ + '{http://calendarserver.org/ns/}calendar-proxy-write' => [ + 'string' => 'Proxy-Write', + 'icon' => 'people', + ], + '{http://calendarserver.org/ns/}calendar-proxy-read' => [ + 'string' => 'Proxy-Read', + 'icon' => 'people', + ], + '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [ + 'string' => 'Outbox', + 'icon' => 'inbox', + ], + '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [ + 'string' => 'Inbox', + 'icon' => 'inbox', + ], + '{urn:ietf:params:xml:ns:caldav}calendar' => [ + 'string' => 'Calendar', + 'icon' => 'calendar', + ], + '{http://calendarserver.org/ns/}shared-owner' => [ + 'string' => 'Shared', + 'icon' => 'calendar', + ], + '{http://calendarserver.org/ns/}subscribed' => [ + 'string' => 'Subscription', + 'icon' => 'calendar', + ], + '{urn:ietf:params:xml:ns:carddav}directory' => [ + 'string' => 'Directory', + 'icon' => 'globe', + ], + '{urn:ietf:params:xml:ns:carddav}addressbook' => [ + 'string' => 'Address book', + 'icon' => 'book', + ], + '{DAV:}principal' => [ + 'string' => 'Principal', + 'icon' => 'person', + ], + '{DAV:}collection' => [ + 'string' => 'Collection', + 'icon' => 'folder', + ], + ]; + + $info = [ + 'string' => [], + 'icon' => 'cog', + ]; + foreach ($resourceTypes as $k => $resourceType) { + if (isset($types[$resourceType])) { + $info['string'][] = $types[$resourceType]['string']; + } else { + $info['string'][] = $resourceType; + } + } + foreach ($types as $key => $resourceInfo) { + if (in_array($key, $resourceTypes)) { + $info['icon'] = $resourceInfo['icon']; + break; + } + } + $info['string'] = implode(', ', $info['string']); + + return $info; + + } + + /** + * Draws a table row for a property + * + * @param string $name + * @param mixed $value + * @return string + */ + private function drawPropertyRow($name, $value) { + + $html = new HtmlOutputHelper( + $this->server->getBaseUri(), + $this->server->xml->namespaceMap + ); + + return "" . $html->xmlName($name) . "" . $this->drawPropertyValue($html, $value) . ""; + + } + + /** + * Draws a table row for a property + * + * @param HtmlOutputHelper $html + * @param mixed $value + * @return string + */ + private function drawPropertyValue($html, $value) { + + if (is_scalar($value)) { + return $html->h($value); + } elseif ($value instanceof HtmlOutput) { + return $value->toHtml($html); + } elseif ($value instanceof \Sabre\Xml\XmlSerializable) { + + // There's no default html output for this property, we're going + // to output the actual xml serialization instead. + $xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri()); + // removing first and last line, as they contain our root + // element. + $xml = explode("\n", $xml); + $xml = array_slice($xml, 2, -2); + return "
    " . $html->h(implode("\n", $xml)) . "
    "; + + } else { + return "unknown"; + } + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins; + * using \Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'browser'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Generates HTML indexes and debug information for your sabre/dav server', + 'link' => 'http://sabre.io/dav/browser-plugin/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php new file mode 100644 index 00000000000..c14b7f2f96d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php @@ -0,0 +1,132 @@ +handle('{DAV:}displayname', function() { + * return 'hello'; + * }); + * + * Note that handle will only work the first time. If null is returned, the + * value is ignored. + * + * It's also possible to not pass a callback, but immediately pass a value + * + * @param string $propertyName + * @param mixed $valueOrCallBack + * @return void + */ + function handle($propertyName, $valueOrCallBack) { + + if (is_callable($valueOrCallBack)) { + $value = $valueOrCallBack(); + } else { + $value = $valueOrCallBack; + } + if (!is_null($value)) { + $this->result[$propertyName] = [200, $value]; + } + + } + + /** + * Sets the value of the property + * + * If status is not supplied, the status will default to 200 for non-null + * properties, and 404 for null properties. + * + * @param string $propertyName + * @param mixed $value + * @param int $status + * @return void + */ + function set($propertyName, $value, $status = null) { + + if (is_null($status)) { + $status = is_null($value) ? 404 : 200; + } + $this->result[$propertyName] = [$status, $value]; + + } + + /** + * Returns the current value for a property. + * + * @param string $propertyName + * @return mixed + */ + function get($propertyName) { + + return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; + + } + + /** + * Returns the current status code for a property name. + * + * If the property does not appear in the list of requested properties, + * null will be returned. + * + * @param string $propertyName + * @return int|null + */ + function getStatus($propertyName) { + + return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : 404; + + } + + /** + * Returns all propertynames that have a 404 status, and thus don't have a + * value yet. + * + * @return array + */ + function get404Properties() { + + $result = []; + foreach ($this->result as $propertyName => $stuff) { + if ($stuff[0] === 404) { + $result[] = $propertyName; + } + } + // If there's nothing in this list, we're adding one fictional item. + if (!$result) { + $result[] = '{http://sabredav.org/ns}idk'; + } + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/favicon.ico b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/favicon.ico new file mode 100644 index 00000000000..2b2c10a22cc Binary files /dev/null and b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/favicon.ico differ diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE new file mode 100644 index 00000000000..2199f4a6941 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Waybury + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css new file mode 100644 index 00000000000..e7486740030 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css @@ -0,0 +1,510 @@ +@font-face { + font-family: 'Icons'; + src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot'); + src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot?#iconic-sm') format('embedded-opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.woff') format('woff'), url('?sabreAction=asset&assetName=openiconic/open-iconic.ttf') format('truetype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.otf') format('opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.svg#iconic-sm') format('svg'); + font-weight: normal; + font-style: normal; +} + +.oi[data-glyph].oi-text-replace { + font-size: 0; + line-height: 0; +} + +.oi[data-glyph].oi-text-replace:before { + width: 1em; + text-align: center; +} + +.oi[data-glyph]:before { + font-family: 'Icons'; + display: inline-block; + speak: none; + line-height: 1; + vertical-align: baseline; + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.oi[data-glyph]:empty:before { + width: 1em; + text-align: center; + box-sizing: content-box; +} + +.oi[data-glyph].oi-align-left:before { + text-align: left; +} + +.oi[data-glyph].oi-align-right:before { + text-align: right; +} + +.oi[data-glyph].oi-align-center:before { + text-align: center; +} + +.oi[data-glyph].oi-flip-horizontal:before { + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.oi[data-glyph].oi-flip-vertical:before { + -webkit-transform: scale(1, -1); + -ms-transform: scale(-1, 1); + transform: scale(1, -1); +} +.oi[data-glyph].oi-flip-horizontal-vertical:before { + -webkit-transform: scale(-1, -1); + -ms-transform: scale(-1, 1); + transform: scale(-1, -1); +} + + +.oi[data-glyph=account-login]:before { content:'\e000'; } + +.oi[data-glyph=account-logout]:before { content:'\e001'; } + +.oi[data-glyph=action-redo]:before { content:'\e002'; } + +.oi[data-glyph=action-undo]:before { content:'\e003'; } + +.oi[data-glyph=align-center]:before { content:'\e004'; } + +.oi[data-glyph=align-left]:before { content:'\e005'; } + +.oi[data-glyph=align-right]:before { content:'\e006'; } + +.oi[data-glyph=aperture]:before { content:'\e007'; } + +.oi[data-glyph=arrow-bottom]:before { content:'\e008'; } + +.oi[data-glyph=arrow-circle-bottom]:before { content:'\e009'; } + +.oi[data-glyph=arrow-circle-left]:before { content:'\e00a'; } + +.oi[data-glyph=arrow-circle-right]:before { content:'\e00b'; } + +.oi[data-glyph=arrow-circle-top]:before { content:'\e00c'; } + +.oi[data-glyph=arrow-left]:before { content:'\e00d'; } + +.oi[data-glyph=arrow-right]:before { content:'\e00e'; } + +.oi[data-glyph=arrow-thick-bottom]:before { content:'\e00f'; } + +.oi[data-glyph=arrow-thick-left]:before { content:'\e010'; } + +.oi[data-glyph=arrow-thick-right]:before { content:'\e011'; } + +.oi[data-glyph=arrow-thick-top]:before { content:'\e012'; } + +.oi[data-glyph=arrow-top]:before { content:'\e013'; } + +.oi[data-glyph=audio-spectrum]:before { content:'\e014'; } + +.oi[data-glyph=audio]:before { content:'\e015'; } + +.oi[data-glyph=badge]:before { content:'\e016'; } + +.oi[data-glyph=ban]:before { content:'\e017'; } + +.oi[data-glyph=bar-chart]:before { content:'\e018'; } + +.oi[data-glyph=basket]:before { content:'\e019'; } + +.oi[data-glyph=battery-empty]:before { content:'\e01a'; } + +.oi[data-glyph=battery-full]:before { content:'\e01b'; } + +.oi[data-glyph=beaker]:before { content:'\e01c'; } + +.oi[data-glyph=bell]:before { content:'\e01d'; } + +.oi[data-glyph=bluetooth]:before { content:'\e01e'; } + +.oi[data-glyph=bold]:before { content:'\e01f'; } + +.oi[data-glyph=bolt]:before { content:'\e020'; } + +.oi[data-glyph=book]:before { content:'\e021'; } + +.oi[data-glyph=bookmark]:before { content:'\e022'; } + +.oi[data-glyph=box]:before { content:'\e023'; } + +.oi[data-glyph=briefcase]:before { content:'\e024'; } + +.oi[data-glyph=british-pound]:before { content:'\e025'; } + +.oi[data-glyph=browser]:before { content:'\e026'; } + +.oi[data-glyph=brush]:before { content:'\e027'; } + +.oi[data-glyph=bug]:before { content:'\e028'; } + +.oi[data-glyph=bullhorn]:before { content:'\e029'; } + +.oi[data-glyph=calculator]:before { content:'\e02a'; } + +.oi[data-glyph=calendar]:before { content:'\e02b'; } + +.oi[data-glyph=camera-slr]:before { content:'\e02c'; } + +.oi[data-glyph=caret-bottom]:before { content:'\e02d'; } + +.oi[data-glyph=caret-left]:before { content:'\e02e'; } + +.oi[data-glyph=caret-right]:before { content:'\e02f'; } + +.oi[data-glyph=caret-top]:before { content:'\e030'; } + +.oi[data-glyph=cart]:before { content:'\e031'; } + +.oi[data-glyph=chat]:before { content:'\e032'; } + +.oi[data-glyph=check]:before { content:'\e033'; } + +.oi[data-glyph=chevron-bottom]:before { content:'\e034'; } + +.oi[data-glyph=chevron-left]:before { content:'\e035'; } + +.oi[data-glyph=chevron-right]:before { content:'\e036'; } + +.oi[data-glyph=chevron-top]:before { content:'\e037'; } + +.oi[data-glyph=circle-check]:before { content:'\e038'; } + +.oi[data-glyph=circle-x]:before { content:'\e039'; } + +.oi[data-glyph=clipboard]:before { content:'\e03a'; } + +.oi[data-glyph=clock]:before { content:'\e03b'; } + +.oi[data-glyph=cloud-download]:before { content:'\e03c'; } + +.oi[data-glyph=cloud-upload]:before { content:'\e03d'; } + +.oi[data-glyph=cloud]:before { content:'\e03e'; } + +.oi[data-glyph=cloudy]:before { content:'\e03f'; } + +.oi[data-glyph=code]:before { content:'\e040'; } + +.oi[data-glyph=cog]:before { content:'\e041'; } + +.oi[data-glyph=collapse-down]:before { content:'\e042'; } + +.oi[data-glyph=collapse-left]:before { content:'\e043'; } + +.oi[data-glyph=collapse-right]:before { content:'\e044'; } + +.oi[data-glyph=collapse-up]:before { content:'\e045'; } + +.oi[data-glyph=command]:before { content:'\e046'; } + +.oi[data-glyph=comment-square]:before { content:'\e047'; } + +.oi[data-glyph=compass]:before { content:'\e048'; } + +.oi[data-glyph=contrast]:before { content:'\e049'; } + +.oi[data-glyph=copywriting]:before { content:'\e04a'; } + +.oi[data-glyph=credit-card]:before { content:'\e04b'; } + +.oi[data-glyph=crop]:before { content:'\e04c'; } + +.oi[data-glyph=dashboard]:before { content:'\e04d'; } + +.oi[data-glyph=data-transfer-download]:before { content:'\e04e'; } + +.oi[data-glyph=data-transfer-upload]:before { content:'\e04f'; } + +.oi[data-glyph=delete]:before { content:'\e050'; } + +.oi[data-glyph=dial]:before { content:'\e051'; } + +.oi[data-glyph=document]:before { content:'\e052'; } + +.oi[data-glyph=dollar]:before { content:'\e053'; } + +.oi[data-glyph=double-quote-sans-left]:before { content:'\e054'; } + +.oi[data-glyph=double-quote-sans-right]:before { content:'\e055'; } + +.oi[data-glyph=double-quote-serif-left]:before { content:'\e056'; } + +.oi[data-glyph=double-quote-serif-right]:before { content:'\e057'; } + +.oi[data-glyph=droplet]:before { content:'\e058'; } + +.oi[data-glyph=eject]:before { content:'\e059'; } + +.oi[data-glyph=elevator]:before { content:'\e05a'; } + +.oi[data-glyph=ellipses]:before { content:'\e05b'; } + +.oi[data-glyph=envelope-closed]:before { content:'\e05c'; } + +.oi[data-glyph=envelope-open]:before { content:'\e05d'; } + +.oi[data-glyph=euro]:before { content:'\e05e'; } + +.oi[data-glyph=excerpt]:before { content:'\e05f'; } + +.oi[data-glyph=expand-down]:before { content:'\e060'; } + +.oi[data-glyph=expand-left]:before { content:'\e061'; } + +.oi[data-glyph=expand-right]:before { content:'\e062'; } + +.oi[data-glyph=expand-up]:before { content:'\e063'; } + +.oi[data-glyph=external-link]:before { content:'\e064'; } + +.oi[data-glyph=eye]:before { content:'\e065'; } + +.oi[data-glyph=eyedropper]:before { content:'\e066'; } + +.oi[data-glyph=file]:before { content:'\e067'; } + +.oi[data-glyph=fire]:before { content:'\e068'; } + +.oi[data-glyph=flag]:before { content:'\e069'; } + +.oi[data-glyph=flash]:before { content:'\e06a'; } + +.oi[data-glyph=folder]:before { content:'\e06b'; } + +.oi[data-glyph=fork]:before { content:'\e06c'; } + +.oi[data-glyph=fullscreen-enter]:before { content:'\e06d'; } + +.oi[data-glyph=fullscreen-exit]:before { content:'\e06e'; } + +.oi[data-glyph=globe]:before { content:'\e06f'; } + +.oi[data-glyph=graph]:before { content:'\e070'; } + +.oi[data-glyph=grid-four-up]:before { content:'\e071'; } + +.oi[data-glyph=grid-three-up]:before { content:'\e072'; } + +.oi[data-glyph=grid-two-up]:before { content:'\e073'; } + +.oi[data-glyph=hard-drive]:before { content:'\e074'; } + +.oi[data-glyph=header]:before { content:'\e075'; } + +.oi[data-glyph=headphones]:before { content:'\e076'; } + +.oi[data-glyph=heart]:before { content:'\e077'; } + +.oi[data-glyph=home]:before { content:'\e078'; } + +.oi[data-glyph=image]:before { content:'\e079'; } + +.oi[data-glyph=inbox]:before { content:'\e07a'; } + +.oi[data-glyph=infinity]:before { content:'\e07b'; } + +.oi[data-glyph=info]:before { content:'\e07c'; } + +.oi[data-glyph=italic]:before { content:'\e07d'; } + +.oi[data-glyph=justify-center]:before { content:'\e07e'; } + +.oi[data-glyph=justify-left]:before { content:'\e07f'; } + +.oi[data-glyph=justify-right]:before { content:'\e080'; } + +.oi[data-glyph=key]:before { content:'\e081'; } + +.oi[data-glyph=laptop]:before { content:'\e082'; } + +.oi[data-glyph=layers]:before { content:'\e083'; } + +.oi[data-glyph=lightbulb]:before { content:'\e084'; } + +.oi[data-glyph=link-broken]:before { content:'\e085'; } + +.oi[data-glyph=link-intact]:before { content:'\e086'; } + +.oi[data-glyph=list-rich]:before { content:'\e087'; } + +.oi[data-glyph=list]:before { content:'\e088'; } + +.oi[data-glyph=location]:before { content:'\e089'; } + +.oi[data-glyph=lock-locked]:before { content:'\e08a'; } + +.oi[data-glyph=lock-unlocked]:before { content:'\e08b'; } + +.oi[data-glyph=loop-circular]:before { content:'\e08c'; } + +.oi[data-glyph=loop-square]:before { content:'\e08d'; } + +.oi[data-glyph=loop]:before { content:'\e08e'; } + +.oi[data-glyph=magnifying-glass]:before { content:'\e08f'; } + +.oi[data-glyph=map-marker]:before { content:'\e090'; } + +.oi[data-glyph=map]:before { content:'\e091'; } + +.oi[data-glyph=media-pause]:before { content:'\e092'; } + +.oi[data-glyph=media-play]:before { content:'\e093'; } + +.oi[data-glyph=media-record]:before { content:'\e094'; } + +.oi[data-glyph=media-skip-backward]:before { content:'\e095'; } + +.oi[data-glyph=media-skip-forward]:before { content:'\e096'; } + +.oi[data-glyph=media-step-backward]:before { content:'\e097'; } + +.oi[data-glyph=media-step-forward]:before { content:'\e098'; } + +.oi[data-glyph=media-stop]:before { content:'\e099'; } + +.oi[data-glyph=medical-cross]:before { content:'\e09a'; } + +.oi[data-glyph=menu]:before { content:'\e09b'; } + +.oi[data-glyph=microphone]:before { content:'\e09c'; } + +.oi[data-glyph=minus]:before { content:'\e09d'; } + +.oi[data-glyph=monitor]:before { content:'\e09e'; } + +.oi[data-glyph=moon]:before { content:'\e09f'; } + +.oi[data-glyph=move]:before { content:'\e0a0'; } + +.oi[data-glyph=musical-note]:before { content:'\e0a1'; } + +.oi[data-glyph=paperclip]:before { content:'\e0a2'; } + +.oi[data-glyph=pencil]:before { content:'\e0a3'; } + +.oi[data-glyph=people]:before { content:'\e0a4'; } + +.oi[data-glyph=person]:before { content:'\e0a5'; } + +.oi[data-glyph=phone]:before { content:'\e0a6'; } + +.oi[data-glyph=pie-chart]:before { content:'\e0a7'; } + +.oi[data-glyph=pin]:before { content:'\e0a8'; } + +.oi[data-glyph=play-circle]:before { content:'\e0a9'; } + +.oi[data-glyph=plus]:before { content:'\e0aa'; } + +.oi[data-glyph=power-standby]:before { content:'\e0ab'; } + +.oi[data-glyph=print]:before { content:'\e0ac'; } + +.oi[data-glyph=project]:before { content:'\e0ad'; } + +.oi[data-glyph=pulse]:before { content:'\e0ae'; } + +.oi[data-glyph=puzzle-piece]:before { content:'\e0af'; } + +.oi[data-glyph=question-mark]:before { content:'\e0b0'; } + +.oi[data-glyph=rain]:before { content:'\e0b1'; } + +.oi[data-glyph=random]:before { content:'\e0b2'; } + +.oi[data-glyph=reload]:before { content:'\e0b3'; } + +.oi[data-glyph=resize-both]:before { content:'\e0b4'; } + +.oi[data-glyph=resize-height]:before { content:'\e0b5'; } + +.oi[data-glyph=resize-width]:before { content:'\e0b6'; } + +.oi[data-glyph=rss-alt]:before { content:'\e0b7'; } + +.oi[data-glyph=rss]:before { content:'\e0b8'; } + +.oi[data-glyph=script]:before { content:'\e0b9'; } + +.oi[data-glyph=share-boxed]:before { content:'\e0ba'; } + +.oi[data-glyph=share]:before { content:'\e0bb'; } + +.oi[data-glyph=shield]:before { content:'\e0bc'; } + +.oi[data-glyph=signal]:before { content:'\e0bd'; } + +.oi[data-glyph=signpost]:before { content:'\e0be'; } + +.oi[data-glyph=sort-ascending]:before { content:'\e0bf'; } + +.oi[data-glyph=sort-descending]:before { content:'\e0c0'; } + +.oi[data-glyph=spreadsheet]:before { content:'\e0c1'; } + +.oi[data-glyph=star]:before { content:'\e0c2'; } + +.oi[data-glyph=sun]:before { content:'\e0c3'; } + +.oi[data-glyph=tablet]:before { content:'\e0c4'; } + +.oi[data-glyph=tag]:before { content:'\e0c5'; } + +.oi[data-glyph=tags]:before { content:'\e0c6'; } + +.oi[data-glyph=target]:before { content:'\e0c7'; } + +.oi[data-glyph=task]:before { content:'\e0c8'; } + +.oi[data-glyph=terminal]:before { content:'\e0c9'; } + +.oi[data-glyph=text]:before { content:'\e0ca'; } + +.oi[data-glyph=thumb-down]:before { content:'\e0cb'; } + +.oi[data-glyph=thumb-up]:before { content:'\e0cc'; } + +.oi[data-glyph=timer]:before { content:'\e0cd'; } + +.oi[data-glyph=transfer]:before { content:'\e0ce'; } + +.oi[data-glyph=trash]:before { content:'\e0cf'; } + +.oi[data-glyph=underline]:before { content:'\e0d0'; } + +.oi[data-glyph=vertical-align-bottom]:before { content:'\e0d1'; } + +.oi[data-glyph=vertical-align-center]:before { content:'\e0d2'; } + +.oi[data-glyph=vertical-align-top]:before { content:'\e0d3'; } + +.oi[data-glyph=video]:before { content:'\e0d4'; } + +.oi[data-glyph=volume-high]:before { content:'\e0d5'; } + +.oi[data-glyph=volume-low]:before { content:'\e0d6'; } + +.oi[data-glyph=volume-off]:before { content:'\e0d7'; } + +.oi[data-glyph=warning]:before { content:'\e0d8'; } + +.oi[data-glyph=wifi]:before { content:'\e0d9'; } + +.oi[data-glyph=wrench]:before { content:'\e0da'; } + +.oi[data-glyph=x]:before { content:'\e0db'; } + +.oi[data-glyph=yen]:before { content:'\e0dc'; } + +.oi[data-glyph=zoom-in]:before { content:'\e0dd'; } + +.oi[data-glyph=zoom-out]:before { content:'\e0de'; } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot new file mode 100644 index 00000000000..7ca7c170f1a Binary files /dev/null and b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot differ diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf new file mode 100644 index 00000000000..d79fb13a10d Binary files /dev/null and b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf differ diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg new file mode 100644 index 00000000000..0792c003a68 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg @@ -0,0 +1,543 @@ + + + + + +Created by FontForge 20120731 at Wed Apr 30 22:56:47 2014 + By P.J. Onori +Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf new file mode 100644 index 00000000000..0f94acd1ebc Binary files /dev/null and b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf differ diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff new file mode 100644 index 00000000000..793176af47c Binary files /dev/null and b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff differ diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/sabredav.css b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/sabredav.css new file mode 100644 index 00000000000..8869597f0ef --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/sabredav.css @@ -0,0 +1,228 @@ +/* Start of reset */ + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +body { + margin: 0; +} + + +/** + * Define consistent border, margin, and padding. + */ +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} + +/** End of reset */ + + +body { + font-family: 'Roboto', sans-serif; + font-size: 14px; + line-height: 22px; + font-weight: 300; +} +h1 { + font-size: 42px; + line-height: 44px; + padding-bottom: 5px; + color: #b10610; + margin-top: 10px; + margin-bottom: 30px; +} +h2 { + color: #333333; + font-size: 28px; + line-height: 44px; + font-weight: 300; +} +h3 { + font-size: 21px; + margin-top: 20px; + margin-bottom: 10px; +} +a { + color: #31a1cd; +} +h1 a { + text-decoration: none; +} +h2 a { + color: #333333; +} +a:visited { + color: #6098a2; +} +h2 a:visited { + color: #333333; +} +a:hover { + color: #b10610; +} +hr { + border: none; + border-top: 1px dashed #c9ea75; + margin-top: 30px; + margin-bottom: 30px; +} +header { + background: #eeeeee; +} +header a { + font-size: 28px; + font-weight: 500; + color: #333; + text-decoration: none; +} +.logo { + padding: 5px 10px; +} +.logo img { + vertical-align: middle; + border: 0; +} +input, button, select { + font: inherit; + color: inherit; +} + +input[type=text], select { + border: 1px solid #bbbbbb; + line-height: 22px; + padding: 5px 10px; + border-radius: 3px; +} + +nav { + padding: 5px; +} + +.btn, button, input[type=submit] { + display: inline-block; + color: white; + background: #4fa3ac; + padding: 9px 15px; + border-radius: 2px; + border: 0; + text-decoration: none; +} +a.btn:visited { + color: white; +} + +.btn.disabled { + background: #eeeeee; + color: #bbbbbb; +} +section { + margin: 40px 10px; +} + +section table { + height: 40px; +} + +.nodeTable tr { + border-bottom: 3px solid white; +} + +.nodeTable td { + padding: 10px 10px 10px 10px; + +} + +.nodeTable a { + text-decoration: none; +} + +.nodeTable .nameColumn { + font-weight: bold; + padding: 10px 20px; + background: #ebf5f6; + min-width: 200px; +} +.nodeTable .oi { + color: #b10610; +} + +.propTable tr { + height: 40px; +} + +.propTable th { + background: #f6f6f6; + padding: 0 10px; + text-align: left; +} + +.propTable td { + padding: 0 10px; + background: #eeeeee; +} + +.propTable pre { + font-size: 80%; + background: #f8f8f8; +} + +.actions { + border: 1px dotted #76baa6; + padding: 20px; + margin-bottom: 20px; + +} + +.actions h3 { + margin-top: 10px; + margin-bottom: 30px; + padding-bottom: 20px; + border-bottom: 1px solid #eeeeee; +} + +.actions label { + width: 150px; + display: inline-block; + line-height: 40px; +} + +.actions input[type=text], select { + width: 450px; +} + +.actions input[type=submit] { + display: inline-block; + margin-left: 153px; +} + +footer { + padding: 50px 0; + font-size: 80%; + text-align: center; +} + +ul.tree { + list-style: none; + margin: 0; + padding: 0; +} + +ul.tree ul { + list-style: none; + padding-left: 10px; + border-left: 4px solid #ccc; +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/sabredav.png b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/sabredav.png new file mode 100644 index 00000000000..48a97398ad0 Binary files /dev/null and b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/assets/sabredav.png differ diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php new file mode 100644 index 00000000000..175ad1bc459 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php @@ -0,0 +1,439 @@ +xml->elementMap. + * It's deprecated as of version 3.0.0, and should no longer be used. + * + * @deprecated + * @var array + */ + public $propertyMap = []; + + /** + * Base URI + * + * This URI will be used to resolve relative urls. + * + * @var string + */ + protected $baseUri; + + /** + * Basic authentication + */ + const AUTH_BASIC = 1; + + /** + * Digest authentication + */ + const AUTH_DIGEST = 2; + + /** + * NTLM authentication + */ + const AUTH_NTLM = 4; + + /** + * Identity encoding, which basically does not nothing. + */ + const ENCODING_IDENTITY = 1; + + /** + * Deflate encoding + */ + const ENCODING_DEFLATE = 2; + + /** + * Gzip encoding + */ + const ENCODING_GZIP = 4; + + /** + * Sends all encoding headers. + */ + const ENCODING_ALL = 7; + + /** + * Content-encoding + * + * @var int + */ + protected $encoding = self::ENCODING_IDENTITY; + + /** + * Constructor + * + * Settings are provided through the 'settings' argument. The following + * settings are supported: + * + * * baseUri + * * userName (optional) + * * password (optional) + * * proxy (optional) + * * authType (optional) + * * encoding (optional) + * + * authType must be a bitmap, using self::AUTH_BASIC, self::AUTH_DIGEST + * and self::AUTH_NTLM. If you know which authentication method will be + * used, it's recommended to set it, as it will save a great deal of + * requests to 'discover' this information. + * + * Encoding is a bitmap with one of the ENCODING constants. + * + * @param array $settings + */ + function __construct(array $settings) { + + if (!isset($settings['baseUri'])) { + throw new \InvalidArgumentException('A baseUri must be provided'); + } + + parent::__construct(); + + $this->baseUri = $settings['baseUri']; + + if (isset($settings['proxy'])) { + $this->addCurlSetting(CURLOPT_PROXY, $settings['proxy']); + } + + if (isset($settings['userName'])) { + $userName = $settings['userName']; + $password = isset($settings['password']) ? $settings['password'] : ''; + + if (isset($settings['authType'])) { + $curlType = 0; + if ($settings['authType'] & self::AUTH_BASIC) { + $curlType |= CURLAUTH_BASIC; + } + if ($settings['authType'] & self::AUTH_DIGEST) { + $curlType |= CURLAUTH_DIGEST; + } + if ($settings['authType'] & self::AUTH_NTLM) { + $curlType |= CURLAUTH_NTLM; + } + } else { + $curlType = CURLAUTH_BASIC | CURLAUTH_DIGEST; + } + + $this->addCurlSetting(CURLOPT_HTTPAUTH, $curlType); + $this->addCurlSetting(CURLOPT_USERPWD, $userName . ':' . $password); + + } + + if (isset($settings['encoding'])) { + $encoding = $settings['encoding']; + + $encodings = []; + if ($encoding & self::ENCODING_IDENTITY) { + $encodings[] = 'identity'; + } + if ($encoding & self::ENCODING_DEFLATE) { + $encodings[] = 'deflate'; + } + if ($encoding & self::ENCODING_GZIP) { + $encodings[] = 'gzip'; + } + $this->addCurlSetting(CURLOPT_ENCODING, implode(',', $encodings)); + } + + $this->addCurlSetting(CURLOPT_USERAGENT, 'sabre-dav/' . Version::VERSION . ' (http://sabre.io/)'); + + $this->xml = new Xml\Service(); + // BC + $this->propertyMap = & $this->xml->elementMap; + + } + + /** + * Does a PROPFIND request + * + * The list of requested properties must be specified as an array, in clark + * notation. + * + * The returned array will contain a list of filenames as keys, and + * properties as values. + * + * The properties array will contain the list of properties. Only properties + * that are actually returned from the server (without error) will be + * returned, anything else is discarded. + * + * Depth should be either 0 or 1. A depth of 1 will cause a request to be + * made to the server to also return all child resources. + * + * @param string $url + * @param array $properties + * @param int $depth + * @return array + */ + function propFind($url, array $properties, $depth = 0) { + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $root = $dom->createElementNS('DAV:', 'd:propfind'); + $prop = $dom->createElement('d:prop'); + + foreach ($properties as $property) { + + list( + $namespace, + $elementName + ) = \Sabre\Xml\Service::parseClarkNotation($property); + + if ($namespace === 'DAV:') { + $element = $dom->createElement('d:' . $elementName); + } else { + $element = $dom->createElementNS($namespace, 'x:' . $elementName); + } + + $prop->appendChild($element); + } + + $dom->appendChild($root)->appendChild($prop); + $body = $dom->saveXML(); + + $url = $this->getAbsoluteUrl($url); + + $request = new HTTP\Request('PROPFIND', $url, [ + 'Depth' => $depth, + 'Content-Type' => 'application/xml' + ], $body); + + $response = $this->send($request); + + if ((int)$response->getStatus() >= 400) { + throw new HTTP\ClientHttpException($response); + } + + $result = $this->parseMultiStatus($response->getBodyAsString()); + + // If depth was 0, we only return the top item + if ($depth === 0) { + reset($result); + $result = current($result); + return isset($result[200]) ? $result[200] : []; + } + + $newResult = []; + foreach ($result as $href => $statusList) { + + $newResult[$href] = isset($statusList[200]) ? $statusList[200] : []; + + } + + return $newResult; + + } + + /** + * Updates a list of properties on the server + * + * The list of properties must have clark-notation properties for the keys, + * and the actual (string) value for the value. If the value is null, an + * attempt is made to delete the property. + * + * @param string $url + * @param array $properties + * @return bool + */ + function propPatch($url, array $properties) { + + $propPatch = new Xml\Request\PropPatch(); + $propPatch->properties = $properties; + $xml = $this->xml->write( + '{DAV:}propertyupdate', + $propPatch + ); + + $url = $this->getAbsoluteUrl($url); + $request = new HTTP\Request('PROPPATCH', $url, [ + 'Content-Type' => 'application/xml', + ], $xml); + $response = $this->send($request); + + if ($response->getStatus() >= 400) { + throw new HTTP\ClientHttpException($response); + } + + if ($response->getStatus() === 207) { + // If it's a 207, the request could still have failed, but the + // information is hidden in the response body. + $result = $this->parseMultiStatus($response->getBodyAsString()); + + $errorProperties = []; + foreach ($result as $href => $statusList) { + foreach ($statusList as $status => $properties) { + + if ($status >= 400) { + foreach ($properties as $propName => $propValue) { + $errorProperties[] = $propName . ' (' . $status . ')'; + } + } + + } + } + if ($errorProperties) { + + throw new HTTP\ClientException('PROPPATCH failed. The following properties errored: ' . implode(', ', $errorProperties)); + } + } + return true; + + } + + /** + * Performs an HTTP options request + * + * This method returns all the features from the 'DAV:' header as an array. + * If there was no DAV header, or no contents this method will return an + * empty array. + * + * @return array + */ + function options() { + + $request = new HTTP\Request('OPTIONS', $this->getAbsoluteUrl('')); + $response = $this->send($request); + + $dav = $response->getHeader('Dav'); + if (!$dav) { + return []; + } + + $features = explode(',', $dav); + foreach ($features as &$v) { + $v = trim($v); + } + return $features; + + } + + /** + * Performs an actual HTTP request, and returns the result. + * + * If the specified url is relative, it will be expanded based on the base + * url. + * + * The returned array contains 3 keys: + * * body - the response body + * * httpCode - a HTTP code (200, 404, etc) + * * headers - a list of response http headers. The header names have + * been lowercased. + * + * For large uploads, it's highly recommended to specify body as a stream + * resource. You can easily do this by simply passing the result of + * fopen(..., 'r'). + * + * This method will throw an exception if an HTTP error was received. Any + * HTTP status code above 399 is considered an error. + * + * Note that it is no longer recommended to use this method, use the send() + * method instead. + * + * @param string $method + * @param string $url + * @param string|resource|null $body + * @param array $headers + * @throws ClientException, in case a curl error occurred. + * @return array + */ + function request($method, $url = '', $body = null, array $headers = []) { + + $url = $this->getAbsoluteUrl($url); + + $response = $this->send(new HTTP\Request($method, $url, $headers, $body)); + return [ + 'body' => $response->getBodyAsString(), + 'statusCode' => (int)$response->getStatus(), + 'headers' => array_change_key_case($response->getHeaders()), + ]; + + } + + /** + * Returns the full url based on the given url (which may be relative). All + * urls are expanded based on the base url as given by the server. + * + * @param string $url + * @return string + */ + function getAbsoluteUrl($url) { + + return Uri\resolve( + $this->baseUri, + $url + ); + + } + + /** + * Parses a WebDAV multistatus response body + * + * This method returns an array with the following structure + * + * [ + * 'url/to/resource' => [ + * '200' => [ + * '{DAV:}property1' => 'value1', + * '{DAV:}property2' => 'value2', + * ], + * '404' => [ + * '{DAV:}property1' => null, + * '{DAV:}property2' => null, + * ], + * ], + * 'url/to/resource2' => [ + * .. etc .. + * ] + * ] + * + * + * @param string $body xml body + * @return array + */ + function parseMultiStatus($body) { + + $multistatus = $this->xml->expect('{DAV:}multistatus', $body); + + $result = []; + + foreach ($multistatus->getResponses() as $response) { + + $result[$response->getHref()] = $response->getResponseProperties(); + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php new file mode 100644 index 00000000000..35c90b5afae --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php @@ -0,0 +1,109 @@ +getChildren() as $child) { + + if ($child->getName() === $name) return $child; + + } + throw new Exception\NotFound('File not found: ' . $name); + + } + + /** + * Checks is a child-node exists. + * + * It is generally a good idea to try and override this. Usually it can be optimized. + * + * @param string $name + * @return bool + */ + function childExists($name) { + + try { + + $this->getChild($name); + return true; + + } catch (Exception\NotFound $e) { + + return false; + + } + + } + + /** + * Creates a new file in the directory + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After successful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string + */ + function createFile($name, $data = null) { + + throw new Exception\Forbidden('Permission denied to create file (filename ' . $name . ')'); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @throws Exception\Forbidden + * @return void + */ + function createDirectory($name) { + + throw new Exception\Forbidden('Permission denied to create directory'); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php new file mode 100644 index 00000000000..676cdd04a29 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php @@ -0,0 +1,959 @@ +server = $server; + $server->on('method:GET', [$this, 'httpGet']); + $server->on('method:OPTIONS', [$this, 'httpOptions']); + $server->on('method:HEAD', [$this, 'httpHead']); + $server->on('method:DELETE', [$this, 'httpDelete']); + $server->on('method:PROPFIND', [$this, 'httpPropFind']); + $server->on('method:PROPPATCH', [$this, 'httpPropPatch']); + $server->on('method:PUT', [$this, 'httpPut']); + $server->on('method:MKCOL', [$this, 'httpMkcol']); + $server->on('method:MOVE', [$this, 'httpMove']); + $server->on('method:COPY', [$this, 'httpCopy']); + $server->on('method:REPORT', [$this, 'httpReport']); + + $server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90); + $server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200); + $server->on('propFind', [$this, 'propFind']); + $server->on('propFind', [$this, 'propFindNode'], 120); + $server->on('propFind', [$this, 'propFindLate'], 200); + + $server->on('exception', [$this, 'exception']); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'core'; + + } + + /** + * This is the default implementation for the GET method. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + $node = $this->server->tree->getNodeForPath($path); + + if (!$node instanceof IFile) return; + + $body = $node->get(); + + // Converting string into stream, if needed. + if (is_string($body)) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $body); + rewind($stream); + $body = $stream; + } + + /* + * TODO: getetag, getlastmodified, getsize should also be used using + * this method + */ + $httpHeaders = $this->server->getHTTPHeaders($path); + + /* ContentType needs to get a default, because many webservers will otherwise + * default to text/html, and we don't want this for security reasons. + */ + if (!isset($httpHeaders['Content-Type'])) { + $httpHeaders['Content-Type'] = 'application/octet-stream'; + } + + + if (isset($httpHeaders['Content-Length'])) { + + $nodeSize = $httpHeaders['Content-Length']; + + // Need to unset Content-Length, because we'll handle that during figuring out the range + unset($httpHeaders['Content-Length']); + + } else { + $nodeSize = null; + } + + $response->addHeaders($httpHeaders); + + $range = $this->server->getHTTPRange(); + $ifRange = $request->getHeader('If-Range'); + $ignoreRangeHeader = false; + + // If ifRange is set, and range is specified, we first need to check + // the precondition. + if ($nodeSize && $range && $ifRange) { + + // if IfRange is parsable as a date we'll treat it as a DateTime + // otherwise, we must treat it as an etag. + try { + $ifRangeDate = new \DateTime($ifRange); + + // It's a date. We must check if the entity is modified since + // the specified date. + if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; + else { + $modified = new \DateTime($httpHeaders['Last-Modified']); + if ($modified > $ifRangeDate) $ignoreRangeHeader = true; + } + + } catch (\Exception $e) { + + // It's an entity. We can do a simple comparison. + if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; + elseif ($httpHeaders['ETag'] !== $ifRange) $ignoreRangeHeader = true; + } + } + + // We're only going to support HTTP ranges if the backend provided a filesize + if (!$ignoreRangeHeader && $nodeSize && $range) { + + // Determining the exact byte offsets + if (!is_null($range[0])) { + + $start = $range[0]; + $end = $range[1] ? $range[1] : $nodeSize - 1; + if ($start >= $nodeSize) + throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); + + if ($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); + if ($end >= $nodeSize) $end = $nodeSize - 1; + + } else { + + $start = $nodeSize - $range[1]; + $end = $nodeSize - 1; + + if ($start < 0) $start = 0; + + } + + // Streams may advertise themselves as seekable, but still not + // actually allow fseek. We'll manually go forward in the stream + // if fseek failed. + if (!stream_get_meta_data($body)['seekable'] || fseek($body, $start, SEEK_SET) === -1) { + $consumeBlock = 8192; + for ($consumed = 0; $start - $consumed > 0;){ + if (feof($body)) throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $start . ') exceeded the size of the entity (' . $consumed . ')'); + $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock))); + } + } + + $response->setHeader('Content-Length', $end - $start + 1); + $response->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $nodeSize); + $response->setStatus(206); + $response->setBody($body); + + } else { + + if ($nodeSize) $response->setHeader('Content-Length', $nodeSize); + $response->setStatus(200); + $response->setBody($body); + + } + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * HTTP OPTIONS + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpOptions(RequestInterface $request, ResponseInterface $response) { + + $methods = $this->server->getAllowedMethods($request->getPath()); + + $response->setHeader('Allow', strtoupper(implode(', ', $methods))); + $features = ['1', '3', 'extended-mkcol']; + + foreach ($this->server->getPlugins() as $plugin) { + $features = array_merge($features, $plugin->getFeatures()); + } + + $response->setHeader('DAV', implode(', ', $features)); + $response->setHeader('MS-Author-Via', 'DAV'); + $response->setHeader('Accept-Ranges', 'bytes'); + $response->setHeader('Content-Length', '0'); + $response->setStatus(200); + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * HTTP HEAD + * + * This method is normally used to take a peak at a url, and only get the + * HTTP response headers, without the body. This is used by clients to + * determine if a remote file was changed, so they can use a local cached + * version, instead of downloading it again + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpHead(RequestInterface $request, ResponseInterface $response) { + + // This is implemented by changing the HEAD request to a GET request, + // and dropping the response body. + $subRequest = clone $request; + $subRequest->setMethod('GET'); + + try { + $this->server->invokeMethod($subRequest, $response, false); + $response->setBody(''); + } catch (Exception\NotImplemented $e) { + // Some clients may do HEAD requests on collections, however, GET + // requests and HEAD requests _may_ not be defined on a collection, + // which would trigger a 501. + // This breaks some clients though, so we're transforming these + // 501s into 200s. + $response->setStatus(200); + $response->setBody(''); + $response->setHeader('Content-Type', 'text/plain'); + $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode()); + } + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * HTTP Delete + * + * The HTTP delete method, deletes a given uri + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpDelete(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + if (!$this->server->emit('beforeUnbind', [$path])) return false; + $this->server->tree->delete($path); + $this->server->emit('afterUnbind', [$path]); + + $response->setStatus(204); + $response->setHeader('Content-Length', '0'); + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * WebDAV PROPFIND + * + * This WebDAV method requests information about an uri resource, or a list of resources + * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value + * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) + * + * The request body contains an XML data structure that has a list of properties the client understands + * The response body is also an xml document, containing information about every uri resource and the requested properties + * + * It has to return a HTTP 207 Multi-status status code + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpPropFind(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + $requestBody = $request->getBodyAsString(); + if (strlen($requestBody)) { + try { + $propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody); + } catch (ParseException $e) { + throw new BadRequest($e->getMessage(), null, $e); + } + } else { + $propFindXml = new Xml\Request\PropFind(); + $propFindXml->allProp = true; + $propFindXml->properties = []; + } + + $depth = $this->server->getHTTPDepth(1); + // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled + if (!$this->server->enablePropfindDepthInfinity && $depth != 0) $depth = 1; + + $newProperties = $this->server->getPropertiesIteratorForPath($path, $propFindXml->properties, $depth); + + // This is a multi-status response + $response->setStatus(207); + $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $response->setHeader('Vary', 'Brief,Prefer'); + + // Normally this header is only needed for OPTIONS responses, however.. + // iCal seems to also depend on these being set for PROPFIND. Since + // this is not harmful, we'll add it. + $features = ['1', '3', 'extended-mkcol']; + foreach ($this->server->getPlugins() as $plugin) { + $features = array_merge($features, $plugin->getFeatures()); + } + $response->setHeader('DAV', implode(', ', $features)); + + $prefer = $this->server->getHTTPPrefer(); + $minimal = $prefer['return'] === 'minimal'; + + $data = $this->server->generateMultiStatus($newProperties, $minimal); + $response->setBody($data); + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * WebDAV PROPPATCH + * + * This method is called to update properties on a Node. The request is an XML body with all the mutations. + * In this XML body it is specified which properties should be set/updated and/or deleted + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpPropPatch(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + try { + $propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody()); + } catch (ParseException $e) { + throw new BadRequest($e->getMessage(), null, $e); + } + $newProperties = $propPatch->properties; + + $result = $this->server->updateProperties($path, $newProperties); + + $prefer = $this->server->getHTTPPrefer(); + $response->setHeader('Vary', 'Brief,Prefer'); + + if ($prefer['return'] === 'minimal') { + + // If return-minimal is specified, we only have to check if the + // request was successful, and don't need to return the + // multi-status. + $ok = true; + foreach ($result as $prop => $code) { + if ((int)$code > 299) { + $ok = false; + } + } + + if ($ok) { + + $response->setStatus(204); + return false; + + } + + } + + $response->setStatus(207); + $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); + + + // Reorganizing the result for generateMultiStatus + $multiStatus = []; + foreach ($result as $propertyName => $code) { + if (isset($multiStatus[$code])) { + $multiStatus[$code][$propertyName] = null; + } else { + $multiStatus[$code] = [$propertyName => null]; + } + } + $multiStatus['href'] = $path; + + $response->setBody( + $this->server->generateMultiStatus([$multiStatus]) + ); + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * HTTP PUT method + * + * This HTTP method updates a file, or creates a new one. + * + * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpPut(RequestInterface $request, ResponseInterface $response) { + + $body = $request->getBodyAsStream(); + $path = $request->getPath(); + + // Intercepting Content-Range + if ($request->getHeader('Content-Range')) { + /* + An origin server that allows PUT on a given target resource MUST send + a 400 (Bad Request) response to a PUT request that contains a + Content-Range header field. + + Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4 + */ + throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.'); + } + + // Intercepting the Finder problem + if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) { + + /* + Many webservers will not cooperate well with Finder PUT requests, + because it uses 'Chunked' transfer encoding for the request body. + + The symptom of this problem is that Finder sends files to the + server, but they arrive as 0-length files in PHP. + + If we don't do anything, the user might think they are uploading + files successfully, but they end up empty on the server. Instead, + we throw back an error if we detect this. + + The reason Finder uses Chunked, is because it thinks the files + might change as it's being uploaded, and therefore the + Content-Length can vary. + + Instead it sends the X-Expected-Entity-Length header with the size + of the file at the very start of the request. If this header is set, + but we don't get a request body we will fail the request to + protect the end-user. + */ + + // Only reading first byte + $firstByte = fread($body, 1); + if (strlen($firstByte) !== 1) { + throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); + } + + // The body needs to stay intact, so we copy everything to a + // temporary stream. + + $newBody = fopen('php://temp', 'r+'); + fwrite($newBody, $firstByte); + stream_copy_to_stream($body, $newBody); + rewind($newBody); + + $body = $newBody; + + } + + if ($this->server->tree->nodeExists($path)) { + + $node = $this->server->tree->getNodeForPath($path); + + // If the node is a collection, we'll deny it + if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.'); + + if (!$this->server->updateFile($path, $body, $etag)) { + return false; + } + + $response->setHeader('Content-Length', '0'); + if ($etag) $response->setHeader('ETag', $etag); + $response->setStatus(204); + + } else { + + $etag = null; + // If we got here, the resource didn't exist yet. + if (!$this->server->createFile($path, $body, $etag)) { + // For one reason or another the file was not created. + return false; + } + + $response->setHeader('Content-Length', '0'); + if ($etag) $response->setHeader('ETag', $etag); + $response->setStatus(201); + + } + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + + /** + * WebDAV MKCOL + * + * The MKCOL method is used to create a new collection (directory) on the server + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpMkcol(RequestInterface $request, ResponseInterface $response) { + + $requestBody = $request->getBodyAsString(); + $path = $request->getPath(); + + if ($requestBody) { + + $contentType = $request->getHeader('Content-Type'); + if (strpos($contentType, 'application/xml') !== 0 && strpos($contentType, 'text/xml') !== 0) { + + // We must throw 415 for unsupported mkcol bodies + throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); + + } + + try { + $mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody); + } catch (\Sabre\Xml\ParseException $e) { + throw new Exception\BadRequest($e->getMessage(), null, $e); + } + + $properties = $mkcol->getProperties(); + + if (!isset($properties['{DAV:}resourcetype'])) + throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property'); + + $resourceType = $properties['{DAV:}resourcetype']->getValue(); + unset($properties['{DAV:}resourcetype']); + + } else { + + $properties = []; + $resourceType = ['{DAV:}collection']; + + } + + $mkcol = new MkCol($resourceType, $properties); + + $result = $this->server->createCollection($path, $mkcol); + + if (is_array($result)) { + $response->setStatus(207); + $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); + + $response->setBody( + $this->server->generateMultiStatus([$result]) + ); + + } else { + $response->setHeader('Content-Length', '0'); + $response->setStatus(201); + } + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * WebDAV HTTP MOVE method + * + * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpMove(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + $moveInfo = $this->server->getCopyAndMoveInfo($request); + + if ($moveInfo['destinationExists']) { + + if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) return false; + + } + if (!$this->server->emit('beforeUnbind', [$path])) return false; + if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) return false; + if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) return false; + + if ($moveInfo['destinationExists']) { + + $this->server->tree->delete($moveInfo['destination']); + $this->server->emit('afterUnbind', [$moveInfo['destination']]); + + } + + $this->server->tree->move($path, $moveInfo['destination']); + + // Its important afterMove is called before afterUnbind, because it + // allows systems to transfer data from one path to another. + // PropertyStorage uses this. If afterUnbind was first, it would clean + // up all the properties before it has a chance. + $this->server->emit('afterMove', [$path, $moveInfo['destination']]); + $this->server->emit('afterUnbind', [$path]); + $this->server->emit('afterBind', [$moveInfo['destination']]); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $response->setHeader('Content-Length', '0'); + $response->setStatus($moveInfo['destinationExists'] ? 204 : 201); + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * WebDAV HTTP COPY method + * + * This method copies one uri to a different uri, and works much like the MOVE request + * A lot of the actual request processing is done in getCopyMoveInfo + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpCopy(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + $copyInfo = $this->server->getCopyAndMoveInfo($request); + + if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false; + if ($copyInfo['destinationExists']) { + if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false; + $this->server->tree->delete($copyInfo['destination']); + } + + $this->server->tree->copy($path, $copyInfo['destination']); + $this->server->emit('afterBind', [$copyInfo['destination']]); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $response->setHeader('Content-Length', '0'); + $response->setStatus($copyInfo['destinationExists'] ? 204 : 201); + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + + } + + /** + * HTTP REPORT method implementation + * + * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) + * It's used in a lot of extensions, so it made sense to implement it into the core. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpReport(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + $result = $this->server->xml->parse( + $request->getBody(), + $request->getUrl(), + $rootElementName + ); + + if ($this->server->emit('report', [$rootElementName, $result, $path])) { + + // If emit returned true, it means the report was not supported + throw new Exception\ReportNotSupported(); + + } + + // Sending back false will interrupt the event chain and tell the server + // we've handled this method. + return false; + + } + + /** + * This method is called during property updates. + * + * Here we check if a user attempted to update a protected property and + * ensure that the process fails if this is the case. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatchProtectedPropertyCheck($path, PropPatch $propPatch) { + + // Comparing the mutation list to the list of protected properties. + $mutations = $propPatch->getMutations(); + + $protected = array_intersect( + $this->server->protectedProperties, + array_keys($mutations) + ); + + if ($protected) { + $propPatch->setResultCode($protected, 403); + } + + } + + /** + * This method is called during property updates. + * + * Here we check if a node implements IProperties and let the node handle + * updating of (some) properties. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatchNodeUpdate($path, PropPatch $propPatch) { + + // This should trigger a 404 if the node doesn't exist. + $node = $this->server->tree->getNodeForPath($path); + + if ($node instanceof IProperties) { + $node->propPatch($propPatch); + } + + } + + /** + * This method is called when properties are retrieved. + * + * Here we add all the default properties. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + $propFind->handle('{DAV:}getlastmodified', function() use ($node) { + $lm = $node->getLastModified(); + if ($lm) { + return new Xml\Property\GetLastModified($lm); + } + }); + + if ($node instanceof IFile) { + $propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']); + $propFind->handle('{DAV:}getetag', [$node, 'getETag']); + $propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']); + } + + if ($node instanceof IQuota) { + $quotaInfo = null; + $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) { + $quotaInfo = $node->getQuotaInfo(); + return $quotaInfo[0]; + }); + $propFind->handle('{DAV:}quota-available-bytes', function() use (&$quotaInfo, $node) { + if (!$quotaInfo) { + $quotaInfo = $node->getQuotaInfo(); + } + return $quotaInfo[1]; + }); + } + + $propFind->handle('{DAV:}supported-report-set', function() use ($propFind) { + $reports = []; + foreach ($this->server->getPlugins() as $plugin) { + $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath())); + } + return new Xml\Property\SupportedReportSet($reports); + }); + $propFind->handle('{DAV:}resourcetype', function() use ($node) { + return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node)); + }); + $propFind->handle('{DAV:}supported-method-set', function() use ($propFind) { + return new Xml\Property\SupportedMethodSet( + $this->server->getAllowedMethods($propFind->getPath()) + ); + }); + + } + + /** + * Fetches properties for a node. + * + * This event is called a bit later, so plugins have a chance first to + * populate the result. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFindNode(PropFind $propFind, INode $node) { + + if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) { + + $nodeProperties = $node->getProperties($propertyNames); + foreach ($nodeProperties as $propertyName => $propertyValue) { + $propFind->set($propertyName, $propertyValue, 200); + } + + } + + } + + /** + * This method is called when properties are retrieved. + * + * This specific handler is called very late in the process, because we + * want other systems to first have a chance to handle the properties. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFindLate(PropFind $propFind, INode $node) { + + $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) { + + // If we already have a sync-token from the current propFind + // request, we can re-use that. + $val = $propFind->get('{http://sabredav.org/ns}sync-token'); + if ($val) return $val; + + $val = $propFind->get('{DAV:}sync-token'); + if ($val && is_scalar($val)) { + return $val; + } + if ($val && $val instanceof Xml\Property\Href) { + return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); + } + + // If we got here, the earlier two properties may simply not have + // been part of the earlier request. We're going to fetch them. + $result = $this->server->getProperties($propFind->getPath(), [ + '{http://sabredav.org/ns}sync-token', + '{DAV:}sync-token', + ]); + + if (isset($result['{http://sabredav.org/ns}sync-token'])) { + return $result['{http://sabredav.org/ns}sync-token']; + } + if (isset($result['{DAV:}sync-token'])) { + $val = $result['{DAV:}sync-token']; + if (is_scalar($val)) { + return $val; + } elseif ($val instanceof Xml\Property\Href) { + return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); + } + } + + }); + + } + + /** + * Listens for exception events, and automatically logs them. + * + * @param Exception $e + */ + function exception($e) { + + $logLevel = \Psr\Log\LogLevel::CRITICAL; + if ($e instanceof \Sabre\DAV\Exception) { + // If it's a standard sabre/dav exception, it means we have a http + // status code available. + $code = $e->getHTTPCode(); + + if ($code >= 400 && $code < 500) { + // user error + $logLevel = \Psr\Log\LogLevel::INFO; + } else { + // Server-side error. We mark it's as an error, but it's not + // critical. + $logLevel = \Psr\Log\LogLevel::ERROR; + } + } + + $this->server->getLogger()->log( + $logLevel, + 'Uncaught exception', + [ + 'exception' => $e, + ] + ); + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'The Core plugin provides a lot of the basic functionality required by WebDAV, such as a default implementation for all HTTP and WebDAV methods.', + 'link' => null, + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php new file mode 100644 index 00000000000..14f5bab2a53 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php @@ -0,0 +1,57 @@ +lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:no-conflicting-lock'); + $errorNode->appendChild($error); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:', 'd:href', $this->lock->uri)); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php new file mode 100644 index 00000000000..77df7ca9ecc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php @@ -0,0 +1,29 @@ +ownerDocument->createElementNS('DAV:', 'd:valid-resourcetype'); + $errorNode->appendChild($error); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php new file mode 100644 index 00000000000..51a253b296b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php @@ -0,0 +1,38 @@ +ownerDocument->createElementNS('DAV:', 'd:valid-sync-token'); + $errorNode->appendChild($error); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php new file mode 100644 index 00000000000..98971855834 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php @@ -0,0 +1,30 @@ +message = 'The locktoken supplied does not match any locks on this entity'; + + } + + /** + * This method allows the exception to include additional information into the WebDAV error response + * + * @param DAV\Server $server + * @param \DOMElement $errorNode + * @return void + */ + function serialize(DAV\Server $server, \DOMElement $errorNode) { + + $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-matches-request-uri'); + $errorNode->appendChild($error); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php new file mode 100644 index 00000000000..8176db46e87 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php @@ -0,0 +1,72 @@ +lock = $lock; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + function getHTTPCode() { + + return 423; + + } + + /** + * This method allows the exception to include additional information into the WebDAV error response + * + * @param DAV\Server $server + * @param \DOMElement $errorNode + * @return void + */ + function serialize(DAV\Server $server, \DOMElement $errorNode) { + + if ($this->lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-submitted'); + $errorNode->appendChild($error); + + $href = $errorNode->ownerDocument->createElementNS('DAV:', 'd:href'); + $href->appendChild($errorNode->ownerDocument->createTextNode($this->lock->uri)); + $error->appendChild( + $href + ); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php new file mode 100644 index 00000000000..30c1c255345 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php @@ -0,0 +1,47 @@ +getAllowedMethods($server->getRequestUri()); + + return [ + 'Allow' => strtoupper(implode(', ', $methods)), + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php new file mode 100644 index 00000000000..e69a60c7549 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php @@ -0,0 +1,30 @@ +header = $header; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + function getHTTPCode() { + + return 412; + + } + + /** + * This method allows the exception to include additional information into the WebDAV error response + * + * @param DAV\Server $server + * @param \DOMElement $errorNode + * @return void + */ + function serialize(DAV\Server $server, \DOMElement $errorNode) { + + if ($this->header) { + $prop = $errorNode->ownerDocument->createElement('s:header'); + $prop->nodeValue = $this->header; + $errorNode->appendChild($prop); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php new file mode 100644 index 00000000000..a8369562702 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php @@ -0,0 +1,32 @@ +ownerDocument->createElementNS('DAV:', 'd:supported-report'); + $errorNode->appendChild($error); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php new file mode 100644 index 00000000000..c8ccfc062a9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php @@ -0,0 +1,30 @@ + + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ServiceUnavailable extends DAV\Exception { + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + function getHTTPCode() { + + return 503; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/TooManyMatches.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/TooManyMatches.php new file mode 100644 index 00000000000..d0f0f84e89b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/TooManyMatches.php @@ -0,0 +1,38 @@ +ownerDocument->createElementNS('DAV:', 'd:number-of-matches-within-limits'); + $errorNode->appendChild($error); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php new file mode 100644 index 00000000000..f3d92842da5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php @@ -0,0 +1,30 @@ +path . '/' . $name; + file_put_contents($newPath, $data); + clearstatcache(true, $newPath); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + function createDirectory($name) { + + $newPath = $this->path . '/' . $name; + mkdir($newPath); + clearstatcache(true, $newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * This method must throw DAV\Exception\NotFound if the node does not + * exist. + * + * @param string $name + * @throws DAV\Exception\NotFound + * @return DAV\INode + */ + function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new DAV\Exception\NotFound('File with name ' . $path . ' could not be located'); + + if (is_dir($path)) { + + return new self($path); + + } else { + + return new File($path); + + } + + } + + /** + * Returns an array with all the child nodes + * + * @return DAV\INode[] + */ + function getChildren() { + + $nodes = []; + $iterator = new \FilesystemIterator( + $this->path, + \FilesystemIterator::CURRENT_AS_SELF + | \FilesystemIterator::SKIP_DOTS + ); + foreach ($iterator as $entry) { + + $nodes[] = $this->getChild($entry->getFilename()); + + } + return $nodes; + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + function childExists($name) { + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return void + */ + function delete() { + + foreach ($this->getChildren() as $child) $child->delete(); + rmdir($this->path); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + function getQuotaInfo() { + $absolute = realpath($this->path); + return [ + disk_total_space($absolute) - disk_free_space($absolute), + disk_free_space($absolute) + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php new file mode 100644 index 00000000000..4fc5af0574b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php @@ -0,0 +1,95 @@ +path, $data); + clearstatcache(true, $this->path); + + } + + /** + * Returns the data + * + * @return resource + */ + function get() { + + return fopen($this->path, 'r'); + + } + + /** + * Delete the current file + * + * @return void + */ + function delete() { + + unlink($this->path); + + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + function getSize() { + + return filesize($this->path); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * + * @return mixed + */ + function getETag() { + + return '"' . sha1( + fileinode($this->path) . + filesize($this->path) . + filemtime($this->path) + ) . '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return mixed + */ + function getContentType() { + + return null; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php new file mode 100644 index 00000000000..424718f966e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php @@ -0,0 +1,80 @@ +path = $path; + + } + + + + /** + * Returns the name of the node + * + * @return string + */ + function getName() { + + list(, $name) = URLUtil::splitPath($this->path); + return $name; + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + function setName($name) { + + list($parentPath, ) = URLUtil::splitPath($this->path); + list(, $newName) = URLUtil::splitPath($name); + + $newPath = $parentPath . '/' . $newName; + rename($this->path, $newPath); + + $this->path = $newPath; + + } + + /** + * Returns the last modification time, as a unix timestamp + * + * @return int + */ + function getLastModified() { + + return filemtime($this->path); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php new file mode 100644 index 00000000000..dd5f992dbce --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php @@ -0,0 +1,211 @@ +path . '/' . $name; + file_put_contents($newPath, $data); + clearstatcache(true, $newPath); + + return '"' . sha1( + fileinode($newPath) . + filesize($newPath) . + filemtime($newPath) + ) . '"'; + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + function createDirectory($name) { + + // We're not allowing dots + if ($name == '.' || $name == '..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); + $newPath = $this->path . '/' . $name; + mkdir($newPath); + clearstatcache(true, $newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * This method must throw Sabre\DAV\Exception\NotFound if the node does not + * exist. + * + * @param string $name + * @throws DAV\Exception\NotFound + * @return DAV\INode + */ + function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new DAV\Exception\NotFound('File could not be located'); + if ($name == '.' || $name == '..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); + + if (is_dir($path)) { + + return new self($path); + + } else { + + return new File($path); + + } + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + function childExists($name) { + + if ($name == '.' || $name == '..') + throw new DAV\Exception\Forbidden('Permission denied to . and ..'); + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Returns an array with all the child nodes + * + * @return DAV\INode[] + */ + function getChildren() { + + $nodes = []; + $iterator = new \FilesystemIterator( + $this->path, + \FilesystemIterator::CURRENT_AS_SELF + | \FilesystemIterator::SKIP_DOTS + ); + + foreach ($iterator as $entry) { + + $nodes[] = $this->getChild($entry->getFilename()); + + } + return $nodes; + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return bool + */ + function delete() { + + // Deleting all children + foreach ($this->getChildren() as $child) $child->delete(); + + // Removing the directory itself + rmdir($this->path); + + return true; + + } + + /** + * Returns available diskspace information + * + * @return array + */ + function getQuotaInfo() { + + $total = disk_total_space(realpath($this->path)); + $free = disk_free_space(realpath($this->path)); + + return [ + $total - $free, + $free + ]; + } + + /** + * Moves a node into this collection. + * + * It is up to the implementors to: + * 1. Create the new resource. + * 2. Remove the old resource. + * 3. Transfer any properties or other data. + * + * Generally you should make very sure that your collection can easily move + * the move. + * + * If you don't, just return false, which will trigger sabre/dav to handle + * the move itself. If you return true from this function, the assumption + * is that the move was successful. + * + * @param string $targetName New local file/collection name. + * @param string $sourcePath Full path to source node + * @param DAV\INode $sourceNode Source node itself + * @return bool + */ + function moveInto($targetName, $sourcePath, DAV\INode $sourceNode) { + + // We only support FSExt\Directory or FSExt\File objects, so + // anything else we want to quickly reject. + if (!$sourceNode instanceof self && !$sourceNode instanceof File) { + return false; + } + + // PHP allows us to access protected properties from other objects, as + // long as they are defined in a class that has a shared inheritence + // with the current class. + rename($sourceNode->path, $this->path . '/' . $targetName); + + return true; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php new file mode 100644 index 00000000000..eb5ae19fec5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php @@ -0,0 +1,152 @@ +path, $data); + clearstatcache(true, $this->path); + return $this->getETag(); + + } + + /** + * Updates the file based on a range specification. + * + * The first argument is the data, which is either a readable stream + * resource or a string. + * + * The second argument is the type of update we're doing. + * This is either: + * * 1. append + * * 2. update based on a start byte + * * 3. update based on an end byte + *; + * The third argument is the start or end byte. + * + * After a successful put operation, you may choose to return an ETag. The + * ETAG must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * @param resource|string $data + * @param int $rangeType + * @param int $offset + * @return string|null + */ + function patch($data, $rangeType, $offset = null) { + + switch ($rangeType) { + case 1 : + $f = fopen($this->path, 'a'); + break; + case 2 : + $f = fopen($this->path, 'c'); + fseek($f, $offset); + break; + case 3 : + $f = fopen($this->path, 'c'); + fseek($f, $offset, SEEK_END); + break; + } + if (is_string($data)) { + fwrite($f, $data); + } else { + stream_copy_to_stream($data, $f); + } + fclose($f); + clearstatcache(true, $this->path); + return $this->getETag(); + + } + + /** + * Returns the data + * + * @return resource + */ + function get() { + + return fopen($this->path, 'r'); + + } + + /** + * Delete the current file + * + * @return bool + */ + function delete() { + + return unlink($this->path); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * + * @return string|null + */ + function getETag() { + + return '"' . sha1( + fileinode($this->path) . + filesize($this->path) . + filemtime($this->path) + ) . '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return string|null + */ + function getContentType() { + + return null; + + } + + /** + * Returns the size of the file, in bytes + * + * @return int + */ + function getSize() { + + return filesize($this->path); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php new file mode 100644 index 00000000000..5161fbd5153 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php @@ -0,0 +1,96 @@ +locksFile = $locksFile; + + } + + /** + * Returns a list of Sabre\DAV\Locks\LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * If returnChildLocks is set to true, this method should also look for + * any locks in the subtree of the uri for locks. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + function getLocks($uri, $returnChildLocks) { + + $newLocks = []; + + $locks = $this->getData(); + + foreach ($locks as $lock) { + + if ($lock->uri === $uri || + //deep locks on parents + ($lock->depth != 0 && strpos($uri, $lock->uri . '/') === 0) || + + // locks on children + ($returnChildLocks && (strpos($lock->uri, $uri . '/') === 0))) { + + $newLocks[] = $lock; + + } + + } + + // Checking if we can remove any of these locks + foreach ($newLocks as $k => $lock) { + if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); + } + return $newLocks; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function lock($uri, LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getData(); + + foreach ($locks as $k => $lock) { + if ( + ($lock->token == $lockInfo->token) || + (time() > $lock->timeout + $lock->created) + ) { + unset($locks[$k]); + } + } + $locks[] = $lockInfo; + $this->putData($locks); + return true; + + } + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function unlock($uri, LockInfo $lockInfo) { + + $locks = $this->getData(); + foreach ($locks as $k => $lock) { + + if ($lock->token == $lockInfo->token) { + + unset($locks[$k]); + $this->putData($locks); + return true; + + } + } + return false; + + } + + /** + * Loads the lockdata from the filesystem. + * + * @return array + */ + protected function getData() { + + if (!file_exists($this->locksFile)) return []; + + // opening up the file, and creating a shared lock + $handle = fopen($this->locksFile, 'r'); + flock($handle, LOCK_SH); + + // Reading data until the eof + $data = stream_get_contents($handle); + + // We're all good + flock($handle, LOCK_UN); + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!$data) return []; + return $data; + + } + + /** + * Saves the lockdata + * + * @param array $newData + * @return void + */ + protected function putData(array $newData) { + + // opening up the file, and creating an exclusive lock + $handle = fopen($this->locksFile, 'a+'); + flock($handle, LOCK_EX); + + // We can only truncate and rewind once the lock is acquired. + ftruncate($handle, 0); + rewind($handle); + + fwrite($handle, serialize($newData)); + flock($handle, LOCK_UN); + fclose($handle); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php new file mode 100644 index 00000000000..510f266f7e6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php @@ -0,0 +1,180 @@ +pdo = $pdo; + + } + + /** + * Returns a list of Sabre\DAV\Locks\LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * If returnChildLocks is set to true, this method should also look for + * any locks in the subtree of the uri for locks. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + function getLocks($uri, $returnChildLocks) { + + // NOTE: the following 10 lines or so could be easily replaced by + // pure sql. MySQL's non-standard string concatenation prevents us + // from doing this though. + $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM ' . $this->tableName . ' WHERE (created > (? - timeout)) AND ((uri = ?)'; + $params = [time(),$uri]; + + // We need to check locks for every part in the uri. + $uriParts = explode('/', $uri); + + // We already covered the last part of the uri + array_pop($uriParts); + + $currentPath = ''; + + foreach ($uriParts as $part) { + + if ($currentPath) $currentPath .= '/'; + $currentPath .= $part; + + $query .= ' OR (depth!=0 AND uri = ?)'; + $params[] = $currentPath; + + } + + if ($returnChildLocks) { + + $query .= ' OR (uri LIKE ?)'; + $params[] = $uri . '/%'; + + } + $query .= ')'; + + $stmt = $this->pdo->prepare($query); + $stmt->execute($params); + $result = $stmt->fetchAll(); + + $lockList = []; + foreach ($result as $row) { + + $lockInfo = new LockInfo(); + $lockInfo->owner = $row['owner']; + $lockInfo->token = $row['token']; + $lockInfo->timeout = $row['timeout']; + $lockInfo->created = $row['created']; + $lockInfo->scope = $row['scope']; + $lockInfo->depth = $row['depth']; + $lockInfo->uri = $row['uri']; + $lockList[] = $lockInfo; + + } + + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function lock($uri, LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 30 * 60; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getLocks($uri, false); + $exists = false; + foreach ($locks as $lock) { + if ($lock->token == $lockInfo->token) $exists = true; + } + + if ($exists) { + $stmt = $this->pdo->prepare('UPDATE ' . $this->tableName . ' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); + $stmt->execute([ + $lockInfo->owner, + $lockInfo->timeout, + $lockInfo->scope, + $lockInfo->depth, + $uri, + $lockInfo->created, + $lockInfo->token + ]); + } else { + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); + $stmt->execute([ + $lockInfo->owner, + $lockInfo->timeout, + $lockInfo->scope, + $lockInfo->depth, + $uri, + $lockInfo->created, + $lockInfo->token + ]); + } + + return true; + + } + + + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function unlock($uri, LockInfo $lockInfo) { + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->tableName . ' WHERE uri = ? AND token = ?'); + $stmt->execute([$uri, $lockInfo->token]); + + return $stmt->rowCount() === 1; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php new file mode 100644 index 00000000000..2c8cca0fee4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php @@ -0,0 +1,80 @@ +addPlugin($lockPlugin); + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Plugin extends DAV\ServerPlugin { + + /** + * locksBackend + * + * @var Backend\BackendInterface + */ + protected $locksBackend; + + /** + * server + * + * @var DAV\Server + */ + protected $server; + + /** + * __construct + * + * @param Backend\BackendInterface $locksBackend + */ + function __construct(Backend\BackendInterface $locksBackend) { + + $this->locksBackend = $locksBackend; + + } + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + $this->server = $server; + + $this->server->xml->elementMap['{DAV:}lockinfo'] = 'Sabre\\DAV\\Xml\\Request\\Lock'; + + $server->on('method:LOCK', [$this, 'httpLock']); + $server->on('method:UNLOCK', [$this, 'httpUnlock']); + $server->on('validateTokens', [$this, 'validateTokens']); + $server->on('propFind', [$this, 'propFind']); + $server->on('afterUnbind', [$this, 'afterUnbind']); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'locks'; + + } + + /** + * This method is called after most properties have been found + * it allows us to add in any Lock-related properties + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFind(DAV\PropFind $propFind, DAV\INode $node) { + + $propFind->handle('{DAV:}supportedlock', function() { + return new DAV\Xml\Property\SupportedLock(); + }); + $propFind->handle('{DAV:}lockdiscovery', function() use ($propFind) { + return new DAV\Xml\Property\LockDiscovery( + $this->getLocks($propFind->getPath()) + ); + }); + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + function getHTTPMethods($uri) { + + return ['LOCK','UNLOCK']; + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * In this case this is only the number 2. The 2 in the Dav: header + * indicates the server supports locks. + * + * @return array + */ + function getFeatures() { + + return [2]; + + } + + /** + * Returns all lock information on a particular uri + * + * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array. + * + * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree + * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object + * for any possible locks and return those as well. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + function getLocks($uri, $returnChildLocks = false) { + + return $this->locksBackend->getLocks($uri, $returnChildLocks); + + } + + /** + * Locks an uri + * + * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock + * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type + * of lock (shared or exclusive) and the owner of the lock + * + * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock + * + * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpLock(RequestInterface $request, ResponseInterface $response) { + + $uri = $request->getPath(); + + $existingLocks = $this->getLocks($uri); + + if ($body = $request->getBodyAsString()) { + // This is a new lock request + + $existingLock = null; + // Checking if there's already non-shared locks on the uri. + foreach ($existingLocks as $existingLock) { + if ($existingLock->scope === LockInfo::EXCLUSIVE) { + throw new DAV\Exception\ConflictingLock($existingLock); + } + } + + $lockInfo = $this->parseLockRequest($body); + $lockInfo->depth = $this->server->getHTTPDepth(); + $lockInfo->uri = $uri; + if ($existingLock && $lockInfo->scope != LockInfo::SHARED) + throw new DAV\Exception\ConflictingLock($existingLock); + + } else { + + // Gonna check if this was a lock refresh. + $existingLocks = $this->getLocks($uri); + $conditions = $this->server->getIfConditions($request); + $found = null; + + foreach ($existingLocks as $existingLock) { + foreach ($conditions as $condition) { + foreach ($condition['tokens'] as $token) { + if ($token['token'] === 'opaquelocktoken:' . $existingLock->token) { + $found = $existingLock; + break 3; + } + } + } + } + + // If none were found, this request is in error. + if (is_null($found)) { + if ($existingLocks) { + throw new DAV\Exception\Locked(reset($existingLocks)); + } else { + throw new DAV\Exception\BadRequest('An xml body is required for lock requests'); + } + + } + + // This must have been a lock refresh + $lockInfo = $found; + + // The resource could have been locked through another uri. + if ($uri != $lockInfo->uri) $uri = $lockInfo->uri; + + } + + if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; + + $newFile = false; + + // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first + try { + $this->server->tree->getNodeForPath($uri); + + // We need to call the beforeWriteContent event for RFC3744 + // Edit: looks like this is not used, and causing problems now. + // + // See Issue 222 + // $this->server->emit('beforeWriteContent',array($uri)); + + } catch (DAV\Exception\NotFound $e) { + + // It didn't, lets create it + $this->server->createFile($uri, fopen('php://memory', 'r')); + $newFile = true; + + } + + $this->lockNode($uri, $lockInfo); + + $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $response->setHeader('Lock-Token', 'token . '>'); + $response->setStatus($newFile ? 201 : 200); + $response->setBody($this->generateLockResponse($lockInfo)); + + // Returning false will interrupt the event chain and mark this method + // as 'handled'. + return false; + + } + + /** + * Unlocks a uri + * + * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header + * The server should return 204 (No content) on success + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpUnlock(RequestInterface $request, ResponseInterface $response) { + + $lockToken = $request->getHeader('Lock-Token'); + + // If the locktoken header is not supplied, we need to throw a bad request exception + if (!$lockToken) throw new DAV\Exception\BadRequest('No lock token was supplied'); + + $path = $request->getPath(); + $locks = $this->getLocks($path); + + // Windows sometimes forgets to include < and > in the Lock-Token + // header + if ($lockToken[0] !== '<') $lockToken = '<' . $lockToken . '>'; + + foreach ($locks as $lock) { + + if ('token . '>' == $lockToken) { + + $this->unlockNode($path, $lock); + $response->setHeader('Content-Length', '0'); + $response->setStatus(204); + + // Returning false will break the method chain, and mark the + // method as 'handled'. + return false; + + } + + } + + // If we got here, it means the locktoken was invalid + throw new DAV\Exception\LockTokenMatchesRequestUri(); + + } + + /** + * This method is called after a node is deleted. + * + * We use this event to clean up any locks that still exist on the node. + * + * @param string $path + * @return void + */ + function afterUnbind($path) { + + $locks = $this->getLocks($path, $includeChildren = true); + foreach ($locks as $lock) { + $this->unlockNode($path, $lock); + } + + } + + /** + * Locks a uri + * + * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored + * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function lockNode($uri, LockInfo $lockInfo) { + + if (!$this->server->emit('beforeLock', [$uri, $lockInfo])) return; + return $this->locksBackend->lock($uri, $lockInfo); + + } + + /** + * Unlocks a uri + * + * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function unlockNode($uri, LockInfo $lockInfo) { + + if (!$this->server->emit('beforeUnlock', [$uri, $lockInfo])) return; + return $this->locksBackend->unlock($uri, $lockInfo); + + } + + + /** + * Returns the contents of the HTTP Timeout header. + * + * The method formats the header into an integer. + * + * @return int + */ + function getTimeoutHeader() { + + $header = $this->server->httpRequest->getHeader('Timeout'); + + if ($header) { + + if (stripos($header, 'second-') === 0) $header = (int)(substr($header, 7)); + elseif (stripos($header, 'infinite') === 0) $header = LockInfo::TIMEOUT_INFINITE; + else throw new DAV\Exception\BadRequest('Invalid HTTP timeout header'); + + } else { + + $header = 0; + + } + + return $header; + + } + + /** + * Generates the response for successful LOCK requests + * + * @param LockInfo $lockInfo + * @return string + */ + protected function generateLockResponse(LockInfo $lockInfo) { + + return $this->server->xml->write('{DAV:}prop', [ + '{DAV:}lockdiscovery' => + new DAV\Xml\Property\LockDiscovery([$lockInfo]) + ]); + } + + /** + * The validateTokens event is triggered before every request. + * + * It's a moment where this plugin can check all the supplied lock tokens + * in the If: header, and check if they are valid. + * + * In addition, it will also ensure that it checks any missing lokens that + * must be present in the request, and reject requests without the proper + * tokens. + * + * @param RequestInterface $request + * @param mixed $conditions + * @return void + */ + function validateTokens(RequestInterface $request, &$conditions) { + + // First we need to gather a list of locks that must be satisfied. + $mustLocks = []; + $method = $request->getMethod(); + + // Methods not in that list are operations that doesn't alter any + // resources, and we don't need to check the lock-states for. + switch ($method) { + + case 'DELETE' : + $mustLocks = array_merge($mustLocks, $this->getLocks( + $request->getPath(), + true + )); + break; + case 'MKCOL' : + case 'MKCALENDAR' : + case 'PROPPATCH' : + case 'PUT' : + case 'PATCH' : + $mustLocks = array_merge($mustLocks, $this->getLocks( + $request->getPath(), + false + )); + break; + case 'MOVE' : + $mustLocks = array_merge($mustLocks, $this->getLocks( + $request->getPath(), + true + )); + $mustLocks = array_merge($mustLocks, $this->getLocks( + $this->server->calculateUri($request->getHeader('Destination')), + false + )); + break; + case 'COPY' : + $mustLocks = array_merge($mustLocks, $this->getLocks( + $this->server->calculateUri($request->getHeader('Destination')), + false + )); + break; + case 'LOCK' : + //Temporary measure.. figure out later why this is needed + // Here we basically ignore all incoming tokens... + foreach ($conditions as $ii => $condition) { + foreach ($condition['tokens'] as $jj => $token) { + $conditions[$ii]['tokens'][$jj]['validToken'] = true; + } + } + return; + + } + + // It's possible that there's identical locks, because of shared + // parents. We're removing the duplicates here. + $tmp = []; + foreach ($mustLocks as $lock) $tmp[$lock->token] = $lock; + $mustLocks = array_values($tmp); + + foreach ($conditions as $kk => $condition) { + + foreach ($condition['tokens'] as $ii => $token) { + + // Lock tokens always start with opaquelocktoken: + if (substr($token['token'], 0, 16) !== 'opaquelocktoken:') { + continue; + } + + $checkToken = substr($token['token'], 16); + // Looping through our list with locks. + foreach ($mustLocks as $jj => $mustLock) { + + if ($mustLock->token == $checkToken) { + + // We have a match! + // Removing this one from mustlocks + unset($mustLocks[$jj]); + + // Marking the condition as valid. + $conditions[$kk]['tokens'][$ii]['validToken'] = true; + + // Advancing to the next token + continue 2; + + } + + } + + // If we got here, it means that there was a + // lock-token, but it was not in 'mustLocks'. + // + // This is an edge-case, as it could mean that token + // was specified with a url that was not 'required' to + // check. So we're doing one extra lookup to make sure + // we really don't know this token. + // + // This also gets triggered when the user specified a + // lock-token that was expired. + $oddLocks = $this->getLocks($condition['uri']); + foreach ($oddLocks as $oddLock) { + + if ($oddLock->token === $checkToken) { + + // We have a hit! + $conditions[$kk]['tokens'][$ii]['validToken'] = true; + continue 2; + + } + } + + // If we get all the way here, the lock-token was + // really unknown. + + + } + + } + + // If there's any locks left in the 'mustLocks' array, it means that + // the resource was locked and we must block it. + if ($mustLocks) { + + throw new DAV\Exception\Locked(reset($mustLocks)); + + } + + } + + /** + * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object + * + * @param string $body + * @return LockInfo + */ + protected function parseLockRequest($body) { + + $result = $this->server->xml->expect( + '{DAV:}lockinfo', + $body + ); + + $lockInfo = new LockInfo(); + + $lockInfo->owner = $result->owner; + $lockInfo->token = DAV\UUIDUtil::getUUID(); + $lockInfo->scope = $result->scope; + + return $lockInfo; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'The locks plugin turns this server into a class-2 WebDAV server and adds support for LOCK and UNLOCK', + 'link' => 'http://sabre.io/dav/locks/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php new file mode 100644 index 00000000000..042e14bca31 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php @@ -0,0 +1,72 @@ +resourceType = $resourceType; + parent::__construct($mutations); + + } + + /** + * Returns the resourcetype of the new collection. + * + * @return string[] + */ + function getResourceType() { + + return $this->resourceType; + + } + + /** + * Returns true or false if the MKCOL operation has at least the specified + * resource type. + * + * If the resourcetype is specified as an array, all resourcetypes are + * checked. + * + * @param string|string[] $resourceType + * @return bool + */ + function hasResourceType($resourceType) { + + return count(array_diff((array)$resourceType, $this->resourceType)) === 0; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php new file mode 100644 index 00000000000..dc923ad8522 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php @@ -0,0 +1,86 @@ +server = $server; + $this->server->on('method:GET', [$this, 'httpGet'], 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?mount + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $response) { + + $queryParams = $request->getQueryParameters(); + if (!array_key_exists('mount', $queryParams)) return; + + $currentUri = $request->getAbsoluteUrl(); + + // Stripping off everything after the ? + list($currentUri) = explode('?', $currentUri); + + $this->davMount($response, $currentUri); + + // Returning false to break the event chain + return false; + + } + + /** + * Generates the davmount response + * + * @param ResponseInterface $response + * @param string $uri absolute uri + * @return void + */ + function davMount(ResponseInterface $response, $uri) { + + $response->setStatus(200); + $response->setHeader('Content-Type', 'application/davmount+xml'); + ob_start(); + echo '', "\n"; + echo "\n"; + echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; + echo ""; + $response->setBody(ob_get_clean()); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php new file mode 100644 index 00000000000..ef6eea18e06 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php @@ -0,0 +1,54 @@ +addPlugin($patchPlugin); + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Plugin extends DAV\ServerPlugin { + + const RANGE_APPEND = 1; + const RANGE_START = 2; + const RANGE_END = 3; + + /** + * Reference to server + * + * @var DAV\Server + */ + protected $server; + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + $this->server = $server; + $server->on('method:PATCH', [$this, 'httpPatch']); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'partialupdate'; + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * We claim to support PATCH method (partirl update) if and only if + * - the node exist + * - the node implements our partial update interface + * + * @param string $uri + * @return array + */ + function getHTTPMethods($uri) { + + $tree = $this->server->tree; + + if ($tree->nodeExists($uri)) { + $node = $tree->getNodeForPath($uri); + if ($node instanceof IPatchSupport) { + return ['PATCH']; + } + } + return []; + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * @return array + */ + function getFeatures() { + + return ['sabredav-partialupdate']; + + } + + /** + * Patch an uri + * + * The WebDAV patch request can be used to modify only a part of an + * existing resource. If the resource does not exist yet and the first + * offset is not 0, the request fails + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function httpPatch(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + + // Get the node. Will throw a 404 if not found + $node = $this->server->tree->getNodeForPath($path); + if (!$node instanceof IPatchSupport) { + throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); + } + + $range = $this->getHTTPUpdateRange($request); + + if (!$range) { + throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); + } + + $contentType = strtolower( + $request->getHeader('Content-Type') + ); + + if ($contentType != 'application/x-sabredav-partialupdate') { + throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); + } + + $len = $this->server->httpRequest->getHeader('Content-Length'); + if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required'); + + switch ($range[0]) { + case self::RANGE_START : + // Calculate the end-range if it doesn't exist. + if (!$range[2]) { + $range[2] = $range[1] + $len - 1; + } else { + if ($range[2] < $range[1]) { + throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); + } + if ($range[2] - $range[1] + 1 != $len) { + throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); + } + } + break; + } + + if (!$this->server->emit('beforeWriteContent', [$path, $node, null])) + return; + + $body = $this->server->httpRequest->getBody(); + + + $etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null); + + $this->server->emit('afterWriteContent', [$path, $node]); + + $response->setHeader('Content-Length', '0'); + if ($etag) $response->setHeader('ETag', $etag); + $response->setStatus(204); + + // Breaks the event chain + return false; + + } + + /** + * Returns the HTTP custom range update header + * + * This method returns null if there is no well-formed HTTP range request + * header. It returns array(1) if it was an append request, array(2, + * $start, $end) if it's a start and end range, lastly it's array(3, + * $endoffset) if the offset was negative, and should be calculated from + * the end of the file. + * + * Examples: + * + * null - invalid + * [1] - append + * [2,10,15] - update bytes 10, 11, 12, 13, 14, 15 + * [2,10,null] - update bytes 10 until the end of the patch body + * [3,-5] - update from 5 bytes from the end of the file. + * + * @param RequestInterface $request + * @return array|null + */ + function getHTTPUpdateRange(RequestInterface $request) { + + $range = $request->getHeader('X-Update-Range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i', $range, $matches)) return null; + + if ($matches[1] === 'append') { + return [self::RANGE_APPEND]; + } elseif (strlen($matches[2]) > 0) { + return [self::RANGE_START, $matches[2], $matches[3] ?: null]; + } else { + return [self::RANGE_END, $matches[4]]; + } + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php new file mode 100644 index 00000000000..0940a1ce29a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php @@ -0,0 +1,347 @@ +path = $path; + $this->properties = $properties; + $this->depth = $depth; + $this->requestType = $requestType; + + if ($requestType === self::ALLPROPS) { + $this->properties = [ + '{DAV:}getlastmodified', + '{DAV:}getcontentlength', + '{DAV:}resourcetype', + '{DAV:}quota-used-bytes', + '{DAV:}quota-available-bytes', + '{DAV:}getetag', + '{DAV:}getcontenttype', + ]; + } + + foreach ($this->properties as $propertyName) { + + // Seeding properties with 404's. + $this->result[$propertyName] = [404, null]; + + } + $this->itemsLeft = count($this->result); + + } + + /** + * Handles a specific property. + * + * This method checks whether the specified property was requested in this + * PROPFIND request, and if so, it will call the callback and use the + * return value for it's value. + * + * Example: + * + * $propFind->handle('{DAV:}displayname', function() { + * return 'hello'; + * }); + * + * Note that handle will only work the first time. If null is returned, the + * value is ignored. + * + * It's also possible to not pass a callback, but immediately pass a value + * + * @param string $propertyName + * @param mixed $valueOrCallBack + * @return void + */ + function handle($propertyName, $valueOrCallBack) { + + if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { + if (is_callable($valueOrCallBack)) { + $value = $valueOrCallBack(); + } else { + $value = $valueOrCallBack; + } + if (!is_null($value)) { + $this->itemsLeft--; + $this->result[$propertyName] = [200, $value]; + } + } + + } + + /** + * Sets the value of the property + * + * If status is not supplied, the status will default to 200 for non-null + * properties, and 404 for null properties. + * + * @param string $propertyName + * @param mixed $value + * @param int $status + * @return void + */ + function set($propertyName, $value, $status = null) { + + if (is_null($status)) { + $status = is_null($value) ? 404 : 200; + } + // If this is an ALLPROPS request and the property is + // unknown, add it to the result; else ignore it: + if (!isset($this->result[$propertyName])) { + if ($this->requestType === self::ALLPROPS) { + $this->result[$propertyName] = [$status, $value]; + } + return; + } + if ($status !== 404 && $this->result[$propertyName][0] === 404) { + $this->itemsLeft--; + } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) { + $this->itemsLeft++; + } + $this->result[$propertyName] = [$status, $value]; + + } + + /** + * Returns the current value for a property. + * + * @param string $propertyName + * @return mixed + */ + function get($propertyName) { + + return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; + + } + + /** + * Returns the current status code for a property name. + * + * If the property does not appear in the list of requested properties, + * null will be returned. + * + * @param string $propertyName + * @return int|null + */ + function getStatus($propertyName) { + + return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null; + + } + + /** + * Updates the path for this PROPFIND. + * + * @param string $path + * @return void + */ + function setPath($path) { + + $this->path = $path; + + } + + /** + * Returns the path this PROPFIND request is for. + * + * @return string + */ + function getPath() { + + return $this->path; + + } + + /** + * Returns the depth of this propfind request. + * + * @return int + */ + function getDepth() { + + return $this->depth; + + } + + /** + * Updates the depth of this propfind request. + * + * @param int $depth + * @return void + */ + function setDepth($depth) { + + $this->depth = $depth; + + } + + /** + * Returns all propertynames that have a 404 status, and thus don't have a + * value yet. + * + * @return array + */ + function get404Properties() { + + if ($this->itemsLeft === 0) { + return []; + } + $result = []; + foreach ($this->result as $propertyName => $stuff) { + if ($stuff[0] === 404) { + $result[] = $propertyName; + } + } + return $result; + + } + + /** + * Returns the full list of requested properties. + * + * This returns just their names, not a status or value. + * + * @return array + */ + function getRequestedProperties() { + + return $this->properties; + + } + + /** + * Returns true if this was an '{DAV:}allprops' request. + * + * @return bool + */ + function isAllProps() { + + return $this->requestType === self::ALLPROPS; + + } + + /** + * Returns a result array that's often used in multistatus responses. + * + * The array uses status codes as keys, and property names and value pairs + * as the value of the top array.. such as : + * + * [ + * 200 => [ '{DAV:}displayname' => 'foo' ], + * ] + * + * @return array + */ + function getResultForMultiStatus() { + + $r = [ + 200 => [], + 404 => [], + ]; + foreach ($this->result as $propertyName => $info) { + if (!isset($r[$info[0]])) { + $r[$info[0]] = [$propertyName => $info[1]]; + } else { + $r[$info[0]][$propertyName] = $info[1]; + } + } + // Removing the 404's for multi-status requests. + if ($this->requestType === self::ALLPROPS) unset($r[404]); + return $r; + + } + + /** + * The path that we're fetching properties for. + * + * @var string + */ + protected $path; + + /** + * The Depth of the request. + * + * 0 means only the current item. 1 means the current item + its children. + * It can also be DEPTH_INFINITY if this is enabled in the server. + * + * @var int + */ + protected $depth = 0; + + /** + * The type of request. See the TYPE constants + */ + protected $requestType; + + /** + * A list of requested properties + * + * @var array + */ + protected $properties = []; + + /** + * The result of the operation. + * + * The keys in this array are property names. + * The values are an array with two elements: the http status code and then + * optionally a value. + * + * Example: + * + * [ + * "{DAV:}owner" : [404], + * "{DAV:}displayname" : [200, "Admin"] + * ] + * + * @var array + */ + protected $result = []; + + /** + * This is used as an internal counter for the number of properties that do + * not yet have a value. + * + * @var int + */ + protected $itemsLeft; + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php new file mode 100644 index 00000000000..6d599dacc4a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php @@ -0,0 +1,373 @@ +mutations = $mutations; + + } + + /** + * Call this function if you wish to handle updating certain properties. + * For instance, your class may be responsible for handling updates for the + * {DAV:}displayname property. + * + * In that case, call this method with the first argument + * "{DAV:}displayname" and a second argument that's a method that does the + * actual updating. + * + * It's possible to specify more than one property as an array. + * + * The callback must return a boolean or an it. If the result is true, the + * operation was considered successful. If it's false, it's consided + * failed. + * + * If the result is an integer, we'll use that integer as the http status + * code associated with the operation. + * + * @param string|string[] $properties + * @param callable $callback + * @return void + */ + function handle($properties, callable $callback) { + + $usedProperties = []; + foreach ((array)$properties as $propertyName) { + + if (array_key_exists($propertyName, $this->mutations) && !isset($this->result[$propertyName])) { + + $usedProperties[] = $propertyName; + // HTTP Accepted + $this->result[$propertyName] = 202; + } + + } + + // Only registering if there's any unhandled properties. + if (!$usedProperties) { + return; + } + $this->propertyUpdateCallbacks[] = [ + // If the original argument to this method was a string, we need + // to also make sure that it stays that way, so the commit function + // knows how to format the arguments to the callback. + is_string($properties) ? $properties : $usedProperties, + $callback + ]; + + } + + /** + * Call this function if you wish to handle _all_ properties that haven't + * been handled by anything else yet. Note that you effectively claim with + * this that you promise to process _all_ properties that are coming in. + * + * @param callable $callback + * @return void + */ + function handleRemaining(callable $callback) { + + $properties = $this->getRemainingMutations(); + if (!$properties) { + // Nothing to do, don't register callback + return; + } + + foreach ($properties as $propertyName) { + // HTTP Accepted + $this->result[$propertyName] = 202; + + $this->propertyUpdateCallbacks[] = [ + $properties, + $callback + ]; + } + + } + + /** + * Sets the result code for one or more properties. + * + * @param string|string[] $properties + * @param int $resultCode + * @return void + */ + function setResultCode($properties, $resultCode) { + + foreach ((array)$properties as $propertyName) { + $this->result[$propertyName] = $resultCode; + } + + if ($resultCode >= 400) { + $this->failed = true; + } + + } + + /** + * Sets the result code for all properties that did not have a result yet. + * + * @param int $resultCode + * @return void + */ + function setRemainingResultCode($resultCode) { + + $this->setResultCode( + $this->getRemainingMutations(), + $resultCode + ); + + } + + /** + * Returns the list of properties that don't have a result code yet. + * + * This method returns a list of property names, but not its values. + * + * @return string[] + */ + function getRemainingMutations() { + + $remaining = []; + foreach ($this->mutations as $propertyName => $propValue) { + if (!isset($this->result[$propertyName])) { + $remaining[] = $propertyName; + } + } + + return $remaining; + + } + + /** + * Returns the list of properties that don't have a result code yet. + * + * This method returns list of properties and their values. + * + * @return array + */ + function getRemainingValues() { + + $remaining = []; + foreach ($this->mutations as $propertyName => $propValue) { + if (!isset($this->result[$propertyName])) { + $remaining[$propertyName] = $propValue; + } + } + + return $remaining; + + } + + /** + * Performs the actual update, and calls all callbacks. + * + * This method returns true or false depending on if the operation was + * successful. + * + * @return bool + */ + function commit() { + + // First we validate if every property has a handler + foreach ($this->mutations as $propertyName => $value) { + + if (!isset($this->result[$propertyName])) { + $this->failed = true; + $this->result[$propertyName] = 403; + } + + } + + foreach ($this->propertyUpdateCallbacks as $callbackInfo) { + + if ($this->failed) { + break; + } + if (is_string($callbackInfo[0])) { + $this->doCallbackSingleProp($callbackInfo[0], $callbackInfo[1]); + } else { + $this->doCallbackMultiProp($callbackInfo[0], $callbackInfo[1]); + } + + } + + /** + * If anywhere in this operation updating a property failed, we must + * update all other properties accordingly. + */ + if ($this->failed) { + + foreach ($this->result as $propertyName => $status) { + if ($status === 202) { + // Failed dependency + $this->result[$propertyName] = 424; + } + } + + } + + return !$this->failed; + + } + + /** + * Executes a property callback with the single-property syntax. + * + * @param string $propertyName + * @param callable $callback + * @return void + */ + private function doCallBackSingleProp($propertyName, callable $callback) { + + $result = $callback($this->mutations[$propertyName]); + if (is_bool($result)) { + if ($result) { + if (is_null($this->mutations[$propertyName])) { + // Delete + $result = 204; + } else { + // Update + $result = 200; + } + } else { + // Fail + $result = 403; + } + } + if (!is_int($result)) { + throw new UnexpectedValueException('A callback sent to handle() did not return an int or a bool'); + } + $this->result[$propertyName] = $result; + if ($result >= 400) { + $this->failed = true; + } + + } + + /** + * Executes a property callback with the multi-property syntax. + * + * @param array $propertyList + * @param callable $callback + * @return void + */ + private function doCallBackMultiProp(array $propertyList, callable $callback) { + + $argument = []; + foreach ($propertyList as $propertyName) { + $argument[$propertyName] = $this->mutations[$propertyName]; + } + + $result = $callback($argument); + + if (is_array($result)) { + foreach ($propertyList as $propertyName) { + if (!isset($result[$propertyName])) { + $resultCode = 500; + } else { + $resultCode = $result[$propertyName]; + } + if ($resultCode >= 400) { + $this->failed = true; + } + $this->result[$propertyName] = $resultCode; + + } + } elseif ($result === true) { + + // Success + foreach ($argument as $propertyName => $propertyValue) { + $this->result[$propertyName] = is_null($propertyValue) ? 204 : 200; + } + + } elseif ($result === false) { + // Fail :( + $this->failed = true; + foreach ($propertyList as $propertyName) { + $this->result[$propertyName] = 403; + } + } else { + throw new UnexpectedValueException('A callback sent to handle() did not return an array or a bool'); + } + + } + + /** + * Returns the result of the operation. + * + * @return array + */ + function getResult() { + + return $this->result; + + } + + /** + * Returns the full list of mutations + * + * @return array + */ + function getMutations() { + + return $this->mutations; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php new file mode 100644 index 00000000000..b15d7fef944 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php @@ -0,0 +1,80 @@ +isAllProps(). + * + * @param string $path + * @param PropFind $propFind + * @return void + */ + function propFind($path, PropFind $propFind); + + /** + * Updates properties for a path + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * Usually you would want to call 'handleRemaining' on this object, to get; + * a list of all properties that need to be stored. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch); + + /** + * This method is called after a node is deleted. + * + * This allows a backend to clean up all associated properties. + * + * The delete method will get called once for the deletion of an entire + * tree. + * + * @param string $path + * @return void + */ + function delete($path); + + /** + * This method is called after a successful MOVE + * + * This should be used to migrate all properties from one path to another. + * Note that entire collections may be moved, so ensure that all properties + * for children are also moved along. + * + * @param string $source + * @param string $destination + * @return void + */ + function move($source, $destination); + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php new file mode 100644 index 00000000000..6f3f1feaf57 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php @@ -0,0 +1,246 @@ +pdo = $pdo; + + } + + /** + * Fetches properties for a path. + * + * This method received a PropFind object, which contains all the + * information about the properties that need to be fetched. + * + * Usually you would just want to call 'get404Properties' on this object, + * as this will give you the _exact_ list of properties that need to be + * fetched, and haven't yet. + * + * However, you can also support the 'allprops' property here. In that + * case, you should check for $propFind->isAllProps(). + * + * @param string $path + * @param PropFind $propFind + * @return void + */ + function propFind($path, PropFind $propFind) { + + if (!$propFind->isAllProps() && count($propFind->get404Properties()) === 0) { + return; + } + + $query = 'SELECT name, value, valuetype FROM ' . $this->tableName . ' WHERE path = ?'; + $stmt = $this->pdo->prepare($query); + $stmt->execute([$path]); + + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if (gettype($row['value']) === 'resource') { + $row['value'] = stream_get_contents($row['value']); + } + switch ($row['valuetype']) { + case null : + case self::VT_STRING : + $propFind->set($row['name'], $row['value']); + break; + case self::VT_XML : + $propFind->set($row['name'], new Complex($row['value'])); + break; + case self::VT_OBJECT : + $propFind->set($row['name'], unserialize($row['value'])); + break; + } + } + + } + + /** + * Updates properties for a path + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * Usually you would want to call 'handleRemaining' on this object, to get; + * a list of all properties that need to be stored. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch) { + + $propPatch->handleRemaining(function($properties) use ($path) { + + + if ($this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'pgsql') { + + $updateSql = <<tableName} (path, name, valuetype, value) +VALUES (:path, :name, :valuetype, :value) +ON CONFLICT (path, name) +DO UPDATE SET valuetype = :valuetype, value = :value +SQL; + + + } else { + $updateSql = <<tableName} (path, name, valuetype, value) +VALUES (:path, :name, :valuetype, :value) +SQL; + + } + + $updateStmt = $this->pdo->prepare($updateSql); + $deleteStmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? AND name = ?"); + + foreach ($properties as $name => $value) { + + if (!is_null($value)) { + if (is_scalar($value)) { + $valueType = self::VT_STRING; + } elseif ($value instanceof Complex) { + $valueType = self::VT_XML; + $value = $value->getXml(); + } else { + $valueType = self::VT_OBJECT; + $value = serialize($value); + } + + $updateStmt->bindParam('path', $path, \PDO::PARAM_STR); + $updateStmt->bindParam('name', $name, \PDO::PARAM_STR); + $updateStmt->bindParam('valuetype', $valueType, \PDO::PARAM_INT); + $updateStmt->bindParam('value', $value, \PDO::PARAM_LOB); + + $updateStmt->execute(); + + } else { + $deleteStmt->execute([$path, $name]); + } + + } + + return true; + + }); + + } + + /** + * This method is called after a node is deleted. + * + * This allows a backend to clean up all associated properties. + * + * The delete method will get called once for the deletion of an entire + * tree. + * + * @param string $path + * @return void + */ + function delete($path) { + + $stmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? OR path LIKE ? ESCAPE '='"); + $childPath = strtr( + $path, + [ + '=' => '==', + '%' => '=%', + '_' => '=_' + ] + ) . '/%'; + + $stmt->execute([$path, $childPath]); + + } + + /** + * This method is called after a successful MOVE + * + * This should be used to migrate all properties from one path to another. + * Note that entire collections may be moved, so ensure that all properties + * for children are also moved along. + * + * @param string $source + * @param string $destination + * @return void + */ + function move($source, $destination) { + + // I don't know a way to write this all in a single sql query that's + // also compatible across db engines, so we're letting PHP do all the + // updates. Much slower, but it should still be pretty fast in most + // cases. + $select = $this->pdo->prepare('SELECT id, path FROM ' . $this->tableName . ' WHERE path = ? OR path LIKE ?'); + $select->execute([$source, $source . '/%']); + + $update = $this->pdo->prepare('UPDATE ' . $this->tableName . ' SET path = ? WHERE id = ?'); + while ($row = $select->fetch(\PDO::FETCH_ASSOC)) { + + // Sanity check. SQL may select too many records, such as records + // with different cases. + if ($row['path'] !== $source && strpos($row['path'], $source . '/') !== 0) continue; + + $trailingPart = substr($row['path'], strlen($source) + 1); + $newPath = $destination; + if ($trailingPart) { + $newPath .= '/' . $trailingPart; + } + $update->execute([$newPath, $row['id']]); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php new file mode 100644 index 00000000000..a66a14113fb --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php @@ -0,0 +1,185 @@ +backend = $backend; + + } + + /** + * This initializes the plugin. + * + * This function is called by Sabre\DAV\Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param Server $server + * @return void + */ + function initialize(Server $server) { + + $server->on('propFind', [$this, 'propFind'], 130); + $server->on('propPatch', [$this, 'propPatch'], 300); + $server->on('afterMove', [$this, 'afterMove']); + $server->on('afterUnbind', [$this, 'afterUnbind']); + + } + + /** + * Called during PROPFIND operations. + * + * If there's any requested properties that don't have a value yet, this + * plugin will look in the property storage backend to find them. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + $path = $propFind->getPath(); + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($path)) return; + $this->backend->propFind($propFind->getPath(), $propFind); + + } + + /** + * Called during PROPPATCH operations + * + * If there's any updated properties that haven't been stored, the + * propertystorage backend can handle it. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch) { + + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($path)) return; + $this->backend->propPatch($path, $propPatch); + + } + + /** + * Called after a node is deleted. + * + * This allows the backend to clean up any properties still in the + * database. + * + * @param string $path + * @return void + */ + function afterUnbind($path) { + + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($path)) return; + $this->backend->delete($path); + + } + + /** + * Called after a node is moved. + * + * This allows the backend to move all the associated properties. + * + * @param string $source + * @param string $destination + * @return void + */ + function afterMove($source, $destination) { + + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($source)) return; + // If the destination is filtered, afterUnbind will handle cleaning up + // the properties. + if ($pathFilter && !$pathFilter($destination)) return; + + $this->backend->move($source, $destination); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using \Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'property-storage'; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.', + 'link' => 'http://sabre.io/dav/property-storage/', + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php new file mode 100644 index 00000000000..6805ec0b013 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php @@ -0,0 +1,1687 @@ + '{DAV:}collection', + ]; + + /** + * This property allows the usage of Depth: infinity on PROPFIND requests. + * + * By default Depth: infinity is treated as Depth: 1. Allowing Depth: + * infinity is potentially risky, as it allows a single client to do a full + * index of the webdav server, which is an easy DoS attack vector. + * + * Only turn this on if you know what you're doing. + * + * @var bool + */ + public $enablePropfindDepthInfinity = false; + + /** + * Reference to the XML utility object. + * + * @var Xml\Service + */ + public $xml; + + /** + * If this setting is turned off, SabreDAV's version number will be hidden + * from various places. + * + * Some people feel this is a good security measure. + * + * @var bool + */ + static $exposeVersion = true; + + /** + * Sets up the server + * + * If a Sabre\DAV\Tree object is passed as an argument, it will + * use it as the directory tree. If a Sabre\DAV\INode is passed, it + * will create a Sabre\DAV\Tree and use the node as the root. + * + * If nothing is passed, a Sabre\DAV\SimpleCollection is created in + * a Sabre\DAV\Tree. + * + * If an array is passed, we automatically create a root node, and use + * the nodes in the array as top-level children. + * + * @param Tree|INode|array|null $treeOrNode The tree object + */ + function __construct($treeOrNode = null) { + + if ($treeOrNode instanceof Tree) { + $this->tree = $treeOrNode; + } elseif ($treeOrNode instanceof INode) { + $this->tree = new Tree($treeOrNode); + } elseif (is_array($treeOrNode)) { + + // If it's an array, a list of nodes was passed, and we need to + // create the root node. + foreach ($treeOrNode as $node) { + if (!($node instanceof INode)) { + throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre\\DAV\\INode'); + } + } + + $root = new SimpleCollection('root', $treeOrNode); + $this->tree = new Tree($root); + + } elseif (is_null($treeOrNode)) { + $root = new SimpleCollection('root'); + $this->tree = new Tree($root); + } else { + throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null'); + } + + $this->xml = new Xml\Service(); + $this->sapi = new HTTP\Sapi(); + $this->httpResponse = new HTTP\Response(); + $this->httpRequest = $this->sapi->getRequest(); + $this->addPlugin(new CorePlugin()); + + } + + /** + * Starts the DAV Server + * + * @return void + */ + function exec() { + + try { + + // If nginx (pre-1.2) is used as a proxy server, and SabreDAV as an + // origin, we must make sure we send back HTTP/1.0 if this was + // requested. + // This is mainly because nginx doesn't support Chunked Transfer + // Encoding, and this forces the webserver SabreDAV is running on, + // to buffer entire responses to calculate Content-Length. + $this->httpResponse->setHTTPVersion($this->httpRequest->getHTTPVersion()); + + // Setting the base url + $this->httpRequest->setBaseUrl($this->getBaseUri()); + $this->invokeMethod($this->httpRequest, $this->httpResponse); + + } catch (\Exception $e) { + + try { + $this->emit('exception', [$e]); + } catch (\Exception $ignore) { + } + $DOM = new \DOMDocument('1.0', 'utf-8'); + $DOM->formatOutput = true; + + $error = $DOM->createElementNS('DAV:', 'd:error'); + $error->setAttribute('xmlns:s', self::NS_SABREDAV); + $DOM->appendChild($error); + + $h = function($v) { + + return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8'); + + }; + + if (self::$exposeVersion) { + $error->appendChild($DOM->createElement('s:sabredav-version', $h(Version::VERSION))); + } + + $error->appendChild($DOM->createElement('s:exception', $h(get_class($e)))); + $error->appendChild($DOM->createElement('s:message', $h($e->getMessage()))); + if ($this->debugExceptions) { + $error->appendChild($DOM->createElement('s:file', $h($e->getFile()))); + $error->appendChild($DOM->createElement('s:line', $h($e->getLine()))); + $error->appendChild($DOM->createElement('s:code', $h($e->getCode()))); + $error->appendChild($DOM->createElement('s:stacktrace', $h($e->getTraceAsString()))); + } + + if ($this->debugExceptions) { + $previous = $e; + while ($previous = $previous->getPrevious()) { + $xPrevious = $DOM->createElement('s:previous-exception'); + $xPrevious->appendChild($DOM->createElement('s:exception', $h(get_class($previous)))); + $xPrevious->appendChild($DOM->createElement('s:message', $h($previous->getMessage()))); + $xPrevious->appendChild($DOM->createElement('s:file', $h($previous->getFile()))); + $xPrevious->appendChild($DOM->createElement('s:line', $h($previous->getLine()))); + $xPrevious->appendChild($DOM->createElement('s:code', $h($previous->getCode()))); + $xPrevious->appendChild($DOM->createElement('s:stacktrace', $h($previous->getTraceAsString()))); + $error->appendChild($xPrevious); + } + } + + + if ($e instanceof Exception) { + + $httpCode = $e->getHTTPCode(); + $e->serialize($this, $error); + $headers = $e->getHTTPHeaders($this); + + } else { + + $httpCode = 500; + $headers = []; + + } + $headers['Content-Type'] = 'application/xml; charset=utf-8'; + + $this->httpResponse->setStatus($httpCode); + $this->httpResponse->setHeaders($headers); + $this->httpResponse->setBody($DOM->saveXML()); + $this->sapi->sendResponse($this->httpResponse); + + } + + } + + /** + * Sets the base server uri + * + * @param string $uri + * @return void + */ + function setBaseUri($uri) { + + // If the baseUri does not end with a slash, we must add it + if ($uri[strlen($uri) - 1] !== '/') + $uri .= '/'; + + $this->baseUri = $uri; + + } + + /** + * Returns the base responding uri + * + * @return string + */ + function getBaseUri() { + + if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); + return $this->baseUri; + + } + + /** + * This method attempts to detect the base uri. + * Only the PATH_INFO variable is considered. + * + * If this variable is not set, the root (/) is assumed. + * + * @return string + */ + function guessBaseUri() { + + $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); + $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); + + // If PATH_INFO is found, we can assume it's accurate. + if (!empty($pathInfo)) { + + // We need to make sure we ignore the QUERY_STRING part + if ($pos = strpos($uri, '?')) + $uri = substr($uri, 0, $pos); + + // PATH_INFO is only set for urls, such as: /example.php/path + // in that case PATH_INFO contains '/path'. + // Note that REQUEST_URI is percent encoded, while PATH_INFO is + // not, Therefore they are only comparable if we first decode + // REQUEST_INFO as well. + $decodedUri = URLUtil::decodePath($uri); + + // A simple sanity check: + if (substr($decodedUri, strlen($decodedUri) - strlen($pathInfo)) === $pathInfo) { + $baseUri = substr($decodedUri, 0, strlen($decodedUri) - strlen($pathInfo)); + return rtrim($baseUri, '/') . '/'; + } + + throw new Exception('The REQUEST_URI (' . $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); + + } + + // The last fallback is that we're just going to assume the server root. + return '/'; + + } + + /** + * Adds a plugin to the server + * + * For more information, console the documentation of Sabre\DAV\ServerPlugin + * + * @param ServerPlugin $plugin + * @return void + */ + function addPlugin(ServerPlugin $plugin) { + + $this->plugins[$plugin->getPluginName()] = $plugin; + $plugin->initialize($this); + + } + + /** + * Returns an initialized plugin by it's name. + * + * This function returns null if the plugin was not found. + * + * @param string $name + * @return ServerPlugin + */ + function getPlugin($name) { + + if (isset($this->plugins[$name])) + return $this->plugins[$name]; + + return null; + + } + + /** + * Returns all plugins + * + * @return array + */ + function getPlugins() { + + return $this->plugins; + + } + + /** + * Returns the PSR-3 logger object. + * + * @return LoggerInterface + */ + function getLogger() { + + if (!$this->logger) { + $this->logger = new NullLogger(); + } + return $this->logger; + + } + + /** + * Handles a http request, and execute a method based on its name + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param bool $sendResponse Whether to send the HTTP response to the DAV client. + * @return void + */ + function invokeMethod(RequestInterface $request, ResponseInterface $response, $sendResponse = true) { + + $method = $request->getMethod(); + + if (!$this->emit('beforeMethod:' . $method, [$request, $response])) return; + if (!$this->emit('beforeMethod', [$request, $response])) return; + + if (self::$exposeVersion) { + $response->setHeader('X-Sabre-Version', Version::VERSION); + } + + $this->transactionType = strtolower($method); + + if (!$this->checkPreconditions($request, $response)) { + $this->sapi->sendResponse($response); + return; + } + + if ($this->emit('method:' . $method, [$request, $response])) { + if ($this->emit('method', [$request, $response])) { + $exMessage = "There was no plugin in the system that was willing to handle this " . $method . " method."; + if ($method === "GET") { + $exMessage .= " Enable the Browser plugin to get a better result here."; + } + + // Unsupported method + throw new Exception\NotImplemented($exMessage); + } + } + + if (!$this->emit('afterMethod:' . $method, [$request, $response])) return; + if (!$this->emit('afterMethod', [$request, $response])) return; + + if ($response->getStatus() === null) { + throw new Exception('No subsystem set a valid HTTP status code. Something must have interrupted the request without providing further detail.'); + } + if ($sendResponse) { + $this->sapi->sendResponse($response); + $this->emit('afterResponse', [$request, $response]); + } + + } + + // {{{ HTTP/WebDAV protocol helpers + + /** + * Returns an array with all the supported HTTP methods for a specific uri. + * + * @param string $path + * @return array + */ + function getAllowedMethods($path) { + + $methods = [ + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ]; + + // The MKCOL is only allowed on an unmapped uri + try { + $this->tree->getNodeForPath($path); + } catch (Exception\NotFound $e) { + $methods[] = 'MKCOL'; + } + + // We're also checking if any of the plugins register any new methods + foreach ($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($path)); + array_unique($methods); + + return $methods; + + } + + /** + * Gets the uri for the request, keeping the base uri into consideration + * + * @return string + */ + function getRequestUri() { + + return $this->calculateUri($this->httpRequest->getUrl()); + + } + + /** + * Turns a URI such as the REQUEST_URI into a local path. + * + * This method: + * * strips off the base path + * * normalizes the path + * * uri-decodes the path + * + * @param string $uri + * @throws Exception\Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri + * @return string + */ + function calculateUri($uri) { + + if ($uri[0] != '/' && strpos($uri, '://')) { + + $uri = parse_url($uri, PHP_URL_PATH); + + } + + $uri = Uri\normalize(str_replace('//', '/', $uri)); + $baseUri = Uri\normalize($this->getBaseUri()); + + if (strpos($uri, $baseUri) === 0) { + + return trim(URLUtil::decodePath(substr($uri, strlen($baseUri))), '/'); + + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + } elseif ($uri . '/' === $baseUri) { + + return ''; + + } else { + + throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); + + } + + } + + /** + * Returns the HTTP depth header + * + * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object + * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent + * + * @param mixed $default + * @return int + */ + function getHTTPDepth($default = self::DEPTH_INFINITY) { + + // If its not set, we'll grab the default + $depth = $this->httpRequest->getHeader('Depth'); + + if (is_null($depth)) return $default; + + if ($depth == 'infinity') return self::DEPTH_INFINITY; + + + // If its an unknown value. we'll grab the default + if (!ctype_digit($depth)) return $default; + + return (int)$depth; + + } + + /** + * Returns the HTTP range header + * + * This method returns null if there is no well-formed HTTP range request + * header or array($start, $end). + * + * The first number is the offset of the first byte in the range. + * The second number is the offset of the last byte in the range. + * + * If the second offset is null, it should be treated as the offset of the last byte of the entity + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * + * @return array|null + */ + function getHTTPRange() { + + $range = $this->httpRequest->getHeader('range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i', $range, $matches)) return null; + + if ($matches[1] === '' && $matches[2] === '') return null; + + return [ + $matches[1] !== '' ? $matches[1] : null, + $matches[2] !== '' ? $matches[2] : null, + ]; + + } + + /** + * Returns the HTTP Prefer header information. + * + * The prefer header is defined in: + * http://tools.ietf.org/html/draft-snell-http-prefer-14 + * + * This method will return an array with options. + * + * Currently, the following options may be returned: + * [ + * 'return-asynch' => true, + * 'return-minimal' => true, + * 'return-representation' => true, + * 'wait' => 30, + * 'strict' => true, + * 'lenient' => true, + * ] + * + * This method also supports the Brief header, and will also return + * 'return-minimal' if the brief header was set to 't'. + * + * For the boolean options, false will be returned if the headers are not + * specified. For the integer options it will be 'null'. + * + * @return array + */ + function getHTTPPrefer() { + + $result = [ + // can be true or false + 'respond-async' => false, + // Could be set to 'representation' or 'minimal'. + 'return' => null, + // Used as a timeout, is usually a number. + 'wait' => null, + // can be 'strict' or 'lenient'. + 'handling' => false, + ]; + + if ($prefer = $this->httpRequest->getHeader('Prefer')) { + + $result = array_merge( + $result, + HTTP\parsePrefer($prefer) + ); + + } elseif ($this->httpRequest->getHeader('Brief') == 't') { + $result['return'] = 'minimal'; + } + + return $result; + + } + + + /** + * Returns information about Copy and Move requests + * + * This function is created to help getting information about the source and the destination for the + * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions + * + * The returned value is an array with the following keys: + * * destination - Destination path + * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten) + * + * @param RequestInterface $request + * @throws Exception\BadRequest upon missing or broken request headers + * @throws Exception\UnsupportedMediaType when trying to copy into a + * non-collection. + * @throws Exception\PreconditionFailed If overwrite is set to false, but + * the destination exists. + * @throws Exception\Forbidden when source and destination paths are + * identical. + * @throws Exception\Conflict When trying to copy a node into its own + * subtree. + * @return array + */ + function getCopyAndMoveInfo(RequestInterface $request) { + + // Collecting the relevant HTTP headers + if (!$request->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied'); + $destination = $this->calculateUri($request->getHeader('Destination')); + $overwrite = $request->getHeader('Overwrite'); + if (!$overwrite) $overwrite = 'T'; + if (strtoupper($overwrite) == 'T') $overwrite = true; + elseif (strtoupper($overwrite) == 'F') $overwrite = false; + // We need to throw a bad request exception, if the header was invalid + else throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F'); + + list($destinationDir) = URLUtil::splitPath($destination); + + try { + $destinationParent = $this->tree->getNodeForPath($destinationDir); + if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection'); + } catch (Exception\NotFound $e) { + + // If the destination parent node is not found, we throw a 409 + throw new Exception\Conflict('The destination node is not found'); + } + + try { + + $destinationNode = $this->tree->getNodeForPath($destination); + + // If this succeeded, it means the destination already exists + // we'll need to throw precondition failed in case overwrite is false + if (!$overwrite) throw new Exception\PreconditionFailed('The destination node already exists, and the overwrite header is set to false', 'Overwrite'); + + } catch (Exception\NotFound $e) { + + // Destination didn't exist, we're all good + $destinationNode = false; + + } + + $requestPath = $request->getPath(); + if ($destination === $requestPath) { + throw new Exception\Forbidden('Source and destination uri are identical.'); + } + if (substr($destination, 0, strlen($requestPath) + 1) === $requestPath . '/') { + throw new Exception\Conflict('The destination may not be part of the same subtree as the source path.'); + } + + // These are the three relevant properties we need to return + return [ + 'destination' => $destination, + 'destinationExists' => !!$destinationNode, + 'destinationNode' => $destinationNode, + ]; + + } + + /** + * Returns a list of properties for a path + * + * This is a simplified version getPropertiesForPath. If you aren't + * interested in status codes, but you just want to have a flat list of + * properties, use this method. + * + * Please note though that any problems related to retrieving properties, + * such as permission issues will just result in an empty array being + * returned. + * + * @param string $path + * @param array $propertyNames + * @return array + */ + function getProperties($path, $propertyNames) { + + $result = $this->getPropertiesForPath($path, $propertyNames, 0); + if (isset($result[0][200])) { + return $result[0][200]; + } else { + return []; + } + + } + + /** + * A kid-friendly way to fetch properties for a node's children. + * + * The returned array will be indexed by the path of the of child node. + * Only properties that are actually found will be returned. + * + * The parent node will not be returned. + * + * @param string $path + * @param array $propertyNames + * @return array + */ + function getPropertiesForChildren($path, $propertyNames) { + + $result = []; + foreach ($this->getPropertiesForPath($path, $propertyNames, 1) as $k => $row) { + + // Skipping the parent path + if ($k === 0) continue; + + $result[$row['href']] = $row[200]; + + } + return $result; + + } + + /** + * Returns a list of HTTP headers for a particular resource + * + * The generated http headers are based on properties provided by the + * resource. The method basically provides a simple mapping between + * DAV property and HTTP header. + * + * The headers are intended to be used for HEAD and GET requests. + * + * @param string $path + * @return array + */ + function getHTTPHeaders($path) { + + $propertyMap = [ + '{DAV:}getcontenttype' => 'Content-Type', + '{DAV:}getcontentlength' => 'Content-Length', + '{DAV:}getlastmodified' => 'Last-Modified', + '{DAV:}getetag' => 'ETag', + ]; + + $properties = $this->getProperties($path, array_keys($propertyMap)); + + $headers = []; + foreach ($propertyMap as $property => $header) { + if (!isset($properties[$property])) continue; + + if (is_scalar($properties[$property])) { + $headers[$header] = $properties[$property]; + + // GetLastModified gets special cased + } elseif ($properties[$property] instanceof Xml\Property\GetLastModified) { + $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime()); + } + + } + + return $headers; + + } + + /** + * Small helper to support PROPFIND with DEPTH_INFINITY. + * + * @param PropFind $propFind + * @param array $yieldFirst + * @return \Iterator + */ + private function generatePathNodes(PropFind $propFind, array $yieldFirst = null) { + if ($yieldFirst !== null) { + yield $yieldFirst; + } + $newDepth = $propFind->getDepth(); + $path = $propFind->getPath(); + + if ($newDepth !== self::DEPTH_INFINITY) { + $newDepth--; + } + + foreach ($this->tree->getChildren($path) as $childNode) { + $subPropFind = clone $propFind; + $subPropFind->setDepth($newDepth); + if ($path !== '') { + $subPath = $path . '/' . $childNode->getName(); + } else { + $subPath = $childNode->getName(); + } + $subPropFind->setPath($subPath); + + yield [ + $subPropFind, + $childNode + ]; + + if (($newDepth === self::DEPTH_INFINITY || $newDepth >= 1) && $childNode instanceof ICollection) { + foreach ($this->generatePathNodes($subPropFind) as $subItem) { + yield $subItem; + } + } + + } + } + + /** + * Returns a list of properties for a given path + * + * The path that should be supplied should have the baseUrl stripped out + * The list of properties should be supplied in Clark notation. If the list is empty + * 'allprops' is assumed. + * + * If a depth of 1 is requested child elements will also be returned. + * + * @param string $path + * @param array $propertyNames + * @param int $depth + * @return array + * + * @deprecated Use getPropertiesIteratorForPath() instead (as it's more memory efficient) + * @see getPropertiesIteratorForPath() + */ + function getPropertiesForPath($path, $propertyNames = [], $depth = 0) { + + return iterator_to_array($this->getPropertiesIteratorForPath($path, $propertyNames, $depth)); + + } + /** + * Returns a list of properties for a given path + * + * The path that should be supplied should have the baseUrl stripped out + * The list of properties should be supplied in Clark notation. If the list is empty + * 'allprops' is assumed. + * + * If a depth of 1 is requested child elements will also be returned. + * + * @param string $path + * @param array $propertyNames + * @param int $depth + * @return \Iterator + */ + function getPropertiesIteratorForPath($path, $propertyNames = [], $depth = 0) { + + // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled + if (!$this->enablePropfindDepthInfinity && $depth != 0) $depth = 1; + + $path = trim($path, '/'); + + $propFindType = $propertyNames ? PropFind::NORMAL : PropFind::ALLPROPS; + $propFind = new PropFind($path, (array)$propertyNames, $depth, $propFindType); + + $parentNode = $this->tree->getNodeForPath($path); + + $propFindRequests = [[ + $propFind, + $parentNode + ]]; + + if (($depth > 0 || $depth === self::DEPTH_INFINITY) && $parentNode instanceof ICollection) { + $propFindRequests = $this->generatePathNodes(clone $propFind, current($propFindRequests)); + } + + foreach ($propFindRequests as $propFindRequest) { + + list($propFind, $node) = $propFindRequest; + $r = $this->getPropertiesByNode($propFind, $node); + if ($r) { + $result = $propFind->getResultForMultiStatus(); + $result['href'] = $propFind->getPath(); + + // WebDAV recommends adding a slash to the path, if the path is + // a collection. + // Furthermore, iCal also demands this to be the case for + // principals. This is non-standard, but we support it. + $resourceType = $this->getResourceTypeForNode($node); + if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { + $result['href'] .= '/'; + } + yield $result; + } + + } + + } + + /** + * Returns a list of properties for a list of paths. + * + * The path that should be supplied should have the baseUrl stripped out + * The list of properties should be supplied in Clark notation. If the list is empty + * 'allprops' is assumed. + * + * The result is returned as an array, with paths for it's keys. + * The result may be returned out of order. + * + * @param array $paths + * @param array $propertyNames + * @return array + */ + function getPropertiesForMultiplePaths(array $paths, array $propertyNames = []) { + + $result = [ + ]; + + $nodes = $this->tree->getMultipleNodes($paths); + + foreach ($nodes as $path => $node) { + + $propFind = new PropFind($path, $propertyNames); + $r = $this->getPropertiesByNode($propFind, $node); + if ($r) { + $result[$path] = $propFind->getResultForMultiStatus(); + $result[$path]['href'] = $path; + + $resourceType = $this->getResourceTypeForNode($node); + if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { + $result[$path]['href'] .= '/'; + } + } + + } + + return $result; + + } + + + /** + * Determines all properties for a node. + * + * This method tries to grab all properties for a node. This method is used + * internally getPropertiesForPath and a few others. + * + * It could be useful to call this, if you already have an instance of your + * target node and simply want to run through the system to get a correct + * list of properties. + * + * @param PropFind $propFind + * @param INode $node + * @return bool + */ + function getPropertiesByNode(PropFind $propFind, INode $node) { + + return $this->emit('propFind', [$propFind, $node]); + + } + + /** + * This method is invoked by sub-systems creating a new file. + * + * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). + * It was important to get this done through a centralized function, + * allowing plugins to intercept this using the beforeCreateFile event. + * + * This method will return true if the file was actually created + * + * @param string $uri + * @param resource $data + * @param string $etag + * @return bool + */ + function createFile($uri, $data, &$etag = null) { + + list($dir, $name) = URLUtil::splitPath($uri); + + if (!$this->emit('beforeBind', [$uri])) return false; + + $parent = $this->tree->getNodeForPath($dir); + if (!$parent instanceof ICollection) { + throw new Exception\Conflict('Files can only be created as children of collections'); + } + + // It is possible for an event handler to modify the content of the + // body, before it gets written. If this is the case, $modified + // should be set to true. + // + // If $modified is true, we must not send back an ETag. + $modified = false; + if (!$this->emit('beforeCreateFile', [$uri, &$data, $parent, &$modified])) return false; + + $etag = $parent->createFile($name, $data); + + if ($modified) $etag = null; + + $this->tree->markDirty($dir . '/' . $name); + + $this->emit('afterBind', [$uri]); + $this->emit('afterCreateFile', [$uri, $parent]); + + return true; + } + + /** + * This method is invoked by sub-systems updating a file. + * + * This method will return true if the file was actually updated + * + * @param string $uri + * @param resource $data + * @param string $etag + * @return bool + */ + function updateFile($uri, $data, &$etag = null) { + + $node = $this->tree->getNodeForPath($uri); + + // It is possible for an event handler to modify the content of the + // body, before it gets written. If this is the case, $modified + // should be set to true. + // + // If $modified is true, we must not send back an ETag. + $modified = false; + if (!$this->emit('beforeWriteContent', [$uri, $node, &$data, &$modified])) return false; + + $etag = $node->put($data); + if ($modified) $etag = null; + $this->emit('afterWriteContent', [$uri, $node]); + + return true; + } + + + + /** + * This method is invoked by sub-systems creating a new directory. + * + * @param string $uri + * @return void + */ + function createDirectory($uri) { + + $this->createCollection($uri, new MkCol(['{DAV:}collection'], [])); + + } + + /** + * Use this method to create a new collection + * + * @param string $uri The new uri + * @param MkCol $mkCol + * @return array|null + */ + function createCollection($uri, MkCol $mkCol) { + + list($parentUri, $newName) = URLUtil::splitPath($uri); + + // Making sure the parent exists + try { + $parent = $this->tree->getNodeForPath($parentUri); + + } catch (Exception\NotFound $e) { + throw new Exception\Conflict('Parent node does not exist'); + + } + + // Making sure the parent is a collection + if (!$parent instanceof ICollection) { + throw new Exception\Conflict('Parent node is not a collection'); + } + + // Making sure the child does not already exist + try { + $parent->getChild($newName); + + // If we got here.. it means there's already a node on that url, and we need to throw a 405 + throw new Exception\MethodNotAllowed('The resource you tried to create already exists'); + + } catch (Exception\NotFound $e) { + // NotFound is the expected behavior. + } + + + if (!$this->emit('beforeBind', [$uri])) return; + + if ($parent instanceof IExtendedCollection) { + + /** + * If the parent is an instance of IExtendedCollection, it means that + * we can pass the MkCol object directly as it may be able to store + * properties immediately. + */ + $parent->createExtendedCollection($newName, $mkCol); + + } else { + + /** + * If the parent is a standard ICollection, it means only + * 'standard' collections can be created, so we should fail any + * MKCOL operation that carries extra resourcetypes. + */ + if (count($mkCol->getResourceType()) > 1) { + throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); + } + + $parent->createDirectory($newName); + + } + + // If there are any properties that have not been handled/stored, + // we ask the 'propPatch' event to handle them. This will allow for + // example the propertyStorage system to store properties upon MKCOL. + if ($mkCol->getRemainingMutations()) { + $this->emit('propPatch', [$uri, $mkCol]); + } + $success = $mkCol->commit(); + + if (!$success) { + $result = $mkCol->getResult(); + + $formattedResult = [ + 'href' => $uri, + ]; + + foreach ($result as $propertyName => $status) { + + if (!isset($formattedResult[$status])) { + $formattedResult[$status] = []; + } + $formattedResult[$status][$propertyName] = null; + + } + return $formattedResult; + } + + $this->tree->markDirty($parentUri); + $this->emit('afterBind', [$uri]); + + } + + /** + * This method updates a resource's properties + * + * The properties array must be a list of properties. Array-keys are + * property names in clarknotation, array-values are it's values. + * If a property must be deleted, the value should be null. + * + * Note that this request should either completely succeed, or + * completely fail. + * + * The response is an array with properties for keys, and http status codes + * as their values. + * + * @param string $path + * @param array $properties + * @return array + */ + function updateProperties($path, array $properties) { + + $propPatch = new PropPatch($properties); + $this->emit('propPatch', [$path, $propPatch]); + $propPatch->commit(); + + return $propPatch->getResult(); + + } + + /** + * This method checks the main HTTP preconditions. + * + * Currently these are: + * * If-Match + * * If-None-Match + * * If-Modified-Since + * * If-Unmodified-Since + * + * The method will return true if all preconditions are met + * The method will return false, or throw an exception if preconditions + * failed. If false is returned the operation should be aborted, and + * the appropriate HTTP response headers are already set. + * + * Normally this method will throw 412 Precondition Failed for failures + * related to If-None-Match, If-Match and If-Unmodified Since. It will + * set the status to 304 Not Modified for If-Modified_since. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function checkPreconditions(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + $node = null; + $lastMod = null; + $etag = null; + + if ($ifMatch = $request->getHeader('If-Match')) { + + // If-Match contains an entity tag. Only if the entity-tag + // matches we are allowed to make the request succeed. + // If the entity-tag is '*' we are only allowed to make the + // request succeed if a resource exists at that url. + try { + $node = $this->tree->getNodeForPath($path); + } catch (Exception\NotFound $e) { + throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist', 'If-Match'); + } + + // Only need to check entity tags if they are not * + if ($ifMatch !== '*') { + + // There can be multiple ETags + $ifMatch = explode(',', $ifMatch); + $haveMatch = false; + foreach ($ifMatch as $ifMatchItem) { + + // Stripping any extra spaces + $ifMatchItem = trim($ifMatchItem, ' '); + + $etag = $node instanceof IFile ? $node->getETag() : null; + if ($etag === $ifMatchItem) { + $haveMatch = true; + } else { + // Evolution has a bug where it sometimes prepends the " + // with a \. This is our workaround. + if (str_replace('\\"', '"', $ifMatchItem) === $etag) { + $haveMatch = true; + } + } + + } + if (!$haveMatch) { + if ($etag) $response->setHeader('ETag', $etag); + throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.', 'If-Match'); + } + } + } + + if ($ifNoneMatch = $request->getHeader('If-None-Match')) { + + // The If-None-Match header contains an ETag. + // Only if the ETag does not match the current ETag, the request will succeed + // The header can also contain *, in which case the request + // will only succeed if the entity does not exist at all. + $nodeExists = true; + if (!$node) { + try { + $node = $this->tree->getNodeForPath($path); + } catch (Exception\NotFound $e) { + $nodeExists = false; + } + } + if ($nodeExists) { + $haveMatch = false; + if ($ifNoneMatch === '*') $haveMatch = true; + else { + + // There might be multiple ETags + $ifNoneMatch = explode(',', $ifNoneMatch); + $etag = $node instanceof IFile ? $node->getETag() : null; + + foreach ($ifNoneMatch as $ifNoneMatchItem) { + + // Stripping any extra spaces + $ifNoneMatchItem = trim($ifNoneMatchItem, ' '); + + if ($etag === $ifNoneMatchItem) $haveMatch = true; + + } + + } + + if ($haveMatch) { + if ($etag) $response->setHeader('ETag', $etag); + if ($request->getMethod() === 'GET') { + $response->setStatus(304); + return false; + } else { + throw new Exception\PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).', 'If-None-Match'); + } + } + } + + } + + if (!$ifNoneMatch && ($ifModifiedSince = $request->getHeader('If-Modified-Since'))) { + + // The If-Modified-Since header contains a date. We + // will only return the entity if it has been changed since + // that date. If it hasn't been changed, we return a 304 + // header + // Note that this header only has to be checked if there was no If-None-Match header + // as per the HTTP spec. + $date = HTTP\Util::parseHTTPDate($ifModifiedSince); + + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($path); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new \DateTime('@' . $lastMod); + if ($lastMod <= $date) { + $response->setStatus(304); + $response->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod)); + return false; + } + } + } + } + + if ($ifUnmodifiedSince = $request->getHeader('If-Unmodified-Since')) { + + // The If-Unmodified-Since will allow allow the request if the + // entity has not changed since the specified date. + $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince); + + // We must only check the date if it's valid + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($path); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new \DateTime('@' . $lastMod); + if ($lastMod > $date) { + throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.', 'If-Unmodified-Since'); + } + } + } + + } + + // Now the hardest, the If: header. The If: header can contain multiple + // urls, ETags and so-called 'state tokens'. + // + // Examples of state tokens include lock-tokens (as defined in rfc4918) + // and sync-tokens (as defined in rfc6578). + // + // The only proper way to deal with these, is to emit events, that a + // Sync and Lock plugin can pick up. + $ifConditions = $this->getIfConditions($request); + + foreach ($ifConditions as $kk => $ifCondition) { + foreach ($ifCondition['tokens'] as $ii => $token) { + $ifConditions[$kk]['tokens'][$ii]['validToken'] = false; + } + } + + // Plugins are responsible for validating all the tokens. + // If a plugin deemed a token 'valid', it will set 'validToken' to + // true. + $this->emit('validateTokens', [$request, &$ifConditions]); + + // Now we're going to analyze the result. + + // Every ifCondition needs to validate to true, so we exit as soon as + // we have an invalid condition. + foreach ($ifConditions as $ifCondition) { + + $uri = $ifCondition['uri']; + $tokens = $ifCondition['tokens']; + + // We only need 1 valid token for the condition to succeed. + foreach ($tokens as $token) { + + $tokenValid = $token['validToken'] || !$token['token']; + + $etagValid = false; + if (!$token['etag']) { + $etagValid = true; + } + // Checking the ETag, only if the token was already deemed + // valid and there is one. + if ($token['etag'] && $tokenValid) { + + // The token was valid, and there was an ETag. We must + // grab the current ETag and check it. + $node = $this->tree->getNodeForPath($uri); + $etagValid = $node instanceof IFile && $node->getETag() == $token['etag']; + + } + + + if (($tokenValid && $etagValid) ^ $token['negate']) { + // Both were valid, so we can go to the next condition. + continue 2; + } + + + } + + // If we ended here, it means there was no valid ETag + token + // combination found for the current condition. This means we fail! + throw new Exception\PreconditionFailed('Failed to find a valid token/etag combination for ' . $uri, 'If'); + + } + + return true; + + } + + /** + * This method is created to extract information from the WebDAV HTTP 'If:' header + * + * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information + * The function will return an array, containing structs with the following keys + * + * * uri - the uri the condition applies to. + * * tokens - The lock token. another 2 dimensional array containing 3 elements + * + * Example 1: + * + * If: () + * + * Would result in: + * + * [ + * [ + * 'uri' => '/request/uri', + * 'tokens' => [ + * [ + * [ + * 'negate' => false, + * 'token' => 'opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2', + * 'etag' => "" + * ] + * ] + * ], + * ] + * ] + * + * Example 2: + * + * If: (Not ["Im An ETag"]) (["Another ETag"]) (Not ["Path2 ETag"]) + * + * Would result in: + * + * [ + * [ + * 'uri' => 'path', + * 'tokens' => [ + * [ + * [ + * 'negate' => true, + * 'token' => 'opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2', + * 'etag' => '"Im An ETag"' + * ], + * [ + * 'negate' => false, + * 'token' => '', + * 'etag' => '"Another ETag"' + * ] + * ] + * ], + * ], + * [ + * 'uri' => 'path2', + * 'tokens' => [ + * [ + * [ + * 'negate' => true, + * 'token' => '', + * 'etag' => '"Path2 ETag"' + * ] + * ] + * ], + * ], + * ] + * + * @param RequestInterface $request + * @return array + */ + function getIfConditions(RequestInterface $request) { + + $header = $request->getHeader('If'); + if (!$header) return []; + + $matches = []; + + $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; + preg_match_all($regex, $header, $matches, PREG_SET_ORDER); + + $conditions = []; + + foreach ($matches as $match) { + + // If there was no uri specified in this match, and there were + // already conditions parsed, we add the condition to the list of + // conditions for the previous uri. + if (!$match['uri'] && count($conditions)) { + $conditions[count($conditions) - 1]['tokens'][] = [ + 'negate' => $match['not'] ? true : false, + 'token' => $match['token'], + 'etag' => isset($match['etag']) ? $match['etag'] : '' + ]; + } else { + + if (!$match['uri']) { + $realUri = $request->getPath(); + } else { + $realUri = $this->calculateUri($match['uri']); + } + + $conditions[] = [ + 'uri' => $realUri, + 'tokens' => [ + [ + 'negate' => $match['not'] ? true : false, + 'token' => $match['token'], + 'etag' => isset($match['etag']) ? $match['etag'] : '' + ] + ], + + ]; + } + + } + + return $conditions; + + } + + /** + * Returns an array with resourcetypes for a node. + * + * @param INode $node + * @return array + */ + function getResourceTypeForNode(INode $node) { + + $result = []; + foreach ($this->resourceTypeMapping as $className => $resourceType) { + if ($node instanceof $className) $result[] = $resourceType; + } + return $result; + + } + + // }}} + // {{{ XML Readers & Writers + + + /** + * Generates a WebDAV propfind response body based on a list of nodes. + * + * If 'strip404s' is set to true, all 404 responses will be removed. + * + * @param array|\Traversable $fileProperties The list with nodes + * @param bool $strip404s + * @return string + */ + function generateMultiStatus($fileProperties, $strip404s = false) { + + $w = $this->xml->getWriter(); + $w->openMemory(); + $w->contextUri = $this->baseUri; + $w->startDocument(); + + $w->startElement('{DAV:}multistatus'); + + foreach ($fileProperties as $entry) { + + $href = $entry['href']; + unset($entry['href']); + if ($strip404s) { + unset($entry[404]); + } + $response = new Xml\Element\Response( + ltrim($href, '/'), + $entry + ); + $w->write([ + 'name' => '{DAV:}response', + 'value' => $response + ]); + } + $w->endElement(); + + return $w->outputMemory(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php new file mode 100644 index 00000000000..b2c468ab387 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php @@ -0,0 +1,110 @@ + $this->getPluginName(), + 'description' => null, + 'link' => null, + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php new file mode 100644 index 00000000000..034aefbdce6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php @@ -0,0 +1,69 @@ +server = $server; + + $server->xml->elementMap['{DAV:}share-resource'] = 'Sabre\\DAV\\Xml\\Request\\ShareResource'; + + array_push( + $server->protectedProperties, + '{DAV:}share-mode' + ); + + $server->on('method:POST', [$this, 'httpPost']); + $server->on('propFind', [$this, 'propFind']); + $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('onBrowserPostAction', [$this, 'browserPostAction']); + + } + + /** + * Updates the list of sharees on a shared resource. + * + * The sharees array is a list of people that are to be added modified + * or removed in the list of shares. + * + * @param string $path + * @param Sharee[] $sharees + * @return void + */ + function shareResource($path, array $sharees) { + + $node = $this->server->tree->getNodeForPath($path); + + if (!$node instanceof ISharedNode) { + + throw new Forbidden('Sharing is not allowed on this node'); + + } + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($path, '{DAV:}share'); + } + + foreach ($sharees as $sharee) { + // We're going to attempt to get a local principal uri for a share + // href by emitting the getPrincipalByUri event. + $principal = null; + $this->server->emit('getPrincipalByUri', [$sharee->href, &$principal]); + $sharee->principal = $principal; + } + $node->updateInvites($sharees); + + } + + /** + * This event is triggered when properties are requested for nodes. + * + * This allows us to inject any sharings-specific properties. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + if ($node instanceof ISharedNode) { + + $propFind->handle('{DAV:}share-access', function() use ($node) { + + return new Property\ShareAccess($node->getShareAccess()); + + }); + $propFind->handle('{DAV:}invite', function() use ($node) { + + return new Property\Invite($node->getInvites()); + + }); + $propFind->handle('{DAV:}share-resource-uri', function() use ($node) { + + return new Property\Href($node->getShareResourceUri()); + + }); + + } + + } + + /** + * We intercept this to handle POST requests on shared resources + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return null|bool + */ + function httpPost(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + $contentType = $request->getHeader('Content-Type'); + + // We're only interested in the davsharing content type. + if (strpos($contentType, 'application/davsharing+xml') === false) { + return; + } + + $message = $this->server->xml->parse( + $request->getBody(), + $request->getUrl(), + $documentType + ); + + switch ($documentType) { + + case '{DAV:}share-resource': + + $this->shareResource($path, $message->sharees); + $response->setStatus(200); + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $response->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + default : + throw new BadRequest('Unexpected document type: ' . $documentType . ' for this Content-Type'); + + } + + } + + /** + * This method is triggered whenever a subsystem reqeuests the privileges + * hat are supported on a particular node. + * + * We need to add a number of privileges for scheduling purposes. + * + * @param INode $node + * @param array $supportedPrivilegeSet + */ + function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) { + + if ($node instanceof ISharedNode) { + $supportedPrivilegeSet['{DAV:}share'] = [ + 'abstract' => false, + 'aggregates' => [], + ]; + } + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'This plugin implements WebDAV resource sharing', + 'link' => 'https://github.com/evert/webdav-sharing' + ]; + + } + + /** + * This method is used to generate HTML output for the + * DAV\Browser\Plugin. + * + * @param INode $node + * @param string $output + * @param string $path + * @return bool|null + */ + function htmlActionsPanel(INode $node, &$output, $path) { + + if (!$node instanceof ISharedNode) { + return; + } + + $aclPlugin = $this->server->getPlugin('acl'); + if ($aclPlugin) { + if (!$aclPlugin->checkPrivileges($path, '{DAV:}share', \Sabre\DAVACL\Plugin::R_PARENT, false)) { + // Sharing is not permitted, we will not draw this interface. + return; + } + } + + $output .= '
    +

    Share this resource

    + +
    + +
    + +
    + '; + + } + + /** + * This method is triggered for POST actions generated by the browser + * plugin. + * + * @param string $path + * @param string $action + * @param array $postVars + */ + function browserPostAction($path, $action, $postVars) { + + if ($action !== 'share') { + return; + } + + if (empty($postVars['href'])) { + throw new BadRequest('The "href" POST parameter is required'); + } + if (empty($postVars['access'])) { + throw new BadRequest('The "access" POST parameter is required'); + } + + $accessMap = [ + 'readwrite' => self::ACCESS_READWRITE, + 'read' => self::ACCESS_READ, + 'no-access' => self::ACCESS_NOACCESS, + ]; + + if (!isset($accessMap[$postVars['access']])) { + throw new BadRequest('The "access" POST must be readwrite, read or no-access'); + } + $sharee = new Sharee([ + 'href' => $postVars['href'], + 'access' => $accessMap[$postVars['access']], + ]); + + $this->shareResource( + $path, + [$sharee] + ); + return false; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php new file mode 100644 index 00000000000..998cfcbff5f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php @@ -0,0 +1,107 @@ +name = $name; + foreach ($children as $child) { + + if (!($child instanceof INode)) throw new Exception('Only instances of Sabre\DAV\INode are allowed to be passed in the children argument'); + $this->addChild($child); + + } + + } + + /** + * Adds a new childnode to this collection + * + * @param INode $child + * @return void + */ + function addChild(INode $child) { + + $this->children[$child->getName()] = $child; + + } + + /** + * Returns the name of the collection + * + * @return string + */ + function getName() { + + return $this->name; + + } + + /** + * Returns a child object, by its name. + * + * This method makes use of the getChildren method to grab all the child nodes, and compares the name. + * Generally its wise to override this, as this can usually be optimized + * + * This method must throw Sabre\DAV\Exception\NotFound if the node does not + * exist. + * + * @param string $name + * @throws Exception\NotFound + * @return INode + */ + function getChild($name) { + + if (isset($this->children[$name])) return $this->children[$name]; + throw new Exception\NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); + + } + + /** + * Returns a list of children for this collection + * + * @return INode[] + */ + function getChildren() { + + return array_values($this->children); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php new file mode 100644 index 00000000000..bcad786f34a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php @@ -0,0 +1,121 @@ +name = $name; + $this->contents = $contents; + $this->mimeType = $mimeType; + + } + + /** + * Returns the node name for this file. + * + * This name is used to construct the url. + * + * @return string + */ + function getName() { + + return $this->name; + + } + + /** + * Returns the data + * + * This method may either return a string or a readable stream resource + * + * @return mixed + */ + function get() { + + return $this->contents; + + } + + /** + * Returns the size of the file, in bytes. + * + * @return int + */ + function getSize() { + + return strlen($this->contents); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * @return string + */ + function getETag() { + + return '"' . sha1($this->contents) . '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * @return string + */ + function getContentType() { + + return $this->mimeType; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php new file mode 100644 index 00000000000..10eecebfd47 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php @@ -0,0 +1,91 @@ + 'The current synctoken', + * 'added' => [ + * 'new.txt', + * ], + * 'modified' => [ + * 'modified.txt', + * ], + * 'deleted' => array( + * 'foo.php.bak', + * 'old.txt' + * ) + * ]; + * + * The syncToken property should reflect the *current* syncToken of the + * collection, as reported getSyncToken(). This is needed here too, to + * ensure the operation is atomic. + * + * If the syncToken is specified as null, this is an initial sync, and all + * members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The second argument is basically the 'depth' of the report. If it's 1, + * you only have to report changes that happened only directly in immediate + * descendants. If it's 2, it should also include changes from the nodes + * below the child collections. (grandchildren) + * + * The third (optional) argument allows a client to specify how many + * results should be returned at most. If the limit is not specified, it + * should be treated as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChanges($syncToken, $syncLevel, $limit = null); + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Sync/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Sync/Plugin.php new file mode 100644 index 00000000000..8e4b1aa6412 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Sync/Plugin.php @@ -0,0 +1,277 @@ +server = $server; + $server->xml->elementMap['{DAV:}sync-collection'] = 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport'; + + $self = $this; + + $server->on('report', function($reportName, $dom, $uri) use ($self) { + + if ($reportName === '{DAV:}sync-collection') { + $this->server->transactionType = 'report-sync-collection'; + $self->syncCollection($uri, $dom); + return false; + } + + }); + + $server->on('propFind', [$this, 'propFind']); + $server->on('validateTokens', [$this, 'validateTokens']); + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + function getSupportedReportSet($uri) { + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof ISyncCollection && $node->getSyncToken()) { + return [ + '{DAV:}sync-collection', + ]; + } + + return []; + + } + + + /** + * This method handles the {DAV:}sync-collection HTTP REPORT. + * + * @param string $uri + * @param SyncCollectionReport $report + * @return void + */ + function syncCollection($uri, SyncCollectionReport $report) { + + // Getting the data + $node = $this->server->tree->getNodeForPath($uri); + if (!$node instanceof ISyncCollection) { + throw new DAV\Exception\ReportNotSupported('The {DAV:}sync-collection REPORT is not supported on this url.'); + } + $token = $node->getSyncToken(); + if (!$token) { + throw new DAV\Exception\ReportNotSupported('No sync information is available at this node'); + } + + $syncToken = $report->syncToken; + if (!is_null($syncToken)) { + // Sync-token must start with our prefix + if (substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { + throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); + } + + $syncToken = substr($syncToken, strlen(self::SYNCTOKEN_PREFIX)); + + } + $changeInfo = $node->getChanges($syncToken, $report->syncLevel, $report->limit); + + if (is_null($changeInfo)) { + + throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); + + } + + // Encoding the response + $this->sendSyncCollectionResponse( + $changeInfo['syncToken'], + $uri, + $changeInfo['added'], + $changeInfo['modified'], + $changeInfo['deleted'], + $report->properties + ); + + } + + /** + * Sends the response to a sync-collection request. + * + * @param string $syncToken + * @param string $collectionUrl + * @param array $added + * @param array $modified + * @param array $deleted + * @param array $properties + * @return void + */ + protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) { + + + $fullPaths = []; + + // Pre-fetching children, if this is possible. + foreach (array_merge($added, $modified) as $item) { + $fullPath = $collectionUrl . '/' . $item; + $fullPaths[] = $fullPath; + } + + $responses = []; + foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) { + + // The 'Property_Response' class is responsible for generating a + // single {DAV:}response xml element. + $responses[] = new DAV\Xml\Element\Response($fullPath, $props); + + } + + + + // Deleted items also show up as 'responses'. They have no properties, + // and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'. + foreach ($deleted as $item) { + + $fullPath = $collectionUrl . '/' . $item; + $responses[] = new DAV\Xml\Element\Response($fullPath, [], 404); + + } + $multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX . $syncToken); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setBody( + $this->server->xml->write('{DAV:}multistatus', $multiStatus, $this->server->getBaseUri()) + ); + + } + + /** + * This method is triggered whenever properties are requested for a node. + * We intercept this to see if we must return a {DAV:}sync-token. + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @return void + */ + function propFind(DAV\PropFind $propFind, DAV\INode $node) { + + $propFind->handle('{DAV:}sync-token', function() use ($node) { + if (!$node instanceof ISyncCollection || !$token = $node->getSyncToken()) { + return; + } + return self::SYNCTOKEN_PREFIX . $token; + }); + + } + + /** + * The validateTokens event is triggered before every request. + * + * It's a moment where this plugin can check all the supplied lock tokens + * in the If: header, and check if they are valid. + * + * @param RequestInterface $request + * @param array $conditions + * @return void + */ + function validateTokens(RequestInterface $request, &$conditions) { + + foreach ($conditions as $kk => $condition) { + + foreach ($condition['tokens'] as $ii => $token) { + + // Sync-tokens must always start with our designated prefix. + if (substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { + continue; + } + + // Checking if the token is a match. + $node = $this->server->tree->getNodeForPath($condition['uri']); + + if ( + $node instanceof ISyncCollection && + $node->getSyncToken() == substr($token['token'], strlen(self::SYNCTOKEN_PREFIX)) + ) { + $conditions[$kk]['tokens'][$ii]['validToken'] = true; + } + + } + + } + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds support for WebDAV Collection Sync (rfc6578)', + 'link' => 'http://sabre.io/dav/sync/', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php new file mode 100644 index 00000000000..7b453d10536 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php @@ -0,0 +1,297 @@ +dataDir = $dataDir; + + } + + /** + * Initialize the plugin + * + * This is called automatically be the Server class after this plugin is + * added with Sabre\DAV\Server::addPlugin() + * + * @param Server $server + * @return void + */ + function initialize(Server $server) { + + $this->server = $server; + $server->on('beforeMethod', [$this, 'beforeMethod']); + $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); + + } + + /** + * This method is called before any HTTP method handler + * + * This method intercepts any GET, DELETE, PUT and PROPFIND calls to + * filenames that are known to match the 'temporary file' regex. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function beforeMethod(RequestInterface $request, ResponseInterface $response) { + + if (!$tempLocation = $this->isTempFile($request->getPath())) + return; + + switch ($request->getMethod()) { + case 'GET' : + return $this->httpGet($request, $response, $tempLocation); + case 'PUT' : + return $this->httpPut($request, $response, $tempLocation); + case 'PROPFIND' : + return $this->httpPropfind($request, $response, $tempLocation); + case 'DELETE' : + return $this->httpDelete($request, $response, $tempLocation); + } + return; + + } + + /** + * This method is invoked if some subsystem creates a new file. + * + * This is used to deal with HTTP LOCK requests which create a new + * file. + * + * @param string $uri + * @param resource $data + * @param ICollection $parent + * @param bool $modified Should be set to true, if this event handler + * changed &$data. + * @return bool + */ + function beforeCreateFile($uri, $data, ICollection $parent, $modified) { + + if ($tempPath = $this->isTempFile($uri)) { + + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp', 'true'); + file_put_contents($tempPath, $data); + return false; + } + return; + + } + + /** + * This method will check if the url matches the temporary file pattern + * if it does, it will return an path based on $this->dataDir for the + * temporary file storage. + * + * @param string $path + * @return bool|string + */ + protected function isTempFile($path) { + + // We're only interested in the basename. + list(, $tempPath) = URLUtil::splitPath($path); + + foreach ($this->temporaryFilePatterns as $tempFile) { + + if (preg_match($tempFile, $tempPath)) { + return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; + } + + } + + return false; + + } + + + /** + * This method handles the GET method for temporary files. + * If the file doesn't exist, it will return false which will kick in + * the regular system for the GET method. + * + * @param RequestInterface $request + * @param ResponseInterface $hR + * @param string $tempLocation + * @return bool + */ + function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation) { + + if (!file_exists($tempLocation)) return; + + $hR->setHeader('Content-Type', 'application/octet-stream'); + $hR->setHeader('Content-Length', filesize($tempLocation)); + $hR->setHeader('X-Sabre-Temp', 'true'); + $hR->setStatus(200); + $hR->setBody(fopen($tempLocation, 'r')); + return false; + + } + + /** + * This method handles the PUT method. + * + * @param RequestInterface $request + * @param ResponseInterface $hR + * @param string $tempLocation + * @return bool + */ + function httpPut(RequestInterface $request, ResponseInterface $hR, $tempLocation) { + + $hR->setHeader('X-Sabre-Temp', 'true'); + + $newFile = !file_exists($tempLocation); + + if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { + throw new Exception\PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); + } + + file_put_contents($tempLocation, $this->server->httpRequest->getBody()); + $hR->setStatus($newFile ? 201 : 200); + return false; + + } + + /** + * This method handles the DELETE method. + * + * If the file didn't exist, it will return false, which will make the + * standard HTTP DELETE handler kick in. + * + * @param RequestInterface $request + * @param ResponseInterface $hR + * @param string $tempLocation + * @return bool + */ + function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation) { + + if (!file_exists($tempLocation)) return; + + unlink($tempLocation); + $hR->setHeader('X-Sabre-Temp', 'true'); + $hR->setStatus(204); + return false; + + } + + /** + * This method handles the PROPFIND method. + * + * It's a very lazy method, it won't bother checking the request body + * for which properties were requested, and just sends back a default + * set of properties. + * + * @param RequestInterface $request + * @param ResponseInterface $hR + * @param string $tempLocation + * @return bool + */ + function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation) { + + if (!file_exists($tempLocation)) return; + + $hR->setHeader('X-Sabre-Temp', 'true'); + $hR->setStatus(207); + $hR->setHeader('Content-Type', 'application/xml; charset=utf-8'); + + $properties = [ + 'href' => $request->getPath(), + 200 => [ + '{DAV:}getlastmodified' => new Xml\Property\GetLastModified(filemtime($tempLocation)), + '{DAV:}getcontentlength' => filesize($tempLocation), + '{DAV:}resourcetype' => new Xml\Property\ResourceType(null), + '{' . Server::NS_SABREDAV . '}tempFile' => true, + + ], + ]; + + $data = $this->server->generateMultiStatus([$properties]); + $hR->setBody($data); + return false; + + } + + + /** + * This method returns the directory where the temporary files should be stored. + * + * @return string + */ + protected function getDataDir() + { + return $this->dataDir; + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php new file mode 100644 index 00000000000..5d2792503f9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php @@ -0,0 +1,340 @@ +rootNode = $rootNode; + + } + + /** + * Returns the INode object for the requested path + * + * @param string $path + * @return INode + */ + function getNodeForPath($path) { + + $path = trim($path, '/'); + if (isset($this->cache[$path])) return $this->cache[$path]; + + // Is it the root node? + if (!strlen($path)) { + return $this->rootNode; + } + + // Attempting to fetch its parent + list($parentName, $baseName) = URLUtil::splitPath($path); + + // If there was no parent, we must simply ask it from the root node. + if ($parentName === "") { + $node = $this->rootNode->getChild($baseName); + } else { + // Otherwise, we recursively grab the parent and ask him/her. + $parent = $this->getNodeForPath($parentName); + + if (!($parent instanceof ICollection)) + throw new Exception\NotFound('Could not find node at path: ' . $path); + + $node = $parent->getChild($baseName); + + } + + $this->cache[$path] = $node; + return $node; + + } + + /** + * This function allows you to check if a node exists. + * + * Implementors of this class should override this method to make + * it cheaper. + * + * @param string $path + * @return bool + */ + function nodeExists($path) { + + try { + + // The root always exists + if ($path === '') return true; + + list($parent, $base) = URLUtil::splitPath($path); + + $parentNode = $this->getNodeForPath($parent); + if (!$parentNode instanceof ICollection) return false; + return $parentNode->childExists($base); + + } catch (Exception\NotFound $e) { + + return false; + + } + + } + + /** + * Copies a file from path to another + * + * @param string $sourcePath The source location + * @param string $destinationPath The full destination path + * @return void + */ + function copy($sourcePath, $destinationPath) { + + $sourceNode = $this->getNodeForPath($sourcePath); + + // grab the dirname and basename components + list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); + + $destinationParent = $this->getNodeForPath($destinationDir); + $this->copyNode($sourceNode, $destinationParent, $destinationName); + + $this->markDirty($destinationDir); + + } + + /** + * Moves a file from one location to another + * + * @param string $sourcePath The path to the file which should be moved + * @param string $destinationPath The full destination path, so not just the destination parent node + * @return int + */ + function move($sourcePath, $destinationPath) { + + list($sourceDir) = URLUtil::splitPath($sourcePath); + list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); + + if ($sourceDir === $destinationDir) { + // If this is a 'local' rename, it means we can just trigger a rename. + $sourceNode = $this->getNodeForPath($sourcePath); + $sourceNode->setName($destinationName); + } else { + $newParentNode = $this->getNodeForPath($destinationDir); + $moveSuccess = false; + if ($newParentNode instanceof IMoveTarget) { + // The target collection may be able to handle the move + $sourceNode = $this->getNodeForPath($sourcePath); + $moveSuccess = $newParentNode->moveInto($destinationName, $sourcePath, $sourceNode); + } + if (!$moveSuccess) { + $this->copy($sourcePath, $destinationPath); + $this->getNodeForPath($sourcePath)->delete(); + } + } + $this->markDirty($sourceDir); + $this->markDirty($destinationDir); + + } + + /** + * Deletes a node from the tree + * + * @param string $path + * @return void + */ + function delete($path) { + + $node = $this->getNodeForPath($path); + $node->delete(); + + list($parent) = URLUtil::splitPath($path); + $this->markDirty($parent); + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + function getChildren($path) { + + $node = $this->getNodeForPath($path); + $children = $node->getChildren(); + $basePath = trim($path, '/'); + if ($basePath !== '') $basePath .= '/'; + + foreach ($children as $child) { + + $this->cache[$basePath . $child->getName()] = $child; + + } + return $children; + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + function markDirty($path) { + + // We don't care enough about sub-paths + // flushing the entire cache + $path = trim($path, '/'); + foreach ($this->cache as $nodePath => $node) { + if ($path === '' || $nodePath == $path || strpos($nodePath, $path . '/') === 0) + unset($this->cache[$nodePath]); + + } + + } + + /** + * This method tells the tree system to pre-fetch and cache a list of + * children of a single parent. + * + * There are a bunch of operations in the WebDAV stack that request many + * children (based on uris), and sometimes fetching many at once can + * optimize this. + * + * This method returns an array with the found nodes. It's keys are the + * original paths. The result may be out of order. + * + * @param array $paths List of nodes that must be fetched. + * @return array + */ + function getMultipleNodes($paths) { + + // Finding common parents + $parents = []; + foreach ($paths as $path) { + list($parent, $node) = URLUtil::splitPath($path); + if (!isset($parents[$parent])) { + $parents[$parent] = [$node]; + } else { + $parents[$parent][] = $node; + } + } + + $result = []; + + foreach ($parents as $parent => $children) { + + $parentNode = $this->getNodeForPath($parent); + if ($parentNode instanceof IMultiGet) { + foreach ($parentNode->getMultipleChildren($children) as $childNode) { + $fullPath = $parent . '/' . $childNode->getName(); + $result[$fullPath] = $childNode; + $this->cache[$fullPath] = $childNode; + } + } else { + foreach ($children as $child) { + $fullPath = $parent . '/' . $child; + $result[$fullPath] = $this->getNodeForPath($fullPath); + } + } + + } + + return $result; + + } + + + /** + * copyNode + * + * @param INode $source + * @param ICollection $destinationParent + * @param string $destinationName + * @return void + */ + protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null) { + + if (!$destinationName) $destinationName = $source->getName(); + + if ($source instanceof IFile) { + + $data = $source->get(); + + // If the body was a string, we need to convert it to a stream + if (is_string($data)) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $data); + rewind($stream); + $data = $stream; + } + $destinationParent->createFile($destinationName, $data); + $destination = $destinationParent->getChild($destinationName); + + } elseif ($source instanceof ICollection) { + + $destinationParent->createDirectory($destinationName); + + $destination = $destinationParent->getChild($destinationName); + foreach ($source->getChildren() as $child) { + + $this->copyNode($child, $destination); + + } + + } + if ($source instanceof IProperties && $destination instanceof IProperties) { + + $props = $source->getProperties([]); + $propPatch = new PropPatch($props); + $destination->propPatch($propPatch); + $propPatch->commit(); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php new file mode 100644 index 00000000000..177adafd3b8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php @@ -0,0 +1,64 @@ +value array. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Prop implements XmlDeserializable { + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + // If there's no children, we don't do anything. + if ($reader->isEmptyElement) { + $reader->next(); + return []; + } + + $values = []; + + $reader->read(); + do { + + if ($reader->nodeType === Reader::ELEMENT) { + + $clark = $reader->getClark(); + $values[$clark] = self::parseCurrentElement($reader)['value']; + + } else { + $reader->read(); + } + + } while ($reader->nodeType !== Reader::END_ELEMENT); + + $reader->read(); + + return $values; + + } + + /** + * This function behaves similar to Sabre\Xml\Reader::parseCurrentElement, + * but instead of creating deep xml array structures, it will turn any + * top-level element it doesn't recognize into either a string, or an + * XmlFragment class. + * + * This method returns arn array with 2 properties: + * * name - A clark-notation XML element name. + * * value - The parsed value. + * + * @param Reader $reader + * @return array + */ + private static function parseCurrentElement(Reader $reader) { + + $name = $reader->getClark(); + + if (array_key_exists($name, $reader->elementMap)) { + $deserializer = $reader->elementMap[$name]; + if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) { + $value = call_user_func([$deserializer, 'xmlDeserialize'], $reader); + } elseif (is_callable($deserializer)) { + $value = call_user_func($deserializer, $reader); + } else { + $type = gettype($deserializer); + if ($type === 'string') { + $type .= ' (' . $deserializer . ')'; + } elseif ($type === 'object') { + $type .= ' (' . get_class($deserializer) . ')'; + } + throw new \LogicException('Could not use this type as a deserializer: ' . $type); + } + } else { + $value = Complex::xmlDeserialize($reader); + } + + return [ + 'name' => $name, + 'value' => $value, + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php new file mode 100644 index 00000000000..ce97ae94366 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php @@ -0,0 +1,253 @@ +href = $href; + $this->responseProperties = $responseProperties; + $this->httpStatus = $httpStatus; + + } + + /** + * Returns the url + * + * @return string + */ + function getHref() { + + return $this->href; + + } + + /** + * Returns the httpStatus value + * + * @return string + */ + function getHttpStatus() { + + return $this->httpStatus; + + } + + /** + * Returns the property list + * + * @return array + */ + function getResponseProperties() { + + return $this->responseProperties; + + } + + + /** + * The serialize method is called during xml writing. + * + * It should use the $writer argument to encode this object into XML. + * + * Important note: it is not needed to create the parent element. The + * parent element is already created, and we only have to worry about + * attributes, child elements and text (if any). + * + * Important note 2: If you are writing any new elements, you are also + * responsible for closing them. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + if ($status = $this->getHTTPStatus()) { + $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); + } + $writer->writeElement('{DAV:}href', $writer->contextUri . \Sabre\HTTP\encodePath($this->getHref())); + + $empty = true; + + foreach ($this->getResponseProperties() as $status => $properties) { + + // Skipping empty lists + if (!$properties || (!ctype_digit($status) && !is_int($status))) { + continue; + } + $empty = false; + $writer->startElement('{DAV:}propstat'); + $writer->writeElement('{DAV:}prop', $properties); + $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); + $writer->endElement(); // {DAV:}propstat + + } + if ($empty) { + /* + * The WebDAV spec _requires_ at least one DAV:propstat to appear for + * every DAV:response. In some circumstances however, there are no + * properties to encode. + * + * In those cases we MUST specify at least one DAV:propstat anyway, with + * no properties. + */ + $writer->writeElement('{DAV:}propstat', [ + '{DAV:}prop' => [], + '{DAV:}status' => 'HTTP/1.1 418 ' . \Sabre\HTTP\Response::$statusCodes[418] + ]); + + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $reader->pushContext(); + + $reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue'; + + // We are overriding the parser for {DAV:}prop. This deserializer is + // almost identical to the one for Sabre\Xml\Element\KeyValue. + // + // The difference is that if there are any child-elements inside of + // {DAV:}prop, that have no value, normally any deserializers are + // called. But we don't want this, because a singular element without + // child-elements implies 'no value' in {DAV:}prop, so we want to skip + // deserializers and just set null for those. + $reader->elementMap['{DAV:}prop'] = function(Reader $reader) { + + if ($reader->isEmptyElement) { + $reader->next(); + return []; + } + $values = []; + $reader->read(); + do { + if ($reader->nodeType === Reader::ELEMENT) { + $clark = $reader->getClark(); + + if ($reader->isEmptyElement) { + $values[$clark] = null; + $reader->next(); + } else { + $values[$clark] = $reader->parseCurrentElement()['value']; + } + } else { + $reader->read(); + } + } while ($reader->nodeType !== Reader::END_ELEMENT); + $reader->read(); + return $values; + + }; + $elems = $reader->parseInnerTree(); + $reader->popContext(); + + $href = null; + $propertyLists = []; + $statusCode = null; + + foreach ($elems as $elem) { + + switch ($elem['name']) { + + case '{DAV:}href' : + $href = $elem['value']; + break; + case '{DAV:}propstat' : + $status = $elem['value']['{DAV:}status']; + list(, $status, ) = explode(' ', $status, 3); + $properties = isset($elem['value']['{DAV:}prop']) ? $elem['value']['{DAV:}prop'] : []; + if ($properties) $propertyLists[$status] = $properties; + break; + case '{DAV:}status' : + list(, $statusCode, ) = explode(' ', $elem['value'], 3); + break; + + } + + } + + return new self($href, $propertyLists, $statusCode); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php new file mode 100644 index 00000000000..e187bf11cb0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php @@ -0,0 +1,199 @@ + $v) { + + if (property_exists($this, $k)) { + $this->$k = $v; + } else { + throw new \InvalidArgumentException('Unknown property: ' . $k); + } + + } + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + + $writer->write([ + new Href($this->href), + '{DAV:}prop' => $this->properties, + '{DAV:}share-access' => new ShareAccess($this->access), + ]); + switch ($this->inviteStatus) { + case Plugin::INVITE_NORESPONSE : + $writer->writeElement('{DAV:}invite-noresponse'); + break; + case Plugin::INVITE_ACCEPTED : + $writer->writeElement('{DAV:}invite-accepted'); + break; + case Plugin::INVITE_DECLINED : + $writer->writeElement('{DAV:}invite-declined'); + break; + case Plugin::INVITE_INVALID : + $writer->writeElement('{DAV:}invite-invalid'); + break; + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + // Temporarily override configuration + $reader->pushContext(); + $reader->elementMap['{DAV:}share-access'] = 'Sabre\DAV\Xml\Property\ShareAccess'; + $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\keyValue'; + + $elems = Deserializer\keyValue($reader, 'DAV:'); + + // Restore previous configuration + $reader->popContext(); + + $sharee = new self(); + if (!isset($elems['href'])) { + throw new BadRequest('Every {DAV:}sharee must have a {DAV:}href child-element'); + } + $sharee->href = $elems['href']; + + if (isset($elems['prop'])) { + $sharee->properties = $elems['prop']; + } + if (isset($elems['comment'])) { + $sharee->comment = $elems['comment']; + } + if (!isset($elems['share-access'])) { + throw new BadRequest('Every {DAV:}sharee must have a {DAV:}share-access child element'); + } + $sharee->access = $elems['share-access']->getValue(); + return $sharee; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php new file mode 100644 index 00000000000..258806e4a59 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php @@ -0,0 +1,89 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $xml = $reader->readInnerXml(); + + if ($reader->nodeType === Reader::ELEMENT && $reader->isEmptyElement) { + // Easy! + $reader->next(); + return null; + } + // Now we have a copy of the inner xml, we need to traverse it to get + // all the strings. If there's no non-string data, we just return the + // string, otherwise we return an instance of this class. + $reader->read(); + + $nonText = false; + $text = ''; + + while (true) { + + switch ($reader->nodeType) { + case Reader::ELEMENT : + $nonText = true; + $reader->next(); + continue 2; + case Reader::TEXT : + case Reader::CDATA : + $text .= $reader->value; + break; + case Reader::END_ELEMENT : + break 2; + } + $reader->read(); + + } + + // Make sure we advance the cursor one step further. + $reader->read(); + + if ($nonText) { + $new = new self($xml); + return $new; + } else { + return $text; + } + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php new file mode 100644 index 00000000000..101a0f0c91a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php @@ -0,0 +1,110 @@ +time = clone $time; + } else { + $this->time = new DateTime('@' . $time); + } + + // Setting timezone to UTC + $this->time->setTimezone(new DateTimeZone('UTC')); + + } + + /** + * getTime + * + * @return DateTime + */ + function getTime() { + + return $this->time; + + } + + /** + * The serialize method is called during xml writing. + * + * It should use the $writer argument to encode this object into XML. + * + * Important note: it is not needed to create the parent element. The + * parent element is already created, and we only have to worry about + * attributes, child elements and text (if any). + * + * Important note 2: If you are writing any new elements, you are also + * responsible for closing them. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + $writer->write( + HTTP\Util::toHTTPDate($this->time) + ); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + return + new self(new DateTime($reader->parseInnerTree())); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php new file mode 100644 index 00000000000..6c4f11b87ad --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php @@ -0,0 +1,165 @@ +hrefs = $hrefs; + + } + + /** + * Returns the first Href. + * + * @return string + */ + function getHref() { + + return $this->hrefs[0]; + + } + + /** + * Returns the hrefs as an array + * + * @return array + */ + function getHrefs() { + + return $this->hrefs; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->getHrefs() as $href) { + $href = Uri\resolve($writer->contextUri, $href); + $writer->writeElement('{DAV:}href', $href); + } + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + $links = []; + foreach ($this->getHrefs() as $href) { + $links[] = $html->link($href); + } + return implode('
    ', $links); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $hrefs = []; + foreach ((array)$reader->parseInnerTree() as $elem) { + if ($elem['name'] !== '{DAV:}href') + continue; + + $hrefs[] = $elem['value']; + + } + if ($hrefs) { + return new self($hrefs, false); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php new file mode 100644 index 00000000000..6adad365040 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php @@ -0,0 +1,70 @@ +sharees = $sharees; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->sharees as $sharee) { + $writer->writeElement('{DAV:}sharee', $sharee); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php new file mode 100644 index 00000000000..00d2fa708d1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php @@ -0,0 +1,48 @@ +locks = $locks; + + } + + /** + * The serialize method is called during xml writing. + * + * It should use the $writer argument to encode this object into XML. + * + * Important note: it is not needed to create the parent element. The + * parent element is already created, and we only have to worry about + * attributes, child elements and text (if any). + * + * Important note 2: If you are writing any new elements, you are also + * responsible for closing them. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->locks as $lock) { + + $writer->startElement('{DAV:}activelock'); + + $writer->startElement('{DAV:}lockscope'); + if ($lock->scope === LockInfo::SHARED) { + $writer->writeElement('{DAV:}shared'); + } else { + $writer->writeElement('{DAV:}exclusive'); + } + + $writer->endElement(); // {DAV:}lockscope + + $writer->startElement('{DAV:}locktype'); + $writer->writeElement('{DAV:}write'); + $writer->endElement(); // {DAV:}locktype + + if (!self::$hideLockRoot) { + $writer->startElement('{DAV:}lockroot'); + $writer->writeElement('{DAV:}href', $writer->contextUri . $lock->uri); + $writer->endElement(); // {DAV:}lockroot + } + $writer->writeElement('{DAV:}depth', ($lock->depth == DAV\Server::DEPTH_INFINITY ? 'infinity' : $lock->depth)); + $writer->writeElement('{DAV:}timeout', 'Second-' . $lock->timeout); + + $writer->startElement('{DAV:}locktoken'); + $writer->writeElement('{DAV:}href', 'opaquelocktoken:' . $lock->token); + $writer->endElement(); // {DAV:}locktoken + + $writer->writeElement('{DAV:}owner', new XmlFragment($lock->owner)); + $writer->endElement(); // {DAV:}activelock + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php new file mode 100644 index 00000000000..ce640ff32db --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php @@ -0,0 +1,128 @@ +value; + + } + + /** + * Checks if the principal contains a certain value + * + * @param string $type + * @return bool + */ + function is($type) { + + return in_array($type, $this->value); + + } + + /** + * Adds a resourcetype value to this property + * + * @param string $type + * @return void + */ + function add($type) { + + $this->value[] = $type; + $this->value = array_unique($this->value); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + return + new self(parent::xmlDeserialize($reader)); + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + return implode( + ', ', + array_map([$html, 'xmlName'], $this->getValue()) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php new file mode 100644 index 00000000000..939062f76fd --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php @@ -0,0 +1,143 @@ +value = $shareAccess; + + } + + /** + * Returns the current value. + * + * @return int + */ + function getValue() { + + return $this->value; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + switch ($this->value) { + + case SharingPlugin::ACCESS_NOTSHARED : + $writer->writeElement('{DAV:}not-shared'); + break; + case SharingPlugin::ACCESS_SHAREDOWNER : + $writer->writeElement('{DAV:}shared-owner'); + break; + case SharingPlugin::ACCESS_READ : + $writer->writeElement('{DAV:}read'); + break; + case SharingPlugin::ACCESS_READWRITE : + $writer->writeElement('{DAV:}read-write'); + break; + case SharingPlugin::ACCESS_NOACCESS : + $writer->writeElement('{DAV:}no-access'); + break; + + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree(); + foreach ($elems as $elem) { + switch ($elem['name']) { + case '{DAV:}not-shared' : + return new self(SharingPlugin::ACCESS_NOTSHARED); + case '{DAV:}shared-owner' : + return new self(SharingPlugin::ACCESS_SHAREDOWNER); + case '{DAV:}read' : + return new self(SharingPlugin::ACCESS_READ); + case '{DAV:}read-write' : + return new self(SharingPlugin::ACCESS_READWRITE); + case '{DAV:}no-access' : + return new self(SharingPlugin::ACCESS_NOACCESS); + } + } + throw new BadRequest('Invalid value for {DAV:}share-access element'); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php new file mode 100644 index 00000000000..677fdde4bdf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php @@ -0,0 +1,54 @@ +writeElement('{DAV:}lockentry', [ + '{DAV:}lockscope' => ['{DAV:}exclusive' => null], + '{DAV:}locktype' => ['{DAV:}write' => null], + ]); + $writer->writeElement('{DAV:}lockentry', [ + '{DAV:}lockscope' => ['{DAV:}shared' => null], + '{DAV:}locktype' => ['{DAV:}write' => null], + ]); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php new file mode 100644 index 00000000000..1a3878ef71f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php @@ -0,0 +1,121 @@ +methods = $methods; + + } + + /** + * Returns the list of supported http methods. + * + * @return string[] + */ + function getValue() { + + return $this->methods; + + } + + /** + * Returns true or false if the property contains a specific method. + * + * @param string $methodName + * @return bool + */ + function has($methodName) { + + return in_array( + $methodName, + $this->methods + ); + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->getValue() as $val) { + $writer->startElement('{DAV:}supported-method'); + $writer->writeAttribute('name', $val); + $writer->endElement(); + } + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + return implode( + ', ', + array_map([$html, 'h'], $this->getValue()) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php new file mode 100644 index 00000000000..96383ec96c8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php @@ -0,0 +1,154 @@ +addReport($reports); + + } + + /** + * Adds a report to this property + * + * The report must be a string in clark-notation. + * Multiple reports can be specified as an array. + * + * @param mixed $report + * @return void + */ + function addReport($report) { + + $report = (array)$report; + + foreach ($report as $r) { + + if (!preg_match('/^{([^}]*)}(.*)$/', $r)) + throw new DAV\Exception('Reportname must be in clark-notation'); + + $this->reports[] = $r; + + } + + } + + /** + * Returns the list of supported reports + * + * @return string[] + */ + function getValue() { + + return $this->reports; + + } + + /** + * Returns true or false if the property contains a specific report. + * + * @param string $reportName + * @return bool + */ + function has($reportName) { + + return in_array( + $reportName, + $this->reports + ); + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->getValue() as $val) { + $writer->startElement('{DAV:}supported-report'); + $writer->startElement('{DAV:}report'); + $writer->writeElement($val); + $writer->endElement(); + $writer->endElement(); + } + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + return implode( + ', ', + array_map([$html, 'xmlName'], $this->getValue()) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php new file mode 100644 index 00000000000..c315a9a4502 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php @@ -0,0 +1,81 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $reader->pushContext(); + $reader->elementMap['{DAV:}owner'] = 'Sabre\\Xml\\Element\\XmlFragment'; + + $values = KeyValue::xmlDeserialize($reader); + + $reader->popContext(); + + $new = new self(); + $new->owner = !empty($values['{DAV:}owner']) ? $values['{DAV:}owner']->getXml() : null; + $new->scope = LockInfo::SHARED; + + if (isset($values['{DAV:}lockscope'])) { + foreach ($values['{DAV:}lockscope'] as $elem) { + if ($elem['name'] === '{DAV:}exclusive') $new->scope = LockInfo::EXCLUSIVE; + } + } + return $new; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php new file mode 100644 index 00000000000..9490bf58cf2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php @@ -0,0 +1,82 @@ +value array with properties that are supposed to get set + * during creation of the new collection. + * + * @return array + */ + function getProperties() { + + return $this->properties; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $self = new self(); + + $elementMap = $reader->elementMap; + $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop'; + $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue'; + $elementMap['{DAV:}remove'] = 'Sabre\Xml\Element\KeyValue'; + + $elems = $reader->parseInnerTree($elementMap); + + foreach ($elems as $elem) { + if ($elem['name'] === '{DAV:}set') { + $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']); + } + } + + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php new file mode 100644 index 00000000000..f1b5b6fdc6f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php @@ -0,0 +1,83 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $self = new self(); + + $reader->pushContext(); + $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements'; + + foreach (KeyValue::xmlDeserialize($reader) as $k => $v) { + + switch ($k) { + case '{DAV:}prop' : + $self->properties = $v; + break; + case '{DAV:}allprop' : + $self->allProp = true; + + } + + } + + $reader->popContext(); + + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php new file mode 100644 index 00000000000..821b9e047d4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php @@ -0,0 +1,118 @@ +properties as $propertyName => $propertyValue) { + + if (is_null($propertyValue)) { + $writer->startElement("{DAV:}remove"); + $writer->write(['{DAV:}prop' => [$propertyName => $propertyValue]]); + $writer->endElement(); + } else { + $writer->startElement("{DAV:}set"); + $writer->write(['{DAV:}prop' => [$propertyName => $propertyValue]]); + $writer->endElement(); + } + + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $self = new self(); + + $elementMap = $reader->elementMap; + $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop'; + $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue'; + $elementMap['{DAV:}remove'] = 'Sabre\Xml\Element\KeyValue'; + + $elems = $reader->parseInnerTree($elementMap); + + foreach ($elems as $elem) { + if ($elem['name'] === '{DAV:}set') { + $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']); + } + if ($elem['name'] === '{DAV:}remove') { + + // Ensuring there are no values. + foreach ($elem['value']['{DAV:}prop'] as $remove => $value) { + $self->properties[$remove] = null; + } + + } + } + + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php new file mode 100644 index 00000000000..526a4eb6ff6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php @@ -0,0 +1,81 @@ +sharees = $sharees; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree([ + '{DAV:}sharee' => 'Sabre\DAV\Xml\Element\Sharee', + '{DAV:}share-access' => 'Sabre\DAV\Xml\Property\ShareAccess', + '{DAV:}prop' => 'Sabre\Xml\Deserializer\keyValue', + ]); + + $sharees = []; + + foreach ($elems as $elem) { + if ($elem['name'] !== '{DAV:}sharee') continue; + $sharees[] = $elem['value']; + + } + + return new self($sharees); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php new file mode 100644 index 00000000000..830293a0178 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php @@ -0,0 +1,122 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $self = new self(); + + $reader->pushContext(); + + $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements'; + $elems = KeyValue::xmlDeserialize($reader); + + $reader->popContext(); + + $required = [ + '{DAV:}sync-token', + '{DAV:}prop', + ]; + + foreach ($required as $elem) { + if (!array_key_exists($elem, $elems)) { + throw new BadRequest('The ' . $elem . ' element in the {DAV:}sync-collection report is required'); + } + } + + + $self->properties = $elems['{DAV:}prop']; + $self->syncToken = $elems['{DAV:}sync-token']; + + if (isset($elems['{DAV:}limit'])) { + $nresults = null; + foreach ($elems['{DAV:}limit'] as $child) { + if ($child['name'] === '{DAV:}nresults') { + $nresults = (int)$child['value']; + } + } + $self->limit = $nresults; + } + + if (isset($elems['{DAV:}sync-level'])) { + + $value = $elems['{DAV:}sync-level']; + if ($value === 'infinity') { + $value = \Sabre\DAV\Server::DEPTH_INFINITY; + } + $self->syncLevel = $value; + + } + + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php new file mode 100644 index 00000000000..cf5a0453b4a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php @@ -0,0 +1,142 @@ +responses = $responses; + $this->syncToken = $syncToken; + + } + + /** + * Returns the response list. + * + * @return \Sabre\DAV\Xml\Element\Response[] + */ + function getResponses() { + + return $this->responses; + + } + + /** + * Returns the sync-token, if available. + * + * @return string|null + */ + function getSyncToken() { + + return $this->syncToken; + + } + + /** + * The serialize method is called during xml writing. + * + * It should use the $writer argument to encode this object into XML. + * + * Important note: it is not needed to create the parent element. The + * parent element is already created, and we only have to worry about + * attributes, child elements and text (if any). + * + * Important note 2: If you are writing any new elements, you are also + * responsible for closing them. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->getResponses() as $response) { + $writer->writeElement('{DAV:}response', $response); + } + if ($syncToken = $this->getSyncToken()) { + $writer->writeElement('{DAV:}sync-token', $syncToken); + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elementMap = $reader->elementMap; + $elementMap['{DAV:}prop'] = 'Sabre\\DAV\\Xml\\Element\\Prop'; + $elements = $reader->parseInnerTree($elementMap); + + $responses = []; + $syncToken = null; + + if ($elements) foreach ($elements as $elem) { + if ($elem['name'] === '{DAV:}response') { + $responses[] = $elem['value']; + } + if ($elem['name'] === '{DAV:}sync-token') { + $syncToken = $elem['value']; + } + } + + return new self($responses, $syncToken); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php new file mode 100644 index 00000000000..f41ed984ad9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php @@ -0,0 +1,47 @@ + 'Sabre\\DAV\\Xml\\Response\\MultiStatus', + '{DAV:}response' => 'Sabre\\DAV\\Xml\\Element\\Response', + + // Requests + '{DAV:}propfind' => 'Sabre\\DAV\\Xml\\Request\\PropFind', + '{DAV:}propertyupdate' => 'Sabre\\DAV\\Xml\\Request\\PropPatch', + '{DAV:}mkcol' => 'Sabre\\DAV\\Xml\\Request\\MkCol', + + // Properties + '{DAV:}resourcetype' => 'Sabre\\DAV\\Xml\\Property\\ResourceType', + + ]; + + /** + * This is a default list of namespaces. + * + * If you are defining your own custom namespace, add it here to reduce + * bandwidth and improve legibility of xml bodies. + * + * @var array + */ + public $namespaceMap = [ + 'DAV:' => 'd', + 'http://sabredav.org/ns' => 's', + ]; + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php new file mode 100644 index 00000000000..602654a2ed3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php @@ -0,0 +1,100 @@ + '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ] + ]; + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's as an array argument. + * + * @param array $acl + * @return void + */ + function setACL(array $acl) { + + throw new \Sabre\DAV\Exception\Forbidden('Setting ACL is not supported on this node'); + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php new file mode 100644 index 00000000000..9d2026380c5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php @@ -0,0 +1,181 @@ +principalPrefix = $principalPrefix; + $this->principalBackend = $principalBackend; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principalInfo + * @return IPrincipal + */ + abstract function getChildForPrincipal(array $principalInfo); + + /** + * Returns the name of this collection. + * + * @return string + */ + function getName() { + + list(, $name) = URLUtil::splitPath($this->principalPrefix); + return $name; + + } + + /** + * Return the list of users + * + * @return array + */ + function getChildren() { + + if ($this->disableListing) + throw new DAV\Exception\MethodNotAllowed('Listing members of this collection is disabled'); + + $children = []; + foreach ($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) { + + $children[] = $this->getChildForPrincipal($principalInfo); + + + } + return $children; + + } + + /** + * Returns a child object, by its name. + * + * @param string $name + * @throws DAV\Exception\NotFound + * @return DAV\INode + */ + function getChild($name) { + + $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); + if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found'); + return $this->getChildForPrincipal($principalInfo); + + } + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * By default, if multiple properties are submitted to this method, the + * various properties should be combined with 'AND'. If $test is set to + * 'anyof', it should be combined using 'OR'. + * + * This method should simply return a list of 'child names', which may be + * used to call $this->getChild in the future. + * + * @param array $searchProperties + * @param string $test + * @return array + */ + function searchPrincipals(array $searchProperties, $test = 'allof') { + + $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties, $test); + $r = []; + + foreach ($result as $row) { + list(, $r[]) = URLUtil::splitPath($row); + } + + return $r; + + } + + /** + * Finds a principal by its URI. + * + * This method may receive any type of uri, but mailto: addresses will be + * the most common. + * + * Implementation of this API is optional. It is currently used by the + * CalDAV system to find principals based on their email addresses. If this + * API is not implemented, some features may not work correctly. + * + * This method must return a relative principal path, or null, if the + * principal was not found or you refuse to find it. + * + * @param string $uri + * @return string + */ + function findByUri($uri) { + + return $this->principalBackend->findByUri($uri, $this->principalPrefix); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php new file mode 100644 index 00000000000..22450b4a681 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php @@ -0,0 +1,35 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:', 'd:no-ace-conflict'); + $errorNode->appendChild($np); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php new file mode 100644 index 00000000000..5624fd22f23 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php @@ -0,0 +1,82 @@ +uri = $uri; + $this->privileges = $privileges; + + parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); + + } + + /** + * Adds in extra information in the xml response. + * + * This method adds the {DAV:}need-privileges element as defined in rfc3744 + * + * @param DAV\Server $server + * @param \DOMElement $errorNode + * @return void + */ + function serialize(DAV\Server $server, \DOMElement $errorNode) { + + $doc = $errorNode->ownerDocument; + + $np = $doc->createElementNS('DAV:', 'd:need-privileges'); + $errorNode->appendChild($np); + + foreach ($this->privileges as $privilege) { + + $resource = $doc->createElementNS('DAV:', 'd:resource'); + $np->appendChild($resource); + + $resource->appendChild($doc->createElementNS('DAV:', 'd:href', $server->getBaseUri() . $this->uri)); + + $priv = $doc->createElementNS('DAV:', 'd:privilege'); + $resource->appendChild($priv); + + preg_match('/^{([^}]*)}(.*)$/', $privilege, $privilegeParts); + $priv->appendChild($doc->createElementNS($privilegeParts[1], 'd:' . $privilegeParts[2])); + + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php new file mode 100644 index 00000000000..a2363b174bf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php @@ -0,0 +1,35 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:', 'd:no-abstract'); + $errorNode->appendChild($np); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php new file mode 100644 index 00000000000..d7ae188ae7e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php @@ -0,0 +1,35 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:', 'd:recognized-principal'); + $errorNode->appendChild($np); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php new file mode 100644 index 00000000000..73b81190dd2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php @@ -0,0 +1,35 @@ +ownerDocument; + + $np = $doc->createElementNS('DAV:', 'd:not-supported-privilege'); + $errorNode->appendChild($np); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php new file mode 100644 index 00000000000..b4fe7a1b0be --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php @@ -0,0 +1,111 @@ +acl = $acl; + $this->owner = $owner; + + } + + /** + * Returns a specific child node, referenced by its name + * + * This method must throw Sabre\DAV\Exception\NotFound if the node does not + * exist. + * + * @param string $name + * @throws NotFound + * @return \Sabre\DAV\INode + */ + function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new NotFound('File could not be located'); + if ($name == '.' || $name == '..') throw new Forbidden('Permission denied to . and ..'); + + if (is_dir($path)) { + + return new self($path, $this->acl, $this->owner); + + } else { + + return new File($path, $this->acl, $this->owner); + + } + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->owner; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return $this->acl; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php new file mode 100644 index 00000000000..aaf2ae148a7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php @@ -0,0 +1,80 @@ +acl = $acl; + $this->owner = $owner; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->owner; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return $this->acl; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php new file mode 100644 index 00000000000..201235e5a0f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php @@ -0,0 +1,128 @@ +storagePath = $storagePath; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + + return $this->collectionName; + + } + + /** + * Returns a principals' collection of files. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principalInfo + * @return \Sabre\DAV\INode + */ + function getChildForPrincipal(array $principalInfo) { + + $owner = $principalInfo['uri']; + $acl = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + ]; + + list(, $principalBaseName) = Uri\split($owner); + + $path = $this->storagePath . '/' . $principalBaseName; + + if (!is_dir($path)) { + mkdir($path, 0777, true); + } + return new Collection( + $path, + $acl, + $owner + ); + + } + + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + + return [ + [ + 'principal' => '{DAV:}authenticated', + 'privilege' => '{DAV:}read', + 'protected' => true, + ] + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php new file mode 100644 index 00000000000..f7a138665a5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php @@ -0,0 +1,74 @@ +getChild in the future. + * + * @param array $searchProperties + * @param string $test + * @return array + */ + function searchPrincipals(array $searchProperties, $test = 'allof'); + + /** + * Finds a principal by its URI. + * + * This method may receive any type of uri, but mailto: addresses will be + * the most common. + * + * Implementation of this API is optional. It is currently used by the + * CalDAV system to find principals based on their email addresses. If this + * API is not implemented, some features may not work correctly. + * + * This method must return a relative principal path, or null, if the + * principal was not found or you refuse to find it. + * + * @param string $uri + * @return string + */ + function findByUri($uri); + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php new file mode 100644 index 00000000000..a2aa118d70b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php @@ -0,0 +1,1636 @@ + 'Display name', + '{http://sabredav.org/ns}email-address' => 'Email address', + ]; + + /** + * Any principal uri's added here, will automatically be added to the list + * of ACL's. They will effectively receive {DAV:}all privileges, as a + * protected privilege. + * + * @var array + */ + public $adminPrincipals = []; + + /** + * The ACL plugin allows privileges to be assigned to users that are not + * logged in. To facilitate that, it modifies the auth plugin's behavior + * to only require login when a privileged operation was denied. + * + * Unauthenticated access can be considered a security concern, so it's + * possible to turn this feature off to harden the server's security. + * + * @var bool + */ + public $allowUnauthenticatedAccess = true; + + /** + * Returns a list of features added by this plugin. + * + * This list is used in the response of a HTTP OPTIONS request. + * + * @return array + */ + function getFeatures() { + + return ['access-control', 'calendarserver-principal-property-search']; + + } + + /** + * Returns a list of available methods for a given url + * + * @param string $uri + * @return array + */ + function getMethods($uri) { + + return ['ACL']; + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre\DAV\Server::getPlugin + * + * @return string + */ + function getPluginName() { + + return 'acl'; + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + function getSupportedReportSet($uri) { + + return [ + '{DAV:}expand-property', + '{DAV:}principal-match', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set', + ]; + + } + + + /** + * Checks if the current user has the specified privilege(s). + * + * You can specify a single privilege, or a list of privileges. + * This method will throw an exception if the privilege is not available + * and return true otherwise. + * + * @param string $uri + * @param array|string $privileges + * @param int $recursion + * @param bool $throwExceptions if set to false, this method won't throw exceptions. + * @throws NeedPrivileges + * @throws NotAuthenticated + * @return bool + */ + function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { + + if (!is_array($privileges)) $privileges = [$privileges]; + + $acl = $this->getCurrentUserPrivilegeSet($uri); + + $failed = []; + foreach ($privileges as $priv) { + + if (!in_array($priv, $acl)) { + $failed[] = $priv; + } + + } + + if ($failed) { + if ($this->allowUnauthenticatedAccess && is_null($this->getCurrentUserPrincipal())) { + // We are not authenticated. Kicking in the Auth plugin. + $authPlugin = $this->server->getPlugin('auth'); + $reasons = $authPlugin->getLoginFailedReasons(); + $authPlugin->challenge( + $this->server->httpRequest, + $this->server->httpResponse + ); + throw new notAuthenticated(implode(', ', $reasons) . '. Login was needed for privilege: ' . implode(', ', $failed) . ' on ' . $uri); + } + if ($throwExceptions) { + + throw new NeedPrivileges($uri, $failed); + } else { + return false; + } + } + return true; + + } + + /** + * Returns the standard users' principal. + * + * This is one authoritative principal url for the current user. + * This method will return null if the user wasn't logged in. + * + * @return string|null + */ + function getCurrentUserPrincipal() { + + /** @var $authPlugin \Sabre\DAV\Auth\Plugin */ + $authPlugin = $this->server->getPlugin('auth'); + if (!$authPlugin) { + return null; + } + return $authPlugin->getCurrentPrincipal(); + + } + + + /** + * Returns a list of principals that's associated to the current + * user, either directly or through group membership. + * + * @return array + */ + function getCurrentUserPrincipals() { + + $currentUser = $this->getCurrentUserPrincipal(); + + if (is_null($currentUser)) return []; + + return array_merge( + [$currentUser], + $this->getPrincipalMembership($currentUser) + ); + + } + + /** + * Sets the default ACL rules. + * + * These rules are used for all nodes that don't implement the IACL interface. + * + * @param array $acl + * @return void + */ + function setDefaultAcl(array $acl) { + + $this->defaultAcl = $acl; + + } + + /** + * Returns the default ACL rules. + * + * These rules are used for all nodes that don't implement the IACL interface. + * + * @return array + */ + function getDefaultAcl() { + + return $this->defaultAcl; + + } + + /** + * The default ACL rules. + * + * These rules are used for nodes that don't implement IACL. These default + * set of rules allow anyone to do anything, as long as they are + * authenticated. + * + * @var array + */ + protected $defaultAcl = [ + [ + 'principal' => '{DAV:}authenticated', + 'protected' => true, + 'privilege' => '{DAV:}all', + ], + ]; + + /** + * This array holds a cache for all the principals that are associated with + * a single principal. + * + * @var array + */ + protected $principalMembershipCache = []; + + + /** + * Returns all the principal groups the specified principal is a member of. + * + * @param string $mainPrincipal + * @return array + */ + function getPrincipalMembership($mainPrincipal) { + + // First check our cache + if (isset($this->principalMembershipCache[$mainPrincipal])) { + return $this->principalMembershipCache[$mainPrincipal]; + } + + $check = [$mainPrincipal]; + $principals = []; + + while (count($check)) { + + $principal = array_shift($check); + + $node = $this->server->tree->getNodeForPath($principal); + if ($node instanceof IPrincipal) { + foreach ($node->getGroupMembership() as $groupMember) { + + if (!in_array($groupMember, $principals)) { + + $check[] = $groupMember; + $principals[] = $groupMember; + + } + + } + + } + + } + + // Store the result in the cache + $this->principalMembershipCache[$mainPrincipal] = $principals; + + return $principals; + + } + + /** + * Find out of a principal equals another principal. + * + * This is a quick way to find out whether a principal URI is part of a + * group, or any subgroups. + * + * The first argument is the principal URI you want to check against. For + * example the principal group, and the second argument is the principal of + * which you want to find out of it is the same as the first principal, or + * in a member of the first principal's group or subgroups. + * + * So the arguments are not interchangeable. If principal A is in group B, + * passing 'B', 'A' will yield true, but 'A', 'B' is false. + * + * If the second argument is not passed, we will use the current user + * principal. + * + * @param string $checkPrincipal + * @param string $currentPrincipal + * @return bool + */ + function principalMatchesPrincipal($checkPrincipal, $currentPrincipal = null) { + + if (is_null($currentPrincipal)) { + $currentPrincipal = $this->getCurrentUserPrincipal(); + } + if ($currentPrincipal === $checkPrincipal) { + return true; + } + return in_array( + $checkPrincipal, + $this->getPrincipalMembership($currentPrincipal) + ); + + } + + + /** + * Returns a tree of supported privileges for a resource. + * + * The returned array structure should be in this form: + * + * [ + * [ + * 'privilege' => '{DAV:}read', + * 'abstract' => false, + * 'aggregates' => [] + * ] + * ] + * + * Privileges can be nested using "aggregates". Doing so means that + * if you assign someone the aggregating privilege, all the + * sub-privileges will automatically be granted. + * + * Marking a privilege as abstract means that the privilege cannot be + * directly assigned, but must be assigned via the parent privilege. + * + * So a more complex version might look like this: + * + * [ + * [ + * 'privilege' => '{DAV:}read', + * 'abstract' => false, + * 'aggregates' => [ + * [ + * 'privilege' => '{DAV:}read-acl', + * 'abstract' => false, + * 'aggregates' => [], + * ] + * ] + * ] + * ] + * + * @param string|INode $node + * @return array + */ + function getSupportedPrivilegeSet($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + + $supportedPrivileges = null; + if ($node instanceof IACL) { + $supportedPrivileges = $node->getSupportedPrivilegeSet(); + } + + if (is_null($supportedPrivileges)) { + + // Default + $supportedPrivileges = [ + '{DAV:}read' => [ + 'abstract' => false, + 'aggregates' => [ + '{DAV:}read-acl' => [ + 'abstract' => false, + 'aggregates' => [], + ], + '{DAV:}read-current-user-privilege-set' => [ + 'abstract' => false, + 'aggregates' => [], + ], + ], + ], + '{DAV:}write' => [ + 'abstract' => false, + 'aggregates' => [ + '{DAV:}write-properties' => [ + 'abstract' => false, + 'aggregates' => [], + ], + '{DAV:}write-content' => [ + 'abstract' => false, + 'aggregates' => [], + ], + '{DAV:}unlock' => [ + 'abstract' => false, + 'aggregates' => [], + ], + ], + ], + ]; + if ($node instanceof DAV\ICollection) { + $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}bind'] = [ + 'abstract' => false, + 'aggregates' => [], + ]; + $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}unbind'] = [ + 'abstract' => false, + 'aggregates' => [], + ]; + } + if ($node instanceof IACL) { + $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}write-acl'] = [ + 'abstract' => false, + 'aggregates' => [], + ]; + } + + } + + $this->server->emit( + 'getSupportedPrivilegeSet', + [$node, &$supportedPrivileges] + ); + + return $supportedPrivileges; + + } + + /** + * Returns the supported privilege set as a flat list + * + * This is much easier to parse. + * + * The returned list will be index by privilege name. + * The value is a struct containing the following properties: + * - aggregates + * - abstract + * - concrete + * + * @param string|INode $node + * @return array + */ + final function getFlatPrivilegeSet($node) { + + $privs = [ + 'abstract' => false, + 'aggregates' => $this->getSupportedPrivilegeSet($node) + ]; + + $fpsTraverse = null; + $fpsTraverse = function($privName, $privInfo, $concrete, &$flat) use (&$fpsTraverse) { + + $myPriv = [ + 'privilege' => $privName, + 'abstract' => isset($privInfo['abstract']) && $privInfo['abstract'], + 'aggregates' => [], + 'concrete' => isset($privInfo['abstract']) && $privInfo['abstract'] ? $concrete : $privName, + ]; + + if (isset($privInfo['aggregates'])) { + + foreach ($privInfo['aggregates'] as $subPrivName => $subPrivInfo) { + + $myPriv['aggregates'][] = $subPrivName; + + } + + } + + $flat[$privName] = $myPriv; + + if (isset($privInfo['aggregates'])) { + + foreach ($privInfo['aggregates'] as $subPrivName => $subPrivInfo) { + + $fpsTraverse($subPrivName, $subPrivInfo, $myPriv['concrete'], $flat); + + } + + } + + }; + + $flat = []; + $fpsTraverse('{DAV:}all', $privs, null, $flat); + + return $flat; + + } + + /** + * Returns the full ACL list. + * + * Either a uri or a INode may be passed. + * + * null will be returned if the node doesn't support ACLs. + * + * @param string|DAV\INode $node + * @return array + */ + function getAcl($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + if (!$node instanceof IACL) { + return $this->getDefaultAcl(); + } + $acl = $node->getACL(); + foreach ($this->adminPrincipals as $adminPrincipal) { + $acl[] = [ + 'principal' => $adminPrincipal, + 'privilege' => '{DAV:}all', + 'protected' => true, + ]; + } + return $acl; + + } + + /** + * Returns a list of privileges the current user has + * on a particular node. + * + * Either a uri or a DAV\INode may be passed. + * + * null will be returned if the node doesn't support ACLs. + * + * @param string|DAV\INode $node + * @return array + */ + function getCurrentUserPrivilegeSet($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + + $acl = $this->getACL($node); + + $collected = []; + + $isAuthenticated = $this->getCurrentUserPrincipal() !== null; + + foreach ($acl as $ace) { + + $principal = $ace['principal']; + + switch ($principal) { + + case '{DAV:}owner' : + $owner = $node->getOwner(); + if ($owner && $this->principalMatchesPrincipal($owner)) { + $collected[] = $ace; + } + break; + + + // 'all' matches for every user + case '{DAV:}all' : + $collected[] = $ace; + break; + + case '{DAV:}authenticated' : + // Authenticated users only + if ($isAuthenticated) { + $collected[] = $ace; + } + break; + + case '{DAV:}unauthenticated' : + // Unauthenticated users only + if (!$isAuthenticated) { + $collected[] = $ace; + } + break; + + default : + if ($this->principalMatchesPrincipal($ace['principal'])) { + $collected[] = $ace; + } + break; + + } + + + } + + // Now we deduct all aggregated privileges. + $flat = $this->getFlatPrivilegeSet($node); + + $collected2 = []; + while (count($collected)) { + + $current = array_pop($collected); + $collected2[] = $current['privilege']; + + if (!isset($flat[$current['privilege']])) { + // Ignoring privileges that are not in the supported-privileges list. + $this->server->getLogger()->debug('A node has the "' . $current['privilege'] . '" in its ACL list, but this privilege was not reported in the supportedPrivilegeSet list. This will be ignored.'); + continue; + } + foreach ($flat[$current['privilege']]['aggregates'] as $subPriv) { + $collected2[] = $subPriv; + $collected[] = $flat[$subPriv]; + } + + } + + return array_values(array_unique($collected2)); + + } + + + /** + * Returns a principal based on its uri. + * + * Returns null if the principal could not be found. + * + * @param string $uri + * @return null|string + */ + function getPrincipalByUri($uri) { + + $result = null; + $collections = $this->principalCollectionSet; + foreach ($collections as $collection) { + + try { + $principalCollection = $this->server->tree->getNodeForPath($collection); + } catch (NotFound $e) { + // Ignore and move on + continue; + } + + if (!$principalCollection instanceof IPrincipalCollection) { + // Not a principal collection, we're simply going to ignore + // this. + continue; + } + + $result = $principalCollection->findByUri($uri); + if ($result) { + return $result; + } + + } + + } + + /** + * Principal property search + * + * This method can search for principals matching certain values in + * properties. + * + * This method will return a list of properties for the matched properties. + * + * @param array $searchProperties The properties to search on. This is a + * key-value list. The keys are property + * names, and the values the strings to + * match them on. + * @param array $requestedProperties This is the list of properties to + * return for every match. + * @param string $collectionUri The principal collection to search on. + * If this is ommitted, the standard + * principal collection-set will be used. + * @param string $test "allof" to use AND to search the + * properties. 'anyof' for OR. + * @return array This method returns an array structure similar to + * Sabre\DAV\Server::getPropertiesForPath. Returned + * properties are index by a HTTP status code. + */ + function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') { + + if (!is_null($collectionUri)) { + $uris = [$collectionUri]; + } else { + $uris = $this->principalCollectionSet; + } + + $lookupResults = []; + foreach ($uris as $uri) { + + $principalCollection = $this->server->tree->getNodeForPath($uri); + if (!$principalCollection instanceof IPrincipalCollection) { + // Not a principal collection, we're simply going to ignore + // this. + continue; + } + + $results = $principalCollection->searchPrincipals($searchProperties, $test); + foreach ($results as $result) { + $lookupResults[] = rtrim($uri, '/') . '/' . $result; + } + + } + + $matches = []; + + foreach ($lookupResults as $lookupResult) { + + list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); + + } + + return $matches; + + } + + /** + * Sets up the plugin + * + * This method is automatically called by the server class. + * + * @param DAV\Server $server + * @return void + */ + function initialize(DAV\Server $server) { + + if ($this->allowUnauthenticatedAccess) { + $authPlugin = $server->getPlugin('auth'); + if (!$authPlugin) { + throw new \Exception('The Auth plugin must be loaded before the ACL plugin if you want to allow unauthenticated access.'); + } + $authPlugin->autoRequireLogin = false; + } + + $this->server = $server; + $server->on('propFind', [$this, 'propFind'], 20); + $server->on('beforeMethod', [$this, 'beforeMethod'], 20); + $server->on('beforeBind', [$this, 'beforeBind'], 20); + $server->on('beforeUnbind', [$this, 'beforeUnbind'], 20); + $server->on('propPatch', [$this, 'propPatch']); + $server->on('beforeUnlock', [$this, 'beforeUnlock'], 20); + $server->on('report', [$this, 'report']); + $server->on('method:ACL', [$this, 'httpAcl']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('getPrincipalByUri', function($principal, &$uri) { + + $uri = $this->getPrincipalByUri($principal); + + // Break event chain + if ($uri) return false; + + }); + + array_push($server->protectedProperties, + '{DAV:}alternate-URI-set', + '{DAV:}principal-URL', + '{DAV:}group-membership', + '{DAV:}principal-collection-set', + '{DAV:}current-user-principal', + '{DAV:}supported-privilege-set', + '{DAV:}current-user-privilege-set', + '{DAV:}acl', + '{DAV:}acl-restrictions', + '{DAV:}inherited-acl-set', + '{DAV:}owner', + '{DAV:}group' + ); + + // Automatically mapping nodes implementing IPrincipal to the + // {DAV:}principal resourcetype. + $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; + + // Mapping the group-member-set property to the HrefList property + // class. + $server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href'; + $server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl'; + $server->xml->elementMap['{DAV:}acl-principal-prop-set'] = 'Sabre\\DAVACL\\Xml\\Request\\AclPrincipalPropSetReport'; + $server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport'; + $server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport'; + $server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport'; + $server->xml->elementMap['{DAV:}principal-match'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalMatchReport'; + + } + + /* {{{ Event handlers */ + + /** + * Triggered before any method is handled + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function beforeMethod(RequestInterface $request, ResponseInterface $response) { + + $method = $request->getMethod(); + $path = $request->getPath(); + + $exists = $this->server->tree->nodeExists($path); + + // If the node doesn't exists, none of these checks apply + if (!$exists) return; + + switch ($method) { + + case 'GET' : + case 'HEAD' : + case 'OPTIONS' : + // For these 3 we only need to know if the node is readable. + $this->checkPrivileges($path, '{DAV:}read'); + break; + + case 'PUT' : + case 'LOCK' : + // This method requires the write-content priv if the node + // already exists, and bind on the parent if the node is being + // created. + // The bind privilege is handled in the beforeBind event. + $this->checkPrivileges($path, '{DAV:}write-content'); + break; + + case 'UNLOCK' : + // Unlock is always allowed at the moment. + break; + + case 'PROPPATCH' : + $this->checkPrivileges($path, '{DAV:}write-properties'); + break; + + case 'ACL' : + $this->checkPrivileges($path, '{DAV:}write-acl'); + break; + + case 'COPY' : + case 'MOVE' : + // Copy requires read privileges on the entire source tree. + // If the target exists write-content normally needs to be + // checked, however, we're deleting the node beforehand and + // creating a new one after, so this is handled by the + // beforeUnbind event. + // + // The creation of the new node is handled by the beforeBind + // event. + // + // If MOVE is used beforeUnbind will also be used to check if + // the sourcenode can be deleted. + $this->checkPrivileges($path, '{DAV:}read', self::R_RECURSIVE); + break; + + } + + } + + /** + * Triggered before a new node is created. + * + * This allows us to check permissions for any operation that creates a + * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. + * + * @param string $uri + * @return void + */ + function beforeBind($uri) { + + list($parentUri) = Uri\split($uri); + $this->checkPrivileges($parentUri, '{DAV:}bind'); + + } + + /** + * Triggered before a node is deleted + * + * This allows us to check permissions for any operation that will delete + * an existing node. + * + * @param string $uri + * @return void + */ + function beforeUnbind($uri) { + + list($parentUri) = Uri\split($uri); + $this->checkPrivileges($parentUri, '{DAV:}unbind', self::R_RECURSIVEPARENTS); + + } + + /** + * Triggered before a node is unlocked. + * + * @param string $uri + * @param DAV\Locks\LockInfo $lock + * @TODO: not yet implemented + * @return void + */ + function beforeUnlock($uri, DAV\Locks\LockInfo $lock) { + + + } + + /** + * Triggered before properties are looked up in specific nodes. + * + * @param DAV\PropFind $propFind + * @param DAV\INode $node + * @TODO really should be broken into multiple methods, or even a class. + * @return bool + */ + function propFind(DAV\PropFind $propFind, DAV\INode $node) { + + $path = $propFind->getPath(); + + // Checking the read permission + if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) { + // User is not allowed to read properties + + // Returning false causes the property-fetching system to pretend + // that the node does not exist, and will cause it to be hidden + // from listings such as PROPFIND or the browser plugin. + if ($this->hideNodesFromListings) { + return false; + } + + // Otherwise we simply mark every property as 403. + foreach ($propFind->getRequestedProperties() as $requestedProperty) { + $propFind->set($requestedProperty, null, 403); + } + + return; + + } + + /* Adding principal properties */ + if ($node instanceof IPrincipal) { + + $propFind->handle('{DAV:}alternate-URI-set', function() use ($node) { + return new Href($node->getAlternateUriSet()); + }); + $propFind->handle('{DAV:}principal-URL', function() use ($node) { + return new Href($node->getPrincipalUrl() . '/'); + }); + $propFind->handle('{DAV:}group-member-set', function() use ($node) { + $members = $node->getGroupMemberSet(); + foreach ($members as $k => $member) { + $members[$k] = rtrim($member, '/') . '/'; + } + return new Href($members); + }); + $propFind->handle('{DAV:}group-membership', function() use ($node) { + $members = $node->getGroupMembership(); + foreach ($members as $k => $member) { + $members[$k] = rtrim($member, '/') . '/'; + } + return new Href($members); + }); + $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']); + + } + + $propFind->handle('{DAV:}principal-collection-set', function() { + + $val = $this->principalCollectionSet; + // Ensuring all collections end with a slash + foreach ($val as $k => $v) $val[$k] = $v . '/'; + return new Href($val); + + }); + $propFind->handle('{DAV:}current-user-principal', function() { + if ($url = $this->getCurrentUserPrincipal()) { + return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url . '/'); + } else { + return new Xml\Property\Principal(Xml\Property\Principal::UNAUTHENTICATED); + } + }); + $propFind->handle('{DAV:}supported-privilege-set', function() use ($node) { + return new Xml\Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); + }); + $propFind->handle('{DAV:}current-user-privilege-set', function() use ($node, $propFind, $path) { + if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) { + $propFind->set('{DAV:}current-user-privilege-set', null, 403); + } else { + $val = $this->getCurrentUserPrivilegeSet($node); + return new Xml\Property\CurrentUserPrivilegeSet($val); + } + }); + $propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) { + /* The ACL property contains all the permissions */ + if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) { + $propFind->set('{DAV:}acl', null, 403); + } else { + $acl = $this->getACL($node); + return new Xml\Property\Acl($this->getACL($node)); + } + }); + $propFind->handle('{DAV:}acl-restrictions', function() { + return new Xml\Property\AclRestrictions(); + }); + + /* Adding ACL properties */ + if ($node instanceof IACL) { + $propFind->handle('{DAV:}owner', function() use ($node) { + return new Href($node->getOwner() . '/'); + }); + } + + } + + /** + * This method intercepts PROPPATCH methods and make sure the + * group-member-set is updated correctly. + * + * @param string $path + * @param DAV\PropPatch $propPatch + * @return void + */ + function propPatch($path, DAV\PropPatch $propPatch) { + + $propPatch->handle('{DAV:}group-member-set', function($value) use ($path) { + if (is_null($value)) { + $memberSet = []; + } elseif ($value instanceof Href) { + $memberSet = array_map( + [$this->server, 'calculateUri'], + $value->getHrefs() + ); + } else { + throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null'); + } + $node = $this->server->tree->getNodeForPath($path); + if (!($node instanceof IPrincipal)) { + // Fail + return false; + } + + $node->setGroupMemberSet($memberSet); + // We must also clear our cache, just in case + + $this->principalMembershipCache = []; + + return true; + }); + + } + + /** + * This method handles HTTP REPORT requests + * + * @param string $reportName + * @param mixed $report + * @param mixed $path + * @return bool + */ + function report($reportName, $report, $path) { + + switch ($reportName) { + + case '{DAV:}principal-property-search' : + $this->server->transactionType = 'report-principal-property-search'; + $this->principalPropertySearchReport($path, $report); + return false; + case '{DAV:}principal-search-property-set' : + $this->server->transactionType = 'report-principal-search-property-set'; + $this->principalSearchPropertySetReport($path, $report); + return false; + case '{DAV:}expand-property' : + $this->server->transactionType = 'report-expand-property'; + $this->expandPropertyReport($path, $report); + return false; + case '{DAV:}principal-match' : + $this->server->transactionType = 'report-principal-match'; + $this->principalMatchReport($path, $report); + return false; + case '{DAV:}acl-principal-prop-set' : + $this->server->transactionType = 'acl-principal-prop-set'; + $this->aclPrincipalPropSetReport($path, $report); + return false; + + } + + } + + /** + * This method is responsible for handling the 'ACL' event. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + function httpAcl(RequestInterface $request, ResponseInterface $response) { + + $path = $request->getPath(); + $body = $request->getBodyAsString(); + + if (!$body) { + throw new DAV\Exception\BadRequest('XML body expected in ACL request'); + } + + $acl = $this->server->xml->expect('{DAV:}acl', $body); + $newAcl = $acl->getPrivileges(); + + // Normalizing urls + foreach ($newAcl as $k => $newAce) { + $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']); + } + $node = $this->server->tree->getNodeForPath($path); + + if (!$node instanceof IACL) { + throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method'); + } + + $oldAcl = $this->getACL($node); + + $supportedPrivileges = $this->getFlatPrivilegeSet($node); + + /* Checking if protected principals from the existing principal set are + not overwritten. */ + foreach ($oldAcl as $oldAce) { + + if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; + + $found = false; + foreach ($newAcl as $newAce) { + if ( + $newAce['privilege'] === $oldAce['privilege'] && + $newAce['principal'] === $oldAce['principal'] && + $newAce['protected'] + ) + $found = true; + } + + if (!$found) + throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); + + } + + foreach ($newAcl as $newAce) { + + // Do we recognize the privilege + if (!isset($supportedPrivileges[$newAce['privilege']])) { + throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server'); + } + + if ($supportedPrivileges[$newAce['privilege']]['abstract']) { + throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege'); + } + + // Looking up the principal + try { + $principal = $this->server->tree->getNodeForPath($newAce['principal']); + } catch (NotFound $e) { + throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); + } + if (!($principal instanceof IPrincipal)) { + throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); + } + + } + $node->setACL($newAcl); + + $response->setStatus(200); + + // Breaking the event chain, because we handled this method. + return false; + + } + + /* }}} */ + + /* Reports {{{ */ + + /** + * The principal-match report is defined in RFC3744, section 9.3. + * + * This report allows a client to figure out based on the current user, + * or a principal URL, the principal URL and principal URLs of groups that + * principal belongs to. + * + * @param string $path + * @param Xml\Request\PrincipalMatchReport $report + * @return void + */ + protected function principalMatchReport($path, Xml\Request\PrincipalMatchReport $report) { + + $depth = $this->server->getHTTPDepth(0); + if ($depth !== 0) { + throw new BadRequest('The principal-match report is only defined on Depth: 0'); + } + + $currentPrincipals = $this->getCurrentUserPrincipals(); + + $result = []; + + if ($report->type === Xml\Request\PrincipalMatchReport::SELF) { + + // Finding all principals under the request uri that match the + // current principal. + foreach ($currentPrincipals as $currentPrincipal) { + + if ($currentPrincipal === $path || strpos($currentPrincipal, $path . '/') === 0) { + $result[] = $currentPrincipal; + } + + } + + } else { + + // We need to find all resources that have a property that matches + // one of the current principals. + $candidates = $this->server->getPropertiesForPath( + $path, + [$report->principalProperty], + 1 + ); + + foreach ($candidates as $candidate) { + + if (!isset($candidate[200][$report->principalProperty])) { + continue; + } + + $hrefs = $candidate[200][$report->principalProperty]; + + if (!$hrefs instanceof Href) { + continue; + } + + foreach ($hrefs->getHrefs() as $href) { + if (in_array(trim($href, '/'), $currentPrincipals)) { + $result[] = $candidate['href']; + continue 2; + } + } + } + + } + + $responses = []; + + foreach ($result as $item) { + + $properties = []; + + if ($report->properties) { + + $foo = $this->server->getPropertiesForPath($item, $report->properties); + $foo = $foo[0]; + $item = $foo['href']; + unset($foo['href']); + $properties = $foo; + + } + + $responses[] = new DAV\Xml\Element\Response( + $item, + $properties, + '200' + ); + + } + + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setBody( + $this->server->xml->write( + '{DAV:}multistatus', + $responses, + $this->server->getBaseUri() + ) + ); + + + } + + /** + * The expand-property report is defined in RFC3253 section 3.8. + * + * This report is very similar to a standard PROPFIND. The difference is + * that it has the additional ability to look at properties containing a + * {DAV:}href element, follow that property and grab additional elements + * there. + * + * Other rfc's, such as ACL rely on this report, so it made sense to put + * it in this plugin. + * + * @param string $path + * @param Xml\Request\ExpandPropertyReport $report + * @return void + */ + protected function expandPropertyReport($path, $report) { + + $depth = $this->server->getHTTPDepth(0); + + $result = $this->expandProperties($path, $report->properties, $depth); + + $xml = $this->server->xml->write( + '{DAV:}multistatus', + new DAV\Xml\Response\MultiStatus($result), + $this->server->getBaseUri() + ); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setBody($xml); + + } + + /** + * This method expands all the properties and returns + * a list with property values + * + * @param array $path + * @param array $requestedProperties the list of required properties + * @param int $depth + * @return array + */ + protected function expandProperties($path, array $requestedProperties, $depth) { + + $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); + + $result = []; + + foreach ($foundProperties as $node) { + + foreach ($requestedProperties as $propertyName => $childRequestedProperties) { + + // We're only traversing if sub-properties were requested + if (count($childRequestedProperties) === 0) continue; + + // We only have to do the expansion if the property was found + // and it contains an href element. + if (!array_key_exists($propertyName, $node[200])) continue; + + if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) { + continue; + } + + $childHrefs = $node[200][$propertyName]->getHrefs(); + $childProps = []; + + foreach ($childHrefs as $href) { + // Gathering the result of the children + $childProps[] = [ + 'name' => '{DAV:}response', + 'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0] + ]; + } + + // Replacing the property with its expanded form. + $node[200][$propertyName] = $childProps; + + } + $result[] = new DAV\Xml\Element\Response($node['href'], $node); + + } + + return $result; + + } + + /** + * principalSearchPropertySetReport + * + * This method responsible for handing the + * {DAV:}principal-search-property-set report. This report returns a list + * of properties the client may search on, using the + * {DAV:}principal-property-search report. + * + * @param string $path + * @param Xml\Request\PrincipalSearchPropertySetReport $report + * @return void + */ + protected function principalSearchPropertySetReport($path, $report) { + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth !== 0) { + throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); + } + + $writer = $this->server->xml->getWriter(); + $writer->openMemory(); + $writer->startDocument(); + + $writer->startElement('{DAV:}principal-search-property-set'); + + foreach ($this->principalSearchPropertySet as $propertyName => $description) { + + $writer->startElement('{DAV:}principal-search-property'); + $writer->startElement('{DAV:}prop'); + + $writer->writeElement($propertyName); + + $writer->endElement(); // prop + + if ($description) { + $writer->write([[ + 'name' => '{DAV:}description', + 'value' => $description, + 'attributes' => ['xml:lang' => 'en'] + ]]); + } + + $writer->endElement(); // principal-search-property + + + } + + $writer->endElement(); // principal-search-property-set + + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setStatus(200); + $this->server->httpResponse->setBody($writer->outputMemory()); + + } + + /** + * principalPropertySearchReport + * + * This method is responsible for handing the + * {DAV:}principal-property-search report. This report can be used for + * clients to search for groups of principals, based on the value of one + * or more properties. + * + * @param string $path + * @param Xml\Request\PrincipalPropertySearchReport $report + * @return void + */ + protected function principalPropertySearchReport($path, Xml\Request\PrincipalPropertySearchReport $report) { + + if ($report->applyToPrincipalCollectionSet) { + $path = null; + } + if ($this->server->getHttpDepth('0') !== 0) { + throw new BadRequest('Depth must be 0'); + } + $result = $this->principalSearch( + $report->searchProperties, + $report->properties, + $path, + $report->test + ); + + $prefer = $this->server->getHTTPPrefer(); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); + $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal')); + + } + + /** + * aclPrincipalPropSet REPORT + * + * This method is responsible for handling the {DAV:}acl-principal-prop-set + * REPORT, as defined in: + * + * https://tools.ietf.org/html/rfc3744#section-9.2 + * + * This REPORT allows a user to quickly fetch information about all + * principals specified in the access control list. Most commonly this + * is used to for example generate a UI with ACL rules, allowing you + * to show names for principals for every entry. + * + * @param string $path + * @param Xml\Request\AclPrincipalPropSetReport $report + * @return void + */ + protected function aclPrincipalPropSetReport($path, Xml\Request\AclPrincipalPropSetReport $report) { + + if ($this->server->getHTTPDepth(0) !== 0) { + throw new BadRequest('The {DAV:}acl-principal-prop-set REPORT only supports Depth 0'); + } + + // Fetching ACL rules for the given path. We're using the property + // API and not the local getACL, because it will ensure that all + // business rules and restrictions are applied. + $acl = $this->server->getProperties($path, '{DAV:}acl'); + + if (!$acl || !isset($acl['{DAV:}acl'])) { + throw new Forbidden('Could not fetch ACL rules for this path'); + } + + $principals = []; + foreach ($acl['{DAV:}acl']->getPrivileges() as $ace) { + + if ($ace['principal'][0] === '{') { + // It's not a principal, it's one of the special rules such as {DAV:}authenticated + continue; + } + + $principals[] = $ace['principal']; + + } + + $properties = $this->server->getPropertiesForMultiplePaths( + $principals, + $report->properties + ); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setBody( + $this->server->generateMultiStatus($properties) + ); + + } + + + /* }}} */ + + /** + * This method is used to generate HTML output for the + * DAV\Browser\Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param DAV\INode $node + * @param string $output + * @return bool + */ + function htmlActionsPanel(DAV\INode $node, &$output) { + + if (!$node instanceof PrincipalCollection) + return; + + $output .= '
    +

    Create new principal

    + + +
    +
    +
    + +
    + '; + + return false; + + } + + /** + * Returns a bunch of meta-data about the plugin. + * + * Providing this information is optional, and is mainly displayed by the + * Browser plugin. + * + * The description key in the returned array may contain html and will not + * be sanitized. + * + * @return array + */ + function getPluginInfo() { + + return [ + 'name' => $this->getPluginName(), + 'description' => 'Adds support for WebDAV ACL (rfc3744)', + 'link' => 'http://sabre.io/dav/acl/', + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php new file mode 100644 index 00000000000..d7db9499946 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php @@ -0,0 +1,221 @@ +principalBackend = $principalBackend; + $this->principalProperties = $principalProperties; + + } + + /** + * Returns the full principal url + * + * @return string + */ + function getPrincipalUrl() { + + return $this->principalProperties['uri']; + + } + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + function getAlternateUriSet() { + + $uris = []; + if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) { + + $uris = $this->principalProperties['{DAV:}alternate-URI-set']; + + } + + if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { + $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; + } + + return array_unique($uris); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + function getGroupMembership() { + + return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $groupMembers + * @return void + */ + function setGroupMemberSet(array $groupMembers) { + + $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers); + + } + + /** + * Returns this principals name. + * + * @return string + */ + function getName() { + + $uri = $this->principalProperties['uri']; + list(, $name) = URLUtil::splitPath($uri); + return $name; + + } + + /** + * Returns the name of the user + * + * @return string + */ + function getDisplayName() { + + if (isset($this->principalProperties['{DAV:}displayname'])) { + return $this->principalProperties['{DAV:}displayname']; + } else { + return $this->getName(); + } + + } + + /** + * Returns a list of properties + * + * @param array $requestedProperties + * @return array + */ + function getProperties($requestedProperties) { + + $newProperties = []; + foreach ($requestedProperties as $propName) { + + if (isset($this->principalProperties[$propName])) { + $newProperties[$propName] = $this->principalProperties[$propName]; + } + + } + + return $newProperties; + + } + + /** + * Updates properties on this node. + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * To update specific properties, call the 'handle' method on this object. + * Read the PropPatch documentation for more information. + * + * @param DAV\PropPatch $propPatch + * @return void + */ + function propPatch(DAV\PropPatch $propPatch) { + + return $this->principalBackend->updatePrincipal( + $this->principalProperties['uri'], + $propPatch + ); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + function getOwner() { + + return $this->principalProperties['uri']; + + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php new file mode 100644 index 00000000000..9bf9ba4453c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php @@ -0,0 +1,53 @@ +searchPrincipals( + $principalPrefix, + ['{http://sabredav.org/ns}email-address' => substr($uri, 7)] + ); + + if ($result) { + return $result[0]; + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php new file mode 100644 index 00000000000..40b6e33ead9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php @@ -0,0 +1,141 @@ + [ + 'dbField' => 'displayname', + ], + + /** + * This is the users' primary email-address. + */ + '{http://sabredav.org/ns}email-address' => [ + 'dbField' => 'email', + ], + ]; + + /** + * Sets up the backend. + * + * @param \PDO $pdo + */ + function __construct(\PDO $pdo) { + + $this->pdo = $pdo; + + } + + /** + * Returns a list of principals based on a prefix. + * + * This prefix will often contain something like 'principals'. You are only + * expected to return principals that are in this base path. + * + * You are expected to return at least a 'uri' for every user, you can + * return any additional properties if you wish so. Common properties are: + * {DAV:}displayname + * {http://sabredav.org/ns}email-address - This is a custom SabreDAV + * field that's actualy injected in a number of other properties. If + * you have an email address, use this property. + * + * @param string $prefixPath + * @return array + */ + function getPrincipalsByPrefix($prefixPath) { + + $fields = [ + 'uri', + ]; + + foreach ($this->fieldMap as $key => $value) { + $fields[] = $value['dbField']; + } + $result = $this->pdo->query('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName); + + $principals = []; + + while ($row = $result->fetch(\PDO::FETCH_ASSOC)) { + + // Checking if the principal is in the prefix + list($rowPrefix) = URLUtil::splitPath($row['uri']); + if ($rowPrefix !== $prefixPath) continue; + + $principal = [ + 'uri' => $row['uri'], + ]; + foreach ($this->fieldMap as $key => $value) { + if ($row[$value['dbField']]) { + $principal[$key] = $row[$value['dbField']]; + } + } + $principals[] = $principal; + + } + + return $principals; + + } + + /** + * Returns a specific principal, specified by it's path. + * The returned structure should be the exact same as from + * getPrincipalsByPrefix. + * + * @param string $path + * @return array + */ + function getPrincipalByPath($path) { + + $fields = [ + 'id', + 'uri', + ]; + + foreach ($this->fieldMap as $key => $value) { + $fields[] = $value['dbField']; + } + $stmt = $this->pdo->prepare('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName . ' WHERE uri = ?'); + $stmt->execute([$path]); + + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + if (!$row) return; + + $principal = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + ]; + foreach ($this->fieldMap as $key => $value) { + if ($row[$value['dbField']]) { + $principal[$key] = $row[$value['dbField']]; + } + } + return $principal; + + } + + /** + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param string $path + * @param DAV\PropPatch $propPatch + */ + function updatePrincipal($path, DAV\PropPatch $propPatch) { + + $propPatch->handle(array_keys($this->fieldMap), function($properties) use ($path) { + + $query = "UPDATE " . $this->tableName . " SET "; + $first = true; + + $values = []; + + foreach ($properties as $key => $value) { + + $dbField = $this->fieldMap[$key]['dbField']; + + if (!$first) { + $query .= ', '; + } + $first = false; + $query .= $dbField . ' = :' . $dbField; + $values[$dbField] = $value; + + } + + $query .= " WHERE uri = :uri"; + $values['uri'] = $path; + + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + return true; + + }); + + } + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * By default, if multiple properties are submitted to this method, the + * various properties should be combined with 'AND'. If $test is set to + * 'anyof', it should be combined using 'OR'. + * + * This method should simply return an array with full principal uri's. + * + * If somebody attempted to search on a property the backend does not + * support, you should simply return 0 results. + * + * You can also just return 0 results if you choose to not support + * searching at all, but keep in mind that this may stop certain features + * from working. + * + * @param string $prefixPath + * @param array $searchProperties + * @param string $test + * @return array + */ + function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { + if (count($searchProperties) == 0) return []; //No criteria + + $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE '; + $values = []; + foreach ($searchProperties as $property => $value) { + switch ($property) { + case '{DAV:}displayname' : + $column = "displayname"; + break; + case '{http://sabredav.org/ns}email-address' : + $column = "email"; + break; + default : + // Unsupported property + return []; + } + if (count($values) > 0) $query .= (strcmp($test, "anyof") == 0 ? " OR " : " AND "); + $query .= 'lower(' . $column . ') LIKE lower(?)'; + $values[] = '%' . $value . '%'; + + } + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + $principals = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + // Checking if the principal is in the prefix + list($rowPrefix) = URLUtil::splitPath($row['uri']); + if ($rowPrefix !== $prefixPath) continue; + + $principals[] = $row['uri']; + + } + + return $principals; + + } + + /** + * Finds a principal by its URI. + * + * This method may receive any type of uri, but mailto: addresses will be + * the most common. + * + * Implementation of this API is optional. It is currently used by the + * CalDAV system to find principals based on their email addresses. If this + * API is not implemented, some features may not work correctly. + * + * This method must return a relative principal path, or null, if the + * principal was not found or you refuse to find it. + * + * @param string $uri + * @param string $principalPrefix + * @return string + */ + function findByUri($uri, $principalPrefix) { + $value = null; + $scheme = null; + list($scheme, $value) = explode(":", $uri, 2); + if (empty($value)) return null; + + $uri = null; + switch ($scheme){ + case "mailto": + $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE lower(email)=lower(?)'; + $stmt = $this->pdo->prepare($query); + $stmt->execute([$value]); + + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // Checking if the principal is in the prefix + list($rowPrefix) = URLUtil::splitPath($row['uri']); + if ($rowPrefix !== $principalPrefix) continue; + + $uri = $row['uri']; + break; //Stop on first match + } + break; + default: + //unsupported uri scheme + return null; + } + return $uri; + } + + /** + * Returns the list of members for a group-principal + * + * @param string $principal + * @return array + */ + function getGroupMemberSet($principal) { + + $principal = $this->getPrincipalByPath($principal); + if (!$principal) throw new DAV\Exception('Principal not found'); + + $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM ' . $this->groupMembersTableName . ' AS groupmembers LEFT JOIN ' . $this->tableName . ' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); + $stmt->execute([$principal['id']]); + + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $result[] = $row['uri']; + } + return $result; + + } + + /** + * Returns the list of groups a principal is a member of + * + * @param string $principal + * @return array + */ + function getGroupMembership($principal) { + + $principal = $this->getPrincipalByPath($principal); + if (!$principal) throw new DAV\Exception('Principal not found'); + + $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM ' . $this->groupMembersTableName . ' AS groupmembers LEFT JOIN ' . $this->tableName . ' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); + $stmt->execute([$principal['id']]); + + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $result[] = $row['uri']; + } + return $result; + + } + + /** + * Updates the list of group members for a group principal. + * + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members + * @return void + */ + function setGroupMemberSet($principal, array $members) { + + // Grabbing the list of principal id's. + $stmt = $this->pdo->prepare('SELECT id, uri FROM ' . $this->tableName . ' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); + $stmt->execute(array_merge([$principal], $members)); + + $memberIds = []; + $principalId = null; + + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($row['uri'] == $principal) { + $principalId = $row['id']; + } else { + $memberIds[] = $row['id']; + } + } + if (!$principalId) throw new DAV\Exception('Principal not found'); + + // Wiping out old members + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->groupMembersTableName . ' WHERE principal_id = ?;'); + $stmt->execute([$principalId]); + + foreach ($memberIds as $memberId) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->groupMembersTableName . ' (principal_id, member_id) VALUES (?, ?);'); + $stmt->execute([$principalId, $memberId]); + + } + + } + + /** + * Creates a new principal. + * + * This method receives a full path for the new principal. The mkCol object + * contains any additional webdav properties specified during the creation + * of the principal. + * + * @param string $path + * @param MkCol $mkCol + * @return void + */ + function createPrincipal($path, MkCol $mkCol) { + + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (uri) VALUES (?)'); + $stmt->execute([$path]); + $this->updatePrincipal($path, $mkCol); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php new file mode 100644 index 00000000000..ee5b88a904e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php @@ -0,0 +1,98 @@ +principalBackend, $principal); + + } + + /** + * Creates a new collection. + * + * This method will receive a MkCol object with all the information about + * the new collection that's being created. + * + * The MkCol object contains information about the resourceType of the new + * collection. If you don't support the specified resourceType, you should + * throw Exception\InvalidResourceType. + * + * The object also contains a list of WebDAV properties for the new + * collection. + * + * You should call the handle() method on this object to specify exactly + * which properties you are storing. This allows the system to figure out + * exactly which properties you didn't store, which in turn allows other + * plugins (such as the propertystorage plugin) to handle storing the + * property for you. + * + * @param string $name + * @param MkCol $mkCol + * @throws InvalidResourceType + * @return void + */ + function createExtendedCollection($name, MkCol $mkCol) { + + if (!$mkCol->hasResourceType('{DAV:}principal')) { + throw new InvalidResourceType('Only resources of type {DAV:}principal may be created here'); + } + + $this->principalBackend->createPrincipal( + $this->principalPrefix . '/' . $name, + $mkCol + ); + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + function getACL() { + return [ + [ + 'principal' => '{DAV:}authenticated', + 'privilege' => '{DAV:}read', + 'protected' => true, + ], + ]; + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php new file mode 100644 index 00000000000..0e1c30ccfea --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php @@ -0,0 +1,277 @@ +privileges = $privileges; + $this->prefixBaseUrl = $prefixBaseUrl; + + } + + /** + * Returns the list of privileges for this property + * + * @return array + */ + function getPrivileges() { + + return $this->privileges; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->privileges as $ace) { + + $this->serializeAce($writer, $ace); + + } + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + ob_start(); + echo ""; + echo ""; + foreach ($this->privileges as $privilege) { + + echo ''; + // if it starts with a {, it's a special principal + if ($privilege['principal'][0] === '{') { + echo ''; + } else { + echo ''; + } + echo ''; + echo ''; + echo ''; + + } + echo "
    PrincipalPrivilege
    ', $html->xmlName($privilege['principal']), '', $html->link($privilege['principal']), '', $html->xmlName($privilege['privilege']), ''; + if (!empty($privilege['protected'])) echo '(protected)'; + echo '
    "; + return ob_get_clean(); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elementMap = [ + '{DAV:}ace' => 'Sabre\Xml\Element\KeyValue', + '{DAV:}privilege' => 'Sabre\Xml\Element\Elements', + '{DAV:}principal' => 'Sabre\DAVACL\Xml\Property\Principal', + ]; + + $privileges = []; + + foreach ((array)$reader->parseInnerTree($elementMap) as $element) { + + if ($element['name'] !== '{DAV:}ace') { + continue; + } + $ace = $element['value']; + + if (empty($ace['{DAV:}principal'])) { + throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); + } + $principal = $ace['{DAV:}principal']; + + switch ($principal->getType()) { + case Principal::HREF : + $principal = $principal->getHref(); + break; + case Principal::AUTHENTICATED : + $principal = '{DAV:}authenticated'; + break; + case Principal::UNAUTHENTICATED : + $principal = '{DAV:}unauthenticated'; + break; + case Principal::ALL : + $principal = '{DAV:}all'; + break; + + } + + $protected = array_key_exists('{DAV:}protected', $ace); + + if (!isset($ace['{DAV:}grant'])) { + throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); + } + foreach ($ace['{DAV:}grant'] as $elem) { + if ($elem['name'] !== '{DAV:}privilege') { + continue; + } + + foreach ($elem['value'] as $priv) { + $privileges[] = [ + 'principal' => $principal, + 'protected' => $protected, + 'privilege' => $priv, + ]; + } + + } + + } + + return new self($privileges); + + } + + /** + * Serializes a single access control entry. + * + * @param Writer $writer + * @param array $ace + * @return void + */ + private function serializeAce(Writer $writer, array $ace) { + + $writer->startElement('{DAV:}ace'); + + switch ($ace['principal']) { + case '{DAV:}authenticated' : + $principal = new Principal(Principal::AUTHENTICATED); + break; + case '{DAV:}unauthenticated' : + $principal = new Principal(Principal::UNAUTHENTICATED); + break; + case '{DAV:}all' : + $principal = new Principal(Principal::ALL); + break; + default: + $principal = new Principal(Principal::HREF, $ace['principal']); + break; + } + + $writer->writeElement('{DAV:}principal', $principal); + $writer->startElement('{DAV:}grant'); + $writer->startElement('{DAV:}privilege'); + + $writer->writeElement($ace['privilege']); + + $writer->endElement(); // privilege + $writer->endElement(); // grant + + if (!empty($ace['protected'])) { + $writer->writeElement('{DAV:}protected'); + } + + $writer->endElement(); // ace + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php new file mode 100644 index 00000000000..8d5854c23c0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php @@ -0,0 +1,45 @@ +writeElement('{DAV:}grant-only'); + $writer->writeElement('{DAV:}no-invert'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php new file mode 100644 index 00000000000..74c09cee150 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php @@ -0,0 +1,159 @@ +privileges = $privileges; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->privileges as $privName) { + + $writer->startElement('{DAV:}privilege'); + $writer->writeElement($privName); + $writer->endElement(); + + } + + + } + + /** + * Returns true or false, whether the specified principal appears in the + * list. + * + * @param string $privilegeName + * @return bool + */ + function has($privilegeName) { + + return in_array($privilegeName, $this->privileges); + + } + + /** + * Returns the list of privileges. + * + * @return array + */ + function getValue() { + + return $this->privileges; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = []; + + $tree = $reader->parseInnerTree(['{DAV:}privilege' => 'Sabre\\Xml\\Element\\Elements']); + foreach ($tree as $element) { + if ($element['name'] !== '{DAV:}privilege') { + continue; + } + $result[] = $element['value'][0]; + } + return new self($result); + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + return implode( + ', ', + array_map([$html, 'xmlName'], $this->getValue()) + ); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php new file mode 100644 index 00000000000..04d22165d27 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php @@ -0,0 +1,196 @@ +type = $type; + if ($type === self::HREF && is_null($href)) { + throw new DAV\Exception('The href argument must be specified for the HREF principal type.'); + } + if ($href) { + $href = rtrim($href, '/') . '/'; + parent::__construct($href); + } + + } + + /** + * Returns the principal type + * + * @return int + */ + function getType() { + + return $this->type; + + } + + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + switch ($this->type) { + + case self::UNAUTHENTICATED : + $writer->writeElement('{DAV:}unauthenticated'); + break; + case self::AUTHENTICATED : + $writer->writeElement('{DAV:}authenticated'); + break; + case self::HREF : + parent::xmlSerialize($writer); + break; + case self::ALL : + $writer->writeElement('{DAV:}all'); + break; + } + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + switch ($this->type) { + + case self::UNAUTHENTICATED : + return 'unauthenticated'; + case self::AUTHENTICATED : + return 'authenticated'; + case self::HREF : + return parent::toHtml($html); + case self::ALL : + return 'all'; + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called staticly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $tree = $reader->parseInnerTree()[0]; + + switch ($tree['name']) { + case '{DAV:}unauthenticated' : + return new self(self::UNAUTHENTICATED); + case '{DAV:}authenticated' : + return new self(self::AUTHENTICATED); + case '{DAV:}href': + return new self(self::HREF, $tree['value']); + case '{DAV:}all': + return new self(self::ALL); + default : + throw new BadRequest('Unknown or unsupported principal type: ' . $tree['name']); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php new file mode 100644 index 00000000000..b963cc8c3ba --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php @@ -0,0 +1,160 @@ +privileges = $privileges; + + } + + /** + * Returns the privilege value. + * + * @return array + */ + function getValue() { + + return $this->privileges; + + } + + /** + * The xmlSerialize method is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + $this->serializePriv($writer, '{DAV:}all', ['aggregates' => $this->privileges]); + + } + + /** + * Generate html representation for this value. + * + * The html output is 100% trusted, and no effort is being made to sanitize + * it. It's up to the implementor to sanitize user provided values. + * + * The output must be in UTF-8. + * + * The baseUri parameter is a url to the root of the application, and can + * be used to construct local links. + * + * @param HtmlOutputHelper $html + * @return string + */ + function toHtml(HtmlOutputHelper $html) { + + $traverse = function($privName, $priv) use (&$traverse, $html) { + echo "
  • "; + echo $html->xmlName($privName); + if (isset($priv['abstract']) && $priv['abstract']) { + echo " (abstract)"; + } + if (isset($priv['description'])) { + echo " " . $html->h($priv['description']); + } + if (isset($priv['aggregates'])) { + echo "\n
      \n"; + foreach ($priv['aggregates'] as $subPrivName => $subPriv) { + $traverse($subPrivName, $subPriv); + } + echo "
    "; + } + echo "
  • \n"; + }; + + ob_start(); + echo "
      "; + $traverse('{DAV:}all', ['aggregates' => $this->getValue()]); + echo "
    \n"; + + return ob_get_clean(); + + } + + + + /** + * Serializes a property + * + * This is a recursive function. + * + * @param Writer $writer + * @param string $privName + * @param array $privilege + * @return void + */ + private function serializePriv(Writer $writer, $privName, $privilege) { + + $writer->startElement('{DAV:}supported-privilege'); + + $writer->startElement('{DAV:}privilege'); + $writer->writeElement($privName); + $writer->endElement(); // privilege + + if (!empty($privilege['abstract'])) { + $writer->writeElement('{DAV:}abstract'); + } + if (!empty($privilege['description'])) { + $writer->writeElement('{DAV:}description', $privilege['description']); + } + if (isset($privilege['aggregates'])) { + foreach ($privilege['aggregates'] as $subPrivName => $subPrivilege) { + $this->serializePriv($writer, $subPrivName, $subPrivilege); + } + } + + $writer->endElement(); // supported-privilege + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php new file mode 100644 index 00000000000..0aa2f29a559 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php @@ -0,0 +1,67 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $reader->pushContext(); + $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\enum'; + + $elems = Deserializer\keyValue( + $reader, + 'DAV:' + ); + + $reader->popContext(); + + $report = new self(); + + if (!empty($elems['prop'])) { + $report->properties = $elems['prop']; + } + + return $report; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php new file mode 100644 index 00000000000..a9938ba5bf0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php @@ -0,0 +1,103 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree(); + + $obj = new self(); + $obj->properties = self::traverse($elems); + + return $obj; + + } + + /** + * This method is used by deserializeXml, to recursively parse the + * {DAV:}property elements. + * + * @param array $elems + * @return void + */ + private static function traverse($elems) { + + $result = []; + + foreach ($elems as $elem) { + + if ($elem['name'] !== '{DAV:}property') { + continue; + } + + $namespace = isset($elem['attributes']['namespace']) ? + $elem['attributes']['namespace'] : + 'DAV:'; + + $propName = '{' . $namespace . '}' . $elem['attributes']['name']; + + $value = null; + if (is_array($elem['value'])) { + $value = self::traverse($elem['value']); + } + + $result[$propName] = $value; + + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php new file mode 100644 index 00000000000..1be15ab2da6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php @@ -0,0 +1,107 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $reader->pushContext(); + $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\enum'; + + $elems = Deserializer\keyValue( + $reader, + 'DAV:' + ); + + $reader->popContext(); + + $principalMatch = new self(); + + if (array_key_exists('self', $elems)) { + $principalMatch->type = self::SELF; + } + + if (array_key_exists('principal-property', $elems)) { + $principalMatch->type = self::PRINCIPAL_PROPERTY; + $principalMatch->principalProperty = $elems['principal-property'][0]['name']; + } + + if (!empty($elems['prop'])) { + $principalMatch->properties = $elems['prop']; + } + + return $principalMatch; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php new file mode 100644 index 00000000000..b0cf0e408ff --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php @@ -0,0 +1,127 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $self = new self(); + + $foundSearchProp = false; + $self->test = 'allof'; + if ($reader->getAttribute('test') === 'anyof') { + $self->test = 'anyof'; + } + + $elemMap = [ + '{DAV:}property-search' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + ]; + + foreach ($reader->parseInnerTree($elemMap) as $elem) { + + switch ($elem['name']) { + + case '{DAV:}prop' : + $self->properties = array_keys($elem['value']); + break; + case '{DAV:}property-search' : + $foundSearchProp = true; + // This property has two sub-elements: + // {DAV:}prop - The property to be searched on. This may + // also be more than one + // {DAV:}match - The value to match with + if (!isset($elem['value']['{DAV:}prop']) || !isset($elem['value']['{DAV:}match'])) { + throw new BadRequest('The {DAV:}property-search element must contain one {DAV:}match and one {DAV:}prop element'); + } + foreach ($elem['value']['{DAV:}prop'] as $propName => $discard) { + $self->searchProperties[$propName] = $elem['value']['{DAV:}match']; + } + break; + case '{DAV:}apply-to-principal-collection-set' : + $self->applyToPrincipalCollectionSet = true; + break; + + } + + } + if (!$foundSearchProp) { + throw new BadRequest('The {DAV:}principal-property-search report must contain at least 1 {DAV:}property-search element'); + } + + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php new file mode 100644 index 00000000000..64d1f7f861e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php @@ -0,0 +1,58 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + if (!$reader->isEmptyElement) { + throw new BadRequest('The {DAV:}principal-search-property-set element must be empty'); + } + + // The element is actually empty, so there's not much to do. + $reader->next(); + + $self = new self(); + return $self; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php new file mode 100644 index 00000000000..406dbe0e8e2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php @@ -0,0 +1,1431 @@ +dropTables([ + 'calendarobjects', + 'calendars', + 'calendarinstances', + 'calendarchanges', + 'calendarsubscriptions', + 'schedulingobjects', + ]); + $this->createSchema('calendars'); + + $this->pdo = $this->getDb(); + + } + + function testConstruct() { + + $backend = new PDO($this->pdo); + $this->assertTrue($backend instanceof PDO); + + } + + /** + * @depends testConstruct + */ + function testGetCalendarsForUserNoCalendars() { + + $backend = new PDO($this->pdo); + $calendars = $backend->getCalendarsForUser('principals/user2'); + $this->assertEquals([], $calendars); + + } + + /** + * @depends testConstruct + */ + function testCreateCalendarAndFetch() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']), + '{DAV:}displayname' => 'Hello!', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + ]); + $calendars = $backend->getCalendarsForUser('principals/user2'); + + $elementCheck = [ + 'uri' => 'somerandomid', + '{DAV:}displayname' => 'Hello!', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => '', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + 'share-access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + ]; + + $this->assertInternalType('array', $calendars); + $this->assertEquals(1, count($calendars)); + + foreach ($elementCheck as $name => $value) { + + $this->assertArrayHasKey($name, $calendars[0]); + $this->assertEquals($value, $calendars[0][$name]); + + } + + } + + /** + * @depends testConstruct + */ + function testUpdateCalendarAndFetch() { + + $backend = new PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'myCalendar', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + ]); + + // Updating the calendar + $backend->updateCalendar($newId, $propPatch); + $result = $propPatch->commit(); + + // Verifying the result of the update + $this->assertTrue($result); + + // Fetching all calendars from this user + $calendars = $backend->getCalendarsForUser('principals/user2'); + + // Checking if all the information is still correct + $elementCheck = [ + 'id' => $newId, + 'uri' => 'somerandomid', + '{DAV:}displayname' => 'myCalendar', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => '', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => '', + '{http://calendarserver.org/ns/}getctag' => 'http://sabre.io/ns/sync/2', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + ]; + + $this->assertInternalType('array', $calendars); + $this->assertEquals(1, count($calendars)); + + foreach ($elementCheck as $name => $value) { + + $this->assertArrayHasKey($name, $calendars[0]); + $this->assertEquals($value, $calendars[0][$name]); + + } + + } + + /** + * @depends testConstruct + * @expectedException \InvalidArgumentException + */ + function testUpdateCalendarBadId() { + + $backend = new PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'myCalendar', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + ]); + + // Updating the calendar + $backend->updateCalendar('raaaa', $propPatch); + + } + + /** + * @depends testUpdateCalendarAndFetch + */ + function testUpdateCalendarUnknownProperty() { + + $backend = new PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'myCalendar', + '{DAV:}yourmom' => 'wittycomment', + ]); + + // Updating the calendar + $backend->updateCalendar($newId, $propPatch); + $propPatch->commit(); + + // Verifying the result of the update + $this->assertEquals([ + '{DAV:}yourmom' => 403, + '{DAV:}displayname' => 424, + ], $propPatch->getResult()); + + } + + /** + * @depends testCreateCalendarAndFetch + */ + function testDeleteCalendar() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']), + '{DAV:}displayname' => 'Hello!', + ]); + + $backend->deleteCalendar($returnedId); + + $calendars = $backend->getCalendarsForUser('principals/user2'); + $this->assertEquals([], $calendars); + + } + + /** + * @depends testCreateCalendarAndFetch + * @expectedException \InvalidArgumentException + */ + function testDeleteCalendarBadID() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']), + '{DAV:}displayname' => 'Hello!', + ]); + + $backend->deleteCalendar('bad-id'); + + } + + /** + * @depends testCreateCalendarAndFetch + * @expectedException \Sabre\DAV\Exception + */ + function testCreateCalendarIncorrectComponentSet() {; + + $backend = new PDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2', 'somerandomid', [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => 'blabla', + ]); + + } + + function testCreateCalendarObject() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('20120101'), + 'lastoccurence' => strtotime('20120101') + (3600 * 24), + 'componenttype' => 'VEVENT', + ], $row); + + } + function testGetMultipleObjects() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'id-1', $object); + $backend->createCalendarObject($returnedId, 'id-2', $object); + + $check = [ + [ + 'id' => 1, + 'etag' => '"' . md5($object) . '"', + 'uri' => 'id-1', + 'size' => strlen($object), + 'calendardata' => $object, + 'lastmodified' => null, + ], + [ + 'id' => 2, + 'etag' => '"' . md5($object) . '"', + 'uri' => 'id-2', + 'size' => strlen($object), + 'calendardata' => $object, + 'lastmodified' => null, + ], + ]; + + $result = $backend->getMultipleCalendarObjects($returnedId, ['id-1', 'id-2']); + + foreach ($check as $index => $props) { + + foreach ($props as $key => $expected) { + + $actual = $result[$index][$key]; + + switch ($key) { + case 'lastmodified' : + $this->assertInternalType('int', $actual); + break; + case 'calendardata' : + if (is_resource($actual)) { + $actual = stream_get_contents($actual); + } + // no break intentional + default : + $this->assertEquals($expected, $actual); + + } + + } + + } + + } + + /** + * @depends testGetMultipleObjects + * @expectedException \InvalidArgumentException + */ + function testGetMultipleObjectsBadId() { + + $backend = new PDO($this->pdo); + $backend->getMultipleCalendarObjects('bad-id', ['foo-bar']); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectNoComponent() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectDuration() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nDURATION:P2D\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('20120101'), + 'lastoccurence' => strtotime('20120101') + (3600 * 48), + 'componenttype' => 'VEVENT', + ], $row); + + } + + /** + * @depends testCreateCalendarObject + * @expectedException \InvalidArgumentException + */ + function testCreateCalendarObjectBadId() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nDURATION:P2D\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject('bad-id', 'random-id', $object); + + } + + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectNoDTEND() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime('2012-01-01 10:00:00'), + 'componenttype' => 'VEVENT', + ], $row); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectWithDTEND() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND:20120101T110000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime('2012-01-01 11:00:00'), + 'componenttype' => 'VEVENT', + ], $row); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectInfiniteRecurrence() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nRRULE:FREQ=DAILY\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime(PDO::MAX_DATE), + 'componenttype' => 'VEVENT', + ], $row); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectEndingRecurrence() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND;VALUE=DATE-TIME:20120101T110000Z\r\nUID:foo\r\nRRULE:FREQ=DAILY;COUNT=1000\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => strtotime('2012-01-01 10:00:00'), + 'lastoccurence' => strtotime('2012-01-01 11:00:00') + (3600 * 24 * 999), + 'componenttype' => 'VEVENT', + ], $row); + + } + + /** + * @depends testCreateCalendarObject + */ + function testCreateCalendarObjectTask() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nDUE;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = \'random-id\''); + $row = $result->fetch(\PDO::FETCH_ASSOC); + if (is_resource($row['calendardata'])) { + $row['calendardata'] = stream_get_contents($row['calendardata']); + } + + $this->assertEquals([ + 'etag' => md5($object), + 'size' => strlen($object), + 'calendardata' => $object, + 'firstoccurence' => null, + 'lastoccurence' => null, + 'componenttype' => 'VTODO', + ], $row); + + } + + /** + * @depends testCreateCalendarObject + */ + function testGetCalendarObjects() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $data = $backend->getCalendarObjects($returnedId); + + $this->assertEquals(1, count($data)); + $data = $data[0]; + + $this->assertEquals('random-id', $data['uri']); + $this->assertEquals(strlen($object), $data['size']); + + } + + /** + * @depends testGetCalendarObjects + * @expectedException \InvalidArgumentException + */ + function testGetCalendarObjectsBadId() { + + $backend = new PDO($this->pdo); + $backend->getCalendarObjects('bad-id'); + + } + + /** + * @depends testGetCalendarObjects + * @expectedException \InvalidArgumentException + */ + function testGetCalendarObjectBadId() { + + $backend = new PDO($this->pdo); + $backend->getCalendarObject('bad-id', 'foo-bar'); + + } + + /** + * @depends testCreateCalendarObject + */ + function testGetCalendarObjectByUID() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $this->assertNull( + $backend->getCalendarObjectByUID('principals/user2', 'bar') + ); + $this->assertEquals( + 'somerandomid/random-id', + $backend->getCalendarObjectByUID('principals/user2', 'foo') + ); + + } + + /** + * @depends testCreateCalendarObject + */ + function testUpdateCalendarObject() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $object2 = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20130101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->updateCalendarObject($returnedId, 'random-id', $object2); + + $data = $backend->getCalendarObject($returnedId, 'random-id'); + + if (is_resource($data['calendardata'])) { + $data['calendardata'] = stream_get_contents($data['calendardata']); + } + + $this->assertEquals($object2, $data['calendardata']); + $this->assertEquals('random-id', $data['uri']); + + + } + + /** + * @depends testUpdateCalendarObject + * @expectedException \InvalidArgumentException + */ + function testUpdateCalendarObjectBadId() { + + $backend = new PDO($this->pdo); + $backend->updateCalendarObject('bad-id', 'object-id', 'objectdata'); + + } + + /** + * @depends testCreateCalendarObject + */ + function testDeleteCalendarObject() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->deleteCalendarObject($returnedId, 'random-id'); + + $data = $backend->getCalendarObject($returnedId, 'random-id'); + $this->assertNull($data); + + } + + /** + * @depends testDeleteCalendarObject + * @expectedException \InvalidArgumentException + */ + function testDeleteCalendarObjectBadId() { + + $backend = new PDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->deleteCalendarObject('bad-id', 'random-id'); + + } + + function testCalendarQueryNoResult() { + + $abstract = new PDO($this->pdo); + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VJOURNAL', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + ], $abstract->calendarQuery([1, 1], $filters)); + + } + + /** + * @expectedException \InvalidArgumentException + * @depends testCalendarQueryNoResult + */ + function testCalendarQueryBadId() { + + $abstract = new PDO($this->pdo); + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VJOURNAL', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $abstract->calendarQuery('bad-id', $filters); + + } + + function testCalendarQueryTodo() { + + $backend = new PDO($this->pdo); + $backend->createCalendarObject([1, 1], "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VTODO', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + "todo", + ], $backend->calendarQuery([1, 1], $filters)); + + } + function testCalendarQueryTodoNotMatch() { + + $backend = new PDO($this->pdo); + $backend->createCalendarObject([1, 1], "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VTODO', + 'comp-filters' => [], + 'prop-filters' => [ + [ + 'name' => 'summary', + 'text-match' => null, + 'time-range' => null, + 'param-filters' => [], + 'is-not-defined' => false, + ], + ], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + ], $backend->calendarQuery([1, 1], $filters)); + + } + + function testCalendarQueryNoFilter() { + + $backend = new PDO($this->pdo); + $backend->createCalendarObject([1, 1], "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $result = $backend->calendarQuery([1, 1], $filters); + $this->assertTrue(in_array('todo', $result)); + $this->assertTrue(in_array('event', $result)); + + } + + function testCalendarQueryTimeRange() { + + $backend = new PDO($this->pdo); + $backend->createCalendarObject([1, 1], "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('20120103'), + 'end' => new \DateTime('20120104'), + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + "event2", + ], $backend->calendarQuery([1, 1], $filters)); + + } + function testCalendarQueryTimeRangeNoEnd() { + + $backend = new PDO($this->pdo); + $backend->createCalendarObject([1, 1], "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject([1, 1], "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('20120102'), + 'end' => null, + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + "event2", + ], $backend->calendarQuery([1, 1], $filters)); + + } + + function testGetChanges() { + + $backend = new PDO($this->pdo); + $id = $backend->createCalendar( + 'principals/user1', + 'bla', + [] + ); + $result = $backend->getChangesForCalendar($id, null, 1); + + $this->assertEquals([ + 'syncToken' => 1, + 'modified' => [], + 'deleted' => [], + 'added' => [], + ], $result); + + $currentToken = $result['syncToken']; + + $dummyTodo = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($id, "todo1.ics", $dummyTodo); + $backend->createCalendarObject($id, "todo2.ics", $dummyTodo); + $backend->createCalendarObject($id, "todo3.ics", $dummyTodo); + $backend->updateCalendarObject($id, "todo1.ics", $dummyTodo); + $backend->deleteCalendarObject($id, "todo2.ics"); + + $result = $backend->getChangesForCalendar($id, $currentToken, 1); + + $this->assertEquals([ + 'syncToken' => 6, + 'modified' => ["todo1.ics"], + 'deleted' => ["todo2.ics"], + 'added' => ["todo3.ics"], + ], $result); + + $result = $backend->getChangesForCalendar($id, null, 1); + + $this->assertEquals([ + 'syncToken' => 6, + 'modified' => [], + 'deleted' => [], + 'added' => ["todo1.ics", "todo3.ics"], + ], $result); + } + + /** + * @depends testGetChanges + * @expectedException \InvalidArgumentException + */ + function testGetChangesBadId() { + + $backend = new PDO($this->pdo); + $id = $backend->createCalendar( + 'principals/user1', + 'bla', + [] + ); + $backend->getChangesForCalendar('bad-id', null, 1); + + } + + function testCreateSubscriptions() { + + $props = [ + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false), + '{DAV:}displayname' => 'cal', + '{http://apple.com/ns/ical/}refreshrate' => 'P1W', + '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', + '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, + //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, + '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, + ]; + + $backend = new PDO($this->pdo); + $backend->createSubscription('principals/user1', 'sub1', $props); + + $subs = $backend->getSubscriptionsForUser('principals/user1'); + + $expected = $props; + $expected['id'] = 1; + $expected['uri'] = 'sub1'; + $expected['principaluri'] = 'principals/user1'; + + unset($expected['{http://calendarserver.org/ns/}source']); + $expected['source'] = 'http://example.org/cal.ics'; + + $this->assertEquals(1, count($subs)); + foreach ($expected as $k => $v) { + $this->assertEquals($subs[0][$k], $expected[$k]); + } + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testCreateSubscriptionFail() { + + $props = [ + ]; + + $backend = new PDO($this->pdo); + $backend->createSubscription('principals/user1', 'sub1', $props); + + } + + function testUpdateSubscriptions() { + + $props = [ + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false), + '{DAV:}displayname' => 'cal', + '{http://apple.com/ns/ical/}refreshrate' => 'P1W', + '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', + '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, + //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, + '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, + ]; + + $backend = new PDO($this->pdo); + $backend->createSubscription('principals/user1', 'sub1', $props); + + $newProps = [ + '{DAV:}displayname' => 'new displayname', + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal2.ics', false), + ]; + + $propPatch = new DAV\PropPatch($newProps); + $backend->updateSubscription(1, $propPatch); + $result = $propPatch->commit(); + + $this->assertTrue($result); + + $subs = $backend->getSubscriptionsForUser('principals/user1'); + + $expected = array_merge($props, $newProps); + $expected['id'] = 1; + $expected['uri'] = 'sub1'; + $expected['principaluri'] = 'principals/user1'; + + unset($expected['{http://calendarserver.org/ns/}source']); + $expected['source'] = 'http://example.org/cal2.ics'; + + $this->assertEquals(1, count($subs)); + foreach ($expected as $k => $v) { + $this->assertEquals($subs[0][$k], $expected[$k]); + } + + } + + function testUpdateSubscriptionsFail() { + + $props = [ + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false), + '{DAV:}displayname' => 'cal', + '{http://apple.com/ns/ical/}refreshrate' => 'P1W', + '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', + '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, + //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, + '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, + ]; + + $backend = new PDO($this->pdo); + $backend->createSubscription('principals/user1', 'sub1', $props); + + $propPatch = new DAV\PropPatch([ + '{DAV:}displayname' => 'new displayname', + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal2.ics', false), + '{DAV:}unknown' => 'foo', + ]); + + $backend->updateSubscription(1, $propPatch); + $propPatch->commit(); + + $this->assertEquals([ + '{DAV:}unknown' => 403, + '{DAV:}displayname' => 424, + '{http://calendarserver.org/ns/}source' => 424, + ], $propPatch->getResult()); + + } + + function testDeleteSubscriptions() { + + $props = [ + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal.ics', false), + '{DAV:}displayname' => 'cal', + '{http://apple.com/ns/ical/}refreshrate' => 'P1W', + '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', + '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, + //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, + '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, + ]; + + $backend = new PDO($this->pdo); + $backend->createSubscription('principals/user1', 'sub1', $props); + + $newProps = [ + '{DAV:}displayname' => 'new displayname', + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/cal2.ics', false), + ]; + + $backend->deleteSubscription(1); + + $subs = $backend->getSubscriptionsForUser('principals/user1'); + $this->assertEquals(0, count($subs)); + } + + function testSchedulingMethods() { + + $backend = new PDO($this->pdo); + + $calData = "BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"; + + $backend->createSchedulingObject( + 'principals/user1', + 'schedule1.ics', + $calData + ); + + $expected = [ + 'calendardata' => $calData, + 'uri' => 'schedule1.ics', + 'etag' => '"' . md5($calData) . '"', + 'size' => strlen($calData) + ]; + + $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics'); + foreach ($expected as $k => $v) { + $this->assertArrayHasKey($k, $result); + if (is_resource($result[$k])) { + $result[$k] = stream_get_contents($result[$k]); + } + $this->assertEquals($v, $result[$k]); + } + + $results = $backend->getSchedulingObjects('principals/user1'); + + $this->assertEquals(1, count($results)); + $result = $results[0]; + foreach ($expected as $k => $v) { + if (is_resource($result[$k])) { + $result[$k] = stream_get_contents($result[$k]); + } + $this->assertEquals($v, $result[$k]); + } + + $backend->deleteSchedulingObject('principals/user1', 'schedule1.ics'); + $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics'); + + $this->assertNull($result); + + } + + function testGetInvites() { + + $backend = new PDO($this->pdo); + + // creating a new calendar + $backend->createCalendar('principals/user1', 'somerandomid', []); + $calendar = $backend->getCalendarsForUser('principals/user1')[0]; + + $result = $backend->getInvites($calendar['id']); + $expected = [ + new Sharee([ + 'href' => 'principals/user1', + 'principal' => 'principals/user1', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]) + ]; + + $this->assertEquals($expected, $result); + + } + + /** + * @depends testGetInvites + * @expectedException \InvalidArgumentException + */ + function testGetInvitesBadId() { + + $backend = new PDO($this->pdo); + + // creating a new calendar + $backend->createCalendar('principals/user1', 'somerandomid', []); + $calendar = $backend->getCalendarsForUser('principals/user1')[0]; + + $backend->getInvites('bad-id'); + + } + + /** + * @depends testCreateCalendarAndFetch + */ + function testUpdateInvites() { + + $backend = new PDO($this->pdo); + + // creating a new calendar + $backend->createCalendar('principals/user1', 'somerandomid', []); + $calendar = $backend->getCalendarsForUser('principals/user1')[0]; + + $ownerSharee = new Sharee([ + 'href' => 'principals/user1', + 'principal' => 'principals/user1', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]); + + // Add a new invite + $backend->updateInvites( + $calendar['id'], + [ + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => 'principals/user2', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'properties' => ['{DAV:}displayname' => 'User 2'], + ]) + ] + ); + + $result = $backend->getInvites($calendar['id']); + $expected = [ + $ownerSharee, + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => 'principals/user2', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'properties' => [ + '{DAV:}displayname' => 'User 2', + ], + ]) + ]; + $this->assertEquals($expected, $result); + + // Checking calendar_instances too + $expectedCalendar = [ + 'id' => [1,2], + 'principaluri' => 'principals/user2', + '{http://calendarserver.org/ns/}getctag' => 'http://sabre.io/ns/sync/1', + '{http://sabredav.org/ns}sync-token' => '1', + 'share-access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'read-only' => true, + 'share-resource-uri' => '/ns/share/1', + ]; + $calendars = $backend->getCalendarsForUser('principals/user2'); + + foreach ($expectedCalendar as $k => $v) { + $this->assertEquals( + $v, + $calendars[0][$k], + "Key " . $k . " in calendars array did not have the expected value." + ); + } + + + // Updating an invite + $backend->updateInvites( + $calendar['id'], + [ + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => 'principals/user2', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]) + ] + ); + + $result = $backend->getInvites($calendar['id']); + $expected = [ + $ownerSharee, + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => 'principals/user2', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'properties' => [ + '{DAV:}displayname' => 'User 2', + ], + ]) + ]; + $this->assertEquals($expected, $result); + + // Removing an invite + $backend->updateInvites( + $calendar['id'], + [ + new Sharee([ + 'href' => 'mailto:user@example.org', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS, + ]) + ] + ); + + $result = $backend->getInvites($calendar['id']); + $expected = [ + $ownerSharee + ]; + $this->assertEquals($expected, $result); + + // Preventing the owner share from being removed + $backend->updateInvites( + $calendar['id'], + [ + new Sharee([ + 'href' => 'principals/user2', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS, + ]) + ] + ); + + $result = $backend->getInvites($calendar['id']); + $expected = [ + new Sharee([ + 'href' => 'principals/user1', + 'principal' => 'principals/user1', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]), + ]; + $this->assertEquals($expected, $result); + + } + + /** + * @depends testUpdateInvites + * @expectedException \InvalidArgumentException + */ + function testUpdateInvitesBadId() { + + $backend = new PDO($this->pdo); + // Add a new invite + $backend->updateInvites( + 'bad-id', + [] + ); + + } + + /** + * @depends testUpdateInvites + */ + function testUpdateInvitesNoPrincipal() { + + $backend = new PDO($this->pdo); + + // creating a new calendar + $backend->createCalendar('principals/user1', 'somerandomid', []); + $calendar = $backend->getCalendarsForUser('principals/user1')[0]; + + $ownerSharee = new Sharee([ + 'href' => 'principals/user1', + 'principal' => 'principals/user1', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]); + + // Add a new invite + $backend->updateInvites( + $calendar['id'], + [ + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => null, + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'properties' => ['{DAV:}displayname' => 'User 2'], + ]) + ] + ); + + $result = $backend->getInvites($calendar['id']); + $expected = [ + $ownerSharee, + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => null, + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_INVALID, + 'properties' => [ + '{DAV:}displayname' => 'User 2', + ], + ]) + ]; + $this->assertEquals($expected, $result, null, 0.0, 10, true); // Last argument is $canonicalize = true, which allows us to compare, ignoring the order, because it's different between MySQL and Sqlite. + + } + + /** + * @depends testUpdateInvites + */ + function testDeleteSharedCalendar() { + + $backend = new PDO($this->pdo); + + // creating a new calendar + $backend->createCalendar('principals/user1', 'somerandomid', []); + $calendar = $backend->getCalendarsForUser('principals/user1')[0]; + + $ownerSharee = new Sharee([ + 'href' => 'principals/user1', + 'principal' => 'principals/user1', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]); + + // Add a new invite + $backend->updateInvites( + $calendar['id'], + [ + new Sharee([ + 'href' => 'mailto:user@example.org', + 'principal' => 'principals/user2', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'properties' => ['{DAV:}displayname' => 'User 2'], + ]) + ] + ); + + $expectedCalendar = [ + 'id' => [1,2], + 'principaluri' => 'principals/user2', + '{http://calendarserver.org/ns/}getctag' => 'http://sabre.io/ns/sync/1', + '{http://sabredav.org/ns}sync-token' => '1', + 'share-access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'read-only' => true, + 'share-resource-uri' => '/ns/share/1', + ]; + $calendars = $backend->getCalendarsForUser('principals/user2'); + + foreach ($expectedCalendar as $k => $v) { + $this->assertEquals( + $v, + $calendars[0][$k], + "Key " . $k . " in calendars array did not have the expected value." + ); + } + + // Removing the shared calendar. + $backend->deleteCalendar($calendars[0]['id']); + + $this->assertEquals( + [], + $backend->getCalendarsForUser('principals/user2') + ); + + $result = $backend->getInvites($calendar['id']); + $expected = [ + new Sharee([ + 'href' => 'principals/user1', + 'principal' => 'principals/user1', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED, + ]), + ]; + $this->assertEquals($expected, $result); + + } + + /** + * @expectedException \Sabre\DAV\Exception\NotImplemented + */ + function testSetPublishStatus() { + + $backend = new PDO($this->pdo); + $backend->setPublishStatus([1, 1], true); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php new file mode 100644 index 00000000000..7f642efc9ae --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/AbstractTest.php @@ -0,0 +1,178 @@ + 'anything']); + + $abstract->updateCalendar('randomid', $propPatch); + $result = $propPatch->commit(); + + $this->assertFalse($result); + + } + + function testCalendarQuery() { + + $abstract = new AbstractMock(); + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + 'event1.ics', + ], $abstract->calendarQuery(1, $filters)); + + } + + function testGetCalendarObjectByUID() { + + $abstract = new AbstractMock(); + $this->assertNull( + $abstract->getCalendarObjectByUID('principal1', 'zim') + ); + $this->assertEquals( + 'cal1/event1.ics', + $abstract->getCalendarObjectByUID('principal1', 'foo') + ); + $this->assertNull( + $abstract->getCalendarObjectByUID('principal3', 'foo') + ); + $this->assertNull( + $abstract->getCalendarObjectByUID('principal1', 'shared') + ); + + } + + function testGetMultipleCalendarObjects() { + + $abstract = new AbstractMock(); + $result = $abstract->getMultipleCalendarObjects(1, [ + 'event1.ics', + 'task1.ics', + ]); + + $expected = [ + [ + 'id' => 1, + 'calendarid' => 1, + 'uri' => 'event1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + ], + [ + 'id' => 2, + 'calendarid' => 1, + 'uri' => 'task1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", + ], + ]; + + $this->assertEquals($expected, $result); + + + } + +} + +class AbstractMock extends AbstractBackend { + + function getCalendarsForUser($principalUri) { + + return [ + [ + 'id' => 1, + 'principaluri' => 'principal1', + 'uri' => 'cal1', + ], + [ + 'id' => 2, + 'principaluri' => 'principal1', + '{http://sabredav.org/ns}owner-principal' => 'principal2', + 'uri' => 'cal1', + ], + ]; + + } + function createCalendar($principalUri, $calendarUri, array $properties) { } + function deleteCalendar($calendarId) { } + function getCalendarObjects($calendarId) { + + switch ($calendarId) { + case 1: + return [ + [ + 'id' => 1, + 'calendarid' => 1, + 'uri' => 'event1.ics', + ], + [ + 'id' => 2, + 'calendarid' => 1, + 'uri' => 'task1.ics', + ], + ]; + case 2: + return [ + [ + 'id' => 3, + 'calendarid' => 2, + 'uri' => 'shared-event.ics', + ] + ]; + } + + } + + function getCalendarObject($calendarId, $objectUri) { + + switch ($objectUri) { + + case 'event1.ics' : + return [ + 'id' => 1, + 'calendarid' => 1, + 'uri' => 'event1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + ]; + case 'task1.ics' : + return [ + 'id' => 2, + 'calendarid' => 1, + 'uri' => 'task1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", + ]; + case 'shared-event.ics' : + return [ + 'id' => 3, + 'calendarid' => 2, + 'uri' => 'event1.ics', + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:shared\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + ]; + + } + + } + function createCalendarObject($calendarId, $objectUri, $calendarData) { } + function updateCalendarObject($calendarId, $objectUri, $calendarData) { } + function deleteCalendarObject($calendarId, $objectUri) { } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php new file mode 100644 index 00000000000..cc665cd8fa3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/Mock.php @@ -0,0 +1,258 @@ +calendars = $calendars; + $this->calendarData = $calendarData; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principalUri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + function getCalendarsForUser($principalUri) { + + $r = []; + foreach ($this->calendars as $row) { + if ($row['principaluri'] == $principalUri) { + $r[] = $row; + } + } + + return $r; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar. + * + * This function must return a server-wide unique id that can be used + * later to reference the calendar. + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string|int + */ + function createCalendar($principalUri, $calendarUri, array $properties) { + + $id = DAV\UUIDUtil::getUUID(); + $this->calendars[] = array_merge([ + 'id' => $id, + 'principaluri' => $principalUri, + 'uri' => $calendarUri, + '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO']), + ], $properties); + + return $id; + + } + + /** + * Updates properties for a calendar. + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param mixed $calendarId + * @param \Sabre\DAV\PropPatch $propPatch + * @return void + */ + function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) { + + $propPatch->handleRemaining(function($props) use ($calendarId) { + + foreach ($this->calendars as $k => $calendar) { + + if ($calendar['id'] === $calendarId) { + foreach ($props as $propName => $propValue) { + if (is_null($propValue)) { + unset($this->calendars[$k][$propName]); + } else { + $this->calendars[$k][$propName] = $propValue; + } + } + return true; + + } + + } + + }); + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + function deleteCalendar($calendarId) { + + foreach ($this->calendars as $k => $calendar) { + if ($calendar['id'] === $calendarId) { + unset($this->calendars[$k]); + } + } + + } + + /** + * Returns all calendar objects within a calendar object. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * @param string $calendarId + * @return array + */ + function getCalendarObjects($calendarId) { + + if (!isset($this->calendarData[$calendarId])) + return []; + + $objects = $this->calendarData[$calendarId]; + + foreach ($objects as $uri => &$object) { + $object['calendarid'] = $calendarId; + $object['uri'] = $uri; + $object['lastmodified'] = null; + } + return $objects; + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * This method must return null if the object did not exist. + * + * @param mixed $calendarId + * @param string $objectUri + * @return array|null + */ + function getCalendarObject($calendarId, $objectUri) { + + if (!isset($this->calendarData[$calendarId][$objectUri])) { + return null; + } + $object = $this->calendarData[$calendarId][$objectUri]; + $object['calendarid'] = $calendarId; + $object['uri'] = $objectUri; + $object['lastmodified'] = null; + return $object; + + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + function createCalendarObject($calendarId, $objectUri, $calendarData) { + + $this->calendarData[$calendarId][$objectUri] = [ + 'calendardata' => $calendarData, + 'calendarid' => $calendarId, + 'uri' => $objectUri, + ]; + return '"' . md5($calendarData) . '"'; + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + function updateCalendarObject($calendarId, $objectUri, $calendarData) { + + $this->calendarData[$calendarId][$objectUri] = [ + 'calendardata' => $calendarData, + 'calendarid' => $calendarId, + 'uri' => $objectUri, + ]; + return '"' . md5($calendarData) . '"'; + + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + function deleteCalendarObject($calendarId, $objectUri) { + + unset($this->calendarData[$calendarId][$objectUri]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockScheduling.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockScheduling.php new file mode 100644 index 00000000000..3ac22f474f6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockScheduling.php @@ -0,0 +1,91 @@ +schedulingObjects[$principalUri][$objectUri])) { + return $this->schedulingObjects[$principalUri][$objectUri]; + } + + } + + /** + * Returns all scheduling objects for the inbox collection. + * + * These objects should be returned as an array. Every item in the array + * should follow the same structure as returned from getSchedulingObject. + * + * The main difference is that 'calendardata' is optional. + * + * @param string $principalUri + * @return array + */ + function getSchedulingObjects($principalUri) { + + if (isset($this->schedulingObjects[$principalUri])) { + return array_values($this->schedulingObjects[$principalUri]); + } + return []; + + } + + /** + * Deletes a scheduling object + * + * @param string $principalUri + * @param string $objectUri + * @return void + */ + function deleteSchedulingObject($principalUri, $objectUri) { + + if (isset($this->schedulingObjects[$principalUri][$objectUri])) { + unset($this->schedulingObjects[$principalUri][$objectUri]); + } + + } + + /** + * Creates a new scheduling object. This should land in a users' inbox. + * + * @param string $principalUri + * @param string $objectUri + * @param string $objectData; + * @return void + */ + function createSchedulingObject($principalUri, $objectUri, $objectData) { + + if (!isset($this->schedulingObjects[$principalUri])) { + $this->schedulingObjects[$principalUri] = []; + } + $this->schedulingObjects[$principalUri][$objectUri] = [ + 'uri' => $objectUri, + 'calendardata' => $objectData, + 'lastmodified' => null, + 'etag' => '"' . md5($objectData) . '"', + 'size' => strlen($objectData) + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockSharing.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockSharing.php new file mode 100644 index 00000000000..eaf52e32f4a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockSharing.php @@ -0,0 +1,204 @@ +notifications = $notifications; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principalUri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + function getCalendarsForUser($principalUri) { + + $calendars = parent::getCalendarsForUser($principalUri); + foreach ($calendars as $k => $calendar) { + + if (isset($calendar['share-access'])) { + continue; + } + if (!empty($this->shares[$calendar['id']])) { + $calendar['share-access'] = DAV\Sharing\Plugin::ACCESS_SHAREDOWNER; + } else { + $calendar['share-access'] = DAV\Sharing\Plugin::ACCESS_NOTSHARED; + } + $calendars[$k] = $calendar; + + } + return $calendars; + + } + + /** + * Returns a list of notifications for a given principal url. + * + * The returned array should only consist of implementations of + * Sabre\CalDAV\Notifications\INotificationType. + * + * @param string $principalUri + * @return array + */ + function getNotificationsForPrincipal($principalUri) { + + if (isset($this->notifications[$principalUri])) { + return $this->notifications[$principalUri]; + } + return []; + + } + + /** + * This deletes a specific notifcation. + * + * This may be called by a client once it deems a notification handled. + * + * @param string $principalUri + * @param NotificationInterface $notification + * @return void + */ + function deleteNotification($principalUri, NotificationInterface $notification) { + + foreach ($this->notifications[$principalUri] as $key => $value) { + if ($notification === $value) { + unset($this->notifications[$principalUri][$key]); + } + } + + } + + /** + * Updates the list of shares. + * + * @param mixed $calendarId + * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees + * @return void + */ + function updateInvites($calendarId, array $sharees) { + + if (!isset($this->shares[$calendarId])) { + $this->shares[$calendarId] = []; + } + + foreach ($sharees as $sharee) { + + $existingKey = null; + foreach ($this->shares[$calendarId] as $k => $existingSharee) { + if ($sharee->href === $existingSharee->href) { + $existingKey = $k; + } + } + // Just making sure we're not affecting an existing copy. + $sharee = clone $sharee; + $sharee->inviteStatus = DAV\Sharing\Plugin::INVITE_NORESPONSE; + + if ($sharee->access === DAV\Sharing\Plugin::ACCESS_NOACCESS) { + // It's a removal + unset($this->shares[$calendarId][$existingKey]); + } elseif ($existingKey) { + // It's an update + $this->shares[$calendarId][$existingKey] = $sharee; + } else { + // It's an addition + $this->shares[$calendarId][] = $sharee; + } + } + + // Re-numbering keys + $this->shares[$calendarId] = array_values($this->shares[$calendarId]); + + } + + /** + * Returns the list of people whom this calendar is shared with. + * + * Every item in the returned list must be a Sharee object with at + * least the following properties set: + * $href + * $shareAccess + * $inviteStatus + * + * and optionally: + * $properties + * + * @param mixed $calendarId + * @return \Sabre\DAV\Xml\Element\Sharee[] + */ + function getInvites($calendarId) { + + if (!isset($this->shares[$calendarId])) { + return []; + } + + return $this->shares[$calendarId]; + + } + + /** + * This method is called when a user replied to a request to share. + * + * @param string href The sharee who is replying (often a mailto: address) + * @param int status One of the \Sabre\DAV\Sharing\Plugin::INVITE_* constants + * @param string $calendarUri The url to the calendar thats being shared + * @param string $inReplyTo The unique id this message is a response to + * @param string $summary A description of the reply + * @return void + */ + function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { + + // This operation basically doesn't do anything yet + if ($status === DAV\Sharing\Plugin::INVITE_ACCEPTED) { + return 'calendars/blabla/calendar'; + } + + } + + /** + * Publishes a calendar + * + * @param mixed $calendarId + * @param bool $value + * @return void + */ + function setPublishStatus($calendarId, $value) { + + foreach ($this->calendars as $k => $cal) { + if ($cal['id'] === $calendarId) { + if (!$value) { + unset($cal['{http://calendarserver.org/ns/}publish-url']); + } else { + $cal['{http://calendarserver.org/ns/}publish-url'] = 'http://example.org/public/ ' . $calendarId . '.ics'; + } + return; + } + } + + throw new DAV\Exception('Calendar with id "' . $calendarId . '" not found'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockSubscriptionSupport.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockSubscriptionSupport.php new file mode 100644 index 00000000000..adf9c8a17d6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/MockSubscriptionSupport.php @@ -0,0 +1,156 @@ +subs[$principalUri])) { + return $this->subs[$principalUri]; + } + return []; + + } + + /** + * Creates a new subscription for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this subscription in other methods, such as updateSubscription. + * + * @param string $principalUri + * @param string $uri + * @param array $properties + * @return mixed + */ + function createSubscription($principalUri, $uri, array $properties) { + + $properties['uri'] = $uri; + $properties['principaluri'] = $principalUri; + $properties['source'] = $properties['{http://calendarserver.org/ns/}source']->getHref(); + + if (!isset($this->subs[$principalUri])) { + $this->subs[$principalUri] = []; + } + + $id = [$principalUri, count($this->subs[$principalUri]) + 1]; + + $properties['id'] = $id; + + $this->subs[$principalUri][] = array_merge($properties, [ + 'id' => $id, + ]); + + return $id; + + } + + /** + * Updates a subscription + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param mixed $subscriptionId + * @param \Sabre\DAV\PropPatch $propPatch + * @return void + */ + function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) { + + $found = null; + foreach ($this->subs[$subscriptionId[0]] as &$sub) { + + if ($sub['id'][1] === $subscriptionId[1]) { + $found = & $sub; + break; + } + + } + + if (!$found) return; + + $propPatch->handleRemaining(function($mutations) use (&$found) { + foreach ($mutations as $k => $v) { + $found[$k] = $v; + } + return true; + }); + + } + + /** + * Deletes a subscription + * + * @param mixed $subscriptionId + * @return void + */ + function deleteSubscription($subscriptionId) { + + foreach ($this->subs[$subscriptionId[0]] as $index => $sub) { + + if ($sub['id'][1] === $subscriptionId[1]) { + unset($this->subs[$subscriptionId[0]][$index]); + return true; + } + + } + + return false; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php new file mode 100644 index 00000000000..e068ff1e78f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php @@ -0,0 +1,9 @@ +markTestSkipped('SQLite driver is not available'); + + if (file_exists(SABRE_TEMPDIR . '/testdb.sqlite')) + unlink(SABRE_TEMPDIR . '/testdb.sqlite'); + + $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/testdb.sqlite'); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + $pdo->exec(<<exec(<<pdo = $pdo; + + } + + function testConstruct() { + + $backend = new SimplePDO($this->pdo); + $this->assertTrue($backend instanceof SimplePDO); + + } + + /** + * @depends testConstruct + */ + function testGetCalendarsForUserNoCalendars() { + + $backend = new SimplePDO($this->pdo); + $calendars = $backend->getCalendarsForUser('principals/user2'); + $this->assertEquals([], $calendars); + + } + + /** + * @depends testConstruct + */ + function testCreateCalendarAndFetch() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']), + '{DAV:}displayname' => 'Hello!', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + ]); + $calendars = $backend->getCalendarsForUser('principals/user2'); + + $elementCheck = [ + 'uri' => 'somerandomid', + ]; + + $this->assertInternalType('array', $calendars); + $this->assertEquals(1, count($calendars)); + + foreach ($elementCheck as $name => $value) { + + $this->assertArrayHasKey($name, $calendars[0]); + $this->assertEquals($value, $calendars[0][$name]); + + } + + } + + /** + * @depends testConstruct + */ + function testUpdateCalendarAndFetch() { + + $backend = new SimplePDO($this->pdo); + + //Creating a new calendar + $newId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'myCalendar', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp('transparent'), + ]); + + // Updating the calendar + $backend->updateCalendar($newId, $propPatch); + $result = $propPatch->commit(); + + // Verifying the result of the update + $this->assertFalse($result); + + } + + /** + * @depends testCreateCalendarAndFetch + */ + function testDeleteCalendar() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']), + '{DAV:}displayname' => 'Hello!', + ]); + + $backend->deleteCalendar($returnedId); + + $calendars = $backend->getCalendarsForUser('principals/user2'); + $this->assertEquals([], $calendars); + + } + + function testCreateCalendarObject() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $result = $this->pdo->query('SELECT calendardata FROM simple_calendarobjects WHERE uri = "random-id"'); + $this->assertEquals([ + 'calendardata' => $object, + ], $result->fetch(\PDO::FETCH_ASSOC)); + + } + function testGetMultipleObjects() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + + $backend->createCalendarObject($returnedId, 'id-1', $object); + $backend->createCalendarObject($returnedId, 'id-2', $object); + + $check = [ + [ + 'id' => 1, + 'etag' => '"' . md5($object) . '"', + 'uri' => 'id-1', + 'size' => strlen($object), + 'calendardata' => $object, + ], + [ + 'id' => 2, + 'etag' => '"' . md5($object) . '"', + 'uri' => 'id-2', + 'size' => strlen($object), + 'calendardata' => $object, + ], + ]; + + $result = $backend->getMultipleCalendarObjects($returnedId, ['id-1', 'id-2']); + + foreach ($check as $index => $props) { + + foreach ($props as $key => $value) { + + if ($key !== 'lastmodified') { + $this->assertEquals($value, $result[$index][$key]); + } else { + $this->assertTrue(isset($result[$index][$key])); + } + + } + + } + + } + + /** + * @depends testCreateCalendarObject + */ + function testGetCalendarObjects() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $data = $backend->getCalendarObjects($returnedId); + + $this->assertEquals(1, count($data)); + $data = $data[0]; + + $this->assertEquals('random-id', $data['uri']); + $this->assertEquals(strlen($object), $data['size']); + + } + + /** + * @depends testCreateCalendarObject + */ + function testGetCalendarObjectByUID() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + + $this->assertNull( + $backend->getCalendarObjectByUID('principals/user2', 'bar') + ); + $this->assertEquals( + 'somerandomid/random-id', + $backend->getCalendarObjectByUID('principals/user2', 'foo') + ); + + } + + /** + * @depends testCreateCalendarObject + */ + function testUpdateCalendarObject() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $object2 = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20130101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->updateCalendarObject($returnedId, 'random-id', $object2); + + $data = $backend->getCalendarObject($returnedId, 'random-id'); + + $this->assertEquals($object2, $data['calendardata']); + $this->assertEquals('random-id', $data['uri']); + + + } + + + /** + * @depends testCreateCalendarObject + */ + function testDeleteCalendarObject() { + + $backend = new SimplePDO($this->pdo); + $returnedId = $backend->createCalendar('principals/user2', 'somerandomid', []); + + $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; + $backend->createCalendarObject($returnedId, 'random-id', $object); + $backend->deleteCalendarObject($returnedId, 'random-id'); + + $data = $backend->getCalendarObject($returnedId, 'random-id'); + $this->assertNull($data); + + } + + + function testCalendarQueryNoResult() { + + $abstract = new SimplePDO($this->pdo); + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VJOURNAL', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + ], $abstract->calendarQuery(1, $filters)); + + } + + function testCalendarQueryTodo() { + + $backend = new SimplePDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VTODO', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + "todo", + ], $backend->calendarQuery(1, $filters)); + + } + function testCalendarQueryTodoNotMatch() { + + $backend = new SimplePDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VTODO', + 'comp-filters' => [], + 'prop-filters' => [ + [ + 'name' => 'summary', + 'text-match' => null, + 'time-range' => null, + 'param-filters' => [], + 'is-not-defined' => false, + ], + ], + 'is-not-defined' => false, + 'time-range' => null, + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + ], $backend->calendarQuery(1, $filters)); + + } + + function testCalendarQueryNoFilter() { + + $backend = new SimplePDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $result = $backend->calendarQuery(1, $filters); + $this->assertTrue(in_array('todo', $result)); + $this->assertTrue(in_array('event', $result)); + + } + + function testCalendarQueryTimeRange() { + + $backend = new SimplePDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('20120103'), + 'end' => new \DateTime('20120104'), + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + "event2", + ], $backend->calendarQuery(1, $filters)); + + } + function testCalendarQueryTimeRangeNoEnd() { + + $backend = new SimplePDO($this->pdo); + $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + $backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('20120102'), + 'end' => null, + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $this->assertEquals([ + "event2", + ], $backend->calendarQuery(1, $filters)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeNotificationsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeNotificationsTest.php new file mode 100644 index 00000000000..36302cc3556 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeNotificationsTest.php @@ -0,0 +1,49 @@ + 'principals/user']); + + $this->assertEquals( + [], + $calendarHome->getChildren() + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\NotFound + */ + function testGetChildNoSupport() { + + $backend = new Backend\Mock(); + $calendarHome = new CalendarHome($backend, ['uri' => 'principals/user']); + $calendarHome->getChild('notifications'); + + } + + function testGetChildren() { + + $backend = new Backend\MockSharing(); + $calendarHome = new CalendarHome($backend, ['uri' => 'principals/user']); + + $result = $calendarHome->getChildren(); + $this->assertEquals('notifications', $result[0]->getName()); + + } + + function testGetChild() { + + $backend = new Backend\MockSharing(); + $calendarHome = new CalendarHome($backend, ['uri' => 'principals/user']); + $result = $calendarHome->getChild('notifications'); + $this->assertEquals('notifications', $result->getName()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeSharedCalendarsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeSharedCalendarsTest.php new file mode 100644 index 00000000000..9079fdecd80 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeSharedCalendarsTest.php @@ -0,0 +1,80 @@ + 1, + 'principaluri' => 'principals/user1', + ], + [ + 'id' => 2, + '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/cal1', + '{http://sabredav.org/ns}owner-principal' => 'principal/owner', + '{http://sabredav.org/ns}read-only' => false, + 'principaluri' => 'principals/user1', + ], + ]; + + $this->backend = new Backend\MockSharing( + $calendars, + [], + [] + ); + + return new CalendarHome($this->backend, [ + 'uri' => 'principals/user1' + ]); + + } + + function testSimple() { + + $instance = $this->getInstance(); + $this->assertEquals('user1', $instance->getName()); + + } + + function testGetChildren() { + + $instance = $this->getInstance(); + $children = $instance->getChildren(); + $this->assertEquals(3, count($children)); + + // Testing if we got all the objects back. + $sharedCalendars = 0; + $hasOutbox = false; + $hasNotifications = false; + + foreach ($children as $child) { + + if ($child instanceof ISharedCalendar) { + $sharedCalendars++; + } + if ($child instanceof Notifications\ICollection) { + $hasNotifications = true; + } + + } + $this->assertEquals(2, $sharedCalendars); + $this->assertTrue($hasNotifications); + + } + + function testShareReply() { + + $instance = $this->getInstance(); + $result = $instance->shareReply('uri', DAV\Sharing\Plugin::INVITE_DECLINED, 'curi', '1'); + $this->assertNull($result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeSubscriptionsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeSubscriptionsTest.php new file mode 100644 index 00000000000..4a479c81602 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeSubscriptionsTest.php @@ -0,0 +1,85 @@ + 'baz', + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/test.ics'), + ]; + $principal = [ + 'uri' => 'principals/user1' + ]; + $this->backend = new Backend\MockSubscriptionSupport([], []); + $this->backend->createSubscription('principals/user1', 'uri', $props); + + return new CalendarHome($this->backend, $principal); + + } + + function testSimple() { + + $instance = $this->getInstance(); + $this->assertEquals('user1', $instance->getName()); + + } + + function testGetChildren() { + + $instance = $this->getInstance(); + $children = $instance->getChildren(); + $this->assertEquals(1, count($children)); + foreach ($children as $child) { + if ($child instanceof Subscriptions\Subscription) { + return; + } + } + $this->fail('There were no subscription nodes in the calendar home'); + + } + + function testCreateSubscription() { + + $instance = $this->getInstance(); + $rt = ['{DAV:}collection', '{http://calendarserver.org/ns/}subscribed']; + + $props = [ + '{DAV:}displayname' => 'baz', + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/test2.ics'), + ]; + $instance->createExtendedCollection('sub2', new MkCol($rt, $props)); + + $children = $instance->getChildren(); + $this->assertEquals(2, count($children)); + + } + + /** + * @expectedException \Sabre\DAV\Exception\InvalidResourceType + */ + function testNoSubscriptionSupport() { + + $principal = [ + 'uri' => 'principals/user1' + ]; + $backend = new Backend\Mock([], []); + $uC = new CalendarHome($backend, $principal); + + $rt = ['{DAV:}collection', '{http://calendarserver.org/ns/}subscribed']; + + $props = [ + '{DAV:}displayname' => 'baz', + '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Xml\Property\Href('http://example.org/test2.ics'), + ]; + $uC->createExtendedCollection('sub2', new MkCol($rt, $props)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeTest.php new file mode 100644 index 00000000000..ff52ea6ad20 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarHomeTest.php @@ -0,0 +1,215 @@ +backend = TestUtil::getBackend(); + $this->usercalendars = new CalendarHome($this->backend, [ + 'uri' => 'principals/user1' + ]); + + } + + function testSimple() { + + $this->assertEquals('user1', $this->usercalendars->getName()); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + * @depends testSimple + */ + function testGetChildNotFound() { + + $this->usercalendars->getChild('randomname'); + + } + + function testChildExists() { + + $this->assertFalse($this->usercalendars->childExists('foo')); + $this->assertTrue($this->usercalendars->childExists('UUID-123467')); + + } + + function testGetOwner() { + + $this->assertEquals('principals/user1', $this->usercalendars->getOwner()); + + } + + function testGetGroup() { + + $this->assertNull($this->usercalendars->getGroup()); + + } + + function testGetACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + ]; + $this->assertEquals($expected, $this->usercalendars->getACL()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $this->usercalendars->setACL([]); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + * @depends testSimple + */ + function testSetName() { + + $this->usercalendars->setName('bla'); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + * @depends testSimple + */ + function testDelete() { + + $this->usercalendars->delete(); + + } + + /** + * @depends testSimple + */ + function testGetLastModified() { + + $this->assertNull($this->usercalendars->getLastModified()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\MethodNotAllowed + * @depends testSimple + */ + function testCreateFile() { + + $this->usercalendars->createFile('bla'); + + } + + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + * @depends testSimple + */ + function testCreateDirectory() { + + $this->usercalendars->createDirectory('bla'); + + } + + /** + * @depends testSimple + */ + function testCreateExtendedCollection() { + + $mkCol = new MkCol( + ['{DAV:}collection', '{urn:ietf:params:xml:ns:caldav}calendar'], + [] + ); + $result = $this->usercalendars->createExtendedCollection('newcalendar', $mkCol); + $this->assertNull($result); + $cals = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3, count($cals)); + + } + + /** + * @expectedException Sabre\DAV\Exception\InvalidResourceType + * @depends testSimple + */ + function testCreateExtendedCollectionBadResourceType() { + + $mkCol = new MkCol( + ['{DAV:}collection', '{DAV:}blabla'], + [] + ); + $this->usercalendars->createExtendedCollection('newcalendar', $mkCol); + + } + + /** + * @expectedException Sabre\DAV\Exception\InvalidResourceType + * @depends testSimple + */ + function testCreateExtendedCollectionNotACalendar() { + + $mkCol = new MkCol( + ['{DAV:}collection'], + [] + ); + $this->usercalendars->createExtendedCollection('newcalendar', $mkCol); + + } + + function testGetSupportedPrivilegesSet() { + + $this->assertNull($this->usercalendars->getSupportedPrivilegeSet()); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotImplemented + */ + function testShareReplyFail() { + + $this->usercalendars->shareReply('uri', DAV\Sharing\Plugin::INVITE_DECLINED, 'curi', '1'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php new file mode 100644 index 00000000000..c92cde66133 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarObjectTest.php @@ -0,0 +1,383 @@ +backend = TestUtil::getBackend(); + + $calendars = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(2, count($calendars)); + $this->calendar = new Calendar($this->backend, $calendars[0]); + + } + + function teardown() { + + unset($this->calendar); + unset($this->backend); + + } + + function testSetup() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $this->assertInternalType('string', $children[0]->getName()); + $this->assertInternalType('string', $children[0]->get()); + $this->assertInternalType('string', $children[0]->getETag()); + $this->assertEquals('text/calendar; charset=utf-8', $children[0]->getContentType()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg1() { + + $obj = new CalendarObject( + new Backend\Mock([], []), + [], + [] + ); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg2() { + + $obj = new CalendarObject( + new Backend\Mock([], []), + [], + ['calendarid' => '1'] + ); + + } + + /** + * @depends testSetup + */ + function testPut() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + $newData = TestUtil::getTestCalendarData(); + + $children[0]->put($newData); + $this->assertEquals($newData, $children[0]->get()); + + } + + /** + * @depends testSetup + */ + function testPutStream() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + $newData = TestUtil::getTestCalendarData(); + + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $newData); + rewind($stream); + $children[0]->put($stream); + $this->assertEquals($newData, $children[0]->get()); + + } + + + /** + * @depends testSetup + */ + function testDelete() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + $obj->delete(); + + $children2 = $this->calendar->getChildren(); + $this->assertEquals(count($children) - 1, count($children2)); + + } + + /** + * @depends testSetup + */ + function testGetLastModified() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + + $lastMod = $obj->getLastModified(); + $this->assertTrue(is_int($lastMod) || ctype_digit($lastMod) || is_null($lastMod)); + + } + + /** + * @depends testSetup + */ + function testGetSize() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + + $size = $obj->getSize(); + $this->assertInternalType('int', $size); + + } + + function testGetOwner() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + $this->assertEquals('principals/user1', $obj->getOwner()); + + } + + function testGetGroup() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + $this->assertNull($obj->getGroup()); + + } + + function testGetACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + ]; + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + $this->assertEquals($expected, $obj->getACL()); + + } + + function testDefaultACL() { + + $backend = new Backend\Mock([], []); + $calendarObject = new CalendarObject($backend, ['principaluri' => 'principals/user1'], ['calendarid' => 1, 'uri' => 'foo']); + $expected = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + ]; + $this->assertEquals($expected, $calendarObject->getACL()); + + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + $obj->setACL([]); + + } + + function testGet() { + + $children = $this->calendar->getChildren(); + $this->assertTrue($children[0] instanceof CalendarObject); + + $obj = $children[0]; + + $expected = "BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//iCal 4.0.1//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Asia/Seoul +BEGIN:DAYLIGHT +TZOFFSETFROM:+0900 +RRULE:FREQ=YEARLY;UNTIL=19880507T150000Z;BYMONTH=5;BYDAY=2SU +DTSTART:19870510T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+1000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1000 +DTSTART:19881009T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+0900 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20100225T154229Z +UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 +TRANSP:TRANSPARENT +SUMMARY:Something here +DTSTAMP:20100228T130202Z +DTSTART;TZID=Asia/Seoul:20100223T060000 +DTEND;TZID=Asia/Seoul:20100223T070000 +ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com +SEQUENCE:2 +END:VEVENT +END:VCALENDAR"; + + + + $this->assertEquals($expected, $obj->get()); + + } + + function testGetRefetch() { + + $backend = new Backend\Mock([], [ + 1 => [ + 'foo' => [ + 'calendardata' => 'foo', + 'uri' => 'foo' + ], + ] + ]); + $obj = new CalendarObject($backend, ['id' => 1], ['uri' => 'foo']); + + $this->assertEquals('foo', $obj->get()); + + } + + function testGetEtag1() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'etag' => 'bar', + 'calendarid' => 1 + ]; + + $backend = new Backend\Mock([], []); + $obj = new CalendarObject($backend, [], $objectInfo); + + $this->assertEquals('bar', $obj->getETag()); + + } + + function testGetEtag2() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ]; + + $backend = new Backend\Mock([], []); + $obj = new CalendarObject($backend, [], $objectInfo); + + $this->assertEquals('"' . md5('foo') . '"', $obj->getETag()); + + } + + function testGetSupportedPrivilegesSet() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ]; + + $backend = new Backend\Mock([], []); + $obj = new CalendarObject($backend, [], $objectInfo); + $this->assertNull($obj->getSupportedPrivilegeSet()); + + } + + function testGetSize1() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ]; + + $backend = new Backend\Mock([], []); + $obj = new CalendarObject($backend, [], $objectInfo); + $this->assertEquals(3, $obj->getSize()); + + } + + function testGetSize2() { + + $objectInfo = [ + 'uri' => 'foo', + 'calendarid' => 1, + 'size' => 4, + ]; + + $backend = new Backend\Mock([], []); + $obj = new CalendarObject($backend, [], $objectInfo); + $this->assertEquals(4, $obj->getSize()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php new file mode 100644 index 00000000000..ca06d8ffa7b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php @@ -0,0 +1,122 @@ +createComponent('VEVENT'); + $vevent->RRULE = 'FREQ=MONTHLY'; + $vevent->DTSTART = '20120101T120000Z'; + $vevent->UID = 'bla'; + + $valarm = $vcalendar->createComponent('VALARM'); + $valarm->TRIGGER = '-P15D'; + $vevent->add($valarm); + + + $vcalendar->add($vevent); + + $filter = [ + 'name' => 'VCALENDAR', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => [], + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => [], + 'comp-filters' => [ + [ + 'name' => 'VALARM', + 'is-not-defined' => false, + 'prop-filters' => [], + 'comp-filters' => [], + 'time-range' => [ + 'start' => new \DateTime('2012-05-10'), + 'end' => new \DateTime('2012-05-20'), + ], + ], + ], + ], + ], + ]; + + $validator = new CalendarQueryValidator(); + $this->assertTrue($validator->validate($vcalendar, $filter)); + + $vcalendar = new VObject\Component\VCalendar(); + + // A limited recurrence rule, should return false + $vevent = $vcalendar->createComponent('VEVENT'); + $vevent->RRULE = 'FREQ=MONTHLY;COUNT=1'; + $vevent->DTSTART = '20120101T120000Z'; + $vevent->UID = 'bla'; + + $valarm = $vcalendar->createComponent('VALARM'); + $valarm->TRIGGER = '-P15D'; + $vevent->add($valarm); + + $vcalendar->add($vevent); + + $this->assertFalse($validator->validate($vcalendar, $filter)); + } + + function testAlarmWayBefore() { + + $vcalendar = new VObject\Component\VCalendar(); + + $vevent = $vcalendar->createComponent('VEVENT'); + $vevent->DTSTART = '20120101T120000Z'; + $vevent->UID = 'bla'; + + $valarm = $vcalendar->createComponent('VALARM'); + $valarm->TRIGGER = '-P2W1D'; + $vevent->add($valarm); + + $vcalendar->add($vevent); + + $filter = [ + 'name' => 'VCALENDAR', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => [], + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'time-range' => null, + 'prop-filters' => [], + 'comp-filters' => [ + [ + 'name' => 'VALARM', + 'is-not-defined' => false, + 'prop-filters' => [], + 'comp-filters' => [], + 'time-range' => [ + 'start' => new \DateTime('2011-12-10'), + 'end' => new \DateTime('2011-12-20'), + ], + ], + ], + ], + ], + ]; + + $validator = new CalendarQueryValidator(); + $this->assertTrue($validator->validate($vcalendar, $filter)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php new file mode 100644 index 00000000000..f3305163bdf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php @@ -0,0 +1,829 @@ +assertFalse($validator->validate($vcal, ['name' => 'VFOO'])); + + } + + /** + * @param string $icalObject + * @param array $filters + * @param int $outcome + * @dataProvider provider + */ + function testValid($icalObject, $filters, $outcome) { + + $validator = new CalendarQueryValidator(); + + // Wrapping filter in a VCALENDAR component filter, as this is always + // there anyway. + $filters = [ + 'name' => 'VCALENDAR', + 'comp-filters' => [$filters], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $vObject = VObject\Reader::read($icalObject); + + switch ($outcome) { + case 0 : + $this->assertFalse($validator->validate($vObject, $filters)); + break; + case 1 : + $this->assertTrue($validator->validate($vObject, $filters)); + break; + case -1 : + try { + $validator->validate($vObject, $filters); + $this->fail('This test was supposed to fail'); + } catch (\Exception $e) { + // We need to test something to be valid for phpunit strict + // mode. + $this->assertTrue(true); + } catch (\Throwable $e) { + // PHP7 + $this->assertTrue(true); + } + break; + + } + + } + + function provider() { + + $blob1 = << 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + $filter2 = $filter1; + $filter2['name'] = 'VTODO'; + + $filter3 = $filter1; + $filter3['is-not-defined'] = true; + + $filter4 = $filter1; + $filter4['name'] = 'VTODO'; + $filter4['is-not-defined'] = true; + + $filter5 = $filter1; + $filter5['comp-filters'] = [ + [ + 'name' => 'VALARM', + 'is-not-defined' => false, + 'comp-filters' => [], + 'prop-filters' => [], + 'time-range' => null, + ], + ]; + $filter6 = $filter1; + $filter6['prop-filters'] = [ + [ + 'name' => 'SUMMARY', + 'is-not-defined' => false, + 'param-filters' => [], + 'time-range' => null, + 'text-match' => null, + ], + ]; + $filter7 = $filter6; + $filter7['prop-filters'][0]['name'] = 'DESCRIPTION'; + + $filter8 = $filter6; + $filter8['prop-filters'][0]['is-not-defined'] = true; + + $filter9 = $filter7; + $filter9['prop-filters'][0]['is-not-defined'] = true; + + $filter10 = $filter5; + $filter10['prop-filters'] = $filter6['prop-filters']; + + // Param filters + $filter11 = $filter1; + $filter11['prop-filters'] = [ + [ + 'name' => 'DTSTART', + 'is-not-defined' => false, + 'param-filters' => [ + [ + 'name' => 'VALUE', + 'is-not-defined' => false, + 'text-match' => null, + ], + ], + 'time-range' => null, + 'text-match' => null, + ], + ]; + + $filter12 = $filter11; + $filter12['prop-filters'][0]['param-filters'][0]['name'] = 'TZID'; + + $filter13 = $filter11; + $filter13['prop-filters'][0]['param-filters'][0]['is-not-defined'] = true; + + $filter14 = $filter12; + $filter14['prop-filters'][0]['param-filters'][0]['is-not-defined'] = true; + + // Param text filter + $filter15 = $filter11; + $filter15['prop-filters'][0]['param-filters'][0]['text-match'] = [ + 'collation' => 'i;ascii-casemap', + 'value' => 'dAtE', + 'negate-condition' => false, + ]; + $filter16 = $filter15; + $filter16['prop-filters'][0]['param-filters'][0]['text-match']['collation'] = 'i;octet'; + + $filter17 = $filter15; + $filter17['prop-filters'][0]['param-filters'][0]['text-match']['negate-condition'] = true; + + $filter18 = $filter15; + $filter18['prop-filters'][0]['param-filters'][0]['text-match']['negate-condition'] = true; + $filter18['prop-filters'][0]['param-filters'][0]['text-match']['collation'] = 'i;octet'; + + // prop + text + $filter19 = $filter5; + $filter19['comp-filters'][0]['prop-filters'] = [ + [ + 'name' => 'action', + 'is-not-defined' => false, + 'time-range' => null, + 'param-filters' => [], + 'text-match' => [ + 'collation' => 'i;ascii-casemap', + 'value' => 'display', + 'negate-condition' => false, + ], + ], + ]; + + // Time range + $filter20 = [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 10:00:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 13:00:00', new \DateTimeZone('GMT')), + ], + ]; + // Time range, no end date + $filter21 = $filter20; + $filter21['time-range']['end'] = null; + + // Time range, no start date + $filter22 = $filter20; + $filter22['time-range']['start'] = null; + + // Time range, other dates + $filter23 = $filter20; + $filter23['time-range'] = [ + 'start' => new \DateTime('2011-02-01 10:00:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-02-01 13:00:00', new \DateTimeZone('GMT')), + ]; + // Time range + $filter24 = [ + 'name' => 'VTODO', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 12:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 13:15:00', new \DateTimeZone('GMT')), + ], + ]; + // Time range, other dates (1 month in the future) + $filter25 = $filter24; + $filter25['time-range'] = [ + 'start' => new \DateTime('2011-02-01 10:00:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-02-01 13:00:00', new \DateTimeZone('GMT')), + ]; + $filter26 = $filter24; + $filter26['time-range'] = [ + 'start' => new \DateTime('2011-01-01 11:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 12:15:00', new \DateTimeZone('GMT')), + ]; + + // Time range for VJOURNAL + $filter27 = [ + 'name' => 'VJOURNAL', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 12:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 13:15:00', new \DateTimeZone('GMT')), + ], + ]; + $filter28 = $filter27; + $filter28['time-range'] = [ + 'start' => new \DateTime('2011-01-01 11:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 12:15:00', new \DateTimeZone('GMT')), + ]; + // Time range for VFREEBUSY + $filter29 = [ + 'name' => 'VFREEBUSY', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 12:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 13:15:00', new \DateTimeZone('GMT')), + ], + ]; + // Time range filter on property + $filter30 = [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [ + [ + 'name' => 'DTSTART', + 'is-not-defined' => false, + 'param-filters' => [], + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 10:00:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 13:00:00', new \DateTimeZone('GMT')), + ], + 'text-match' => null, + ], + ], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + // Time range for alarm + $filter31 = [ + 'name' => 'VEVENT', + 'prop-filters' => [], + 'comp-filters' => [ + [ + 'name' => 'VALARM', + 'is-not-defined' => false, + 'comp-filters' => [], + 'prop-filters' => [], + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 10:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 11:15:00', new \DateTimeZone('GMT')), + ], + 'text-match' => null, + ], + ], + 'is-not-defined' => false, + 'time-range' => null, + ]; + $filter32 = $filter31; + $filter32['comp-filters'][0]['time-range'] = [ + 'start' => new \DateTime('2011-01-01 11:45:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 12:15:00', new \DateTimeZone('GMT')), + ]; + + $filter33 = $filter31; + $filter33['name'] = 'VTODO'; + $filter34 = $filter32; + $filter34['name'] = 'VTODO'; + $filter35 = $filter31; + $filter35['name'] = 'VJOURNAL'; + $filter36 = $filter32; + $filter36['name'] = 'VJOURNAL'; + + // Time range filter on non-datetime property + $filter37 = [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [ + [ + 'name' => 'SUMMARY', + 'is-not-defined' => false, + 'param-filters' => [], + 'time-range' => [ + 'start' => new \DateTime('2011-01-01 10:00:00', new \DateTimeZone('GMT')), + 'end' => new \DateTime('2011-01-01 13:00:00', new \DateTimeZone('GMT')), + ], + 'text-match' => null, + ], + ], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + $filter38 = [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2012-07-01 00:00:00', new \DateTimeZone('UTC')), + 'end' => new \DateTime('2012-08-01 00:00:00', new \DateTimeZone('UTC')), + ] + ]; + $filter39 = [ + 'name' => 'VEVENT', + 'comp-filters' => [ + [ + 'name' => 'VALARM', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2012-09-01 00:00:00', new \DateTimeZone('UTC')), + 'end' => new \DateTime('2012-10-01 00:00:00', new \DateTimeZone('UTC')), + ] + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + + return [ + + // Component check + + [$blob1, $filter1, 1], + [$blob1, $filter2, 0], + [$blob1, $filter3, 0], + [$blob1, $filter4, 1], + + // Subcomponent check (4) + [$blob1, $filter5, 0], + [$blob2, $filter5, 1], + + // Property checki (6) + [$blob1, $filter6, 1], + [$blob1, $filter7, 0], + [$blob1, $filter8, 0], + [$blob1, $filter9, 1], + + // Subcomponent + property (10) + [$blob2, $filter10, 1], + + // Param filter (11) + [$blob3, $filter11, 1], + [$blob3, $filter12, 0], + [$blob3, $filter13, 0], + [$blob3, $filter14, 1], + + // Param + text (15) + [$blob3, $filter15, 1], + [$blob3, $filter16, 0], + [$blob3, $filter17, 0], + [$blob3, $filter18, 1], + + // Prop + text (19) + [$blob2, $filter19, 1], + + // Incorrect object (vcard) (20) + [$blob4, $filter1, -1], + + // Time-range for event (21) + [$blob5, $filter20, 1], + [$blob6, $filter20, 1], + [$blob7, $filter20, 1], + [$blob8, $filter20, 1], + + [$blob5, $filter21, 1], + [$blob5, $filter22, 1], + + [$blob5, $filter23, 0], + [$blob6, $filter23, 0], + [$blob7, $filter23, 0], + [$blob8, $filter23, 0], + + // Time-range for todo (31) + [$blob9, $filter24, 1], + [$blob9, $filter25, 0], + [$blob9, $filter26, 1], + [$blob10, $filter24, 1], + [$blob10, $filter25, 0], + [$blob10, $filter26, 1], + + [$blob11, $filter24, 0], + [$blob11, $filter25, 0], + [$blob11, $filter26, 1], + + [$blob12, $filter24, 1], + [$blob12, $filter25, 0], + [$blob12, $filter26, 0], + + [$blob13, $filter24, 1], + [$blob13, $filter25, 0], + [$blob13, $filter26, 1], + + [$blob14, $filter24, 1], + [$blob14, $filter25, 0], + [$blob14, $filter26, 0], + + [$blob15, $filter24, 1], + [$blob15, $filter25, 1], + [$blob15, $filter26, 1], + + [$blob16, $filter24, 1], + [$blob16, $filter25, 1], + [$blob16, $filter26, 1], + + // Time-range for journals (55) + [$blob17, $filter27, 0], + [$blob17, $filter28, 0], + [$blob18, $filter27, 0], + [$blob18, $filter28, 1], + [$blob19, $filter27, 1], + [$blob19, $filter28, 1], + + // Time-range for free-busy (61) + [$blob20, $filter29, -1], + + // Time-range on property (62) + [$blob5, $filter30, 1], + [$blob3, $filter37, -1], + [$blob3, $filter30, 0], + + // Time-range on alarm in vevent (65) + [$blob21, $filter31, 1], + [$blob21, $filter32, 0], + [$blob22, $filter31, 1], + [$blob22, $filter32, 0], + [$blob23, $filter31, 1], + [$blob23, $filter32, 0], + [$blob24, $filter31, 1], + [$blob24, $filter32, 0], + [$blob25, $filter31, 1], + [$blob25, $filter32, 0], + [$blob26, $filter31, 1], + [$blob26, $filter32, 0], + + // Time-range on alarm for vtodo (77) + [$blob27, $filter33, 1], + [$blob27, $filter34, 0], + + // Time-range on alarm for vjournal (79) + [$blob28, $filter35, -1], + [$blob28, $filter36, -1], + + // Time-range on alarm with duration (81) + [$blob29, $filter31, 1], + [$blob29, $filter32, 0], + [$blob30, $filter31, 0], + [$blob30, $filter32, 0], + + // Time-range with RRULE (85) + [$blob31, $filter20, 1], + [$blob32, $filter20, 0], + + // Bug reported on mailing list, related to all-day events (87) + //array($blob33, $filter38, 1), + + // Event in timerange, but filtered alarm is in the far future (88). + [$blob34, $filter39, 0], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php new file mode 100644 index 00000000000..df85b6ded0d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/CalendarTest.php @@ -0,0 +1,256 @@ +backend = TestUtil::getBackend(); + + $this->calendars = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(2, count($this->calendars)); + $this->calendar = new Calendar($this->backend, $this->calendars[0]); + + + } + + function teardown() { + + unset($this->backend); + + } + + function testSimple() { + + $this->assertEquals($this->calendars[0]['uri'], $this->calendar->getName()); + + } + + /** + * @depends testSimple + */ + function testUpdateProperties() { + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'NewName', + ]); + + $result = $this->calendar->propPatch($propPatch); + $result = $propPatch->commit(); + + $this->assertEquals(true, $result); + + $calendars2 = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals('NewName', $calendars2[0]['{DAV:}displayname']); + + } + + /** + * @depends testSimple + */ + function testGetProperties() { + + $question = [ + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set', + ]; + + $result = $this->calendar->getProperties($question); + + foreach ($question as $q) $this->assertArrayHasKey($q, $result); + + $this->assertEquals(['VEVENT', 'VTODO'], $result['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue()); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + * @depends testSimple + */ + function testGetChildNotFound() { + + $this->calendar->getChild('randomname'); + + } + + /** + * @depends testSimple + */ + function testGetChildren() { + + $children = $this->calendar->getChildren(); + $this->assertEquals(1, count($children)); + + $this->assertTrue($children[0] instanceof CalendarObject); + + } + + /** + * @depends testGetChildren + */ + function testChildExists() { + + $this->assertFalse($this->calendar->childExists('foo')); + + $children = $this->calendar->getChildren(); + $this->assertTrue($this->calendar->childExists($children[0]->getName())); + } + + + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testCreateDirectory() { + + $this->calendar->createDirectory('hello'); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testSetName() { + + $this->calendar->setName('hello'); + + } + + function testGetLastModified() { + + $this->assertNull($this->calendar->getLastModified()); + + } + + function testCreateFile() { + + $file = fopen('php://memory', 'r+'); + fwrite($file, TestUtil::getTestCalendarData()); + rewind($file); + + $this->calendar->createFile('hello', $file); + + $file = $this->calendar->getChild('hello'); + $this->assertTrue($file instanceof CalendarObject); + + } + + function testCreateFileNoSupportedComponents() { + + $file = fopen('php://memory', 'r+'); + fwrite($file, TestUtil::getTestCalendarData()); + rewind($file); + + $calendar = new Calendar($this->backend, $this->calendars[1]); + $calendar->createFile('hello', $file); + + $file = $calendar->getChild('hello'); + $this->assertTrue($file instanceof CalendarObject); + + } + + function testDelete() { + + $this->calendar->delete(); + + $calendars = $this->backend->getCalendarsForUser('principals/user1'); + $this->assertEquals(1, count($calendars)); + } + + function testGetOwner() { + + $this->assertEquals('principals/user1', $this->calendar->getOwner()); + + } + + function testGetGroup() { + + $this->assertNull($this->calendar->getGroup()); + + } + + function testGetACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + ]; + $this->assertEquals($expected, $this->calendar->getACL()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $this->calendar->setACL([]); + + } + + function testGetSyncToken() { + + $this->assertNull($this->calendar->getSyncToken()); + + } + + function testGetSyncTokenNoSyncSupport() { + + $calendar = new Calendar(new Backend\Mock([], []), []); + $this->assertNull($calendar->getSyncToken()); + + } + + function testGetChanges() { + + $this->assertNull($this->calendar->getChanges(1, 1)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php new file mode 100644 index 00000000000..9a3d47828fb --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php @@ -0,0 +1,113 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTEND;TZID=Europe/Berlin:20120207T191500 +RRULE:FREQ=DAILY;INTERVAL=1;COUNT=3 +SUMMARY:RecurringEvents 3 times +DTSTART;TZID=Europe/Berlin:20120207T181500 +END:VEVENT +BEGIN:VEVENT +CREATED:20120207T111900Z +UID:foobar +DTEND;TZID=Europe/Berlin:20120208T191500 +SUMMARY:RecurringEvents 3 times OVERWRITTEN +DTSTART;TZID=Europe/Berlin:20120208T181500 +RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500 +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testExpand() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + try { + $vObject = VObject\Reader::read($body); + } catch (VObject\ParseException $e) { + $this->fail('Could not parse object. Error:' . $e->getMessage() . ' full object: ' . $response->getBodyAsString()); + } + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre\VObject\Component\VEvent */ + foreach ($vevent->children() as $child) { + /** @var $child Sabre\VObject\Property */ + if ($child->name == 'DTSTART') { + // DTSTART has to be one of three valid values + $this->assertContains($child->getValue(), ['20120207T171500Z', '20120208T171500Z', '20120209T171500Z'], 'DTSTART is not a valid value: ' . $child->getValue()); + } elseif ($child->name == 'DTEND') { + // DTEND has to be one of three valid values + $this->assertContains($child->getValue(), ['20120207T181500Z', '20120208T181500Z', '20120209T181500Z'], 'DTEND is not a valid value: ' . $child->getValue()); + } + } + } + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php new file mode 100644 index 00000000000..efc49673f3f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php @@ -0,0 +1,102 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTEND;TZID=Europe/Berlin:20120207T191500 +RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,TH +SUMMARY:RecurringEvents on tuesday and thursday +DTSTART;TZID=Europe/Berlin:20120207T181500 +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testExpandRecurringByDayEvent() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + $this->assertEquals(2, count($vObject->VEVENT)); + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre\VObject\Component\VEvent */ + foreach ($vevent->children() as $child) { + /** @var $child Sabre\VObject\Property */ + if ($child->name == 'DTSTART') { + // DTSTART has to be one of two valid values + $this->assertContains($child->getValue(), ['20120214T171500Z', '20120216T171500Z'], 'DTSTART is not a valid value: ' . $child->getValue()); + } elseif ($child->name == 'DTEND') { + // DTEND has to be one of two valid values + $this->assertContains($child->getValue(), ['20120214T181500Z', '20120216T181500Z'], 'DTEND is not a valid value: ' . $child->getValue()); + } + } + } + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php new file mode 100644 index 00000000000..3a22e03d4a5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php @@ -0,0 +1,103 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTEND;TZID=Europe/Berlin:20120207T191500 +RRULE:FREQ=DAILY;INTERVAL=1;COUNT=3 +SUMMARY:RecurringEvents 3 times +DTSTART;TZID=Europe/Berlin:20120207T181500 +END:VEVENT +BEGIN:VEVENT +CREATED:20120207T111900Z +UID:foobar +DTEND;TZID=Europe/Berlin:20120208T191500 +SUMMARY:RecurringEvents 3 times OVERWRITTEN +DTSTART;TZID=Europe/Berlin:20120208T181500 +RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500 +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testExpand() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + // We only expect 3 events + $this->assertEquals(3, count($vObject->VEVENT), 'We got 6 events instead of 3. Output: ' . $body); + + // TZID should be gone + $this->assertFalse(isset($vObject->VEVENT->DTSTART['TZID'])); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsFloatingTimeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsFloatingTimeTest.php new file mode 100644 index 00000000000..fba47d79ba1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ExpandEventsFloatingTimeTest.php @@ -0,0 +1,207 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +DTSTART:19810329T020000 +TZNAME:GMT+2 +TZOFFSETTO:+0200 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +DTSTART:19961027T030000 +TZNAME:GMT+1 +TZOFFSETTO:+0100 +END:STANDARD +END:VTIMEZONE +END:VCALENDAR', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +BEGIN:VEVENT +CREATED:20140701T143658Z +UID:dba46fe8-1631-4d98-a575-97963c364dfe +DTEND:20141108T073000 +TRANSP:OPAQUE +SUMMARY:Floating Time event, starting 05:30am Europe/Berlin +DTSTART:20141108T053000 +DTSTAMP:20140701T143706Z +SEQUENCE:1 +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testExpandCalendarQuery() { + + $request = new HTTP\Request('REPORT', '/calendars/user1/calendar1', [ + 'Depth' => 1, + 'Content-Type' => 'application/xml', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre\VObject\Component\VEvent */ + foreach ($vevent->children() as $child) { + /** @var $child Sabre\VObject\Property */ + if ($child->name == 'DTSTART') { + // DTSTART should be the UTC equivalent of given floating time + $this->assertEquals('20141108T043000Z', $child->getValue()); + } elseif ($child->name == 'DTEND') { + // DTEND should be the UTC equivalent of given floating time + $this->assertEquals('20141108T063000Z', $child->getValue()); + } + } + } + } + + function testExpandMultiGet() { + + $request = new HTTP\Request('REPORT', '/calendars/user1/calendar1', [ + 'Depth' => 1, + 'Content-Type' => 'application/xml', + ]); + + $request->setBody(' + + + + + + + + /calendars/user1/calendar1/event.ics +'); + + $response = $this->request($request); + + $this->assertEquals(207, $response->getStatus()); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre\VObject\Component\VEvent */ + foreach ($vevent->children() as $child) { + /** @var $child Sabre\VObject\Property */ + if ($child->name == 'DTSTART') { + // DTSTART should be the UTC equivalent of given floating time + $this->assertEquals($child->getValue(), '20141108T043000Z'); + } elseif ($child->name == 'DTEND') { + // DTEND should be the UTC equivalent of given floating time + $this->assertEquals($child->getValue(), '20141108T063000Z'); + } + } + } + } + + function testExpandExport() { + + $request = new HTTP\Request('GET', '/calendars/user1/calendar1?export&start=1&end=2000000000&expand=1', [ + 'Depth' => 1, + 'Content-Type' => 'application/xml', + ]); + + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + // check if DTSTARTs and DTENDs are correct + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre\VObject\Component\VEvent */ + foreach ($vevent->children() as $child) { + /** @var $child Sabre\VObject\Property */ + if ($child->name == 'DTSTART') { + // DTSTART should be the UTC equivalent of given floating time + $this->assertEquals('20141108T043000Z', $child->getValue()); + } elseif ($child->name == 'DTEND') { + // DTEND should be the UTC equivalent of given floating time + $this->assertEquals('20141108T063000Z', $child->getValue()); + } + } + } + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php new file mode 100644 index 00000000000..7604c7f4c1b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/FreeBusyReportTest.php @@ -0,0 +1,174 @@ + [ + 'obj1' => [ + 'calendarid' => 1, + 'uri' => 'event1.ics', + 'calendardata' => $obj1, + ], + 'obj2' => [ + 'calendarid' => 1, + 'uri' => 'event2.ics', + 'calendardata' => $obj2 + ], + 'obj3' => [ + 'calendarid' => 1, + 'uri' => 'event3.ics', + 'calendardata' => $obj3 + ] + ], + ]; + + + $caldavBackend = new Backend\Mock([], $calendarData); + + $calendar = new Calendar($caldavBackend, [ + 'id' => 1, + 'uri' => 'calendar', + 'principaluri' => 'principals/user1', + '{' . Plugin::NS_CALDAV . '}calendar-timezone' => "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nEND:VTIMEZONE\r\nEND:VCALENDAR", + ]); + + $this->server = new DAV\Server([$calendar]); + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_URI' => '/calendar', + ]); + $this->server->httpRequest = $request; + $this->server->httpResponse = new HTTP\ResponseMock(); + + $this->plugin = new Plugin(); + $this->server->addPlugin($this->plugin); + + } + + function testFreeBusyReport() { + + $reportXML = << + + + +XML; + + $report = $this->server->xml->parse($reportXML, null, $rootElem); + $this->plugin->report($rootElem, $report, null); + + $this->assertEquals(200, $this->server->httpResponse->status); + $this->assertEquals('text/calendar', $this->server->httpResponse->getHeader('Content-Type')); + $this->assertTrue(strpos($this->server->httpResponse->body, 'BEGIN:VFREEBUSY') !== false); + $this->assertTrue(strpos($this->server->httpResponse->body, '20111005T120000Z/20111005T130000Z') !== false); + $this->assertTrue(strpos($this->server->httpResponse->body, '20111006T100000Z/20111006T110000Z') !== false); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testFreeBusyReportNoTimeRange() { + + $reportXML = << + + +XML; + + $report = $this->server->xml->parse($reportXML, null, $rootElem); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotImplemented + */ + function testFreeBusyReportWrongNode() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_URI' => '/', + ]); + $this->server->httpRequest = $request; + + $reportXML = << + + + +XML; + + $report = $this->server->xml->parse($reportXML, null, $rootElem); + $this->plugin->report($rootElem, $report, null); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testFreeBusyReportNoACLPlugin() { + + $this->server = new DAV\Server(); + $this->plugin = new Plugin(); + $this->server->addPlugin($this->plugin); + + $reportXML = << + + + +XML; + + $report = $this->server->xml->parse($reportXML, null, $rootElem); + $this->plugin->report($rootElem, $report, null); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php new file mode 100644 index 00000000000..5fd8d29a11b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php @@ -0,0 +1,82 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +CREATED:20120313T142342Z +UID:171EBEFC-C951-499D-B234-7BA7D677B45D +DTEND;TZID=Europe/Berlin:20120227T010000 +TRANSP:OPAQUE +SUMMARY:Monday 0h +DTSTART;TZID=Europe/Berlin:20120227T000000 +DTSTAMP:20120313T142416Z +SEQUENCE:4 +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testQueryTimerange() { + + $request = new HTTP\Request( + 'REPORT', + '/calendars/user1/calendar1', + [ + 'Content-Type' => 'application/xml', + 'Depth' => '1', + ] + ); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + $this->assertTrue(strpos($response->body, 'BEGIN:VCALENDAR') !== false); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php new file mode 100644 index 00000000000..75412577e9b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ICSExportPluginTest.php @@ -0,0 +1,386 @@ +icsExportPlugin = new ICSExportPlugin(); + $this->server->addPlugin( + $this->icsExportPlugin + ); + + $id = $this->caldavBackend->createCalendar( + 'principals/admin', + 'UUID-123467', + [ + '{DAV:}displayname' => 'Hello!', + '{http://apple.com/ns/ical/}calendar-color' => '#AA0000FF', + ] + ); + + $this->caldavBackend->createCalendarObject( + $id, + 'event-1', + <<caldavBackend->createCalendarObject( + $id, + 'todo-1', + <<assertEquals( + $this->icsExportPlugin, + $this->server->getPlugin('ics-export') + ); + $this->assertEquals($this->icsExportPlugin, $this->server->getPlugin('ics-export')); + $this->assertEquals('ics-export', $this->icsExportPlugin->getPluginInfo()['name']); + + } + + function testBeforeMethod() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export' + ); + + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals('text/calendar', $response->getHeader('Content-Type')); + + $obj = VObject\Reader::read($response->body); + + $this->assertEquals(8, count($obj->children())); + $this->assertEquals(1, count($obj->VERSION)); + $this->assertEquals(1, count($obj->CALSCALE)); + $this->assertEquals(1, count($obj->PRODID)); + $this->assertTrue(strpos((string)$obj->PRODID, DAV\Version::VERSION) !== false); + $this->assertEquals(1, count($obj->VTIMEZONE)); + $this->assertEquals(1, count($obj->VEVENT)); + $this->assertEquals("Hello!", $obj->{"X-WR-CALNAME"}); + $this->assertEquals("#AA0000FF", $obj->{"X-APPLE-CALENDAR-COLOR"}); + + } + function testBeforeMethodNoVersion() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export' + ); + DAV\Server::$exposeVersion = false; + $response = $this->request($request); + DAV\Server::$exposeVersion = true; + + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals('text/calendar', $response->getHeader('Content-Type')); + + $obj = VObject\Reader::read($response->body); + + $this->assertEquals(8, count($obj->children())); + $this->assertEquals(1, count($obj->VERSION)); + $this->assertEquals(1, count($obj->CALSCALE)); + $this->assertEquals(1, count($obj->PRODID)); + $this->assertFalse(strpos((string)$obj->PRODID, DAV\Version::VERSION) !== false); + $this->assertEquals(1, count($obj->VTIMEZONE)); + $this->assertEquals(1, count($obj->VEVENT)); + + } + + function testBeforeMethodNoExport() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467' + ); + $response = new HTTP\Response(); + $this->assertNull($this->icsExportPlugin->httpGet($request, $response)); + + } + + function testACLIntegrationBlocked() { + + $aclPlugin = new DAVACL\Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $this->server->addPlugin( + $aclPlugin + ); + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export' + ); + + $this->request($request, 403); + + } + + function testACLIntegrationNotBlocked() { + + $aclPlugin = new DAVACL\Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $this->server->addPlugin( + $aclPlugin + ); + $this->server->addPlugin( + new Plugin() + ); + + $this->autoLogin('admin'); + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export' + ); + + $response = $this->request($request, 200); + $this->assertEquals('text/calendar', $response->getHeader('Content-Type')); + + $obj = VObject\Reader::read($response->body); + + $this->assertEquals(8, count($obj->children())); + $this->assertEquals(1, count($obj->VERSION)); + $this->assertEquals(1, count($obj->CALSCALE)); + $this->assertEquals(1, count($obj->PRODID)); + $this->assertTrue(strpos((string)$obj->PRODID, DAV\Version::VERSION) !== false); + $this->assertEquals(1, count($obj->VTIMEZONE)); + $this->assertEquals(1, count($obj->VEVENT)); + + } + + function testBadStartParam() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&start=foo' + ); + $this->request($request, 400); + + } + + function testBadEndParam() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&end=foo' + ); + $this->request($request, 400); + + } + + function testFilterStartEnd() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&start=1&end=2' + ); + $response = $this->request($request, 200); + + $obj = VObject\Reader::read($response->getBody()); + + $this->assertEquals(0, count($obj->VTIMEZONE)); + $this->assertEquals(0, count($obj->VEVENT)); + + } + + function testExpandNoStart() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&expand=1&end=2' + ); + $this->request($request, 400); + + } + + function testExpand() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&start=1&end=2000000000&expand=1' + ); + $response = $this->request($request, 200); + + $obj = VObject\Reader::read($response->getBody()); + + $this->assertEquals(0, count($obj->VTIMEZONE)); + $this->assertEquals(1, count($obj->VEVENT)); + + } + + function testJCal() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export', + ['Accept' => 'application/calendar+json'] + ); + + $response = $this->request($request, 200); + $this->assertEquals('application/calendar+json', $response->getHeader('Content-Type')); + + } + + function testJCalInUrl() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&accept=jcal' + ); + + $response = $this->request($request, 200); + $this->assertEquals('application/calendar+json', $response->getHeader('Content-Type')); + + } + + function testNegotiateDefault() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export', + ['Accept' => 'text/plain'] + ); + + $response = $this->request($request, 200); + $this->assertEquals('text/calendar', $response->getHeader('Content-Type')); + + } + + function testFilterComponentVEVENT() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&componentType=VEVENT' + ); + + $response = $this->request($request, 200); + + $obj = VObject\Reader::read($response->body); + $this->assertEquals(1, count($obj->VTIMEZONE)); + $this->assertEquals(1, count($obj->VEVENT)); + $this->assertEquals(0, count($obj->VTODO)); + + } + + function testFilterComponentVTODO() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&componentType=VTODO' + ); + + $response = $this->request($request, 200); + + $obj = VObject\Reader::read($response->body); + + $this->assertEquals(0, count($obj->VTIMEZONE)); + $this->assertEquals(0, count($obj->VEVENT)); + $this->assertEquals(1, count($obj->VTODO)); + + } + + function testFilterComponentBadComponent() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export&componentType=VVOODOO' + ); + + $response = $this->request($request, 400); + + } + + function testContentDisposition() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export' + ); + + $response = $this->request($request, 200); + $this->assertEquals('text/calendar', $response->getHeader('Content-Type')); + $this->assertEquals( + 'attachment; filename="UUID-123467-' . date('Y-m-d') . '.ics"', + $response->getHeader('Content-Disposition') + ); + + } + + function testContentDispositionJson() { + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-123467?export', + ['Accept' => 'application/calendar+json'] + ); + + $response = $this->request($request, 200); + $this->assertEquals('application/calendar+json', $response->getHeader('Content-Type')); + $this->assertEquals( + 'attachment; filename="UUID-123467-' . date('Y-m-d') . '.json"', + $response->getHeader('Content-Disposition') + ); + + } + + function testContentDispositionBadChars() { + + $this->caldavBackend->createCalendar( + 'principals/admin', + 'UUID-b_ad"(ch)ars', + [ + '{DAV:}displayname' => 'Test bad characters', + '{http://apple.com/ns/ical/}calendar-color' => '#AA0000FF', + ] + ); + + $request = new HTTP\Request( + 'GET', + '/calendars/admin/UUID-b_ad"(ch)ars?export', + ['Accept' => 'application/calendar+json'] + ); + + $response = $this->request($request, 200); + $this->assertEquals('application/calendar+json', $response->getHeader('Content-Type')); + $this->assertEquals( + 'attachment; filename="UUID-b_adchars-' . date('Y-m-d') . '.json"', + $response->getHeader('Content-Disposition') + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue166Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue166Test.php new file mode 100644 index 00000000000..a1a9b7c0443 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue166Test.php @@ -0,0 +1,63 @@ + 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2011-12-01'), + 'end' => new \DateTime('2012-02-01'), + ], + ], + ], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => null, + ]; + $input = VObject\Reader::read($input); + $this->assertTrue($validator->validate($input, $filters)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue172Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue172Test.php new file mode 100644 index 00000000000..e2b85c2bcdd --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue172Test.php @@ -0,0 +1,135 @@ + 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2012-01-18 21:00:00 GMT-08:00'), + 'end' => new \DateTime('2012-01-18 21:00:00 GMT-08:00'), + ], + ], + ], + 'prop-filters' => [], + ]; + $input = VObject\Reader::read($input); + $this->assertTrue($validator->validate($input, $filters)); + } + + // Pacific Standard Time, translates to America/Los_Angeles (GMT-8 in January) + function testOutlookTimezoneName() { + $input = << 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), + 'end' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), + ], + ], + ], + 'prop-filters' => [], + ]; + $input = VObject\Reader::read($input); + $this->assertTrue($validator->validate($input, $filters)); + } + + // X-LIC-LOCATION, translates to America/Los_Angeles (GMT-8 in January) + function testLibICalLocationName() { + $input = << 'VCALENDAR', + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), + 'end' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), + ], + ], + ], + 'prop-filters' => [], + ]; + $input = VObject\Reader::read($input); + $this->assertTrue($validator->validate($input, $filters)); + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php new file mode 100644 index 00000000000..369e9a70c12 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue203Test.php @@ -0,0 +1,137 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120330T155305CEST-6585fBUVgV +DTSTAMP:20120330T135305Z +DTSTART;TZID=Europe/Berlin:20120326T155200 +DTEND;TZID=Europe/Berlin:20120326T165200 +RRULE:FREQ=DAILY;COUNT=2;INTERVAL=1 +SUMMARY:original summary +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +UID:20120330T155305CEST-6585fBUVgV +DTSTAMP:20120330T135352Z +DESCRIPTION: +DTSTART;TZID=Europe/Berlin:20120328T155200 +DTEND;TZID=Europe/Berlin:20120328T165200 +RECURRENCE-ID;TZID=Europe/Berlin:20120327T155200 +SEQUENCE:1 +SUMMARY:overwritten summary +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testIssue203() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + $this->assertEquals(2, count($vObject->VEVENT)); + + + $expectedEvents = [ + [ + 'DTSTART' => '20120326T135200Z', + 'DTEND' => '20120326T145200Z', + 'SUMMARY' => 'original summary', + ], + [ + 'DTSTART' => '20120328T135200Z', + 'DTEND' => '20120328T145200Z', + 'SUMMARY' => 'overwritten summary', + 'RECURRENCE-ID' => '20120327T135200Z', + ] + ]; + + // try to match agains $expectedEvents array + foreach ($expectedEvents as $expectedEvent) { + + $matching = false; + + foreach ($vObject->VEVENT as $vevent) { + /** @var $vevent Sabre\VObject\Component\VEvent */ + foreach ($vevent->children() as $child) { + /** @var $child Sabre\VObject\Property */ + if (isset($expectedEvent[$child->name])) { + if ($expectedEvent[$child->name] != $child->getValue()) { + continue 2; + } + } + } + + $matching = true; + break; + } + + $this->assertTrue($matching, 'Did not find the following event in the response: ' . var_export($expectedEvent, true)); + } + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php new file mode 100644 index 00000000000..ce40a90b035 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue205Test.php @@ -0,0 +1,98 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120330T155305CEST-6585fBUVgV +DTSTAMP:20120330T135305Z +DTSTART;TZID=Europe/Berlin:20120326T155200 +DTEND;TZID=Europe/Berlin:20120326T165200 +SUMMARY:original summary +TRANSP:OPAQUE +BEGIN:VALARM +ACTION:AUDIO +ATTACH;VALUE=URI:Basso +TRIGGER:PT0S +END:VALARM +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testIssue205() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + $this->assertFalse(strpos($response->body, 'Exception'), 'Exception occurred: ' . $response->body); + $this->assertFalse(strpos($response->body, 'Unknown or bad format'), 'DateTime unknown format Exception: ' . $response->body); + + // Everts super awesome xml parser. + $body = substr( + $response->body, + $start = strpos($response->body, 'BEGIN:VCALENDAR'), + strpos($response->body, 'END:VCALENDAR') - $start + 13 + ); + $body = str_replace(' ', '', $body); + + $vObject = VObject\Reader::read($body); + + $this->assertEquals(1, count($vObject->VEVENT)); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php new file mode 100644 index 00000000000..950629fd813 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue211Test.php @@ -0,0 +1,89 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120418T172519CEST-3510gh1hVw +DTSTAMP:20120418T152519Z +DTSTART;VALUE=DATE:20120330 +DTEND;VALUE=DATE:20120531 +EXDATE;TZID=Europe/Berlin:20120330T000000 +RRULE:FREQ=YEARLY;INTERVAL=1 +SEQUENCE:1 +SUMMARY:Birthday +TRANSP:TRANSPARENT +BEGIN:VALARM +ACTION:EMAIL +ATTENDEE:MAILTO:xxx@domain.de +DESCRIPTION:Dies ist eine Kalender Erinnerung +SUMMARY:Kalender Alarm Erinnerung +TRIGGER;VALUE=DATE-TIME:20120329T060000Z +END:VALARM +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testIssue211() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // if this assert is reached, the endless loop is gone + // There should be no matching events + $this->assertFalse(strpos('BEGIN:VEVENT', $response->body)); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php new file mode 100644 index 00000000000..c3c0b5b48a8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue220Test.php @@ -0,0 +1,99 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20120601T180000 +SUMMARY:Brot backen +RRULE:FREQ=DAILY;INTERVAL=1;WKST=MO +TRANSP:OPAQUE +DURATION:PT20M +LAST-MODIFIED:20120601T064634Z +CREATED:20120601T064634Z +DTSTAMP:20120601T064634Z +UID:b64f14c5-dccc-4eda-947f-bdb1f763fbcd +BEGIN:VALARM +TRIGGER;VALUE=DURATION:-PT5M +ACTION:DISPLAY +DESCRIPTION:Default Event Notification +X-WR-ALARMUID:cd952c1b-b3d6-41fb-b0a6-ec3a1a5bdd58 +END:VALARM +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20120606T180000 +SUMMARY:Brot backen +TRANSP:OPAQUE +STATUS:CANCELLED +DTEND;TZID=Europe/Berlin:20120606T182000 +LAST-MODIFIED:20120605T094310Z +SEQUENCE:1 +RECURRENCE-ID:20120606T160000Z +UID:b64f14c5-dccc-4eda-947f-bdb1f763fbcd +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testIssue220() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + $this->assertFalse(strpos($response->body, 'PHPUnit_Framework_Error_Warning'), 'Error Warning occurred: ' . $response->body); + $this->assertFalse(strpos($response->body, 'Invalid argument supplied for foreach()'), 'Invalid argument supplied for foreach(): ' . $response->body); + + $this->assertEquals(207, $response->status); + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php new file mode 100644 index 00000000000..d0783701de9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Issue228Test.php @@ -0,0 +1,79 @@ + 1, + 'name' => 'Calendar', + 'principaluri' => 'principals/user1', + 'uri' => 'calendar1', + ] + ]; + + protected $caldavCalendarObjects = [ + 1 => [ + 'event.ics' => [ + 'calendardata' => 'BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:20120730T113415CEST-6804EGphkd@xxxxxx.de +DTSTAMP:20120730T093415Z +DTSTART;VALUE=DATE:20120729 +DTEND;VALUE=DATE:20120730 +SUMMARY:sunday event +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR +', + ], + ], + ]; + + function testIssue228() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'REQUEST_URI' => '/calendars/user1/calendar1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody(' + + + + + + + + + + + + + + +'); + + $response = $this->request($request); + + // We must check if absolutely nothing was returned from this query. + $this->assertFalse(strpos($response->body, 'BEGIN:VCALENDAR')); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/JCalTransformTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/JCalTransformTest.php new file mode 100644 index 00000000000..f1eed177597 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/JCalTransformTest.php @@ -0,0 +1,262 @@ + 1, + 'principaluri' => 'principals/user1', + 'uri' => 'foo', + ] + ]; + protected $caldavCalendarObjects = [ + 1 => [ + 'bar.ics' => [ + 'uri' => 'bar.ics', + 'calendarid' => 1, + 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", + 'lastmodified' => null + ] + ], + ]; + + function testGet() { + + $headers = [ + 'Accept' => 'application/calendar+json', + ]; + $request = new Request('GET', '/calendars/user1/foo/bar.ics', $headers); + + $response = $this->request($request); + + $body = $response->getBodyAsString(); + $this->assertEquals(200, $response->getStatus(), "Incorrect status code: " . $body); + + $response = json_decode($body, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $this->fail('Json decoding error: ' . json_last_error_msg()); + } + $this->assertEquals( + [ + 'vcalendar', + [], + [ + [ + 'vevent', + [], + [], + ], + ], + ], + $response + ); + + } + + function testMultiGet() { + + $xml = << + + + + + /calendars/user1/foo/bar.ics + +XML; + + $headers = []; + $request = new Request('REPORT', '/calendars/user1/foo', $headers, $xml); + + $response = $this->request($request); + + $this->assertEquals(207, $response->getStatus(), 'Full rsponse: ' . $response->getBodyAsString()); + + $multiStatus = $this->server->xml->parse( + $response->getBodyAsString() + ); + + $responses = $multiStatus->getResponses(); + $this->assertEquals(1, count($responses)); + + $response = $responses[0]->getResponseProperties()[200]["{urn:ietf:params:xml:ns:caldav}calendar-data"]; + + $jresponse = json_decode($response, true); + if (json_last_error()) { + $this->fail('Json decoding error: ' . json_last_error_msg() . '. Full response: ' . $response); + } + $this->assertEquals( + [ + 'vcalendar', + [], + [ + [ + 'vevent', + [], + [], + ], + ], + ], + $jresponse + ); + + } + + function testCalendarQueryDepth1() { + + $xml = << + + + + + + + + +XML; + + $headers = [ + 'Depth' => '1', + ]; + $request = new Request('REPORT', '/calendars/user1/foo', $headers, $xml); + + $response = $this->request($request); + + $this->assertEquals(207, $response->getStatus(), "Invalid response code. Full body: " . $response->getBodyAsString()); + + $multiStatus = $this->server->xml->parse( + $response->getBodyAsString() + ); + + $responses = $multiStatus->getResponses(); + + $this->assertEquals(1, count($responses)); + + $response = $responses[0]->getResponseProperties()[200]["{urn:ietf:params:xml:ns:caldav}calendar-data"]; + $response = json_decode($response, true); + if (json_last_error()) { + $this->fail('Json decoding error: ' . json_last_error_msg()); + } + $this->assertEquals( + [ + 'vcalendar', + [], + [ + [ + 'vevent', + [], + [], + ], + ], + ], + $response + ); + + } + + function testCalendarQueryDepth0() { + + $xml = << + + + + + + + + +XML; + + $headers = [ + 'Depth' => '0', + ]; + $request = new Request('REPORT', '/calendars/user1/foo/bar.ics', $headers, $xml); + + $response = $this->request($request); + + $this->assertEquals(207, $response->getStatus(), "Invalid response code. Full body: " . $response->getBodyAsString()); + + $multiStatus = $this->server->xml->parse( + $response->getBodyAsString() + ); + + $responses = $multiStatus->getResponses(); + + $this->assertEquals(1, count($responses)); + + $response = $responses[0]->getResponseProperties()[200]["{urn:ietf:params:xml:ns:caldav}calendar-data"]; + $response = json_decode($response, true); + if (json_last_error()) { + $this->fail('Json decoding error: ' . json_last_error_msg()); + } + $this->assertEquals( + [ + 'vcalendar', + [], + [ + [ + 'vevent', + [], + [], + ], + ], + ], + $response + ); + + } + + function testValidateICalendar() { + + $input = [ + 'vcalendar', + [], + [ + [ + 'vevent', + [ + ['uid', (object)[], 'text', 'foo'], + ['dtstart', (object)[], 'date', '2016-04-06'], + ], + [], + ], + ], + ]; + $input = json_encode($input); + $this->caldavPlugin->beforeWriteContent( + 'calendars/user1/foo/bar.ics', + $this->server->tree->getNodeForPath('calendars/user1/foo/bar.ics'), + $input, + $modified + ); + + + $expected = <<assertVObjectEqualsVObject( + $expected, + $input + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php new file mode 100644 index 00000000000..6585f85c379 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/CollectionTest.php @@ -0,0 +1,85 @@ +principalUri = 'principals/user1'; + + $this->notification = new CalDAV\Xml\Notification\SystemStatus(1, '"1"'); + + $this->caldavBackend = new CalDAV\Backend\MockSharing([], [], [ + 'principals/user1' => [ + $this->notification + ] + ]); + + return new Collection($this->caldavBackend, $this->principalUri); + + } + + function testGetChildren() { + + $col = $this->getInstance(); + $this->assertEquals('notifications', $col->getName()); + + $this->assertEquals([ + new Node($this->caldavBackend, $this->principalUri, $this->notification) + ], $col->getChildren()); + + } + + function testGetOwner() { + + $col = $this->getInstance(); + $this->assertEquals('principals/user1', $col->getOwner()); + + } + + function testGetGroup() { + + $col = $this->getInstance(); + $this->assertNull($col->getGroup()); + + } + + function testGetACL() { + + $col = $this->getInstance(); + $expected = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + ]; + + $this->assertEquals($expected, $col->getACL()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $col = $this->getInstance(); + $col->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $col = $this->getInstance(); + $this->assertNull($col->getSupportedPrivilegeSet()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php new file mode 100644 index 00000000000..6c6e02da81a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/NodeTest.php @@ -0,0 +1,96 @@ +systemStatus = new CalDAV\Xml\Notification\SystemStatus(1, '"1"'); + + $this->caldavBackend = new CalDAV\Backend\MockSharing([], [], [ + 'principals/user1' => [ + $this->systemStatus + ] + ]); + + $node = new Node($this->caldavBackend, 'principals/user1', $this->systemStatus); + return $node; + + } + + function testGetId() { + + $node = $this->getInstance(); + $this->assertEquals($this->systemStatus->getId() . '.xml', $node->getName()); + + } + + function testGetEtag() { + + $node = $this->getInstance(); + $this->assertEquals('"1"', $node->getETag()); + + } + + function testGetNotificationType() { + + $node = $this->getInstance(); + $this->assertEquals($this->systemStatus, $node->getNotificationType()); + + } + + function testDelete() { + + $node = $this->getInstance(); + $node->delete(); + $this->assertEquals([], $this->caldavBackend->getNotificationsForPrincipal('principals/user1')); + + } + + function testGetGroup() { + + $node = $this->getInstance(); + $this->assertNull($node->getGroup()); + + } + + function testGetACL() { + + $node = $this->getInstance(); + $expected = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + ]; + + $this->assertEquals($expected, $node->getACL()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $node = $this->getInstance(); + $node->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $node = $this->getInstance(); + $this->assertNull($node->getSupportedPrivilegeSet()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/PluginTest.php new file mode 100644 index 00000000000..73f256c98e7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Notifications/PluginTest.php @@ -0,0 +1,168 @@ +caldavBackend = new CalDAV\Backend\MockSharing(); + $principalBackend = new DAVACL\PrincipalBackend\Mock(); + $calendars = new CalDAV\CalendarRoot($principalBackend, $this->caldavBackend); + $principals = new CalDAV\Principal\Collection($principalBackend); + + $root = new DAV\SimpleCollection('root'); + $root->addChild($calendars); + $root->addChild($principals); + + $this->server = new DAV\Server($root); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->debugExceptions = true; + $this->server->setBaseUri('/'); + $this->plugin = new Plugin(); + $this->server->addPlugin($this->plugin); + + + // Adding ACL plugin + $aclPlugin = new DAVACL\Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $this->server->addPlugin($aclPlugin); + + // CalDAV is also required. + $this->server->addPlugin(new CalDAV\Plugin()); + // Adding Auth plugin, and ensuring that we are logged in. + $authBackend = new DAV\Auth\Backend\Mock(); + $authPlugin = new DAV\Auth\Plugin($authBackend); + $this->server->addPlugin($authPlugin); + + // This forces a login + $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $this->response = new HTTP\ResponseMock(); + $this->server->httpResponse = $this->response; + + } + + function testSimple() { + + $this->assertEquals([], $this->plugin->getFeatures()); + $this->assertEquals('notifications', $this->plugin->getPluginName()); + $this->assertEquals( + 'notifications', + $this->plugin->getPluginInfo()['name'] + ); + + } + + function testPrincipalProperties() { + + $httpRequest = new Request('GET', '/', ['Host' => 'sabredav.org']); + $this->server->httpRequest = $httpRequest; + + $props = $this->server->getPropertiesForPath('principals/admin', [ + '{' . Plugin::NS_CALENDARSERVER . '}notification-URL', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + + $this->assertArrayHasKey('{' . Plugin::NS_CALENDARSERVER . '}notification-URL', $props[0][200]); + $prop = $props[0][200]['{' . Plugin::NS_CALENDARSERVER . '}notification-URL']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals('calendars/admin/notifications/', $prop->getHref()); + + } + + function testNotificationProperties() { + + $notification = new Node( + $this->caldavBackend, + 'principals/user1', + new SystemStatus('foo', '"1"') + ); + $propFind = new DAV\PropFind('calendars/user1/notifications', [ + '{' . Plugin::NS_CALENDARSERVER . '}notificationtype', + ]); + + $this->plugin->propFind($propFind, $notification); + + $this->assertEquals( + $notification->getNotificationType(), + $propFind->get('{' . Plugin::NS_CALENDARSERVER . '}notificationtype') + ); + + } + + function testNotificationGet() { + + $notification = new Node( + $this->caldavBackend, + 'principals/user1', + new SystemStatus('foo', '"1"') + ); + + $server = new DAV\Server([$notification]); + $caldav = new Plugin(); + + $server->httpRequest = new Request('GET', '/foo.xml'); + $httpResponse = new HTTP\ResponseMock(); + $server->httpResponse = $httpResponse; + + $server->addPlugin($caldav); + + $caldav->httpGet($server->httpRequest, $server->httpResponse); + + $this->assertEquals(200, $httpResponse->status); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + 'ETag' => ['"1"'], + ], $httpResponse->getHeaders()); + + $expected = +' + + + +'; + + $this->assertXmlStringEqualsXmlString($expected, $httpResponse->getBodyAsString()); + + } + + function testGETPassthrough() { + + $server = new DAV\Server(); + $caldav = new Plugin(); + + $httpResponse = new HTTP\ResponseMock(); + $server->httpResponse = $httpResponse; + + $server->addPlugin($caldav); + + $this->assertNull($caldav->httpGet(new HTTP\Request('GET', '/foozz'), $server->httpResponse)); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/PluginTest.php new file mode 100644 index 00000000000..859f6aa0c4e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/PluginTest.php @@ -0,0 +1,1086 @@ +caldavBackend = new Backend\Mock([ + [ + 'id' => 1, + 'uri' => 'UUID-123467', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'user1 calendar', + $caldavNS . 'calendar-description' => 'Calendar description', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + $caldavNS . 'supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO']), + ], + [ + 'id' => 2, + 'uri' => 'UUID-123468', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'user1 calendar2', + $caldavNS . 'calendar-description' => 'Calendar description', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + $caldavNS . 'supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO']), + ] + ], [ + 1 => [ + 'UUID-2345' => [ + 'calendardata' => TestUtil::getTestCalendarData(), + ] + ] + ]); + $principalBackend = new DAVACL\PrincipalBackend\Mock(); + $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read', ['principals/user1']); + $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write', ['principals/user1']); + $principalBackend->addPrincipal([ + 'uri' => 'principals/admin/calendar-proxy-read', + ]); + $principalBackend->addPrincipal([ + 'uri' => 'principals/admin/calendar-proxy-write', + ]); + + $calendars = new CalendarRoot($principalBackend, $this->caldavBackend); + $principals = new Principal\Collection($principalBackend); + + $root = new DAV\SimpleCollection('root'); + $root->addChild($calendars); + $root->addChild($principals); + + $this->server = new DAV\Server($root); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->debugExceptions = true; + $this->server->setBaseUri('/'); + $this->plugin = new Plugin(); + $this->server->addPlugin($this->plugin); + + // Adding ACL plugin + $aclPlugin = new DAVACL\Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $this->server->addPlugin($aclPlugin); + + // Adding Auth plugin, and ensuring that we are logged in. + $authBackend = new DAV\Auth\Backend\Mock(); + $authBackend->setPrincipal('principals/user1'); + $authPlugin = new DAV\Auth\Plugin($authBackend); + $authPlugin->beforeMethod(new \Sabre\HTTP\Request(), new \Sabre\HTTP\Response()); + $this->server->addPlugin($authPlugin); + + // This forces a login + $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $this->response = new HTTP\ResponseMock(); + $this->server->httpResponse = $this->response; + + } + + function testSimple() { + + $this->assertEquals(['MKCALENDAR'], $this->plugin->getHTTPMethods('calendars/user1/randomnewcalendar')); + $this->assertEquals(['calendar-access', 'calendar-proxy'], $this->plugin->getFeatures()); + $this->assertEquals( + 'caldav', + $this->plugin->getPluginInfo()['name'] + ); + + } + + function testUnknownMethodPassThrough() { + + $request = new HTTP\Request('MKBREAKFAST', '/'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(501, $this->response->status, 'Incorrect status returned. Full response body:' . $this->response->body); + + } + + function testReportPassThrough() { + + $request = new HTTP\Request('REPORT', '/', ['Content-Type' => 'application/xml']); + $request->setBody(''); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(415, $this->response->status); + + } + + function testMkCalendarBadLocation() { + + $request = new HTTP\Request('MKCALENDAR', '/blabla'); + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(403, $this->response->status); + + } + + function testMkCalendarNoParentNode() { + + $request = new HTTP\Request('MKCALENDAR', '/doesntexist/calendar'); + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(409, $this->response->status); + + } + + function testMkCalendarExistingCalendar() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'MKCALENDAR', + 'REQUEST_URI' => '/calendars/user1/UUID-123467', + ]); + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(405, $this->response->status); + + } + + function testMkCalendarSucceed() { + + $request = new HTTP\Request('MKCALENDAR', '/calendars/user1/NEWCALENDAR'); + + $timezone = 'BEGIN:VCALENDAR +PRODID:-//Example Corp.//CalDAV Client//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:Eastern Standard Time (US & Canada) +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:Eastern Daylight Time (US & Canada) +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR'; + + $body = ' + + + + Lisa\'s Events + Calendar restricted to events. + + + + + + + '; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(201, $this->response->status, 'Invalid response code received. Full response body: ' . $this->response->body); + + $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3, count($calendars)); + + $newCalendar = null; + foreach ($calendars as $calendar) { + if ($calendar['uri'] === 'NEWCALENDAR') { + $newCalendar = $calendar; + break; + } + } + + $this->assertInternalType('array', $newCalendar); + + $keys = [ + 'uri' => 'NEWCALENDAR', + 'id' => null, + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar restricted to events.', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $timezone, + '{DAV:}displayname' => 'Lisa\'s Events', + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null, + ]; + + foreach ($keys as $key => $value) { + + $this->assertArrayHasKey($key, $newCalendar); + + if (is_null($value)) continue; + $this->assertEquals($value, $newCalendar[$key]); + + } + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $this->assertTrue($newCalendar[$sccs] instanceof Xml\Property\SupportedCalendarComponentSet); + $this->assertEquals(['VEVENT'], $newCalendar[$sccs]->getValue()); + + } + + function testMkCalendarEmptyBodySucceed() { + + $request = new HTTP\Request('MKCALENDAR', '/calendars/user1/NEWCALENDAR'); + + $request->setBody(''); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(201, $this->response->status, 'Invalid response code received. Full response body: ' . $this->response->body); + + $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); + $this->assertEquals(3, count($calendars)); + + $newCalendar = null; + foreach ($calendars as $calendar) { + if ($calendar['uri'] === 'NEWCALENDAR') { + $newCalendar = $calendar; + break; + } + } + + $this->assertInternalType('array', $newCalendar); + + $keys = [ + 'uri' => 'NEWCALENDAR', + 'id' => null, + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null, + ]; + + foreach ($keys as $key => $value) { + + $this->assertArrayHasKey($key, $newCalendar); + + if (is_null($value)) continue; + $this->assertEquals($value, $newCalendar[$key]); + + } + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $this->assertTrue($newCalendar[$sccs] instanceof Xml\Property\SupportedCalendarComponentSet); + $this->assertEquals(['VEVENT', 'VTODO'], $newCalendar[$sccs]->getValue()); + + } + + function testMkCalendarBadXml() { + + $request = new HTTP\Request('MKCALENDAR', '/blabla'); + $body = 'This is not xml'; + + $request->setBody($body); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(400, $this->response->status); + + } + + function testPrincipalProperties() { + + $httpRequest = new HTTP\Request('FOO', '/blabla', ['Host' => 'sabredav.org']); + $this->server->httpRequest = $httpRequest; + + $props = $this->server->getPropertiesForPath('/principals/user1', [ + '{' . Plugin::NS_CALDAV . '}calendar-home-set', + '{' . Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for', + '{' . Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for', + '{' . Plugin::NS_CALENDARSERVER . '}notification-URL', + '{' . Plugin::NS_CALENDARSERVER . '}email-address-set', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-home-set', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-home-set']; + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $prop); + $this->assertEquals('calendars/user1/', $prop->getHref()); + + $this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-read-for', $props[0][200]); + $prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-read-for']; + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $prop); + $this->assertEquals(['principals/admin/'], $prop->getHrefs()); + + $this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-write-for', $props[0][200]); + $prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-write-for']; + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $prop); + $this->assertEquals(['principals/admin/'], $prop->getHrefs()); + + $this->assertArrayHasKey('{' . Plugin::NS_CALENDARSERVER . '}email-address-set', $props[0][200]); + $prop = $props[0][200]['{' . Plugin::NS_CALENDARSERVER . '}email-address-set']; + $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\EmailAddressSet', $prop); + $this->assertEquals(['user1.sabredav@sabredav.org'], $prop->getValue()); + + } + + function testSupportedReportSetPropertyNonCalendar() { + + $props = $this->server->getPropertiesForPath('/calendars/user1', [ + '{DAV:}supported-report-set', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]); + + $prop = $props[0][200]['{DAV:}supported-report-set']; + + $this->assertInstanceOf('\\Sabre\\DAV\\Xml\\Property\\SupportedReportSet', $prop); + $value = [ + '{DAV:}expand-property', + '{DAV:}principal-match', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set', + ]; + $this->assertEquals($value, $prop->getValue()); + + } + + /** + * @depends testSupportedReportSetPropertyNonCalendar + */ + function testSupportedReportSetProperty() { + + $props = $this->server->getPropertiesForPath('/calendars/user1/UUID-123467', [ + '{DAV:}supported-report-set', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]); + + $prop = $props[0][200]['{DAV:}supported-report-set']; + + $this->assertInstanceOf('\\Sabre\\DAV\\Xml\\Property\\SupportedReportSet', $prop); + $value = [ + '{urn:ietf:params:xml:ns:caldav}calendar-multiget', + '{urn:ietf:params:xml:ns:caldav}calendar-query', + '{urn:ietf:params:xml:ns:caldav}free-busy-query', + '{DAV:}expand-property', + '{DAV:}principal-match', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set' + ]; + $this->assertEquals($value, $prop->getValue()); + + } + + function testSupportedReportSetUserCalendars() { + + $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); + + $props = $this->server->getPropertiesForPath('/calendars/user1', [ + '{DAV:}supported-report-set', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]); + + $prop = $props[0][200]['{DAV:}supported-report-set']; + + $this->assertInstanceOf('\\Sabre\\DAV\\Xml\\Property\\SupportedReportSet', $prop); + $value = [ + '{DAV:}sync-collection', + '{DAV:}expand-property', + '{DAV:}principal-match', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set', + ]; + $this->assertEquals($value, $prop->getValue()); + + } + + /** + * @depends testSupportedReportSetProperty + */ + function testCalendarMultiGetReport() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Invalid HTTP status received. Full response body'); + + $expectedIcal = TestUtil::getTestCalendarData(); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + $expectedIcal + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportExpand() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body); + + $expectedIcal = TestUtil::getTestCalendarData(); + $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal); + $expectedIcal = $expectedIcal->expand( + new DateTime('2011-01-01 00:00:00', new DateTimeZone('UTC')), + new DateTime('2011-12-31 23:59:59', new DateTimeZone('UTC')) + ); + $expectedIcal = str_replace("\r\n", " \n", $expectedIcal->serialize()); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + $expectedIcal + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReport() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', ['Depth' => '1']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + $expectedIcal = TestUtil::getTestCalendarData(); + $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal); + $expectedIcal = $expectedIcal->expand( + new DateTime('2000-01-01 00:00:00', new DateTimeZone('UTC')), + new DateTime('2010-12-31 23:59:59', new DateTimeZone('UTC')) + ); + $expectedIcal = str_replace("\r\n", " \n", $expectedIcal->serialize()); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + $expectedIcal + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReportWindowsPhone() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', [ + 'Depth' => '0', + 'User-Agent' => 'MSFT-WP/8.10.14219 (gzip)', + ]); + + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + $expectedIcal = TestUtil::getTestCalendarData(); + $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal); + $expectedIcal = $expectedIcal->expand( + new DateTime('2000-01-01 00:00:00', new DateTimeZone('UTC')), + new DateTime('2010-12-31 23:59:59', new DateTimeZone('UTC')) + ); + $expectedIcal = str_replace("\r\n", " \n", $expectedIcal->serialize()); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + $expectedIcal + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReportBadDepth() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', [ + 'Depth' => '0', + ]); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(400, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + } + + /** + * @depends testCalendarQueryReport + */ + function testCalendarQueryReportNoCalData() { + + $body = + '' . + '' . + '' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467', [ + 'Depth' => '1', + ]); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + /** + * @depends testCalendarQueryReport + */ + function testCalendarQueryReportNoFilters() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467'); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(400, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReport1Object() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467/UUID-2345', ['Depth' => '0']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + $expectedIcal = TestUtil::getTestCalendarData(); + $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal); + $expectedIcal = $expectedIcal->expand( + new DateTime('2000-01-01 00:00:00', new DateTimeZone('UTC')), + new DateTime('2010-12-31 23:59:59', new DateTimeZone('UTC')) + ); + $expectedIcal = str_replace("\r\n", " \n", $expectedIcal->serialize()); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + $expectedIcal + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + /** + * @depends testSupportedReportSetProperty + * @depends testCalendarMultiGetReport + */ + function testCalendarQueryReport1ObjectNoCalData() { + + $body = + '' . + '' . + '' . + ' ' . + '' . + '' . + ' ' . + ' ' . + ' ' . + '' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1/UUID-123467/UUID-2345', ['Depth' => '0']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Received an unexpected status. Full response body: ' . $this->response->body); + + $expected = << + + + /calendars/user1/UUID-123467/UUID-2345 + + + "e207e33c10e5fb9c12cfb35b5d9116e1" + + HTTP/1.1 200 OK + + + +XML; + + $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); + + } + + function testHTMLActionsPanel() { + + $output = ''; + $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('calendars/user1'), &$output]); + $this->assertFalse($r); + + $this->assertTrue(!!strpos($output, 'Display name')); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportNoEnd() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(400, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportNoStart() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(400, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testCalendarMultiGetReport + */ + function testCalendarMultiGetReportEndBeforeStart() { + + $body = + '' . + '' . + '' . + ' ' . + ' ' . + ' ' . + ' ' . + '' . + '/calendars/user1/UUID-123467/UUID-2345' . + ''; + + $request = new HTTP\Request('REPORT', '/calendars/user1', ['Depth' => '1']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(400, $this->response->status, 'Invalid HTTP status received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testSupportedReportSetPropertyNonCalendar + */ + function testCalendarProperties() { + + $ns = '{urn:ietf:params:xml:ns:caldav}'; + $props = $this->server->getProperties('calendars/user1/UUID-123467', [ + $ns . 'max-resource-size', + $ns . 'supported-calendar-data', + $ns . 'supported-collation-set', + ]); + + $this->assertEquals([ + $ns . 'max-resource-size' => 10000000, + $ns . 'supported-calendar-data' => new Xml\Property\SupportedCalendarData(), + $ns . 'supported-collation-set' => new Xml\Property\SupportedCollationSet(), + ], $props); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/CollectionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/CollectionTest.php new file mode 100644 index 00000000000..23c2488257f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/CollectionTest.php @@ -0,0 +1,20 @@ +getChildForPrincipal([ + 'uri' => 'principals/admin', + ]); + $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\User', $r); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/ProxyReadTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/ProxyReadTest.php new file mode 100644 index 00000000000..fe07f013108 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/ProxyReadTest.php @@ -0,0 +1,102 @@ + 'principal/user', + ]); + $this->backend = $backend; + return $principal; + + } + + function testGetName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-read', $i->getName()); + + } + function testGetDisplayName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-read', $i->getDisplayName()); + + } + + function testGetLastModified() { + + $i = $this->getInstance(); + $this->assertNull($i->getLastModified()); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testDelete() { + + $i = $this->getInstance(); + $i->delete(); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testSetName() { + + $i = $this->getInstance(); + $i->setName('foo'); + + } + + function testGetAlternateUriSet() { + + $i = $this->getInstance(); + $this->assertEquals([], $i->getAlternateUriSet()); + + } + + function testGetPrincipalUri() { + + $i = $this->getInstance(); + $this->assertEquals('principal/user/calendar-proxy-read', $i->getPrincipalUrl()); + + } + + function testGetGroupMemberSet() { + + $i = $this->getInstance(); + $this->assertEquals([], $i->getGroupMemberSet()); + + } + + function testGetGroupMembership() { + + $i = $this->getInstance(); + $this->assertEquals([], $i->getGroupMembership()); + + } + + function testSetGroupMemberSet() { + + $i = $this->getInstance(); + $i->setGroupMemberSet(['principals/foo']); + + $expected = [ + $i->getPrincipalUrl() => ['principals/foo'] + ]; + + $this->assertEquals($expected, $this->backend->groupMembers); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php new file mode 100644 index 00000000000..6cdb9b30e2b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php @@ -0,0 +1,40 @@ + 'principal/user', + ]); + $this->backend = $backend; + return $principal; + + } + + function testGetName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-write', $i->getName()); + + } + function testGetDisplayName() { + + $i = $this->getInstance(); + $this->assertEquals('calendar-proxy-write', $i->getDisplayName()); + + } + + function testGetPrincipalUri() { + + $i = $this->getInstance(); + $this->assertEquals('principal/user/calendar-proxy-write', $i->getPrincipalUrl()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php new file mode 100644 index 00000000000..420bb3b1afc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Principal/UserTest.php @@ -0,0 +1,127 @@ +addPrincipal([ + 'uri' => 'principals/user/calendar-proxy-read', + ]); + $backend->addPrincipal([ + 'uri' => 'principals/user/calendar-proxy-write', + ]); + $backend->addPrincipal([ + 'uri' => 'principals/user/random', + ]); + return new User($backend, [ + 'uri' => 'principals/user', + ]); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testCreateFile() { + + $u = $this->getInstance(); + $u->createFile('test'); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testCreateDirectory() { + + $u = $this->getInstance(); + $u->createDirectory('test'); + + } + + function testGetChildProxyRead() { + + $u = $this->getInstance(); + $child = $u->getChild('calendar-proxy-read'); + $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyRead', $child); + + } + + function testGetChildProxyWrite() { + + $u = $this->getInstance(); + $child = $u->getChild('calendar-proxy-write'); + $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyWrite', $child); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + */ + function testGetChildNotFound() { + + $u = $this->getInstance(); + $child = $u->getChild('foo'); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + */ + function testGetChildNotFound2() { + + $u = $this->getInstance(); + $child = $u->getChild('random'); + + } + + function testGetChildren() { + + $u = $this->getInstance(); + $children = $u->getChildren(); + $this->assertEquals(2, count($children)); + $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyRead', $children[0]); + $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyWrite', $children[1]); + + } + + function testChildExist() { + + $u = $this->getInstance(); + $this->assertTrue($u->childExists('calendar-proxy-read')); + $this->assertTrue($u->childExists('calendar-proxy-write')); + $this->assertFalse($u->childExists('foo')); + + } + + function testGetACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user/calendar-proxy-write', + 'protected' => true, + ], + ]; + + $u = $this->getInstance(); + $this->assertEquals($expected, $u->getACL()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/DeliverNewEventTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/DeliverNewEventTest.php new file mode 100644 index 00000000000..79e323f5c32 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/DeliverNewEventTest.php @@ -0,0 +1,92 @@ +caldavBackend->createCalendar( + 'principals/user1', + 'default', + [ + + ] + ); + $this->caldavBackend->createCalendar( + 'principals/user2', + 'default', + [ + + ] + ); + + } + + function testDelivery() { + + $request = new Request('PUT', '/calendars/user1/default/foo.ics'); + $request->setBody(<<server->on('schedule', function($message) use (&$messages) { + $messages[] = $message; + }); + + $response = $this->request($request); + + $this->assertEquals(201, $response->getStatus(), 'Incorrect status code received. Response body:' . $response->getBodyAsString()); + + $result = $this->request(new Request('GET', '/calendars/user1/default/foo.ics'))->getBody(); + $resultVObj = VObject\Reader::read($result); + + $this->assertEquals( + '1.2', + $resultVObj->VEVENT->ATTENDEE[1]['SCHEDULE-STATUS']->getValue() + ); + + $this->assertEquals(1, count($messages)); + $message = $messages[0]; + + $this->assertInstanceOf('\Sabre\VObject\ITip\Message', $message); + $this->assertEquals('mailto:user2.sabredav@sabredav.org', $message->recipient); + $this->assertEquals('Roxy Kesh', $message->recipientName); + $this->assertEquals('mailto:user1.sabredav@sabredav.org', $message->sender); + $this->assertEquals('Administrator', $message->senderName); + $this->assertEquals('REQUEST', $message->method); + + $this->assertEquals('REQUEST', $message->message->METHOD->getValue()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/FreeBusyRequestTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/FreeBusyRequestTest.php new file mode 100644 index 00000000000..0e0b609a11c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/FreeBusyRequestTest.php @@ -0,0 +1,611 @@ + 'principals/user2', + 'id' => 1, + 'uri' => 'calendar1', + $caldavNS . 'calendar-timezone' => "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nEND:VTIMEZONE\r\nEND:VCALENDAR", + ], + [ + 'principaluri' => 'principals/user2', + 'id' => 2, + 'uri' => 'calendar2', + $caldavNS . 'schedule-calendar-transp' => new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT), + ], + ]; + $calendarobjects = [ + 1 => ['1.ics' => [ + 'uri' => '1.ics', + 'calendardata' => 'BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20110101T130000 +DURATION:PT1H +END:VEVENT +END:VCALENDAR', + 'calendarid' => 1, + ]], + 2 => ['2.ics' => [ + 'uri' => '2.ics', + 'calendardata' => 'BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20110101T080000 +DURATION:PT1H +END:VEVENT +END:VCALENDAR', + 'calendarid' => 2, + ]] + + ]; + + $principalBackend = new DAVACL\PrincipalBackend\Mock(); + $this->caldavBackend = new CalDAV\Backend\MockScheduling($calendars, $calendarobjects); + + $tree = [ + new DAVACL\PrincipalCollection($principalBackend), + new CalDAV\CalendarRoot($principalBackend, $this->caldavBackend), + ]; + + $this->request = HTTP\Sapi::createFromServerArray([ + 'CONTENT_TYPE' => 'text/calendar', + ]); + $this->response = new HTTP\ResponseMock(); + + $this->server = new DAV\Server($tree); + $this->server->httpRequest = $this->request; + $this->server->httpResponse = $this->response; + + $this->aclPlugin = new DAVACL\Plugin(); + $this->aclPlugin->allowUnauthenticatedAccess = false; + $this->server->addPlugin($this->aclPlugin); + + $authBackend = new DAV\Auth\Backend\Mock(); + $authBackend->setPrincipal('principals/user1'); + $this->authPlugin = new DAV\Auth\Plugin($authBackend); + // Forcing authentication to work. + $this->authPlugin->beforeMethod($this->request, $this->response); + $this->server->addPlugin($this->authPlugin); + + // CalDAV plugin + $this->plugin = new CalDAV\Plugin(); + $this->server->addPlugin($this->plugin); + + // Scheduling plugin + $this->plugin = new Plugin(); + $this->server->addPlugin($this->plugin); + + } + + function testWrongContentType() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/plain'] + ); + + $this->assertNull( + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse) + ); + + } + + function testNotFound() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/blabla', + ['Content-Type' => 'text/calendar'] + ); + + $this->assertNull( + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse) + ); + + } + + function testNotOutbox() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/inbox', + ['Content-Type' => 'text/calendar'] + ); + + $this->assertNull( + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse) + ); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testNoItipMethod() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); + + } + + /** + * @expectedException \Sabre\DAV\Exception\NotImplemented + */ + function testNoVFreeBusy() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testIncorrectOrganizer() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + + $body = <<server->httpRequest->setBody($body); + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testNoAttendees() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testNoDTStart() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); + + } + + function testSucceed() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + + // Lazily making the current principal an admin. + $this->aclPlugin->adminPrincipals[] = 'principals/user1'; + + $this->assertFalse( + $this->plugin->httpPost($this->server->httpRequest, $this->response) + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + ], $this->response->getHeaders()); + + $strings = [ + 'mailto:user2.sabredav@sabredav.org', + 'mailto:user3.sabredav@sabredav.org', + '2.0;Success', + '3.7;Could not find principal', + 'FREEBUSY:20110101T120000Z/20110101T130000Z', + ]; + + foreach ($strings as $string) { + $this->assertTrue( + strpos($this->response->body, $string) !== false, + 'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body + ); + } + + $this->assertTrue( + strpos($this->response->body, 'FREEBUSY;FBTYPE=BUSY:20110101T080000Z/20110101T090000Z') == false, + 'The response body did contain free busy info from a transparent calendar.' + ); + + } + + /** + * Testing if the freebusy request still works, even if there are no + * calendars in the target users' account. + */ + function testSucceedNoCalendars() { + + // Deleting calendars + $this->caldavBackend->deleteCalendar(1); + $this->caldavBackend->deleteCalendar(2); + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + + // Lazily making the current principal an admin. + $this->aclPlugin->adminPrincipals[] = 'principals/user1'; + + $this->assertFalse( + $this->plugin->httpPost($this->server->httpRequest, $this->response) + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + ], $this->response->getHeaders()); + + $strings = [ + 'mailto:user2.sabredav@sabredav.org', + '2.0;Success', + ]; + + foreach ($strings as $string) { + $this->assertTrue( + strpos($this->response->body, $string) !== false, + 'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body + ); + } + + } + + function testNoCalendarHomeFound() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + + // Lazily making the current principal an admin. + $this->aclPlugin->adminPrincipals[] = 'principals/user1'; + + // Removing the calendar home + $this->server->on('propFind', function(DAV\PropFind $propFind) { + + $propFind->set('{' . Plugin::NS_CALDAV . '}calendar-home-set', null, 403); + + }); + + $this->assertFalse( + $this->plugin->httpPost($this->server->httpRequest, $this->response) + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + ], $this->response->getHeaders()); + + $strings = [ + 'mailto:user2.sabredav@sabredav.org', + '3.7;No calendar-home-set property found', + ]; + + foreach ($strings as $string) { + $this->assertTrue( + strpos($this->response->body, $string) !== false, + 'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body + ); + } + + } + + function testNoInboxFound() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + + // Lazily making the current principal an admin. + $this->aclPlugin->adminPrincipals[] = 'principals/user1'; + + // Removing the inbox + $this->server->on('propFind', function(DAV\PropFind $propFind) { + + $propFind->set('{' . Plugin::NS_CALDAV . '}schedule-inbox-URL', null, 403); + + }); + + $this->assertFalse( + $this->plugin->httpPost($this->server->httpRequest, $this->response) + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + ], $this->response->getHeaders()); + + $strings = [ + 'mailto:user2.sabredav@sabredav.org', + '3.7;No schedule-inbox-URL property found', + ]; + + foreach ($strings as $string) { + $this->assertTrue( + strpos($this->response->body, $string) !== false, + 'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body + ); + } + + } + + function testSucceedUseVAVAILABILITY() { + + $this->server->httpRequest = new HTTP\Request( + 'POST', + '/calendars/user1/outbox', + ['Content-Type' => 'text/calendar'] + ); + + $body = <<server->httpRequest->setBody($body); + + // Lazily making the current principal an admin. + $this->aclPlugin->adminPrincipals[] = 'principals/user1'; + + // Adding VAVAILABILITY manually + $this->server->on('propFind', function(DAV\PropFind $propFind) { + + $propFind->handle('{' . Plugin::NS_CALDAV . '}calendar-availability', function() { + + $avail = <<assertFalse( + $this->plugin->httpPost($this->server->httpRequest, $this->response) + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + ], $this->response->getHeaders()); + + $strings = [ + 'mailto:user2.sabredav@sabredav.org', + '2.0;Success', + 'FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T080000Z/20110101T090000Z', + 'FREEBUSY:20110101T120000Z/20110101T130000Z', + 'FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T170000Z/20110101T180000Z', + ]; + + foreach ($strings as $string) { + $this->assertTrue( + strpos($this->response->body, $string) !== false, + 'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body + ); + } + + } + + /* + function testNoPrivilege() { + + $this->markTestIncomplete('Currently there\'s no "no privilege" situation'); + + $this->server->httpRequest = HTTP\Sapi::createFromServerArray(array( + 'CONTENT_TYPE' => 'text/calendar', + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + )); + + $body = <<server->httpRequest->setBody($body); + + $this->assertFalse( + $this->plugin->httpPost($this->server->httpRequest, $this->response) + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'Content-Type' => 'application/xml', + ], $this->response->getHeaders()); + + $strings = [ + 'mailto:user2.sabredav@sabredav.org', + '3.7;No calendar-home-set property found', + ]; + + foreach($strings as $string) { + $this->assertTrue( + strpos($this->response->body, $string)!==false, + 'The response body did not contain: ' . $string .'Full response: ' . $this->response->body + ); + } + + + }*/ + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/MockPlugin.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/MockPlugin.php new file mode 100644 index 00000000000..02846609354 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/IMip/MockPlugin.php @@ -0,0 +1,50 @@ +emails[] = [ + 'to' => $to, + 'subject' => $subject, + 'body' => $body, + 'headers' => $headers, + ]; + + } + + function getSentEmails() { + + return $this->emails; + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/IMipPluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/IMipPluginTest.php new file mode 100644 index 00000000000..7311999f5b4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/IMipPluginTest.php @@ -0,0 +1,221 @@ +assertEquals( + 'imip', + $plugin->getPluginInfo()['name'] + ); + + } + + function testDeliverReply() { + + $message = new Message(); + $message->sender = 'mailto:sender@example.org'; + $message->senderName = 'Sender'; + $message->recipient = 'mailto:recipient@example.org'; + $message->recipientName = 'Recipient'; + $message->method = 'REPLY'; + + $ics = <<message = Reader::read($ics); + + $result = $this->schedule($message); + + $expected = [ + [ + 'to' => 'Recipient ', + 'subject' => 'Re: Birthday party', + 'body' => $ics, + 'headers' => [ + 'Reply-To: Sender ', + 'From: system@example.org', + 'Content-Type: text/calendar; charset=UTF-8; method=REPLY', + 'X-Sabre-Version: ' . \Sabre\DAV\Version::VERSION, + ], + ] + ]; + + $this->assertEquals($expected, $result); + + } + + function testDeliverReplyNoMailto() { + + $message = new Message(); + $message->sender = 'mailto:sender@example.org'; + $message->senderName = 'Sender'; + $message->recipient = 'http://example.org/recipient'; + $message->recipientName = 'Recipient'; + $message->method = 'REPLY'; + + $ics = <<message = Reader::read($ics); + + $result = $this->schedule($message); + + $expected = []; + + $this->assertEquals($expected, $result); + + } + + function testDeliverRequest() { + + $message = new Message(); + $message->sender = 'mailto:sender@example.org'; + $message->senderName = 'Sender'; + $message->recipient = 'mailto:recipient@example.org'; + $message->recipientName = 'Recipient'; + $message->method = 'REQUEST'; + + $ics = <<message = Reader::read($ics); + + $result = $this->schedule($message); + + $expected = [ + [ + 'to' => 'Recipient ', + 'subject' => 'Birthday party', + 'body' => $ics, + 'headers' => [ + 'Reply-To: Sender ', + 'From: system@example.org', + 'Content-Type: text/calendar; charset=UTF-8; method=REQUEST', + 'X-Sabre-Version: ' . \Sabre\DAV\Version::VERSION, + ], + ] + ]; + + $this->assertEquals($expected, $result); + + } + + function testDeliverCancel() { + + $message = new Message(); + $message->sender = 'mailto:sender@example.org'; + $message->senderName = 'Sender'; + $message->recipient = 'mailto:recipient@example.org'; + $message->recipientName = 'Recipient'; + $message->method = 'CANCEL'; + + $ics = <<message = Reader::read($ics); + + $result = $this->schedule($message); + + $expected = [ + [ + 'to' => 'Recipient ', + 'subject' => 'Cancelled: Birthday party', + 'body' => $ics, + 'headers' => [ + 'Reply-To: Sender ', + 'From: system@example.org', + 'Content-Type: text/calendar; charset=UTF-8; method=CANCEL', + 'X-Sabre-Version: ' . \Sabre\DAV\Version::VERSION, + ], + ] + ]; + + $this->assertEquals($expected, $result); + $this->assertEquals('1.1', substr($message->scheduleStatus, 0, 3)); + + } + + function schedule(Message $message) { + + $plugin = new IMip\MockPlugin('system@example.org'); + + $server = new Server(); + $server->addPlugin($plugin); + $server->emit('schedule', [$message]); + + return $plugin->getSentEmails(); + + } + + function testDeliverInsignificantRequest() { + + $message = new Message(); + $message->sender = 'mailto:sender@example.org'; + $message->senderName = 'Sender'; + $message->recipient = 'mailto:recipient@example.org'; + $message->recipientName = 'Recipient'; + $message->method = 'REQUEST'; + $message->significantChange = false; + + $ics = <<message = Reader::read($ics); + + $result = $this->schedule($message); + + $expected = []; + $this->assertEquals($expected, $result); + $this->assertEquals('1.0', $message->getScheduleStatus()[0]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/InboxTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/InboxTest.php new file mode 100644 index 00000000000..01c3488afd6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/InboxTest.php @@ -0,0 +1,136 @@ +assertEquals('inbox', $inbox->getName()); + $this->assertEquals([], $inbox->getChildren()); + $this->assertEquals('principals/user1', $inbox->getOwner()); + $this->assertEquals(null, $inbox->getGroup()); + + $this->assertEquals([ + [ + 'privilege' => '{DAV:}read', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write-properties', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}unbind', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}unbind', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{urn:ietf:params:xml:ns:caldav}schedule-deliver', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + ], $inbox->getACL()); + + $ok = false; + + } + + /** + * @depends testSetup + */ + function testGetChildren() { + + $backend = new CalDAV\Backend\MockScheduling(); + $inbox = new Inbox( + $backend, + 'principals/user1' + ); + + $this->assertEquals( + 0, + count($inbox->getChildren()) + ); + $backend->createSchedulingObject('principals/user1', 'schedule1.ics', "BEGIN:VCALENDAR\r\nEND:VCALENDAR"); + $this->assertEquals( + 1, + count($inbox->getChildren()) + ); + $this->assertInstanceOf('Sabre\CalDAV\Schedule\SchedulingObject', $inbox->getChildren()[0]); + $this->assertEquals( + 'schedule1.ics', + $inbox->getChildren()[0]->getName() + ); + + } + + /** + * @depends testGetChildren + */ + function testCreateFile() { + + $backend = new CalDAV\Backend\MockScheduling(); + $inbox = new Inbox( + $backend, + 'principals/user1' + ); + + $this->assertEquals( + 0, + count($inbox->getChildren()) + ); + $inbox->createFile('schedule1.ics', "BEGIN:VCALENDAR\r\nEND:VCALENDAR"); + $this->assertEquals( + 1, + count($inbox->getChildren()) + ); + $this->assertInstanceOf('Sabre\CalDAV\Schedule\SchedulingObject', $inbox->getChildren()[0]); + $this->assertEquals( + 'schedule1.ics', + $inbox->getChildren()[0]->getName() + ); + + } + + /** + * @depends testSetup + */ + function testCalendarQuery() { + + $backend = new CalDAV\Backend\MockScheduling(); + $inbox = new Inbox( + $backend, + 'principals/user1' + ); + + $this->assertEquals( + 0, + count($inbox->getChildren()) + ); + $backend->createSchedulingObject('principals/user1', 'schedule1.ics', "BEGIN:VCALENDAR\r\nEND:VCALENDAR"); + $this->assertEquals( + ['schedule1.ics'], + $inbox->calendarQuery([ + 'name' => 'VCALENDAR', + 'comp-filters' => [], + 'prop-filters' => [], + 'is-not-defined' => false + ]) + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxPostTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxPostTest.php new file mode 100644 index 00000000000..3ab2c2288cf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxPostTest.php @@ -0,0 +1,134 @@ + 'POST', + 'REQUEST_URI' => '/notfound', + 'HTTP_CONTENT_TYPE' => 'text/calendar', + ]); + + $this->assertHTTPStatus(501, $req); + + } + + function testPostPassThruNotTextCalendar() { + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + ]); + + $this->assertHTTPStatus(501, $req); + + } + + function testPostPassThruNoOutBox() { + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars', + 'HTTP_CONTENT_TYPE' => 'text/calendar', + ]); + + $this->assertHTTPStatus(501, $req); + + } + + function testInvalidIcalBody() { + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + 'HTTP_CONTENT_TYPE' => 'text/calendar', + ]); + $req->setBody('foo'); + + $this->assertHTTPStatus(400, $req); + + } + + function testNoVEVENT() { + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + 'HTTP_CONTENT_TYPE' => 'text/calendar', + ]); + + $body = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VTIMEZONE', + 'END:VTIMEZONE', + 'END:VCALENDAR', + ]; + + $req->setBody(implode("\r\n", $body)); + + $this->assertHTTPStatus(400, $req); + + } + + function testNoMETHOD() { + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + 'HTTP_CONTENT_TYPE' => 'text/calendar', + ]); + + $body = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'END:VEVENT', + 'END:VCALENDAR', + ]; + + $req->setBody(implode("\r\n", $body)); + + $this->assertHTTPStatus(400, $req); + + } + + function testUnsupportedMethod() { + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/outbox', + 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', + 'HTTP_RECIPIENT' => 'mailto:user2@example.org', + 'HTTP_CONTENT_TYPE' => 'text/calendar', + ]); + + $body = [ + 'BEGIN:VCALENDAR', + 'METHOD:PUBLISH', + 'BEGIN:VEVENT', + 'END:VEVENT', + 'END:VCALENDAR', + ]; + + $req->setBody(implode("\r\n", $body)); + + $this->assertHTTPStatus(501, $req); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php new file mode 100644 index 00000000000..04d4b12379e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/OutboxTest.php @@ -0,0 +1,48 @@ +assertEquals('outbox', $outbox->getName()); + $this->assertEquals([], $outbox->getChildren()); + $this->assertEquals('principals/user1', $outbox->getOwner()); + $this->assertEquals(null, $outbox->getGroup()); + + $this->assertEquals([ + [ + 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-send', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-send', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + ], $outbox->getACL()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginBasicTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginBasicTest.php new file mode 100644 index 00000000000..cee911b6e0b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginBasicTest.php @@ -0,0 +1,39 @@ +assertEquals( + 'caldav-schedule', + $plugin->getPluginInfo()['name'] + ); + + } + + function testOptions() { + + $plugin = new Plugin(); + $expected = [ + 'calendar-auto-schedule', + 'calendar-availability', + ]; + $this->assertEquals($expected, $plugin->getFeatures()); + + } + + function testGetHTTPMethods() { + + $this->assertEquals([], $this->caldavSchedulePlugin->getHTTPMethods('notfound')); + $this->assertEquals([], $this->caldavSchedulePlugin->getHTTPMethods('calendars/user1')); + $this->assertEquals(['POST'], $this->caldavSchedulePlugin->getHTTPMethods('calendars/user1/outbox')); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginPropertiesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginPropertiesTest.php new file mode 100644 index 00000000000..2d0391893ef --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginPropertiesTest.php @@ -0,0 +1,146 @@ +caldavBackend->createCalendar( + 'principals/user1', + 'default', + [ + + ] + ); + $this->principalBackend->addPrincipal([ + 'uri' => 'principals/user1/calendar-proxy-read' + ]); + + } + + function testPrincipalProperties() { + + $props = $this->server->getPropertiesForPath('/principals/user1', [ + '{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', + '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', + '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', + '{urn:ietf:params:xml:ns:caldav}calendar-user-type', + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals('calendars/user1/outbox/', $prop->getHref()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals('calendars/user1/inbox/', $prop->getHref()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals(['mailto:user1.sabredav@sabredav.org', '/principals/user1/'], $prop->getHrefs()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-type', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-type']; + $this->assertEquals('INDIVIDUAL', $prop); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL']; + $this->assertEquals('calendars/user1/default/', $prop->getHref()); + + } + function testPrincipalPropertiesBadPrincipal() { + + $props = $this->server->getPropertiesForPath('principals/user1/calendar-proxy-read', [ + '{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', + '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', + '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', + '{urn:ietf:params:xml:ns:caldav}calendar-user-type', + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + $this->assertArrayHasKey(404, $props[0]); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', $props[0][404]); + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', $props[0][404]); + + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals(['/principals/user1/calendar-proxy-read/'], $prop->getHrefs()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-type', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-type']; + $this->assertEquals('INDIVIDUAL', $prop); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', $props[0][404]); + + } + function testNoDefaultCalendar() { + + foreach ($this->caldavBackend->getCalendarsForUser('principals/user1') as $calendar) { + $this->caldavBackend->deleteCalendar($calendar['id']); + } + $props = $this->server->getPropertiesForPath('/principals/user1', [ + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(404, $props[0]); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', $props[0][404]); + + } + + /** + * There are two properties for availability. The server should + * automatically map the old property to the standard property. + */ + function testAvailabilityMapping() { + + $path = 'calendars/user1/inbox'; + $oldProp = '{http://calendarserver.org/ns/}calendar-availability'; + $newProp = '{urn:ietf:params:xml:ns:caldav}calendar-availability'; + $value1 = 'first value'; + $value2 = 'second value'; + + // Storing with the old name + $this->server->updateProperties($path, [ + $oldProp => $value1 + ]); + + // Retrieving with the new name + $this->assertEquals( + [$newProp => $value1], + $this->server->getProperties($path, [$newProp]) + ); + + // Storing with the new name + $this->server->updateProperties($path, [ + $newProp => $value2 + ]); + + // Retrieving with the old name + $this->assertEquals( + [$oldProp => $value2], + $this->server->getProperties($path, [$oldProp]) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginPropertiesWithSharedCalendarTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginPropertiesWithSharedCalendarTest.php new file mode 100644 index 00000000000..870f14c1447 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/PluginPropertiesWithSharedCalendarTest.php @@ -0,0 +1,71 @@ +caldavBackend->createCalendar( + 'principals/user1', + 'shared', + [ + 'share-access' => DAV\Sharing\Plugin::ACCESS_READWRITE + ] + ); + $this->caldavBackend->createCalendar( + 'principals/user1', + 'default', + [ + + ] + ); + + } + + function testPrincipalProperties() { + + $props = $this->server->getPropertiesForPath('/principals/user1', [ + '{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', + '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', + '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', + '{urn:ietf:params:xml:ns:caldav}calendar-user-type', + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', + ]); + + $this->assertArrayHasKey(0, $props); + $this->assertArrayHasKey(200, $props[0]); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals('calendars/user1/outbox/', $prop->getHref()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals('calendars/user1/inbox/', $prop->getHref()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; + $this->assertTrue($prop instanceof DAV\Xml\Property\Href); + $this->assertEquals(['mailto:user1.sabredav@sabredav.org', '/principals/user1/'], $prop->getHrefs()); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-type', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-type']; + $this->assertEquals('INDIVIDUAL', $prop); + + $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', $props[0][200]); + $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL']; + $this->assertEquals('calendars/user1/default/', $prop->getHref()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/ScheduleDeliverTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/ScheduleDeliverTest.php new file mode 100644 index 00000000000..8123c685c76 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/ScheduleDeliverTest.php @@ -0,0 +1,666 @@ + 'principals/user1', + 'uri' => 'cal', + ], + [ + 'principaluri' => 'principals/user2', + 'uri' => 'cal', + ], + ]; + + function setUp() { + + $this->calendarObjectUri = '/calendars/user1/cal/object.ics'; + + parent::setUp(); + + } + + function testNewInvite() { + + $newObject = <<deliver(null, $newObject); + $this->assertItemsInInbox('user2', 1); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + + function testNewOnWrongCollection() { + + $newObject = <<calendarObjectUri = '/calendars/user1/object.ics'; + $this->deliver(null, $newObject); + $this->assertItemsInInbox('user2', 0); + + + } + function testNewInviteSchedulingDisabled() { + + $newObject = <<deliver(null, $newObject, true); + $this->assertItemsInInbox('user2', 0); + + } + function testUpdatedInvite() { + + $newObject = <<deliver($oldObject, $newObject); + $this->assertItemsInInbox('user2', 1); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + + } + function testUpdatedInviteSchedulingDisabled() { + + $newObject = <<deliver($oldObject, $newObject, true); + $this->assertItemsInInbox('user2', 0); + + } + + function testUpdatedInviteWrongPath() { + + $newObject = <<calendarObjectUri = '/calendars/user1/inbox/foo.ics'; + $this->deliver($oldObject, $newObject); + $this->assertItemsInInbox('user2', 0); + + } + + function testDeletedInvite() { + + $newObject = null; + + $oldObject = <<deliver($oldObject, $newObject); + $this->assertItemsInInbox('user2', 1); + + } + + function testDeletedInviteSchedulingDisabled() { + + $newObject = null; + + $oldObject = <<deliver($oldObject, $newObject, true); + $this->assertItemsInInbox('user2', 0); + + } + + /** + * A MOVE request will trigger an unbind on a scheduling resource. + * + * However, we must not treat it as a cancellation, it just got moved to a + * different calendar. + */ + function testUnbindIgnoredOnMove() { + + $newObject = null; + + $oldObject = <<server->httpRequest->setMethod('MOVE'); + $this->deliver($oldObject, $newObject); + $this->assertItemsInInbox('user2', 0); + + } + + function testDeletedInviteWrongUrl() { + + $newObject = null; + + $oldObject = <<calendarObjectUri = '/calendars/user1/inbox/foo.ics'; + $this->deliver($oldObject, $newObject); + $this->assertItemsInInbox('user2', 0); + + } + + function testReply() { + + $oldObject = <<putPath('calendars/user2/cal/foo.ics', $oldObject); + + $this->deliver($oldObject, $newObject); + $this->assertItemsInInbox('user2', 1); + $this->assertItemsInInbox('user1', 0); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + + + + function testInviteUnknownUser() { + + $newObject = <<deliver(null, $newObject); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + + function testInviteNoInboxUrl() { + + $newObject = <<server->on('propFind', function($propFind) { + $propFind->set('{' . Plugin::NS_CALDAV . '}schedule-inbox-URL', null, 403); + }); + $this->deliver(null, $newObject); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + + function testInviteNoCalendarHomeSet() { + + $newObject = <<server->on('propFind', function($propFind) { + $propFind->set('{' . Plugin::NS_CALDAV . '}calendar-home-set', null, 403); + }); + $this->deliver(null, $newObject); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + function testInviteNoDefaultCalendar() { + + $newObject = <<server->on('propFind', function($propFind) { + $propFind->set('{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL', null, 403); + }); + $this->deliver(null, $newObject); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + function testInviteNoScheduler() { + + $newObject = <<server->removeAllListeners('schedule'); + $this->deliver(null, $newObject); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + function testInviteNoACLPlugin() { + + $this->setupACL = false; + parent::setUp(); + + $newObject = <<deliver(null, $newObject); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $newObject + ); + + } + + protected $calendarObjectUri; + + function deliver($oldObject, &$newObject, $disableScheduling = false) { + + $this->server->httpRequest->setUrl($this->calendarObjectUri); + if ($disableScheduling) { + $this->server->httpRequest->setHeader('Schedule-Reply', 'F'); + } + + if ($oldObject && $newObject) { + // update + $this->putPath($this->calendarObjectUri, $oldObject); + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $newObject); + rewind($stream); + $modified = false; + + $this->server->emit('beforeWriteContent', [ + $this->calendarObjectUri, + $this->server->tree->getNodeForPath($this->calendarObjectUri), + &$stream, + &$modified + ]); + if ($modified) { + $newObject = $stream; + } + + } elseif ($oldObject && !$newObject) { + // delete + $this->putPath($this->calendarObjectUri, $oldObject); + + $this->caldavSchedulePlugin->beforeUnbind( + $this->calendarObjectUri + ); + } else { + + // create + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $newObject); + rewind($stream); + $modified = false; + $this->server->emit('beforeCreateFile', [ + $this->calendarObjectUri, + &$stream, + $this->server->tree->getNodeForPath(dirname($this->calendarObjectUri)), + &$modified + ]); + + if ($modified) { + $newObject = $stream; + } + } + + } + + + /** + * Creates or updates a node at the specified path. + * + * This circumvents sabredav's internal server apis, so all events and + * access control is skipped. + * + * @param string $path + * @param string $data + * @return void + */ + function putPath($path, $data) { + + list($parent, $base) = \Sabre\HTTP\UrlUtil::splitPath($path); + $parentNode = $this->server->tree->getNodeForPath($parent); + + /* + if ($parentNode->childExists($base)) { + $childNode = $parentNode->getChild($base); + $childNode->put($data); + } else {*/ + $parentNode->createFile($base, $data); + //} + + } + + function assertItemsInInbox($user, $count) { + + $inboxNode = $this->server->tree->getNodeForPath('calendars/' . $user . '/inbox'); + $this->assertEquals($count, count($inboxNode->getChildren())); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/SchedulingObjectTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/SchedulingObjectTest.php new file mode 100644 index 00000000000..be83cd08110 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Schedule/SchedulingObjectTest.php @@ -0,0 +1,378 @@ +markTestSkipped('SQLite driver is not available'); + $this->backend = new Backend\MockScheduling(); + + $this->data = <<data = <<inbox = new Inbox($this->backend, 'principals/user1'); + $this->inbox->createFile('item1.ics', $this->data); + + } + + function teardown() { + + unset($this->inbox); + unset($this->backend); + + } + + function testSetup() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $this->assertInternalType('string', $children[0]->getName()); + $this->assertInternalType('string', $children[0]->get()); + $this->assertInternalType('string', $children[0]->getETag()); + $this->assertEquals('text/calendar; charset=utf-8', $children[0]->getContentType()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg1() { + + $obj = new SchedulingObject( + new Backend\MockScheduling([], []), + [], + [] + ); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg2() { + + $obj = new SchedulingObject( + new Backend\MockScheduling([], []), + [], + ['calendarid' => '1'] + ); + + } + + /** + * @depends testSetup + * @expectedException \Sabre\DAV\Exception\MethodNotAllowed + */ + function testPut() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $children[0]->put(''); + + } + + /** + * @depends testSetup + */ + function testDelete() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + $obj->delete(); + + $children2 = $this->inbox->getChildren(); + $this->assertEquals(count($children) - 1, count($children2)); + + } + + /** + * @depends testSetup + */ + function testGetLastModified() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + + $lastMod = $obj->getLastModified(); + $this->assertTrue(is_int($lastMod) || ctype_digit($lastMod) || is_null($lastMod)); + + } + + /** + * @depends testSetup + */ + function testGetSize() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + + $size = $obj->getSize(); + $this->assertInternalType('int', $size); + + } + + function testGetOwner() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + $this->assertEquals('principals/user1', $obj->getOwner()); + + } + + function testGetGroup() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + $this->assertNull($obj->getGroup()); + + } + + function testGetACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + ]; + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + $this->assertEquals($expected, $obj->getACL()); + + } + + function testDefaultACL() { + + $backend = new Backend\MockScheduling([], []); + $calendarObject = new SchedulingObject($backend, ['calendarid' => 1, 'uri' => 'foo', 'principaluri' => 'principals/user1']); + $expected = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ], + ]; + $this->assertEquals($expected, $calendarObject->getACL()); + + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + $obj->setACL([]); + + } + + function testGet() { + + $children = $this->inbox->getChildren(); + $this->assertTrue($children[0] instanceof SchedulingObject); + + $obj = $children[0]; + + $this->assertEquals($this->data, $obj->get()); + + } + + function testGetRefetch() { + + $backend = new Backend\MockScheduling(); + $backend->createSchedulingObject('principals/user1', 'foo', 'foo'); + + $obj = new SchedulingObject($backend, [ + 'calendarid' => 1, + 'uri' => 'foo', + 'principaluri' => 'principals/user1', + ]); + + $this->assertEquals('foo', $obj->get()); + + } + + function testGetEtag1() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'etag' => 'bar', + 'calendarid' => 1 + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + + $this->assertEquals('bar', $obj->getETag()); + + } + + function testGetEtag2() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + + $this->assertEquals('"' . md5('foo') . '"', $obj->getETag()); + + } + + function testGetSupportedPrivilegesSet() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + $this->assertNull($obj->getSupportedPrivilegeSet()); + + } + + function testGetSize1() { + + $objectInfo = [ + 'calendardata' => 'foo', + 'uri' => 'foo', + 'calendarid' => 1 + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + $this->assertEquals(3, $obj->getSize()); + + } + + function testGetSize2() { + + $objectInfo = [ + 'uri' => 'foo', + 'calendarid' => 1, + 'size' => 4, + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + $this->assertEquals(4, $obj->getSize()); + + } + + function testGetContentType() { + + $objectInfo = [ + 'uri' => 'foo', + 'calendarid' => 1, + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + $this->assertEquals('text/calendar; charset=utf-8', $obj->getContentType()); + + } + + function testGetContentType2() { + + $objectInfo = [ + 'uri' => 'foo', + 'calendarid' => 1, + 'component' => 'VEVENT', + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + $this->assertEquals('text/calendar; charset=utf-8; component=VEVENT', $obj->getContentType()); + + } + function testGetACL2() { + + $objectInfo = [ + 'uri' => 'foo', + 'calendarid' => 1, + 'acl' => [], + ]; + + $backend = new Backend\MockScheduling([], []); + $obj = new SchedulingObject($backend, $objectInfo); + $this->assertEquals([], $obj->getACL()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php new file mode 100644 index 00000000000..f71c195238c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/SharedCalendarTest.php @@ -0,0 +1,176 @@ + 1, + '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/original', + '{http://sabredav.org/ns}owner-principal' => 'principals/owner', + '{http://sabredav.org/ns}read-only' => false, + 'share-access' => Sharing\Plugin::ACCESS_READWRITE, + 'principaluri' => 'principals/sharee', + ]; + } + + $this->backend = new Backend\MockSharing( + [$props], + [], + [] + ); + + $sharee = new Sharee(); + $sharee->href = 'mailto:removeme@example.org'; + $sharee->properties['{DAV:}displayname'] = 'To be removed'; + $sharee->access = Sharing\Plugin::ACCESS_READ; + $this->backend->updateInvites(1, [$sharee]); + + return new SharedCalendar($this->backend, $props); + + } + + function testGetInvites() { + + $sharee = new Sharee(); + $sharee->href = 'mailto:removeme@example.org'; + $sharee->properties['{DAV:}displayname'] = 'To be removed'; + $sharee->access = Sharing\Plugin::ACCESS_READ; + $sharee->inviteStatus = Sharing\Plugin::INVITE_NORESPONSE; + + $this->assertEquals( + [$sharee], + $this->getInstance()->getInvites() + ); + + } + + function testGetOwner() { + $this->assertEquals('principals/sharee', $this->getInstance()->getOwner()); + } + + function testGetACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/sharee', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/sharee/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write-properties', + 'principal' => 'principals/sharee', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write-properties', + 'principal' => 'principals/sharee/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/sharee', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/sharee/calendar-proxy-read', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/sharee/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ], + ]; + + $this->assertEquals($expected, $this->getInstance()->getACL()); + + } + + function testGetChildACL() { + + $expected = [ + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/sharee', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}write', + 'principal' => 'principals/sharee/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/sharee', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/sharee/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/sharee/calendar-proxy-read', + 'protected' => true, + ], + + ]; + + $this->assertEquals($expected, $this->getInstance()->getChildACL()); + + } + + function testUpdateInvites() { + + $instance = $this->getInstance(); + $newSharees = [ + new Sharee(), + new Sharee() + ]; + $newSharees[0]->href = 'mailto:test@example.org'; + $newSharees[0]->properties['{DAV:}displayname'] = 'Foo Bar'; + $newSharees[0]->comment = 'Booh'; + $newSharees[0]->access = Sharing\Plugin::ACCESS_READWRITE; + + $newSharees[1]->href = 'mailto:removeme@example.org'; + $newSharees[1]->access = Sharing\Plugin::ACCESS_NOACCESS; + + $instance->updateInvites($newSharees); + + $expected = [ + clone $newSharees[0] + ]; + $expected[0]->inviteStatus = Sharing\Plugin::INVITE_NORESPONSE; + $this->assertEquals($expected, $instance->getInvites()); + + } + + function testPublish() { + + $instance = $this->getInstance(); + $this->assertNull($instance->setPublishStatus(true)); + $this->assertNull($instance->setPublishStatus(false)); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php new file mode 100644 index 00000000000..9589176a368 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/SharingPluginTest.php @@ -0,0 +1,396 @@ +caldavCalendars = [ + [ + 'principaluri' => 'principals/user1', + 'id' => 1, + 'uri' => 'cal1', + ], + [ + 'principaluri' => 'principals/user1', + 'id' => 2, + 'uri' => 'cal2', + 'share-access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE, + ], + [ + 'principaluri' => 'principals/user1', + 'id' => 3, + 'uri' => 'cal3', + ], + ]; + + parent::setUp(); + + // Making the logged in user an admin, for full access: + $this->aclPlugin->adminPrincipals[] = 'principals/user2'; + + } + + function testSimple() { + + $this->assertInstanceOf('Sabre\\CalDAV\\SharingPlugin', $this->server->getPlugin('caldav-sharing')); + $this->assertEquals( + 'caldav-sharing', + $this->caldavSharingPlugin->getPluginInfo()['name'] + ); + + } + + /** + * @expectedException \LogicException + */ + function testSetupWithoutCoreSharingPlugin() { + + $server = new DAV\Server(); + $server->addPlugin( + new SharingPlugin() + ); + + } + + function testGetFeatures() { + + $this->assertEquals(['calendarserver-sharing'], $this->caldavSharingPlugin->getFeatures()); + + } + + function testBeforeGetShareableCalendar() { + + // Forcing the server to authenticate: + $this->authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); + $props = $this->server->getProperties('calendars/user1/cal1', [ + '{' . Plugin::NS_CALENDARSERVER . '}invite', + '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', + ]); + + $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']); + $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\AllowedSharingModes', $props['{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes']); + + } + + function testBeforeGetSharedCalendar() { + + $props = $this->server->getProperties('calendars/user1/cal2', [ + '{' . Plugin::NS_CALENDARSERVER . '}shared-url', + '{' . Plugin::NS_CALENDARSERVER . '}invite', + ]); + + $this->assertInstanceOf('Sabre\\CalDAV\\Xml\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']); + //$this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $props['{' . Plugin::NS_CALENDARSERVER . '}shared-url']); + + } + + function testUpdateResourceType() { + + $this->caldavBackend->updateInvites(1, + [ + new Sharee([ + 'href' => 'mailto:joe@example.org', + ]) + ] + ); + $result = $this->server->updateProperties('calendars/user1/cal1', [ + '{DAV:}resourcetype' => new DAV\Xml\Property\ResourceType(['{DAV:}collection']) + ]); + + $this->assertEquals([ + '{DAV:}resourcetype' => 200 + ], $result); + + $this->assertEquals(0, count($this->caldavBackend->getInvites(1))); + + } + + function testUpdatePropertiesPassThru() { + + $result = $this->server->updateProperties('calendars/user1/cal3', [ + '{DAV:}foo' => 'bar', + ]); + + $this->assertEquals([ + '{DAV:}foo' => 200, + ], $result); + + } + + function testUnknownMethodNoPOST() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/', + ]); + + $response = $this->request($request); + + $this->assertEquals(501, $response->status, $response->body); + + } + + function testUnknownMethodNoXML() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/', + 'CONTENT_TYPE' => 'text/plain', + ]); + + $response = $this->request($request); + + $this->assertEquals(501, $response->status, $response->body); + + } + + function testUnknownMethodNoNode() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/foo', + 'CONTENT_TYPE' => 'text/xml', + ]); + + $response = $this->request($request); + + $this->assertEquals(501, $response->status, $response->body); + + } + + function testShareRequest() { + + $request = new HTTP\Request('POST', '/calendars/user1/cal1', ['Content-Type' => 'text/xml']); + + $xml = << + + + mailto:joe@example.org + Joe Shmoe + + + + mailto:nancy@example.org + + +RRR; + + $request->setBody($xml); + + $response = $this->request($request, 200); + + $this->assertEquals( + [ + new Sharee([ + 'href' => 'mailto:joe@example.org', + 'properties' => [ + '{DAV:}displayname' => 'Joe Shmoe', + ], + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE, + 'comment' => '', + ]), + ], + $this->caldavBackend->getInvites(1) + ); + + // Wiping out tree cache + $this->server->tree->markDirty(''); + + // Verifying that the calendar is now marked shared. + $props = $this->server->getProperties('calendars/user1/cal1', ['{DAV:}resourcetype']); + $this->assertTrue( + $props['{DAV:}resourcetype']->is('{http://calendarserver.org/ns/}shared-owner') + ); + + } + + function testShareRequestNoShareableCalendar() { + + $request = new HTTP\Request( + 'POST', + '/calendars/user1/cal2', + ['Content-Type' => 'text/xml'] + ); + + $xml = ' + + + mailto:joe@example.org + Joe Shmoe + + + + mailto:nancy@example.org + + +'; + + $request->setBody($xml); + + $response = $this->request($request, 403); + + } + + function testInviteReply() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1', + 'CONTENT_TYPE' => 'text/xml', + ]); + + $xml = ' + + /principals/owner + + +'; + + $request->setBody($xml); + $response = $this->request($request); + $this->assertEquals(200, $response->status, $response->body); + + } + + function testInviteBadXML() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1', + 'CONTENT_TYPE' => 'text/xml', + ]); + + $xml = ' + + +'; + $request->setBody($xml); + $response = $this->request($request); + $this->assertEquals(400, $response->status, $response->body); + + } + + function testInviteWrongUrl() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/calendars/user1/cal1', + 'CONTENT_TYPE' => 'text/xml', + ]); + + $xml = ' + + /principals/owner + +'; + $request->setBody($xml); + $response = $this->request($request); + $this->assertEquals(501, $response->status, $response->body); + + // If the plugin did not handle this request, it must ensure that the + // body is still accessible by other plugins. + $this->assertEquals($xml, $request->getBody(true)); + + } + + function testPublish() { + + $request = new HTTP\Request('POST', '/calendars/user1/cal1', ['Content-Type' => 'text/xml']); + + $xml = ' + +'; + + $request->setBody($xml); + + $response = $this->request($request); + $this->assertEquals(202, $response->status, $response->body); + + } + + + function testUnpublish() { + + $request = new HTTP\Request( + 'POST', + '/calendars/user1/cal1', + ['Content-Type' => 'text/xml'] + ); + + $xml = ' + +'; + + $request->setBody($xml); + + $response = $this->request($request); + $this->assertEquals(200, $response->status, $response->body); + + } + + function testPublishWrongUrl() { + + $request = new HTTP\Request( + 'POST', + '/calendars/user1', + ['Content-Type' => 'text/xml'] + ); + + $xml = ' + +'; + + $request->setBody($xml); + $this->request($request, 501); + + } + + function testUnpublishWrongUrl() { + + $request = new HTTP\Request( + 'POST', + '/calendars/user1', + ['Content-Type' => 'text/xml'] + ); + $xml = ' + +'; + + $request->setBody($xml); + + $this->request($request, 501); + + } + + function testUnknownXmlDoc() { + + + $request = new HTTP\Request( + 'POST', + '/calendars/user1/cal2', + ['Content-Type' => 'text/xml'] + ); + + $xml = ' +'; + + $request->setBody($xml); + + $response = $this->request($request); + $this->assertEquals(501, $response->status, $response->body); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/CreateSubscriptionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/CreateSubscriptionTest.php new file mode 100644 index 00000000000..8ad0f8ac574 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/CreateSubscriptionTest.php @@ -0,0 +1,123 @@ + + + + + + + + + + #1C4587FF + Jewish holidays + Foo + 19 + + webcal://www.example.org/ + + P1W + + + + +XML; + + $headers = [ + 'Content-Type' => 'application/xml', + ]; + $request = new Request('MKCOL', '/calendars/user1/subscription1', $headers, $body); + + $response = $this->request($request); + $this->assertEquals(201, $response->getStatus()); + $subscriptions = $this->caldavBackend->getSubscriptionsForUser('principals/user1'); + $this->assertSubscription($subscriptions[0]); + + + } + /** + * OS X 10.9.2 and up + */ + function testMKCALENDAR() { + + $body = << + + + + + + + + + + + + P1W + + webcal://www.example.org/ + + #1C4587FF + 19 + Foo + + Jewish holidays + + + +XML; + + $headers = [ + 'Content-Type' => 'application/xml', + ]; + $request = new Request('MKCALENDAR', '/calendars/user1/subscription1', $headers, $body); + + $response = $this->request($request); + $this->assertEquals(201, $response->getStatus()); + $subscriptions = $this->caldavBackend->getSubscriptionsForUser('principals/user1'); + $this->assertSubscription($subscriptions[0]); + + // Also seeing if it works when calling this as a PROPFIND. + $this->assertEquals([ + '{http://calendarserver.org/ns/}subscribed-strip-alarms' => '', + ], + $this->server->getProperties('calendars/user1/subscription1', ['{http://calendarserver.org/ns/}subscribed-strip-alarms']) + ); + + + } + + function assertSubscription($subscription) { + + $this->assertEquals('', $subscription['{http://calendarserver.org/ns/}subscribed-strip-attachments']); + $this->assertEquals('', $subscription['{http://calendarserver.org/ns/}subscribed-strip-todos']); + $this->assertEquals('#1C4587FF', $subscription['{http://apple.com/ns/ical/}calendar-color']); + $this->assertEquals('Jewish holidays', $subscription['{DAV:}displayname']); + $this->assertEquals('Foo', $subscription['{urn:ietf:params:xml:ns:caldav}calendar-description']); + $this->assertEquals('19', $subscription['{http://apple.com/ns/ical/}calendar-order']); + $this->assertEquals('webcal://www.example.org/', $subscription['{http://calendarserver.org/ns/}source']->getHref()); + $this->assertEquals('P1W', $subscription['{http://apple.com/ns/ical/}refreshrate']); + $this->assertEquals('subscription1', $subscription['uri']); + $this->assertEquals('principals/user1', $subscription['principaluri']); + $this->assertEquals('webcal://www.example.org/', $subscription['source']); + $this->assertEquals(['principals/user1', 1], $subscription['id']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/PluginTest.php new file mode 100644 index 00000000000..dc6d2d5f04c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/PluginTest.php @@ -0,0 +1,50 @@ +addPlugin($plugin); + + $this->assertEquals( + '{http://calendarserver.org/ns/}subscribed', + $server->resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] + ); + $this->assertEquals( + 'Sabre\\DAV\\Xml\\Property\\Href', + $server->xml->elementMap['{http://calendarserver.org/ns/}source'] + ); + + $this->assertEquals( + ['calendarserver-subscribed'], + $plugin->getFeatures() + ); + + $this->assertEquals( + 'subscriptions', + $plugin->getPluginInfo()['name'] + ); + + } + + function testPropFind() { + + $propName = '{http://calendarserver.org/ns/}subscribed-strip-alarms'; + $propFind = new PropFind('foo', [$propName]); + $propFind->set($propName, null, 200); + + $plugin = new Plugin(); + $plugin->propFind($propFind, new \Sabre\DAV\SimpleCollection('hi')); + + $this->assertFalse(is_null($propFind->get($propName))); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/SubscriptionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/SubscriptionTest.php new file mode 100644 index 00000000000..559d526cd1d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Subscriptions/SubscriptionTest.php @@ -0,0 +1,131 @@ + new Href('http://example.org/src', false), + 'lastmodified' => date('2013-04-06 11:40:00'), // tomorrow is my birthday! + '{DAV:}displayname' => 'displayname', + ]; + + + $id = $caldavBackend->createSubscription('principals/user1', 'uri', array_merge($info, $override)); + $subInfo = $caldavBackend->getSubscriptionsForUser('principals/user1'); + + $this->assertEquals(1, count($subInfo)); + $subscription = new Subscription($caldavBackend, $subInfo[0]); + + $this->backend = $caldavBackend; + return $subscription; + + } + + function testValues() { + + $sub = $this->getSub(); + + $this->assertEquals('uri', $sub->getName()); + $this->assertEquals(date('2013-04-06 11:40:00'), $sub->getLastModified()); + $this->assertEquals([], $sub->getChildren()); + + $this->assertEquals( + [ + '{DAV:}displayname' => 'displayname', + '{http://calendarserver.org/ns/}source' => new Href('http://example.org/src', false), + ], + $sub->getProperties(['{DAV:}displayname', '{http://calendarserver.org/ns/}source']) + ); + + $this->assertEquals('principals/user1', $sub->getOwner()); + $this->assertNull($sub->getGroup()); + + $acl = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1/calendar-proxy-write', + 'protected' => true, + ], + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1/calendar-proxy-read', + 'protected' => true, + ] + ]; + $this->assertEquals($acl, $sub->getACL()); + + $this->assertNull($sub->getSupportedPrivilegeSet()); + + } + + function testValues2() { + + $sub = $this->getSub([ + 'lastmodified' => null, + ]); + + $this->assertEquals(null, $sub->getLastModified()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $sub = $this->getSub(); + $sub->setACL([]); + + } + + function testDelete() { + + $sub = $this->getSub(); + $sub->delete(); + + $this->assertEquals([], $this->backend->getSubscriptionsForUser('principals1/user1')); + + } + + function testUpdateProperties() { + + $sub = $this->getSub(); + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'foo', + ]); + $sub->propPatch($propPatch); + $this->assertTrue($propPatch->commit()); + + $this->assertEquals( + 'foo', + $this->backend->getSubscriptionsForUser('principals/user1')[0]['{DAV:}displayname'] + ); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testBadConstruct() { + + $caldavBackend = new \Sabre\CalDAV\Backend\MockSubscriptionSupport([], []); + new Subscription($caldavBackend, []); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/TestUtil.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/TestUtil.php new file mode 100644 index 00000000000..673d39c0aa5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/TestUtil.php @@ -0,0 +1,189 @@ +createCalendar( + 'principals/user1', + 'UUID-123467', + [ + '{DAV:}displayname' => 'user1 calendar', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + ] + ); + $backend->createCalendar( + 'principals/user1', + 'UUID-123468', + [ + '{DAV:}displayname' => 'user1 calendar2', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + ] + ); + $backend->createCalendarObject($calendarId, 'UUID-2345', self::getTestCalendarData()); + return $backend; + + } + + static function getTestCalendarData($type = 1) { + + $calendarData = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//iCal 4.0.1//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Asia/Seoul +BEGIN:DAYLIGHT +TZOFFSETFROM:+0900 +RRULE:FREQ=YEARLY;UNTIL=19880507T150000Z;BYMONTH=5;BYDAY=2SU +DTSTART:19870510T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+1000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1000 +DTSTART:19881009T000000 +TZNAME:GMT+09:00 +TZOFFSETTO:+0900 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20100225T154229Z +UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 +TRANSP:TRANSPARENT +SUMMARY:Something here +DTSTAMP:20100228T130202Z'; + + switch ($type) { + case 1 : + $calendarData .= "\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDTEND;TZID=Asia/Seoul:20100223T070000\n"; + break; + case 2 : + $calendarData .= "\nDTSTART:20100223T060000\nDTEND:20100223T070000\n"; + break; + case 3 : + $calendarData .= "\nDTSTART;VALUE=DATE:20100223\nDTEND;VALUE=DATE:20100223\n"; + break; + case 4 : + $calendarData .= "\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDURATION:PT1H\n"; + break; + case 5 : + $calendarData .= "\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDURATION:-P5D\n"; + break; + case 6 : + $calendarData .= "\nDTSTART;VALUE=DATE:20100223\n"; + break; + case 7 : + $calendarData .= "\nDTSTART;VALUE=DATETIME:20100223T060000\n"; + break; + + // No DTSTART, so intentionally broken + case 'X' : + $calendarData .= "\n"; + break; + } + + + $calendarData .= 'ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com +SEQUENCE:2 +END:VEVENT +END:VCALENDAR'; + + return $calendarData; + + } + + static function getTestTODO($type = 'due') { + + switch ($type) { + + case 'due' : + $extra = "DUE:20100104T000000Z"; + break; + case 'due2' : + $extra = "DUE:20060104T000000Z"; + break; + case 'due_date' : + $extra = "DUE;VALUE=DATE:20060104"; + break; + case 'due_tz' : + $extra = "DUE;TZID=Asia/Seoul:20060104T000000Z"; + break; + case 'due_dtstart' : + $extra = "DTSTART:20050223T060000Z\nDUE:20060104T000000Z"; + break; + case 'due_dtstart2' : + $extra = "DTSTART:20090223T060000Z\nDUE:20100104T000000Z"; + break; + case 'dtstart' : + $extra = 'DTSTART:20100223T060000Z'; + break; + case 'dtstart2' : + $extra = 'DTSTART:20060223T060000Z'; + break; + case 'dtstart_date' : + $extra = 'DTSTART;VALUE=DATE:20100223'; + break; + case 'dtstart_tz' : + $extra = 'DTSTART;TZID=Asia/Seoul:20100223T060000Z'; + break; + case 'dtstart_duration' : + $extra = "DTSTART:20061023T060000Z\nDURATION:PT1H"; + break; + case 'dtstart_duration2' : + $extra = "DTSTART:20101023T060000Z\nDURATION:PT1H"; + break; + case 'completed' : + $extra = 'COMPLETED:20060601T000000Z'; + break; + case 'completed2' : + $extra = 'COMPLETED:20090601T000000Z'; + break; + case 'created' : + $extra = 'CREATED:20060601T000000Z'; + break; + case 'created2' : + $extra = 'CREATED:20090601T000000Z'; + break; + case 'completedcreated' : + $extra = "CREATED:20060601T000000Z\nCOMPLETED:20070101T000000Z"; + break; + case 'completedcreated2' : + $extra = "CREATED:20090601T000000Z\nCOMPLETED:20100101T000000Z"; + break; + case 'notime' : + $extra = 'X-FILLER:oh hello'; + break; + default : + throw new Exception('Unknown type: ' . $type); + + } + + $todo = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Example Corp.//CalDAV Client//EN +BEGIN:VTODO +DTSTAMP:20060205T235335Z +' . $extra . ' +STATUS:NEEDS-ACTION +SUMMARY:Task #1 +UID:DDDEEB7915FA61233B861457@example.com +BEGIN:VALARM +ACTION:AUDIO +TRIGGER;RELATED=START:-PT10M +END:VALARM +END:VTODO +END:VCALENDAR'; + + return $todo; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php new file mode 100644 index 00000000000..629df90c119 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/ValidateICalTest.php @@ -0,0 +1,406 @@ + 'calendar1', + 'principaluri' => 'principals/admin', + 'uri' => 'calendar1', + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO', 'VJOURNAL']), + ], + [ + 'id' => 'calendar2', + 'principaluri' => 'principals/admin', + 'uri' => 'calendar2', + '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VJOURNAL']), + ] + ]; + + $this->calBackend = new Backend\Mock($calendars, []); + $principalBackend = new DAVACL\PrincipalBackend\Mock(); + + $tree = [ + new CalendarRoot($principalBackend, $this->calBackend), + ]; + + $this->server = new DAV\Server($tree); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->debugExceptions = true; + + $plugin = new Plugin(); + $this->server->addPlugin($plugin); + + $response = new HTTP\ResponseMock(); + $this->server->httpResponse = $response; + + } + + function request(HTTP\Request $request) { + + $this->server->httpRequest = $request; + $this->server->exec(); + + return $this->server->httpResponse; + + } + + function testCreateFile() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status); + + } + + function testCreateFileValid() { + + $request = new HTTP\Request( + 'PUT', + '/calendars/admin/calendar1/blabla.ics', + ['Prefer' => 'handling=strict'] + ); + + $ics = <<setBody($ics); + + $response = $this->request($request); + + $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5($ics) . '"'], + ], $response->getHeaders()); + + $expected = [ + 'uri' => 'blabla.ics', + 'calendardata' => $ics, + 'calendarid' => 'calendar1', + 'lastmodified' => null, + ]; + + $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1', 'blabla.ics')); + + } + + function testCreateFileNoVersion() { + + $request = new HTTP\Request( + 'PUT', + '/calendars/admin/calendar1/blabla.ics', + ['Prefer' => 'handling=strict'] + ); + + $ics = <<setBody($ics); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileNoVersionFixed() { + + $request = new HTTP\Request( + 'PUT', + '/calendars/admin/calendar1/blabla.ics', + ['Prefer' => 'handling=lenient'] + ); + + $ics = <<setBody($ics); + + $response = $this->request($request); + + $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + 'X-Sabre-Ew-Gross' => ['iCalendar validation warning: VERSION MUST appear exactly once in a VCALENDAR component'], + ], $response->getHeaders()); + + $ics = << 'blabla.ics', + 'calendardata' => $ics, + 'calendarid' => 'calendar1', + 'lastmodified' => null, + ]; + + $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1', 'blabla.ics')); + + } + + function testCreateFileNoComponents() { + + $request = new HTTP\Request( + 'PUT', + '/calendars/admin/calendar1/blabla.ics', + ['Prefer' => 'handling=strict'] + ); + $ics = <<setBody($ics); + + $response = $this->request($request); + $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileNoUID() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileVCard() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + $request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFile2Components() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VJOURNAL\r\nUID:foo\r\nEND:VJOURNAL\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFile2UIDS() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:bar\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testCreateFileWrongComponent() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VFREEBUSY\r\nUID:foo\r\nEND:VFREEBUSY\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testUpdateFile() { + + $this->calBackend->createCalendarObject('calendar1', 'blabla.ics', 'foo'); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', + ]); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status); + + } + + function testUpdateFileParsableBody() { + + $this->calBackend->createCalendarObject('calendar1', 'blabla.ics', 'foo'); + $request = new HTTP\Request( + 'PUT', + '/calendars/admin/calendar1/blabla.ics' + ); + $ics = <<setBody($ics); + $response = $this->request($request); + + $this->assertEquals(204, $response->status); + + $expected = [ + 'uri' => 'blabla.ics', + 'calendardata' => $ics, + 'calendarid' => 'calendar1', + 'lastmodified' => null, + ]; + + $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1', 'blabla.ics')); + + } + + function testCreateFileInvalidComponent() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testUpdateFileInvalidComponent() { + + $this->calBackend->createCalendarObject('calendar2', 'blabla.ics', 'foo'); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + /** + * What we are testing here, is if we send in a latin1 character, the + * server should automatically transform this into UTF-8. + * + * More importantly. If any transformation happens, the etag must no longer + * be returned by the server. + */ + function testCreateFileModified() { + + $request = new HTTP\Request( + 'PUT', + '/calendars/admin/calendar1/blabla.ics' + ); + $ics = <<setBody($ics); + + $response = $this->request($request); + + $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + $this->assertNull($response->getHeader('ETag')); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/InviteReplyTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/InviteReplyTest.php new file mode 100644 index 00000000000..cd700893d67 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/InviteReplyTest.php @@ -0,0 +1,146 @@ +assertEquals('foo', $notification->getId()); + $this->assertEquals('"1"', $notification->getETag()); + + $simpleExpected = '' . "\n" . ''; + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://calendarserver.org/ns/' => 'cs', + ]; + $writer->openMemory(); + $writer->startDocument('1.0', 'UTF-8'); + $writer->startElement('{http://calendarserver.org/ns/}root'); + $writer->write($notification); + $writer->endElement(); + + $this->assertEquals($simpleExpected, $writer->outputMemory()); + + $writer = new Writer(); + $writer->contextUri = '/'; + $writer->namespaceMap = [ + 'http://calendarserver.org/ns/' => 'cs', + 'DAV:' => 'd', + ]; + $writer->openMemory(); + $writer->startDocument('1.0', 'UTF-8'); + $writer->startElement('{http://calendarserver.org/ns/}root'); + $notification->xmlSerializeFull($writer); + $writer->endElement(); + + $this->assertXmlStringEqualsXmlString($expected, $writer->outputMemory()); + + + } + + function dataProvider() { + + $dtStamp = new \DateTime('2012-01-01 00:00:00 GMT'); + return [ + [ + [ + 'id' => 'foo', + 'dtStamp' => $dtStamp, + 'etag' => '"1"', + 'inReplyTo' => 'bar', + 'href' => 'mailto:foo@example.org', + 'type' => DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'hostUrl' => 'calendar' + ], +<< + + 20120101T000000Z + + foo + bar + mailto:foo@example.org + + + /calendar + + + + +FOO + ], + [ + [ + 'id' => 'foo', + 'dtStamp' => $dtStamp, + 'etag' => '"1"', + 'inReplyTo' => 'bar', + 'href' => 'mailto:foo@example.org', + 'type' => DAV\Sharing\Plugin::INVITE_DECLINED, + 'hostUrl' => 'calendar', + 'summary' => 'Summary!' + ], +<< + + 20120101T000000Z + + foo + bar + mailto:foo@example.org + + + /calendar + + Summary! + + + +FOO + ], + + ]; + + } + + /** + * @expectedException InvalidArgumentException + */ + function testMissingArg() { + + new InviteReply([]); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testUnknownArg() { + + new InviteReply([ + 'foo-i-will-break' => true, + + 'id' => 1, + 'etag' => '"bla"', + 'href' => 'abc', + 'dtStamp' => 'def', + 'inReplyTo' => 'qrs', + 'type' => 'ghi', + 'hostUrl' => 'jkl', + ]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/InviteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/InviteTest.php new file mode 100644 index 00000000000..f03093916e9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/InviteTest.php @@ -0,0 +1,165 @@ +assertEquals('foo', $notification->getId()); + $this->assertEquals('"1"', $notification->getETag()); + + $simpleExpected = '' . "\n"; + $this->namespaceMap['http://calendarserver.org/ns/'] = 'cs'; + + $xml = $this->write($notification); + + $this->assertXmlStringEqualsXmlString($simpleExpected, $xml); + + $this->namespaceMap['urn:ietf:params:xml:ns:caldav'] = 'cal'; + $xml = $this->writeFull($notification); + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + + } + + function dataProvider() { + + $dtStamp = new \DateTime('2012-01-01 00:00:00', new \DateTimeZone('GMT')); + return [ + [ + [ + 'id' => 'foo', + 'dtStamp' => $dtStamp, + 'etag' => '"1"', + 'href' => 'mailto:foo@example.org', + 'type' => DAV\Sharing\Plugin::INVITE_ACCEPTED, + 'readOnly' => true, + 'hostUrl' => 'calendar', + 'organizer' => 'principal/user1', + 'commonName' => 'John Doe', + 'summary' => 'Awesome stuff!' + ], +<< + + 20120101T000000Z + + foo + mailto:foo@example.org + + + /calendar + + Awesome stuff! + + + + + /principal/user1 + John Doe + + John Doe + + + +FOO + ], + [ + [ + 'id' => 'foo', + 'dtStamp' => $dtStamp, + 'etag' => '"1"', + 'href' => 'mailto:foo@example.org', + 'type' => DAV\Sharing\Plugin::INVITE_NORESPONSE, + 'readOnly' => true, + 'hostUrl' => 'calendar', + 'organizer' => 'principal/user1', + 'firstName' => 'Foo', + 'lastName' => 'Bar', + ], +<< + + 20120101T000000Z + + foo + mailto:foo@example.org + + + /calendar + + + + + + /principal/user1 + Foo + Bar + + Foo + Bar + + + +FOO + ], + + ]; + + } + + /** + * @expectedException InvalidArgumentException + */ + function testMissingArg() { + + new Invite([]); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testUnknownArg() { + + new Invite([ + 'foo-i-will-break' => true, + + 'id' => 1, + 'etag' => '"bla"', + 'href' => 'abc', + 'dtStamp' => 'def', + 'type' => 'ghi', + 'readOnly' => true, + 'hostUrl' => 'jkl', + 'organizer' => 'mno', + ]); + + } + + function writeFull($input) { + + $writer = new Writer(); + $writer->contextUri = '/'; + $writer->namespaceMap = $this->namespaceMap; + $writer->openMemory(); + $writer->startElement('{http://calendarserver.org/ns/}root'); + $input->xmlSerializeFull($writer); + $writer->endElement(); + return $writer->outputMemory(); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/SystemStatusTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/SystemStatusTest.php new file mode 100644 index 00000000000..1f9034340f5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Notification/SystemStatusTest.php @@ -0,0 +1,69 @@ +assertEquals('foo', $notification->getId()); + $this->assertEquals('"1"', $notification->getETag()); + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://calendarserver.org/ns/' => 'cs', + ]; + $writer->openMemory(); + $writer->startDocument('1.0', 'UTF-8'); + $writer->startElement('{http://calendarserver.org/ns/}root'); + $writer->write($notification); + $writer->endElement(); + $this->assertXmlStringEqualsXmlString($expected1, $writer->outputMemory()); + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://calendarserver.org/ns/' => 'cs', + 'DAV:' => 'd', + ]; + $writer->openMemory(); + $writer->startDocument('1.0', 'UTF-8'); + $writer->startElement('{http://calendarserver.org/ns/}root'); + $notification->xmlSerializeFull($writer); + $writer->endElement(); + $this->assertXmlStringEqualsXmlString($expected2, $writer->outputMemory()); + + } + + function dataProvider() { + + return [ + + [ + new SystemStatus('foo', '"1"'), + '' . "\n" . '' . "\n", + '' . "\n" . '' . "\n", + ], + [ + new SystemStatus('foo', '"1"', SystemStatus::TYPE_MEDIUM, 'bar'), + '' . "\n" . '' . "\n", + '' . "\n" . 'bar' . "\n", + ], + [ + new SystemStatus('foo', '"1"', SystemStatus::TYPE_LOW, null, 'http://example.org/'), + '' . "\n" . '' . "\n", + '' . "\n" . 'http://example.org/' . "\n", + ] + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/AllowedSharingModesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/AllowedSharingModesTest.php new file mode 100644 index 00000000000..0602d4f24f4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/AllowedSharingModesTest.php @@ -0,0 +1,38 @@ +assertInstanceOf('Sabre\CalDAV\Xml\Property\AllowedSharingModes', $sccs); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new AllowedSharingModes(true, true); + + $this->namespaceMap[CalDAV\Plugin::NS_CALDAV] = 'cal'; + $this->namespaceMap[CalDAV\Plugin::NS_CALENDARSERVER] = 'cs'; + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + + + + +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/EmailAddressSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/EmailAddressSetTest.php new file mode 100644 index 00000000000..30651a080e4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/EmailAddressSetTest.php @@ -0,0 +1,40 @@ + 'cs', + 'DAV:' => 'd', + ]; + + function testSimple() { + + $eas = new EmailAddressSet(['foo@example.org']); + $this->assertEquals(['foo@example.org'], $eas->getValue()); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new EmailAddressSet(['foo@example.org']); + + $xml = $this->write([ + '{DAV:}root' => $property + ]); + + $this->assertXmlStringEqualsXmlString( +' + +foo@example.org +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/InviteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/InviteTest.php new file mode 100644 index 00000000000..1397dcca2b7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/InviteTest.php @@ -0,0 +1,112 @@ +namespaceMap[CalDAV\Plugin::NS_CALDAV] = 'cal'; + $this->namespaceMap[CalDAV\Plugin::NS_CALENDARSERVER] = 'cs'; + + + } + + function testSimple() { + + $invite = new Invite([]); + $this->assertInstanceOf('Sabre\CalDAV\Xml\Property\Invite', $invite); + $this->assertEquals([], $invite->getValue()); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new Invite([ + new Sharee([ + 'href' => 'mailto:thedoctor@example.org', + 'properties' => ['{DAV:}displayname' => 'The Doctor'], + 'inviteStatus' => SP::INVITE_ACCEPTED, + 'access' => SP::ACCESS_SHAREDOWNER, + ]), + new Sharee([ + 'href' => 'mailto:user1@example.org', + 'inviteStatus' => SP::INVITE_ACCEPTED, + 'access' => SP::ACCESS_READWRITE, + ]), + new Sharee([ + 'href' => 'mailto:user2@example.org', + 'properties' => ['{DAV:}displayname' => 'John Doe'], + 'inviteStatus' => SP::INVITE_DECLINED, + 'access' => SP::ACCESS_READ, + ]), + new Sharee([ + 'href' => 'mailto:user3@example.org', + 'properties' => ['{DAV:}displayname' => 'Joe Shmoe'], + 'inviteStatus' => SP::INVITE_NORESPONSE, + 'access' => SP::ACCESS_READ, + 'comment' => 'Something, something', + ]), + new Sharee([ + 'href' => 'mailto:user4@example.org', + 'properties' => ['{DAV:}displayname' => 'Hoe Boe'], + 'inviteStatus' => SP::INVITE_INVALID, + 'access' => SP::ACCESS_READ, + ]), + ]); + + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + + + mailto:thedoctor@example.org + The Doctor + + + + + + + mailto:user1@example.org + + + + + + + mailto:user2@example.org + John Doe + + + + + + + mailto:user3@example.org + Joe Shmoe + Something, something + + + + + + + mailto:user4@example.org + Hoe Boe + + +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/ScheduleCalendarTranspTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/ScheduleCalendarTranspTest.php new file mode 100644 index 00000000000..729db4569e5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/ScheduleCalendarTranspTest.php @@ -0,0 +1,118 @@ +namespaceMap[CalDAV\Plugin::NS_CALDAV] = 'cal'; + $this->namespaceMap[CalDAV\Plugin::NS_CALENDARSERVER] = 'cs'; + + + } + + function testSimple() { + + $prop = new ScheduleCalendarTransp(ScheduleCalendarTransp::OPAQUE); + $this->assertEquals( + ScheduleCalendarTransp::OPAQUE, + $prop->getValue() + ); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testBadValue() { + + new ScheduleCalendarTransp('ahhh'); + + } + + /** + * @depends testSimple + */ + function testSerializeOpaque() { + + $property = new ScheduleCalendarTransp(ScheduleCalendarTransp::OPAQUE); + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + + + +', $xml); + + } + + /** + * @depends testSimple + */ + function testSerializeTransparent() { + + $property = new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT); + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + + + +', $xml); + + } + + function testUnserializeTransparent() { + + $cal = CalDAV\Plugin::NS_CALDAV; + $cs = CalDAV\Plugin::NS_CALENDARSERVER; + +$xml = << + + + +XML; + + $result = $this->parse( + $xml, + ['{DAV:}root' => 'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp'] + ); + + $this->assertEquals( + new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT), + $result['value'] + ); + + } + + function testUnserializeOpaque() { + + $cal = CalDAV\Plugin::NS_CALDAV; + $cs = CalDAV\Plugin::NS_CALENDARSERVER; + +$xml = << + + + +XML; + + $result = $this->parse( + $xml, + ['{DAV:}root' => 'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp'] + ); + + $this->assertEquals( + new ScheduleCalendarTransp(ScheduleCalendarTransp::OPAQUE), + $result['value'] + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCalendarComponentSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCalendarComponentSetTest.php new file mode 100644 index 00000000000..1acc402d36b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCalendarComponentSetTest.php @@ -0,0 +1,102 @@ +namespaceMap[CalDAV\Plugin::NS_CALDAV] = 'cal'; + $this->namespaceMap[CalDAV\Plugin::NS_CALENDARSERVER] = 'cs'; + + } + + function testSimple() { + + $prop = new SupportedCalendarComponentSet(['VEVENT']); + $this->assertEquals( + ['VEVENT'], + $prop->getValue() + ); + + } + + function testMultiple() { + + $prop = new SupportedCalendarComponentSet(['VEVENT', 'VTODO']); + $this->assertEquals( + ['VEVENT', 'VTODO'], + $prop->getValue() + ); + + } + + /** + * @depends testSimple + * @depends testMultiple + */ + function testSerialize() { + + $property = new SupportedCalendarComponentSet(['VEVENT', 'VTODO']); + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + + + + +', $xml); + + } + + function testUnserialize() { + + $cal = CalDAV\Plugin::NS_CALDAV; + $cs = CalDAV\Plugin::NS_CALENDARSERVER; + +$xml = << + + + + +XML; + + $result = $this->parse( + $xml, + ['{DAV:}root' => 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'] + ); + + $this->assertEquals( + new SupportedCalendarComponentSet(['VEVENT', 'VTODO']), + $result['value'] + ); + + } + + /** + * @expectedException \Sabre\Xml\ParseException + */ + function testUnserializeEmpty() { + + $cal = CalDAV\Plugin::NS_CALDAV; + $cs = CalDAV\Plugin::NS_CALENDARSERVER; + +$xml = << + + +XML; + + $result = $this->parse( + $xml, + ['{DAV:}root' => 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'] + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCalendarDataTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCalendarDataTest.php new file mode 100644 index 00000000000..442b6a059f4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCalendarDataTest.php @@ -0,0 +1,36 @@ +assertInstanceOf('Sabre\CalDAV\Xml\Property\SupportedCalendarData', $sccs); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $this->namespaceMap[CalDAV\Plugin::NS_CALDAV] = 'cal'; + $property = new SupportedCalendarData(); + + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + + + +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCollationSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCollationSetTest.php new file mode 100644 index 00000000000..e009fb6cd85 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Property/SupportedCollationSetTest.php @@ -0,0 +1,37 @@ +assertInstanceOf('Sabre\CalDAV\Xml\Property\SupportedCollationSet', $scs); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new SupportedCollationSet(); + + $this->namespaceMap[CalDAV\Plugin::NS_CALDAV] = 'cal'; + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' + +i;ascii-casemap +i;octet +i;unicode-casemap +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/CalendarQueryReportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/CalendarQueryReportTest.php new file mode 100644 index 00000000000..d5e87db854b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/CalendarQueryReportTest.php @@ -0,0 +1,369 @@ + 'Sabre\\CalDAV\\Xml\\Request\CalendarQueryReport', + ]; + + function testDeserialize() { + + $xml = << + + + + + + + + +XML; + + $result = $this->parse($xml); + $calendarQueryReport = new CalendarQueryReport(); + $calendarQueryReport->properties = [ + '{DAV:}getetag', + ]; + $calendarQueryReport->filters = [ + 'name' => 'VCALENDAR', + 'is-not-defined' => false, + 'comp-filters' => [], + 'prop-filters' => [], + 'time-range' => false, + ]; + + $this->assertEquals( + $calendarQueryReport, + $result['value'] + ); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testDeserializeNoFilter() { + + $xml = << + + + + + +XML; + + $this->parse($xml); + + } + + function testDeserializeComplex() { + + $xml = << + + + + + + + + + + + + + + + + + + + + + + hi + + + + + + + + + + Hello + + + + + +XML; + + $result = $this->parse($xml); + $calendarQueryReport = new CalendarQueryReport(); + $calendarQueryReport->version = '2.0'; + $calendarQueryReport->contentType = 'application/json+calendar'; + $calendarQueryReport->properties = [ + '{DAV:}getetag', + '{urn:ietf:params:xml:ns:caldav}calendar-data', + ]; + $calendarQueryReport->expand = [ + 'start' => new DateTimeImmutable('2015-01-01 00:00:00', new DateTimeZone('UTC')), + 'end' => new DateTimeImmutable('2016-01-01 00:00:00', new DateTimeZone('UTC')), + ]; + $calendarQueryReport->filters = [ + 'name' => 'VCALENDAR', + 'is-not-defined' => false, + 'comp-filters' => [ + [ + 'name' => 'VEVENT', + 'is-not-defined' => false, + 'comp-filters' => [ + [ + 'name' => 'VALARM', + 'is-not-defined' => true, + 'comp-filters' => [], + 'prop-filters' => [], + 'time-range' => false, + ], + ], + 'prop-filters' => [ + [ + 'name' => 'UID', + 'is-not-defined' => false, + 'time-range' => false, + 'text-match' => null, + 'param-filters' => [], + ], + [ + 'name' => 'X-PROP', + 'is-not-defined' => false, + 'time-range' => false, + 'text-match' => null, + 'param-filters' => [ + [ + 'name' => 'X-PARAM', + 'is-not-defined' => false, + 'text-match' => null, + ], + [ + 'name' => 'X-PARAM2', + 'is-not-defined' => true, + 'text-match' => null, + ], + [ + 'name' => 'X-PARAM3', + 'is-not-defined' => false, + 'text-match' => [ + 'negate-condition' => true, + 'collation' => 'i;ascii-casemap', + 'value' => 'hi', + ], + ], + ], + ], + [ + 'name' => 'X-PROP2', + 'is-not-defined' => true, + 'time-range' => false, + 'text-match' => null, + 'param-filters' => [], + ], + [ + 'name' => 'X-PROP3', + 'is-not-defined' => false, + 'time-range' => [ + 'start' => new DateTimeImmutable('2015-01-01 00:00:00', new DateTimeZone('UTC')), + 'end' => new DateTimeImmutable('2016-01-01 00:00:00', new DateTimeZone('UTC')), + ], + 'text-match' => null, + 'param-filters' => [], + ], + [ + 'name' => 'X-PROP4', + 'is-not-defined' => false, + 'time-range' => false, + 'text-match' => [ + 'negate-condition' => false, + 'collation' => 'i;ascii-casemap', + 'value' => 'Hello', + ], + 'param-filters' => [], + ], + ], + 'time-range' => [ + 'start' => new DateTimeImmutable('2015-01-01 00:00:00', new DateTimeZone('UTC')), + 'end' => new DateTimeImmutable('2016-01-01 00:00:00', new DateTimeZone('UTC')), + ] + ], + ], + 'prop-filters' => [], + 'time-range' => false, + ]; + + $this->assertEquals( + $calendarQueryReport, + $result['value'] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeDoubleTopCompFilter() { + + $xml = << + + + + + + + + + + + + +XML; + + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeMissingExpandEnd() { + + $xml = << + + + + + + + + + + + +XML; + + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeExpandEndBeforeStart() { + + $xml = << + + + + + + + + + + + +XML; + + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeTimeRangeOnVCALENDAR() { + + $xml = << + + + + + + + + + + + +XML; + + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeTimeRangeEndBeforeStart() { + + $xml = << + + + + + + + + + + + + + +XML; + + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeTimeRangePropEndBeforeStart() { + + $xml = << + + + + + + + + + + + + + + + +XML; + + $this->parse($xml); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/InviteReplyTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/InviteReplyTest.php new file mode 100644 index 00000000000..b0770899914 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/InviteReplyTest.php @@ -0,0 +1,78 @@ + 'Sabre\\CalDAV\\Xml\\Request\\InviteReply', + ]; + + function testDeserialize() { + + $xml = << + + /principal/1 + /calendar/1 + + blabla + Summary + +XML; + + $result = $this->parse($xml); + $inviteReply = new InviteReply('/principal/1', '/calendar/1', 'blabla', 'Summary', DAV\Sharing\Plugin::INVITE_ACCEPTED); + + $this->assertEquals( + $inviteReply, + $result['value'] + ); + + } + + function testDeserializeDeclined() { + + $xml = << + + /principal/1 + /calendar/1 + + blabla + Summary + +XML; + + $result = $this->parse($xml); + $inviteReply = new InviteReply('/principal/1', '/calendar/1', 'blabla', 'Summary', DAV\Sharing\Plugin::INVITE_DECLINED); + + $this->assertEquals( + $inviteReply, + $result['value'] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeNoHostUrl() { + + $xml = << + + /principal/1 + + blabla + Summary + +XML; + + $this->parse($xml); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/ShareTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/ShareTest.php new file mode 100644 index 00000000000..73a2c3a13d2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CalDAV/Xml/Request/ShareTest.php @@ -0,0 +1,83 @@ + 'Sabre\\CalDAV\\Xml\\Request\\Share', + ]; + + function testDeserialize() { + + $xml = << + + + mailto:eric@example.com + Eric York + Shared workspace + + + + mailto:foo@bar + + +XML; + + $result = $this->parse($xml); + $share = new Share([ + new Sharee([ + 'href' => 'mailto:eric@example.com', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE, + 'properties' => [ + '{DAV:}displayname' => 'Eric York', + ], + 'comment' => 'Shared workspace', + ]), + new Sharee([ + 'href' => 'mailto:foo@bar', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS, + ]), + ]); + + $this->assertEquals( + $share, + $result['value'] + ); + + } + + function testDeserializeMinimal() { + + $xml = << + + + mailto:eric@example.com + + + +XML; + + $result = $this->parse($xml); + $share = new Share([ + new Sharee([ + 'href' => 'mailto:eric@example.com', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + ]), + ]); + + $this->assertEquals( + $share, + $result['value'] + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php new file mode 100644 index 00000000000..552e2ba77e5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AbstractPluginTest.php @@ -0,0 +1,43 @@ +backend = new Backend\Mock(); + $principalBackend = new DAVACL\PrincipalBackend\Mock(); + + $tree = [ + new AddressBookRoot($principalBackend, $this->backend), + new DAVACL\PrincipalCollection($principalBackend) + ]; + + $this->plugin = new Plugin(); + $this->plugin->directories = ['directory']; + $this->server = new DAV\Server($tree); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->addPlugin($this->plugin); + $this->server->debugExceptions = true; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookHomeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookHomeTest.php new file mode 100644 index 00000000000..871f4a457c1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookHomeTest.php @@ -0,0 +1,159 @@ +backend = new Backend\Mock(); + $this->s = new AddressBookHome( + $this->backend, + 'principals/user1' + ); + + } + + function testGetName() { + + $this->assertEquals('user1', $this->s->getName()); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testSetName() { + + $this->s->setName('user2'); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testDelete() { + + $this->s->delete(); + + } + + function testGetLastModified() { + + $this->assertNull($this->s->getLastModified()); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testCreateFile() { + + $this->s->createFile('bla'); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testCreateDirectory() { + + $this->s->createDirectory('bla'); + + } + + function testGetChild() { + + $child = $this->s->getChild('book1'); + $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $child); + $this->assertEquals('book1', $child->getName()); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + */ + function testGetChild404() { + + $this->s->getChild('book2'); + + } + + function testGetChildren() { + + $children = $this->s->getChildren(); + $this->assertEquals(2, count($children)); + $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $children[0]); + $this->assertEquals('book1', $children[0]->getName()); + + } + + function testCreateExtendedCollection() { + + $resourceType = [ + '{' . Plugin::NS_CARDDAV . '}addressbook', + '{DAV:}collection', + ]; + $this->s->createExtendedCollection('book2', new MkCol($resourceType, ['{DAV:}displayname' => 'a-book 2'])); + + $this->assertEquals([ + 'id' => 'book2', + 'uri' => 'book2', + '{DAV:}displayname' => 'a-book 2', + 'principaluri' => 'principals/user1', + ], $this->backend->addressBooks[2]); + + } + + /** + * @expectedException Sabre\DAV\Exception\InvalidResourceType + */ + function testCreateExtendedCollectionInvalid() { + + $resourceType = [ + '{DAV:}collection', + ]; + $this->s->createExtendedCollection('book2', new MkCol($resourceType, ['{DAV:}displayname' => 'a-book 2'])); + + } + + + function testACLMethods() { + + $this->assertEquals('principals/user1', $this->s->getOwner()); + $this->assertNull($this->s->getGroup()); + $this->assertEquals([ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + ], $this->s->getACL()); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $this->s->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->s->getSupportedPrivilegeSet() + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php new file mode 100644 index 00000000000..f8da38a16dc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookQueryTest.php @@ -0,0 +1,355 @@ + '1'] + ); + + $request->setBody( +' + + + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + ], + ], + '/addressbooks/user1/book1/card2' => [ + 404 => [ + '{DAV:}getetag' => null, + ], + ] + ], $result); + + + } + + function testQueryDepth0() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book1/card1', + ['Depth' => '0'] + ); + + $request->setBody( +' + + + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + ], + ], + ], $result); + + + } + + function testQueryNoMatch() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book1', + ['Depth' => '1'] + ); + + $request->setBody( +' + + + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([], $result); + + } + + function testQueryLimit() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + 'HTTP_DEPTH' => '1', + ]); + + $request->setBody( +' + + + + + + + + 1 +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + ], + ], + ], $result); + + + } + + function testJson() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book1/card1', + ['Depth' => '0'] + ); + + $request->setBody( +' + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $vobjVersion = \Sabre\VObject\Version::VERSION; + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => '["vcard",[["version",{},"text","4.0"],["prodid",{},"text","-\/\/Sabre\/\/Sabre VObject ' . $vobjVersion . '\/\/EN"],["uid",{},"text","12345"]]]', + ], + ], + ], $result); + + } + + function testVCard4() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book1/card1', + ['Depth' => '0'] + ); + + $request->setBody( +' + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $vobjVersion = \Sabre\VObject\Version::VERSION; + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject $vobjVersion//EN\r\nUID:12345\r\nEND:VCARD\r\n", + ], + ], + ], $result); + + } + + function testAddressBookDepth0() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book1', + ['Depth' => '0'] + ); + + $request->setBody( + ' + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(415, $response->status, 'Incorrect status code. Full response body:' . $response->body); + } + + function testAddressBookProperties() { + + $request = new HTTP\Request( + 'REPORT', + '/addressbooks/user1/book3', + ['Depth' => '1'] + ); + + $request->setBody( + ' + + + + + + + + +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([ + '/addressbooks/user1/book3/card3' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nFN:Test-Card\nEMAIL;TYPE=home:bar@example.org\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:3.0\r\nUID:12345\r\nFN:Test-Card\r\nEND:VCARD\r\n", + ], + ], + ], $result); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php new file mode 100644 index 00000000000..fc20480f2d4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookRootTest.php @@ -0,0 +1,31 @@ +assertEquals('addressbooks', $root->getName()); + + } + + function testGetChildForPrincipal() { + + $pBackend = new DAVACL\PrincipalBackend\Mock(); + $cBackend = new Backend\Mock(); + $root = new AddressBookRoot($pBackend, $cBackend); + + $children = $root->getChildren(); + $this->assertEquals(3, count($children)); + + $this->assertInstanceOf('Sabre\\CardDAV\\AddressBookHome', $children[0]); + $this->assertEquals('user1', $children[0]->getName()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php new file mode 100644 index 00000000000..1f0064dd38a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/AddressBookTest.php @@ -0,0 +1,194 @@ +backend = new Backend\Mock(); + $this->ab = new AddressBook( + $this->backend, + [ + 'uri' => 'book1', + 'id' => 'foo', + '{DAV:}displayname' => 'd-name', + 'principaluri' => 'principals/user1', + ] + ); + + } + + function testGetName() { + + $this->assertEquals('book1', $this->ab->getName()); + + } + + function testGetChild() { + + $card = $this->ab->getChild('card1'); + $this->assertInstanceOf('Sabre\\CardDAV\\Card', $card); + $this->assertEquals('card1', $card->getName()); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + */ + function testGetChildNotFound() { + + $card = $this->ab->getChild('card3'); + + } + + function testGetChildren() { + + $cards = $this->ab->getChildren(); + $this->assertEquals(2, count($cards)); + + $this->assertEquals('card1', $cards[0]->getName()); + $this->assertEquals('card2', $cards[1]->getName()); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testCreateDirectory() { + + $this->ab->createDirectory('name'); + + } + + function testCreateFile() { + + $file = fopen('php://memory', 'r+'); + fwrite($file, 'foo'); + rewind($file); + $this->ab->createFile('card2', $file); + + $this->assertEquals('foo', $this->backend->cards['foo']['card2']); + + } + + function testDelete() { + + $this->ab->delete(); + $this->assertEquals(1, count($this->backend->addressBooks)); + + } + + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testSetName() { + + $this->ab->setName('foo'); + + } + + function testGetLastModified() { + + $this->assertNull($this->ab->getLastModified()); + + } + + function testUpdateProperties() { + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'barrr', + ]); + $this->ab->propPatch($propPatch); + $this->assertTrue($propPatch->commit()); + + $this->assertEquals('barrr', $this->backend->addressBooks[0]['{DAV:}displayname']); + + } + + function testGetProperties() { + + $props = $this->ab->getProperties(['{DAV:}displayname']); + $this->assertEquals([ + '{DAV:}displayname' => 'd-name', + ], $props); + + } + + function testACLMethods() { + + $this->assertEquals('principals/user1', $this->ab->getOwner()); + $this->assertNull($this->ab->getGroup()); + $this->assertEquals([ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + ], $this->ab->getACL()); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $this->ab->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->ab->getSupportedPrivilegeSet() + ); + + } + + function testGetSyncTokenNoSyncSupport() { + + $this->assertNull($this->ab->getSyncToken()); + + } + function testGetChangesNoSyncSupport() { + + $this->assertNull($this->ab->getChanges(1, null)); + + } + + function testGetSyncToken() { + + $this->driver = 'sqlite'; + $this->dropTables(['addressbooks', 'cards', 'addressbookchanges']); + $this->createSchema('addressbooks'); + $backend = new Backend\PDO( + $this->getPDO() + ); + $ab = new AddressBook($backend, ['id' => 1, '{DAV:}sync-token' => 2]); + $this->assertEquals(2, $ab->getSyncToken()); + } + + function testGetSyncToken2() { + + $this->driver = 'sqlite'; + $this->dropTables(['addressbooks', 'cards', 'addressbookchanges']); + $this->createSchema('addressbooks'); + $backend = new Backend\PDO( + $this->getPDO() + ); + $ab = new AddressBook($backend, ['id' => 1, '{http://sabredav.org/ns}sync-token' => 2]); + $this->assertEquals(2, $ab->getSyncToken()); + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php new file mode 100644 index 00000000000..f62bfb1ae68 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php @@ -0,0 +1,373 @@ +dropTables([ + 'addressbooks', + 'cards', + 'addressbookchanges', + ]); + $this->createSchema('addressbooks'); + $pdo = $this->getPDO(); + + $this->backend = new PDO($pdo); + $pdo->exec("INSERT INTO addressbooks (principaluri, displayname, uri, description, synctoken) VALUES ('principals/user1', 'book1', 'book1', 'addressbook 1', 1)"); + $pdo->exec("INSERT INTO cards (addressbookid, carddata, uri, lastmodified, etag, size) VALUES (1, 'card1', 'card1', 0, '" . md5('card1') . "', 5)"); + + } + + function testGetAddressBooksForUser() { + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = [ + [ + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{http://sabredav.org/ns}sync-token' => 1 + ] + ]; + + $this->assertEquals($expected, $result); + + } + + function testUpdateAddressBookInvalidProp() { + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'updated', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', + '{DAV:}foo' => 'bar', + ]); + + $this->backend->updateAddressBook(1, $propPatch); + $result = $propPatch->commit(); + + $this->assertFalse($result); + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = [ + [ + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{http://sabredav.org/ns}sync-token' => 1 + ] + ]; + + $this->assertEquals($expected, $result); + + } + + function testUpdateAddressBookNoProps() { + + $propPatch = new PropPatch([ + ]); + + $this->backend->updateAddressBook(1, $propPatch); + $result = $propPatch->commit(); + $this->assertTrue($result); + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = [ + [ + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{http://sabredav.org/ns}sync-token' => 1 + ] + ]; + + $this->assertEquals($expected, $result); + + + } + + function testUpdateAddressBookSuccess() { + + $propPatch = new PropPatch([ + '{DAV:}displayname' => 'updated', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', + ]); + + $this->backend->updateAddressBook(1, $propPatch); + $result = $propPatch->commit(); + + $this->assertTrue($result); + + $result = $this->backend->getAddressBooksForUser('principals/user1'); + + $expected = [ + [ + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'updated', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', + '{http://calendarserver.org/ns/}getctag' => 2, + '{http://sabredav.org/ns}sync-token' => 2 + ] + ]; + + $this->assertEquals($expected, $result); + + + } + + function testDeleteAddressBook() { + + $this->backend->deleteAddressBook(1); + + $this->assertEquals([], $this->backend->getAddressBooksForUser('principals/user1')); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testCreateAddressBookUnsupportedProp() { + + $this->backend->createAddressBook('principals/user1', 'book2', [ + '{DAV:}foo' => 'bar', + ]); + + } + + function testCreateAddressBookSuccess() { + + $this->backend->createAddressBook('principals/user1', 'book2', [ + '{DAV:}displayname' => 'book2', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2', + ]); + + $expected = [ + [ + 'id' => 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book1', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', + '{http://calendarserver.org/ns/}getctag' => 1, + '{http://sabredav.org/ns}sync-token' => 1, + ], + [ + 'id' => 2, + 'uri' => 'book2', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'book2', + '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2', + '{http://calendarserver.org/ns/}getctag' => 1, + '{http://sabredav.org/ns}sync-token' => 1, + ] + ]; + $result = $this->backend->getAddressBooksForUser('principals/user1'); + $this->assertEquals($expected, $result); + + } + + function testGetCards() { + + $result = $this->backend->getCards(1); + + $expected = [ + [ + 'id' => 1, + 'uri' => 'card1', + 'lastmodified' => 0, + 'etag' => '"' . md5('card1') . '"', + 'size' => 5 + ] + ]; + + $this->assertEquals($expected, $result); + + } + + function testGetCard() { + + $result = $this->backend->getCard(1, 'card1'); + + $expected = [ + 'id' => 1, + 'uri' => 'card1', + 'carddata' => 'card1', + 'lastmodified' => 0, + 'etag' => '"' . md5('card1') . '"', + 'size' => 5 + ]; + + if (is_resource($result['carddata'])) { + $result['carddata'] = stream_get_contents($result['carddata']); + } + + $this->assertEquals($expected, $result); + + } + + /** + * @depends testGetCard + */ + function testCreateCard() { + + $result = $this->backend->createCard(1, 'card2', 'data2'); + $this->assertEquals('"' . md5('data2') . '"', $result); + $result = $this->backend->getCard(1, 'card2'); + $this->assertEquals(2, $result['id']); + $this->assertEquals('card2', $result['uri']); + if (is_resource($result['carddata'])) { + $result['carddata'] = stream_get_contents($result['carddata']); + } + $this->assertEquals('data2', $result['carddata']); + + } + + /** + * @depends testCreateCard + */ + function testGetMultiple() { + + $result = $this->backend->createCard(1, 'card2', 'data2'); + $result = $this->backend->createCard(1, 'card3', 'data3'); + $check = [ + [ + 'id' => 1, + 'uri' => 'card1', + 'carddata' => 'card1', + 'lastmodified' => 0, + ], + [ + 'id' => 2, + 'uri' => 'card2', + 'carddata' => 'data2', + 'lastmodified' => time(), + ], + [ + 'id' => 3, + 'uri' => 'card3', + 'carddata' => 'data3', + 'lastmodified' => time(), + ], + ]; + + $result = $this->backend->getMultipleCards(1, ['card1', 'card2', 'card3']); + + foreach ($check as $index => $node) { + + foreach ($node as $k => $v) { + + $expected = $v; + $actual = $result[$index][$k]; + + switch ($k) { + case 'lastmodified' : + $this->assertInternalType('int', $actual); + break; + case 'carddata' : + if (is_resource($actual)) { + $actual = stream_get_contents($actual); + } + // No break intended. + default : + $this->assertEquals($expected, $actual); + break; + } + + } + + } + + + } + + /** + * @depends testGetCard + */ + function testUpdateCard() { + + $result = $this->backend->updateCard(1, 'card1', 'newdata'); + $this->assertEquals('"' . md5('newdata') . '"', $result); + + $result = $this->backend->getCard(1, 'card1'); + $this->assertEquals(1, $result['id']); + if (is_resource($result['carddata'])) { + $result['carddata'] = stream_get_contents($result['carddata']); + } + $this->assertEquals('newdata', $result['carddata']); + + } + + /** + * @depends testGetCard + */ + function testDeleteCard() { + + $this->backend->deleteCard(1, 'card1'); + $result = $this->backend->getCard(1, 'card1'); + $this->assertFalse($result); + + } + + function testGetChanges() { + + $backend = $this->backend; + $id = $backend->createAddressBook( + 'principals/user1', + 'bla', + [] + ); + $result = $backend->getChangesForAddressBook($id, null, 1); + + $this->assertEquals([ + 'syncToken' => 1, + "added" => [], + 'modified' => [], + 'deleted' => [], + ], $result); + + $currentToken = $result['syncToken']; + + $dummyCard = "BEGIN:VCARD\r\nEND:VCARD\r\n"; + + $backend->createCard($id, "card1.ics", $dummyCard); + $backend->createCard($id, "card2.ics", $dummyCard); + $backend->createCard($id, "card3.ics", $dummyCard); + $backend->updateCard($id, "card1.ics", $dummyCard); + $backend->deleteCard($id, "card2.ics"); + + $result = $backend->getChangesForAddressBook($id, $currentToken, 1); + + $this->assertEquals([ + 'syncToken' => 6, + 'modified' => ["card1.ics"], + 'deleted' => ["card2.ics"], + "added" => ["card3.ics"], + ], $result); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php new file mode 100644 index 00000000000..8638dc74a65 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/Mock.php @@ -0,0 +1,258 @@ +addressBooks = $addressBooks; + $this->cards = $cards; + + if (is_null($this->addressBooks)) { + $this->addressBooks = [ + [ + 'id' => 'foo', + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'd-name', + ], + [ + 'id' => 'bar', + 'uri' => 'book3', + 'principaluri' => 'principals/user1', + '{DAV:}displayname' => 'd-name', + ], + ]; + + $card2 = fopen('php://memory', 'r+'); + fwrite($card2, "BEGIN:VCARD\nVERSION:3.0\nUID:45678\nEND:VCARD"); + rewind($card2); + $this->cards = [ + 'foo' => [ + 'card1' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", + 'card2' => $card2, + ], + 'bar' => [ + 'card3' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nFN:Test-Card\nEMAIL;TYPE=home:bar@example.org\nEND:VCARD", + ], + ]; + } + + } + + + function getAddressBooksForUser($principalUri) { + + $books = []; + foreach ($this->addressBooks as $book) { + if ($book['principaluri'] === $principalUri) { + $books[] = $book; + } + } + return $books; + + } + + /** + * Updates properties for an address book. + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param string $addressBookId + * @param \Sabre\DAV\PropPatch $propPatch + * @return void + */ + function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) { + + foreach ($this->addressBooks as &$book) { + if ($book['id'] !== $addressBookId) + continue; + + $propPatch->handleRemaining(function($mutations) use (&$book) { + foreach ($mutations as $key => $value) { + $book[$key] = $value; + } + return true; + }); + + } + + } + + function createAddressBook($principalUri, $url, array $properties) { + + $this->addressBooks[] = array_merge($properties, [ + 'id' => $url, + 'uri' => $url, + 'principaluri' => $principalUri, + ]); + + } + + function deleteAddressBook($addressBookId) { + + foreach ($this->addressBooks as $key => $value) { + if ($value['id'] === $addressBookId) + unset($this->addressBooks[$key]); + } + unset($this->cards[$addressBookId]); + + } + + /** + * Returns all cards for a specific addressbook id. + * + * This method should return the following properties for each card: + * * carddata - raw vcard data + * * uri - Some unique url + * * lastmodified - A unix timestamp + * + * It's recommended to also return the following properties: + * * etag - A unique etag. This must change every time the card changes. + * * size - The size of the card in bytes. + * + * If these last two properties are provided, less time will be spent + * calculating them. If they are specified, you can also ommit carddata. + * This may speed up certain requests, especially with large cards. + * + * @param mixed $addressBookId + * @return array + */ + function getCards($addressBookId) { + + $cards = []; + foreach ($this->cards[$addressBookId] as $uri => $data) { + if (is_resource($data)) { + $cards[] = [ + 'uri' => $uri, + 'carddata' => $data, + ]; + } else { + $cards[] = [ + 'uri' => $uri, + 'carddata' => $data, + 'etag' => '"' . md5($data) . '"', + 'size' => strlen($data) + ]; + } + } + return $cards; + + } + + /** + * Returns a specfic card. + * + * The same set of properties must be returned as with getCards. The only + * exception is that 'carddata' is absolutely required. + * + * If the card does not exist, you must return false. + * + * @param mixed $addressBookId + * @param string $cardUri + * @return array + */ + function getCard($addressBookId, $cardUri) { + + if (!isset($this->cards[$addressBookId][$cardUri])) { + return false; + } + + $data = $this->cards[$addressBookId][$cardUri]; + return [ + 'uri' => $cardUri, + 'carddata' => $data, + 'etag' => '"' . md5($data) . '"', + 'size' => strlen($data) + ]; + + } + + /** + * Creates a new card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressBooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag is for the + * newly created resource, and must be enclosed with double quotes (that + * is, the string itself must contain the double quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null + */ + function createCard($addressBookId, $cardUri, $cardData) { + + if (is_resource($cardData)) { + $cardData = stream_get_contents($cardData); + } + $this->cards[$addressBookId][$cardUri] = $cardData; + return '"' . md5($cardData) . '"'; + + } + + /** + * Updates a card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressBooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag should + * match that of the updated resource, and must be enclosed with double + * quotes (that is: the string itself must contain the actual quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null + */ + function updateCard($addressBookId, $cardUri, $cardData) { + + if (is_resource($cardData)) { + $cardData = stream_get_contents($cardData); + } + $this->cards[$addressBookId][$cardUri] = $cardData; + return '"' . md5($cardData) . '"'; + + } + + function deleteCard($addressBookId, $cardUri) { + + unset($this->cards[$addressBookId][$cardUri]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php new file mode 100644 index 00000000000..c1b0e274ebd --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php @@ -0,0 +1,9 @@ +backend = new Backend\Mock(); + $this->card = new Card( + $this->backend, + [ + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ], + [ + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'carddata' => 'card', + ] + ); + + } + + function testGet() { + + $result = $this->card->get(); + $this->assertEquals('card', $result); + + } + function testGet2() { + + $this->card = new Card( + $this->backend, + [ + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ], + [ + 'uri' => 'card1', + 'addressbookid' => 'foo', + ] + ); + $result = $this->card->get(); + $this->assertEquals("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", $result); + + } + + + /** + * @depends testGet + */ + function testPut() { + + $file = fopen('php://memory', 'r+'); + fwrite($file, 'newdata'); + rewind($file); + $this->card->put($file); + $result = $this->card->get(); + $this->assertEquals('newdata', $result); + + } + + + function testDelete() { + + $this->card->delete(); + $this->assertEquals(1, count($this->backend->cards['foo'])); + + } + + function testGetContentType() { + + $this->assertEquals('text/vcard; charset=utf-8', $this->card->getContentType()); + + } + + function testGetETag() { + + $this->assertEquals('"' . md5('card') . '"', $this->card->getETag()); + + } + + function testGetETag2() { + + $card = new Card( + $this->backend, + [ + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ], + [ + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'carddata' => 'card', + 'etag' => '"blabla"', + ] + ); + $this->assertEquals('"blabla"', $card->getETag()); + + } + + function testGetLastModified() { + + $this->assertEquals(null, $this->card->getLastModified()); + + } + + function testGetSize() { + + $this->assertEquals(4, $this->card->getSize()); + $this->assertEquals(4, $this->card->getSize()); + + } + + function testGetSize2() { + + $card = new Card( + $this->backend, + [ + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ], + [ + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'etag' => '"blabla"', + 'size' => 4, + ] + ); + $this->assertEquals(4, $card->getSize()); + + } + + function testACLMethods() { + + $this->assertEquals('principals/user1', $this->card->getOwner()); + $this->assertNull($this->card->getGroup()); + $this->assertEquals([ + [ + 'privilege' => '{DAV:}all', + 'principal' => 'principals/user1', + 'protected' => true, + ], + ], $this->card->getACL()); + + } + function testOverrideACL() { + + $card = new Card( + $this->backend, + [ + 'uri' => 'book1', + 'id' => 'foo', + 'principaluri' => 'principals/user1', + ], + [ + 'uri' => 'card1', + 'addressbookid' => 'foo', + 'carddata' => 'card', + 'acl' => [ + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ], + ], + ] + ); + $this->assertEquals([ + [ + 'privilege' => '{DAV:}read', + 'principal' => 'principals/user1', + 'protected' => true, + ], + ], $card->getACL()); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testSetACL() { + + $this->card->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->card->getSupportedPrivilegeSet() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/IDirectoryTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/IDirectoryTest.php new file mode 100644 index 00000000000..4796a131f87 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/IDirectoryTest.php @@ -0,0 +1,30 @@ +addPlugin($plugin); + + $props = $server->getProperties('directory', ['{DAV:}resourcetype']); + $this->assertTrue($props['{DAV:}resourcetype']->is('{' . Plugin::NS_CARDDAV . '}directory')); + + } + +} + +class DirectoryMock extends DAV\SimpleCollection implements IDirectory { + + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php new file mode 100644 index 00000000000..2d57c6ae759 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/MultiGetTest.php @@ -0,0 +1,99 @@ + 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + ]); + + $request->setBody( +' + + + + + + /addressbooks/user1/book1/card1 +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", + ] + ] + ], $result); + + } + + function testMultiGetVCard4() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book1', + ]); + + $request->setBody( +' + + + + + + /addressbooks/user1/book1/card1 +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($response->body); + + $prodId = "PRODID:-//Sabre//Sabre VObject " . \Sabre\VObject\Version::VERSION . "//EN"; + + $this->assertEquals([ + '/addressbooks/user1/book1/card1' => [ + 200 => [ + '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:4.0\r\n$prodId\r\nUID:12345\r\nEND:VCARD\r\n", + ] + ] + ], $result); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/PluginTest.php new file mode 100644 index 00000000000..6962e7830c5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/PluginTest.php @@ -0,0 +1,102 @@ +assertEquals('{' . Plugin::NS_CARDDAV . '}addressbook', $this->server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook']); + + $this->assertTrue(in_array('addressbook', $this->plugin->getFeatures())); + $this->assertEquals('carddav', $this->plugin->getPluginInfo()['name']); + + } + + function testSupportedReportSet() { + + $this->assertEquals([ + '{' . Plugin::NS_CARDDAV . '}addressbook-multiget', + '{' . Plugin::NS_CARDDAV . '}addressbook-query', + ], $this->plugin->getSupportedReportSet('addressbooks/user1/book1')); + + } + + function testSupportedReportSetEmpty() { + + $this->assertEquals([ + ], $this->plugin->getSupportedReportSet('')); + + } + + function testAddressBookHomeSet() { + + $result = $this->server->getProperties('principals/user1', ['{' . Plugin::NS_CARDDAV . '}addressbook-home-set']); + + $this->assertEquals(1, count($result)); + $this->assertTrue(isset($result['{' . Plugin::NS_CARDDAV . '}addressbook-home-set'])); + $this->assertEquals('addressbooks/user1/', $result['{' . Plugin::NS_CARDDAV . '}addressbook-home-set']->getHref()); + + } + + function testDirectoryGateway() { + + $result = $this->server->getProperties('principals/user1', ['{' . Plugin::NS_CARDDAV . '}directory-gateway']); + + $this->assertEquals(1, count($result)); + $this->assertTrue(isset($result['{' . Plugin::NS_CARDDAV . '}directory-gateway'])); + $this->assertEquals(['directory'], $result['{' . Plugin::NS_CARDDAV . '}directory-gateway']->getHrefs()); + + } + + function testReportPassThrough() { + + $this->assertNull($this->plugin->report('{DAV:}foo', new \DomDocument(), '')); + + } + + function testHTMLActionsPanel() { + + $output = ''; + $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('addressbooks/user1'), &$output]); + $this->assertFalse($r); + + $this->assertTrue(!!strpos($output, 'Display name')); + + } + + function testAddressbookPluginProperties() { + + $ns = '{' . Plugin::NS_CARDDAV . '}'; + $propFind = new DAV\PropFind('addressbooks/user1/book1', [ + $ns . 'supported-address-data', + $ns . 'supported-collation-set', + ]); + $node = $this->server->tree->getNodeForPath('addressbooks/user1/book1'); + $this->plugin->propFindEarly($propFind, $node); + + $this->assertInstanceOf( + 'Sabre\\CardDAV\\Xml\\Property\\SupportedAddressData', + $propFind->get($ns . 'supported-address-data') + ); + $this->assertInstanceOf( + 'Sabre\\CardDAV\\Xml\\Property\\SupportedCollationSet', + $propFind->get($ns . 'supported-collation-set') + ); + + + } + + function testGetTransform() { + + $request = new \Sabre\HTTP\Request('GET', '/addressbooks/user1/book1/card1', ['Accept: application/vcard+json']); + $response = new \Sabre\HTTP\ResponseMock(); + $this->server->invokeMethod($request, $response); + + $this->assertEquals(200, $response->getStatus()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php new file mode 100644 index 00000000000..d4bc48098dc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/SogoStripContentTypeTest.php @@ -0,0 +1,56 @@ + 1, + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + ], + ]; + protected $carddavCards = [ + 1 => [ + 'card1.vcf' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", + ], + ]; + + function testDontStrip() { + + $result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf', ['{DAV:}getcontenttype']); + $this->assertEquals([ + '{DAV:}getcontenttype' => 'text/vcard; charset=utf-8' + ], $result); + + } + function testStrip() { + + $this->server->httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1', + ]); + $result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf', ['{DAV:}getcontenttype']); + $this->assertEquals([ + '{DAV:}getcontenttype' => 'text/x-vcard' + ], $result); + + } + function testDontTouchOtherMimeTypes() { + + $this->server->httpRequest = new HTTP\Request('GET', '/addressbooks/user1/book1/card1.vcf', [ + 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1', + ]); + + $propFind = new PropFind('hello', ['{DAV:}getcontenttype']); + $propFind->set('{DAV:}getcontenttype', 'text/plain'); + $this->carddavPlugin->propFindLate($propFind, new \Sabre\DAV\SimpleCollection('foo')); + $this->assertEquals('text/plain', $propFind->get('{DAV:}getcontenttype')); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/TestUtil.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/TestUtil.php new file mode 100644 index 00000000000..ec8a3501e81 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/TestUtil.php @@ -0,0 +1,62 @@ +createAddressBook( + 'principals/user1', + 'UUID-123467', + [ + '{DAV:}displayname' => 'user1 addressbook', + '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'AddressBook description', + ] + ); + $backend->createAddressBook( + 'principals/user1', + 'UUID-123468', + [ + '{DAV:}displayname' => 'user1 addressbook2', + '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'AddressBook description', + ] + ); + $backend->createCard($addressbookId, 'UUID-2345', self::getTestCardData()); + return $pdo; + + } + + static function deleteSQLiteDB() { + $sqliteTest = new Backend\PDOSqliteTest(); + $pdo = $sqliteTest->tearDown(); + } + + static function getTestCardData() { + + $addressbookData = 'BEGIN:VCARD +VERSION:3.0 +PRODID:-//Acme Inc.//RoadRunner 1.0//EN +FN:Wile E. Coyote +N:Coyote;Wile;Erroll;; +ORG:Acme Inc. +UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 +REV:2012-06-20T07:00:39+00:00 +END:VCARD'; + + return $addressbookData; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php new file mode 100644 index 00000000000..82d82faddc1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/VCFExportTest.php @@ -0,0 +1,135 @@ + 'book1', + 'uri' => 'book1', + 'principaluri' => 'principals/user1', + ] + ]; + protected $carddavCards = [ + 'book1' => [ + "card1" => "BEGIN:VCARD\r\nFN:Person1\r\nEND:VCARD\r\n", + "card2" => "BEGIN:VCARD\r\nFN:Person2\r\nEND:VCARD", + "card3" => "BEGIN:VCARD\r\nFN:Person3\r\nEND:VCARD\r\n", + "card4" => "BEGIN:VCARD\nFN:Person4\nEND:VCARD\n", + ] + ]; + + function setUp() { + + parent::setUp(); + $plugin = new VCFExportPlugin(); + $this->server->addPlugin( + $plugin + ); + + } + + function testSimple() { + + $plugin = $this->server->getPlugin('vcf-export'); + $this->assertInstanceOf('Sabre\\CardDAV\\VCFExportPlugin', $plugin); + + $this->assertEquals( + 'vcf-export', + $plugin->getPluginInfo()['name'] + ); + + } + + function testExport() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_URI' => '/addressbooks/user1/book1?export', + 'QUERY_STRING' => 'export', + 'REQUEST_METHOD' => 'GET', + ]); + + $response = $this->request($request); + $this->assertEquals(200, $response->status, $response->body); + + $expected = "BEGIN:VCARD +FN:Person1 +END:VCARD +BEGIN:VCARD +FN:Person2 +END:VCARD +BEGIN:VCARD +FN:Person3 +END:VCARD +BEGIN:VCARD +FN:Person4 +END:VCARD +"; + // We actually expected windows line endings + $expected = str_replace("\n", "\r\n", $expected); + + $this->assertEquals($expected, $response->body); + + } + + function testBrowserIntegration() { + + $plugin = $this->server->getPlugin('vcf-export'); + $actions = ''; + $addressbook = new AddressBook($this->carddavBackend, []); + $this->server->emit('browserButtonActions', ['/foo', $addressbook, &$actions]); + $this->assertContains('/foo?export', $actions); + + } + + function testContentDisposition() { + + $request = new HTTP\Request( + 'GET', + '/addressbooks/user1/book1?export' + ); + + $response = $this->request($request, 200); + $this->assertEquals('text/directory', $response->getHeader('Content-Type')); + $this->assertEquals( + 'attachment; filename="book1-' . date('Y-m-d') . '.vcf"', + $response->getHeader('Content-Disposition') + ); + + } + + function testContentDispositionBadChars() { + + $this->carddavBackend->createAddressBook( + 'principals/user1', + 'book-b_ad"(ch)ars', + [] + ); + $this->carddavBackend->createCard( + 'book-b_ad"(ch)ars', + 'card1', + "BEGIN:VCARD\r\nFN:Person1\r\nEND:VCARD\r\n" + ); + + $request = new HTTP\Request( + 'GET', + '/addressbooks/user1/book-b_ad"(ch)ars?export' + ); + + $response = $this->request($request, 200); + $this->assertEquals('text/directory', $response->getHeader('Content-Type')); + $this->assertEquals( + 'attachment; filename="book-b_adchars-' . date('Y-m-d') . '.vcf"', + $response->getHeader('Content-Disposition') + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/ValidateFilterTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/ValidateFilterTest.php new file mode 100644 index 00000000000..03c468f868f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/ValidateFilterTest.php @@ -0,0 +1,209 @@ +assertTrue($this->plugin->validateFilters($input, $filters, $test), $message); + } else { + $this->assertFalse($this->plugin->validateFilters($input, $filters, $test), $message); + } + + } + + function data() { + + $body1 = << 'title', 'is-not-defined' => false, 'param-filters' => [], 'text-matches' => []]; + + // Check if FOO is defined + $filter2 = + ['name' => 'foo', 'is-not-defined' => false, 'param-filters' => [], 'text-matches' => []]; + + // Check if TITLE is not defined + $filter3 = + ['name' => 'title', 'is-not-defined' => true, 'param-filters' => [], 'text-matches' => []]; + + // Check if FOO is not defined + $filter4 = + ['name' => 'foo', 'is-not-defined' => true, 'param-filters' => [], 'text-matches' => []]; + + // Check if TEL[TYPE] is defined + $filter5 = + [ + 'name' => 'tel', + 'is-not-defined' => false, + 'test' => 'anyof', + 'param-filters' => [ + [ + 'name' => 'type', + 'is-not-defined' => false, + 'text-match' => null + ], + ], + 'text-matches' => [], + ]; + + // Check if TEL[FOO] is defined + $filter6 = $filter5; + $filter6['param-filters'][0]['name'] = 'FOO'; + + // Check if TEL[TYPE] is not defined + $filter7 = $filter5; + $filter7['param-filters'][0]['is-not-defined'] = true; + + // Check if TEL[FOO] is not defined + $filter8 = $filter5; + $filter8['param-filters'][0]['name'] = 'FOO'; + $filter8['param-filters'][0]['is-not-defined'] = true; + + // Combining property filters + $filter9 = $filter5; + $filter9['param-filters'][] = $filter6['param-filters'][0]; + + $filter10 = $filter5; + $filter10['param-filters'][] = $filter6['param-filters'][0]; + $filter10['test'] = 'allof'; + + // Check if URL contains 'google' + $filter11 = + [ + 'name' => 'url', + 'is-not-defined' => false, + 'test' => 'anyof', + 'param-filters' => [], + 'text-matches' => [ + [ + 'match-type' => 'contains', + 'value' => 'google', + 'negate-condition' => false, + 'collation' => 'i;octet', + ], + ], + ]; + + // Check if URL contains 'bing' + $filter12 = $filter11; + $filter12['text-matches'][0]['value'] = 'bing'; + + // Check if URL does not contain 'google' + $filter13 = $filter11; + $filter13['text-matches'][0]['negate-condition'] = true; + + // Check if URL does not contain 'bing' + $filter14 = $filter11; + $filter14['text-matches'][0]['value'] = 'bing'; + $filter14['text-matches'][0]['negate-condition'] = true; + + // Param filter with text + $filter15 = $filter5; + $filter15['param-filters'][0]['text-match'] = [ + 'match-type' => 'contains', + 'value' => 'WORK', + 'collation' => 'i;octet', + 'negate-condition' => false, + ]; + $filter16 = $filter15; + $filter16['param-filters'][0]['text-match']['negate-condition'] = true; + + + // Param filter + text filter + $filter17 = $filter5; + $filter17['test'] = 'anyof'; + $filter17['text-matches'][] = [ + 'match-type' => 'contains', + 'value' => '444', + 'collation' => 'i;octet', + 'negate-condition' => false, + ]; + + $filter18 = $filter17; + $filter18['text-matches'][0]['negate-condition'] = true; + + $filter18['test'] = 'allof'; + + return [ + + // Basic filters + [$body1, [$filter1], 'anyof',true], + [$body1, [$filter2], 'anyof',false], + [$body1, [$filter3], 'anyof',false], + [$body1, [$filter4], 'anyof',true], + + // Combinations + [$body1, [$filter1, $filter2], 'anyof',true], + [$body1, [$filter1, $filter2], 'allof',false], + [$body1, [$filter1, $filter4], 'anyof',true], + [$body1, [$filter1, $filter4], 'allof',true], + [$body1, [$filter2, $filter3], 'anyof',false], + [$body1, [$filter2, $filter3], 'allof',false], + + // Basic parameters + [$body1, [$filter5], 'anyof', true, 'TEL;TYPE is defined, so this should return true'], + [$body1, [$filter6], 'anyof', false, 'TEL;FOO is not defined, so this should return false'], + + [$body1, [$filter7], 'anyof', false, 'TEL;TYPE is defined, so this should return false'], + [$body1, [$filter8], 'anyof', true, 'TEL;TYPE is not defined, so this should return true'], + + // Combined parameters + [$body1, [$filter9], 'anyof', true], + [$body1, [$filter10], 'anyof', false], + + // Text-filters + [$body1, [$filter11], 'anyof', true], + [$body1, [$filter12], 'anyof', false], + [$body1, [$filter13], 'anyof', false], + [$body1, [$filter14], 'anyof', true], + + // Param filter with text-match + [$body1, [$filter15], 'anyof', true], + [$body1, [$filter16], 'anyof', false], + + // Param filter + text filter + [$body1, [$filter17], 'anyof', true], + [$body1, [$filter18], 'anyof', false], + [$body1, [$filter18], 'anyof', false], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php new file mode 100644 index 00000000000..acba2cfc8f3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/ValidateVCardTest.php @@ -0,0 +1,305 @@ + 'addressbook1', + 'principaluri' => 'principals/admin', + 'uri' => 'addressbook1', + ] + ]; + + $this->cardBackend = new Backend\Mock($addressbooks, []); + $principalBackend = new DAVACL\PrincipalBackend\Mock(); + + $tree = [ + new AddressBookRoot($principalBackend, $this->cardBackend), + ]; + + $this->server = new DAV\Server($tree); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->debugExceptions = true; + + $plugin = new Plugin(); + $this->server->addPlugin($plugin); + + $response = new HTTP\ResponseMock(); + $this->server->httpResponse = $response; + + } + + function request(HTTP\Request $request, $expectedStatus = null) { + + $this->server->httpRequest = $request; + $this->server->exec(); + + if ($expectedStatus) { + + $realStatus = $this->server->httpResponse->getStatus(); + + $msg = ''; + if ($realStatus !== $expectedStatus) { + $msg = 'Response body: ' . $this->server->httpResponse->getBodyAsString(); + } + $this->assertEquals( + $expectedStatus, + $realStatus, + $msg + ); + } + + return $this->server->httpResponse; + + } + + function testCreateFile() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + ]); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status); + + } + + function testCreateFileValid() { + + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf' + ); + + $vcard = <<setBody($vcard); + + $response = $this->request($request, 201); + + // The custom Ew header should not be set + $this->assertNull( + $response->getHeader('X-Sabre-Ew-Gross') + ); + // Valid, non-auto-fixed responses should contain an ETag. + $this->assertTrue( + $response->getHeader('ETag') !== null, + 'We did not receive an etag' + ); + + + $expected = [ + 'uri' => 'blabla.vcf', + 'carddata' => $vcard, + 'size' => strlen($vcard), + 'etag' => '"' . md5($vcard) . '"', + ]; + + $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1', 'blabla.vcf')); + + } + + /** + * This test creates an intentionally broken vCard that vobject is able + * to automatically repair. + * + * @depends testCreateFileValid + */ + function testCreateVCardAutoFix() { + + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf' + ); + + // The error in this vcard is that there's not enough semi-colons in N + $vcard = <<setBody($vcard); + + $response = $this->request($request, 201); + + // Auto-fixed vcards should NOT return an etag + $this->assertNull( + $response->getHeader('ETag') + ); + + // We should have gotten an Ew header + $this->assertNotNull( + $response->getHeader('X-Sabre-Ew-Gross') + ); + + $expectedVCard = << 'blabla.vcf', + 'carddata' => $expectedVCard, + 'size' => strlen($expectedVCard), + 'etag' => '"' . md5($expectedVCard) . '"', + ]; + + $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1', 'blabla.vcf')); + + } + + /** + * This test creates an intentionally broken vCard that vobject is able + * to automatically repair. + * + * However, we're supplying a heading asking the server to treat the + * request as strict, so the server should still let the request fail. + * + * @depends testCreateFileValid + */ + function testCreateVCardStrictFail() { + + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf', + [ + 'Prefer' => 'handling=strict', + ] + ); + + // The error in this vcard is that there's not enough semi-colons in N + $vcard = <<setBody($vcard); + $this->request($request, 415); + + } + + function testCreateFileNoUID() { + + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf' + ); + $vcard = <<setBody($vcard); + + $response = $this->request($request, 201); + + $foo = $this->cardBackend->getCard('addressbook1', 'blabla.vcf'); + $this->assertTrue( + strpos($foo['carddata'], 'UID') !== false, + print_r($foo, true) + ); + } + + function testCreateFileJson() { + + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf' + ); + $request->setBody('[ "vcard" , [ [ "VERSION", {}, "text", "4.0"], [ "UID" , {}, "text", "foo" ], [ "FN", {}, "text", "FirstName LastName"] ] ]'); + + $response = $this->request($request); + + $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + $foo = $this->cardBackend->getCard('addressbook1', 'blabla.vcf'); + $this->assertEquals("BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nFN:FirstName LastName\r\nEND:VCARD\r\n", $foo['carddata']); + + } + + function testCreateFileVCalendar() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', + ]); + $request->setBody("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"); + + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); + + } + + function testUpdateFile() { + + $this->cardBackend->createCard('addressbook1', 'blabla.vcf', 'foo'); + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf' + ); + + $response = $this->request($request, 415); + + } + + function testUpdateFileParsableBody() { + + $this->cardBackend->createCard('addressbook1', 'blabla.vcf', 'foo'); + $request = new HTTP\Request( + 'PUT', + '/addressbooks/admin/addressbook1/blabla.vcf' + ); + + $body = "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nFN:FirstName LastName\r\nEND:VCARD\r\n"; + $request->setBody($body); + + $response = $this->request($request, 204); + + $expected = [ + 'uri' => 'blabla.vcf', + 'carddata' => $body, + 'size' => strlen($body), + 'etag' => '"' . md5($body) . '"', + ]; + + $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1', 'blabla.vcf')); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Property/SupportedAddressDataTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Property/SupportedAddressDataTest.php new file mode 100644 index 00000000000..43abebdcec1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Property/SupportedAddressDataTest.php @@ -0,0 +1,38 @@ +assertInstanceOf('Sabre\CardDAV\Xml\Property\SupportedAddressData', $property); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new SupportedAddressData(); + + $this->namespaceMap[CardDAV\Plugin::NS_CARDDAV] = 'card'; + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' +' . +'' . +'' . +'' . +' +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Property/SupportedCollationSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Property/SupportedCollationSetTest.php new file mode 100644 index 00000000000..e06aff101d9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Property/SupportedCollationSetTest.php @@ -0,0 +1,38 @@ +assertInstanceOf('Sabre\CardDAV\Xml\Property\SupportedCollationSet', $property); + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $property = new SupportedCollationSet(); + + $this->namespaceMap[CardDAV\Plugin::NS_CARDDAV] = 'card'; + $xml = $this->write(['{DAV:}root' => $property]); + + $this->assertXmlStringEqualsXmlString( +' +' . +'i;ascii-casemap' . +'i;octet' . +'i;unicode-casemap' . +' +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Request/AddressBookMultiGetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Request/AddressBookMultiGetTest.php new file mode 100644 index 00000000000..ea2ab75ceec --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Request/AddressBookMultiGetTest.php @@ -0,0 +1,47 @@ + 'Sabre\\CardDAV\\Xml\\Request\AddressBookMultiGetReport', + ]; + + function testDeserialize() { + + /* lines look a bit odd but this triggers an XML parsing bug */ + $xml = << + + + + + /foo.vcf + +XML; + + $result = $this->parse($xml); + $addressBookMultiGetReport = new AddressBookMultiGetReport(); + $addressBookMultiGetReport->properties = [ + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{urn:ietf:params:xml:ns:carddav}address-data', + ]; + $addressBookMultiGetReport->hrefs = ['/foo.vcf']; + $addressBookMultiGetReport->contentType = 'text/vcard'; + $addressBookMultiGetReport->version = '4.0'; + $addressBookMultiGetReport->addressDataProperties = []; + + + $this->assertEquals( + $addressBookMultiGetReport, + $result['value'] + ); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php new file mode 100644 index 00000000000..3a2e4b46a0e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/CardDAV/Xml/Request/AddressBookQueryReportTest.php @@ -0,0 +1,350 @@ + 'Sabre\\CardDAV\\Xml\\Request\AddressBookQueryReport', + ]; + + function testDeserialize() { + + $xml = << + + + + + + + + +XML; + + $result = $this->parse($xml); + $addressBookQueryReport = new AddressBookQueryReport(); + $addressBookQueryReport->properties = [ + '{DAV:}getetag', + ]; + $addressBookQueryReport->test = 'anyof'; + $addressBookQueryReport->filters = [ + [ + 'name' => 'uid', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => [], + 'text-matches' => [], + ] + ]; + + $this->assertEquals( + $addressBookQueryReport, + $result['value'] + ); + + } + + function testDeserializeAllOf() { + + $xml = << + + + + + + + + +XML; + + $result = $this->parse($xml); + $addressBookQueryReport = new AddressBookQueryReport(); + $addressBookQueryReport->properties = [ + '{DAV:}getetag', + ]; + $addressBookQueryReport->test = 'allof'; + $addressBookQueryReport->filters = [ + [ + 'name' => 'uid', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => [], + 'text-matches' => [], + ] + ]; + + $this->assertEquals( + $addressBookQueryReport, + $result['value'] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeBadTest() { + + $xml = << + + + + + + + + +XML; + + $this->parse($xml); + + } + + /** + * We should error on this, but KDE does this, so we chose to support it. + */ + function testDeserializeNoFilter() { + + $xml = << + + + + + +XML; + + $result = $this->parse($xml); + $addressBookQueryReport = new AddressBookQueryReport(); + $addressBookQueryReport->properties = [ + '{DAV:}getetag', + ]; + $addressBookQueryReport->test = 'anyof'; + $addressBookQueryReport->filters = []; + + $this->assertEquals( + $addressBookQueryReport, + $result['value'] + ); + + } + + function testDeserializeComplex() { + + $xml = << + + + + + + + + + + + + + + + + Hello! + + + + No + + + 10 + +XML; + + $result = $this->parse($xml); + $addressBookQueryReport = new AddressBookQueryReport(); + $addressBookQueryReport->properties = [ + '{DAV:}getetag', + '{urn:ietf:params:xml:ns:carddav}address-data', + ]; + $addressBookQueryReport->test = 'anyof'; + $addressBookQueryReport->filters = [ + [ + 'name' => 'uid', + 'test' => 'anyof', + 'is-not-defined' => true, + 'param-filters' => [], + 'text-matches' => [], + ], + [ + 'name' => 'x-foo', + 'test' => 'allof', + 'is-not-defined' => false, + 'param-filters' => [ + [ + 'name' => 'x-param1', + 'is-not-defined' => false, + 'text-match' => null, + ], + [ + 'name' => 'x-param2', + 'is-not-defined' => true, + 'text-match' => null, + ], + [ + 'name' => 'x-param3', + 'is-not-defined' => false, + 'text-match' => [ + 'negate-condition' => false, + 'value' => 'Hello!', + 'match-type' => 'contains', + 'collation' => 'i;unicode-casemap', + ], + ], + ], + 'text-matches' => [], + ], + [ + 'name' => 'x-prop2', + 'test' => 'anyof', + 'is-not-defined' => false, + 'param-filters' => [], + 'text-matches' => [ + [ + 'negate-condition' => true, + 'value' => 'No', + 'match-type' => 'starts-with', + 'collation' => 'i;unicode-casemap', + ], + ], + ] + ]; + + $addressBookQueryReport->version = '4.0'; + $addressBookQueryReport->contentType = 'application/vcard+json'; + $addressBookQueryReport->limit = 10; + + $this->assertEquals( + $addressBookQueryReport, + $result['value'] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeBadMatchType() { + + $xml = << + + + + + + + + Hello! + + + + +XML; + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeBadMatchType2() { + + $xml = << + + + + + + + No + + + +XML; + $this->parse($xml); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeDoubleFilter() { + + $xml = << + + + + + + + + + +XML; + $this->parse($xml); + + } + + function testDeserializeAddressbookElements() { + + $xml = << + + + + + + + + + + + + + +XML; + + $result = $this->parse($xml); + $addressBookQueryReport = new AddressBookQueryReport(); + $addressBookQueryReport->properties = [ + '{DAV:}getetag', + '{urn:ietf:params:xml:ns:carddav}address-data' + ]; + $addressBookQueryReport->filters = []; + $addressBookQueryReport->test = 'anyof'; + $addressBookQueryReport->contentType = 'text/vcard'; + $addressBookQueryReport->version = '3.0'; + $addressBookQueryReport->addressDataProperties = [ + 'VERSION', + 'UID', + 'NICKNAME', + 'EMAIL', + 'FN', + 'TEL', + ]; + + $this->assertEquals( + $addressBookQueryReport, + $result['value'] + ); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/AbstractServer.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/AbstractServer.php new file mode 100644 index 00000000000..6a8d389a008 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/AbstractServer.php @@ -0,0 +1,64 @@ +response = new HTTP\ResponseMock(); + $this->server = new Server($this->getRootNode()); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->httpResponse = $this->response; + $this->server->debugExceptions = true; + $this->deleteTree(SABRE_TEMPDIR, false); + file_put_contents(SABRE_TEMPDIR . '/test.txt', 'Test contents'); + mkdir(SABRE_TEMPDIR . '/dir'); + file_put_contents(SABRE_TEMPDIR . '/dir/child.txt', 'Child contents'); + + + } + + function tearDown() { + + $this->deleteTree(SABRE_TEMPDIR, false); + + } + + protected function getRootNode() { + + return new FS\Directory(SABRE_TEMPDIR); + + } + + private function deleteTree($path, $deleteRoot = true) { + + foreach (scandir($path) as $node) { + + if ($node == '.' || $node == '.svn' || $node == '..') continue; + $myPath = $path . '/' . $node; + if (is_file($myPath)) { + unlink($myPath); + } else { + $this->deleteTree($myPath); + } + + } + if ($deleteRoot) rmdir($path); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php new file mode 100644 index 00000000000..455403affe7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php @@ -0,0 +1,91 @@ +assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheckUnknownUser() { + + $request = HTTP\Sapi::createFromServerArray([ + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'wrongpassword', + ]); + $response = new HTTP\Response(); + + $backend = new AbstractBasicMock(); + + $this->assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheckSuccess() { + + $request = HTTP\Sapi::createFromServerArray([ + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'password', + ]); + $response = new HTTP\Response(); + + $backend = new AbstractBasicMock(); + $this->assertEquals( + [true, 'principals/username'], + $backend->check($request, $response) + ); + + } + + function testRequireAuth() { + + $request = new HTTP\Request(); + $response = new HTTP\Response(); + + $backend = new AbstractBasicMock(); + $backend->setRealm('writing unittests on a saturday night'); + $backend->challenge($request, $response); + + $this->assertEquals( + 'Basic realm="writing unittests on a saturday night"', + $response->getHeader('WWW-Authenticate') + ); + + } + +} + + +class AbstractBasicMock extends AbstractBasic { + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + function validateUserPass($username, $password) { + + return ($username == 'username' && $password == 'password'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBearerTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBearerTest.php new file mode 100644 index 00000000000..c3857883007 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractBearerTest.php @@ -0,0 +1,90 @@ +assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheckInvalidToken() { + + $request = HTTP\Sapi::createFromServerArray([ + 'HTTP_AUTHORIZATION' => 'Bearer foo', + ]); + $response = new HTTP\Response(); + + $backend = new AbstractBearerMock(); + + $this->assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheckSuccess() { + + $request = HTTP\Sapi::createFromServerArray([ + 'HTTP_AUTHORIZATION' => 'Bearer valid', + ]); + $response = new HTTP\Response(); + + $backend = new AbstractBearerMock(); + $this->assertEquals( + [true, 'principals/username'], + $backend->check($request, $response) + ); + + } + + function testRequireAuth() { + + $request = new HTTP\Request(); + $response = new HTTP\Response(); + + $backend = new AbstractBearerMock(); + $backend->setRealm('writing unittests on a saturday night'); + $backend->challenge($request, $response); + + $this->assertEquals( + 'Bearer realm="writing unittests on a saturday night"', + $response->getHeader('WWW-Authenticate') + ); + + } + +} + + +class AbstractBearerMock extends AbstractBearer { + + /** + * Validates a bearer token + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $bearerToken + * @return bool + */ + function validateBearerToken($bearerToken) { + + return 'valid' === $bearerToken ? 'principals/username' : false; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php new file mode 100644 index 00000000000..14c72aaa0f6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php @@ -0,0 +1,138 @@ +assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheckBadGetUserInfoResponse() { + + $header = 'username=null, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = HTTP\Sapi::createFromServerArray([ + 'PHP_AUTH_DIGEST' => $header, + ]); + $response = new HTTP\Response(); + + $backend = new AbstractDigestMock(); + $this->assertFalse( + $backend->check($request, $response)[0] + ); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testCheckBadGetUserInfoResponse2() { + + $header = 'username=array, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = HTTP\Sapi::createFromServerArray([ + 'PHP_AUTH_DIGEST' => $header, + ]); + + $response = new HTTP\Response(); + + $backend = new AbstractDigestMock(); + $backend->check($request, $response); + + } + + function testCheckUnknownUser() { + + $header = 'username=false, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = HTTP\Sapi::createFromServerArray([ + 'PHP_AUTH_DIGEST' => $header, + ]); + + $response = new HTTP\Response(); + + $backend = new AbstractDigestMock(); + $this->assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheckBadPassword() { + + $header = 'username=user, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; + $request = HTTP\Sapi::createFromServerArray([ + 'PHP_AUTH_DIGEST' => $header, + 'REQUEST_METHOD' => 'PUT', + ]); + + $response = new HTTP\Response(); + + $backend = new AbstractDigestMock(); + $this->assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testCheck() { + + $digestHash = md5('HELLO:12345:1:1:auth:' . md5('GET:/')); + $header = 'username=user, realm=myRealm, nonce=12345, uri=/, response=' . $digestHash . ', opaque=1, qop=auth, nc=1, cnonce=1'; + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'GET', + 'PHP_AUTH_DIGEST' => $header, + 'REQUEST_URI' => '/', + ]); + + $response = new HTTP\Response(); + + $backend = new AbstractDigestMock(); + $this->assertEquals( + [true, 'principals/user'], + $backend->check($request, $response) + ); + + } + + function testRequireAuth() { + + $request = new HTTP\Request(); + $response = new HTTP\Response(); + + $backend = new AbstractDigestMock(); + $backend->setRealm('writing unittests on a saturday night'); + $backend->challenge($request, $response); + + $this->assertStringStartsWith( + 'Digest realm="writing unittests on a saturday night"', + $response->getHeader('WWW-Authenticate') + ); + + } + +} + + +class AbstractDigestMock extends AbstractDigest { + + function getDigestHash($realm, $userName) { + + switch ($userName) { + case 'null' : return null; + case 'false' : return false; + case 'array' : return []; + case 'user' : return 'HELLO'; + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php new file mode 100644 index 00000000000..b14e9fa2ea3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php @@ -0,0 +1,45 @@ +dropTables('users'); + $this->createSchema('users'); + + $this->getPDO()->query( + "INSERT INTO users (username,digesta1) VALUES ('user','hash')" + + ); + + } + + function testConstruct() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + $this->assertTrue($backend instanceof PDO); + + } + + /** + * @depends testConstruct + */ + function testUserInfo() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + + $this->assertNull($backend->getDigestHash('realm', 'blabla')); + + $expected = 'hash'; + + $this->assertEquals($expected, $backend->getDigestHash('realm', 'user')); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php new file mode 100644 index 00000000000..29cbc216282 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/ApacheTest.php @@ -0,0 +1,71 @@ +assertInstanceOf('Sabre\DAV\Auth\Backend\Apache', $backend); + + } + + function testNoHeader() { + + $request = new HTTP\Request(); + $response = new HTTP\Response(); + $backend = new Apache(); + + $this->assertFalse( + $backend->check($request, $response)[0] + ); + + } + + function testRemoteUser() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REMOTE_USER' => 'username', + ]); + $response = new HTTP\Response(); + $backend = new Apache(); + + $this->assertEquals( + [true, 'principals/username'], + $backend->check($request, $response) + ); + + } + + function testRedirectRemoteUser() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REDIRECT_REMOTE_USER' => 'username', + ]); + $response = new HTTP\Response(); + $backend = new Apache(); + + $this->assertEquals( + [true, 'principals/username'], + $backend->check($request, $response) + ); + + } + + function testRequireAuth() { + + $request = new HTTP\Request(); + $response = new HTTP\Response(); + + $backend = new Apache(); + $backend->challenge($request, $response); + + $this->assertNull( + $response->getHeader('WWW-Authenticate') + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/BasicCallBackTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/BasicCallBackTest.php new file mode 100644 index 00000000000..167f31eab37 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/BasicCallBackTest.php @@ -0,0 +1,36 @@ + 'Basic ' . base64_encode('foo:bar'), + ]); + $response = new Response(); + + $this->assertEquals( + [true, 'principals/foo'], + $backend->check($request, $response) + ); + + $this->assertEquals(['foo', 'bar'], $args); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php new file mode 100644 index 00000000000..f694f4806ee --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/FileTest.php @@ -0,0 +1,41 @@ +assertTrue($file instanceof File); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testLoadFileBroken() { + + file_put_contents(SABRE_TEMPDIR . '/backend', 'user:realm:hash'); + $file = new File(SABRE_TEMPDIR . '/backend'); + + } + + function testLoadFile() { + + file_put_contents(SABRE_TEMPDIR . '/backend', 'user:realm:' . md5('user:realm:password')); + $file = new File(); + $file->loadFile(SABRE_TEMPDIR . '/backend'); + + $this->assertFalse($file->getDigestHash('realm', 'blabla')); + $this->assertEquals(md5('user:realm:password'), $file->getDigestHash('realm', 'user')); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php new file mode 100644 index 00000000000..369bc249e47 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/Mock.php @@ -0,0 +1,87 @@ +principal = $principal; + + } + + /** + * When this method is called, the backend must check if authentication was + * successful. + * + * The returned value must be one of the following + * + * [true, "principals/username"] + * [false, "reason for failure"] + * + * If authentication was successful, it's expected that the authentication + * backend returns a so-called principal url. + * + * Examples of a principal url: + * + * principals/admin + * principals/user1 + * principals/users/joe + * principals/uid/123457 + * + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply + * return a string such as: + * + * principals/users/[username] + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array + */ + function check(RequestInterface $request, ResponseInterface $response) { + + if ($this->invalidCheckResponse) { + return 'incorrect!'; + } + if ($this->fail) { + return [false, "fail!"]; + } + return [true, $this->principal]; + + } + + /** + * This method is called when a user could not be authenticated, and + * authentication was required for the current request. + * + * This gives you the oppurtunity to set authentication headers. The 401 + * status code will already be set. + * + * In this case of Basic Auth, this would for example mean that the + * following header needs to be set: + * + * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); + * + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + function challenge(RequestInterface $request, ResponseInterface $response) { + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php new file mode 100644 index 00000000000..18f59793ad7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php @@ -0,0 +1,9 @@ +assertTrue($plugin instanceof Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('auth')); + $this->assertInternalType('array', $plugin->getPluginInfo()); + + } + + /** + * @depends testInit + */ + function testAuthenticate() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + $plugin = new Plugin(new Backend\Mock()); + $fakeServer->addPlugin($plugin); + $this->assertTrue( + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]) + ); + + } + + /** + * @depends testInit + * @expectedException Sabre\DAV\Exception\NotAuthenticated + */ + function testAuthenticateFail() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + $backend = new Backend\Mock(); + $backend->fail = true; + + $plugin = new Plugin($backend); + $fakeServer->addPlugin($plugin); + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); + + } + + /** + * @depends testAuthenticateFail + */ + function testAuthenticateFailDontAutoRequire() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + $backend = new Backend\Mock(); + $backend->fail = true; + + $plugin = new Plugin($backend); + $plugin->autoRequireLogin = false; + $fakeServer->addPlugin($plugin); + $this->assertTrue( + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]) + ); + $this->assertEquals(1, count($plugin->getLoginFailedReasons())); + + } + + /** + * @depends testAuthenticate + */ + function testMultipleBackend() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + $backend1 = new Backend\Mock(); + $backend2 = new Backend\Mock(); + $backend2->fail = true; + + $plugin = new Plugin(); + $plugin->addBackend($backend1); + $plugin->addBackend($backend2); + + $fakeServer->addPlugin($plugin); + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); + + $this->assertEquals('principals/admin', $plugin->getCurrentPrincipal()); + + } + + /** + * @depends testInit + * @expectedException Sabre\DAV\Exception + */ + function testNoAuthBackend() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + + $plugin = new Plugin(); + $fakeServer->addPlugin($plugin); + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); + + } + /** + * @depends testInit + * @expectedException Sabre\DAV\Exception + */ + function testInvalidCheckResponse() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + $backend = new Backend\Mock(); + $backend->invalidCheckResponse = true; + + $plugin = new Plugin($backend); + $fakeServer->addPlugin($plugin); + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); + + } + + /** + * @depends testAuthenticate + */ + function testGetCurrentPrincipal() { + + $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); + $plugin = new Plugin(new Backend\Mock()); + $fakeServer->addPlugin($plugin); + $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); + $this->assertEquals('principals/admin', $plugin->getCurrentPrincipal()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php new file mode 100644 index 00000000000..ec104ec805e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/BasicNodeTest.php @@ -0,0 +1,235 @@ +put('hi'); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testGet() { + + $file = new FileMock(); + $file->get(); + + } + + function testGetSize() { + + $file = new FileMock(); + $this->assertEquals(0, $file->getSize()); + + } + + + function testGetETag() { + + $file = new FileMock(); + $this->assertNull($file->getETag()); + + } + + function testGetContentType() { + + $file = new FileMock(); + $this->assertNull($file->getContentType()); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testDelete() { + + $file = new FileMock(); + $file->delete(); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testSetName() { + + $file = new FileMock(); + $file->setName('hi'); + + } + + function testGetLastModified() { + + $file = new FileMock(); + // checking if lastmod is within the range of a few seconds + $lastMod = $file->getLastModified(); + $compareTime = ($lastMod + 1) - time(); + $this->assertTrue($compareTime < 3); + + } + + function testGetChild() { + + $dir = new DirectoryMock(); + $file = $dir->getChild('mockfile'); + $this->assertTrue($file instanceof FileMock); + + } + + function testChildExists() { + + $dir = new DirectoryMock(); + $this->assertTrue($dir->childExists('mockfile')); + + } + + function testChildExistsFalse() { + + $dir = new DirectoryMock(); + $this->assertFalse($dir->childExists('mockfile2')); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotFound + */ + function testGetChild404() { + + $dir = new DirectoryMock(); + $file = $dir->getChild('blabla'); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testCreateFile() { + + $dir = new DirectoryMock(); + $dir->createFile('hello', 'data'); + + } + + /** + * @expectedException Sabre\DAV\Exception\Forbidden + */ + function testCreateDirectory() { + + $dir = new DirectoryMock(); + $dir->createDirectory('hello'); + + } + + function testSimpleDirectoryConstruct() { + + $dir = new SimpleCollection('simpledir', []); + $this->assertInstanceOf('Sabre\DAV\SimpleCollection', $dir); + + } + + /** + * @depends testSimpleDirectoryConstruct + */ + function testSimpleDirectoryConstructChild() { + + $file = new FileMock(); + $dir = new SimpleCollection('simpledir', [$file]); + $file2 = $dir->getChild('mockfile'); + + $this->assertEquals($file, $file2); + + } + + /** + * @expectedException Sabre\DAV\Exception + * @depends testSimpleDirectoryConstruct + */ + function testSimpleDirectoryBadParam() { + + $dir = new SimpleCollection('simpledir', ['string shouldn\'t be here']); + + } + + /** + * @depends testSimpleDirectoryConstruct + */ + function testSimpleDirectoryAddChild() { + + $file = new FileMock(); + $dir = new SimpleCollection('simpledir'); + $dir->addChild($file); + $file2 = $dir->getChild('mockfile'); + + $this->assertEquals($file, $file2); + + } + + /** + * @depends testSimpleDirectoryConstruct + * @depends testSimpleDirectoryAddChild + */ + function testSimpleDirectoryGetChildren() { + + $file = new FileMock(); + $dir = new SimpleCollection('simpledir'); + $dir->addChild($file); + + $this->assertEquals([$file], $dir->getChildren()); + + } + + /* + * @depends testSimpleDirectoryConstruct + */ + function testSimpleDirectoryGetName() { + + $dir = new SimpleCollection('simpledir'); + $this->assertEquals('simpledir', $dir->getName()); + + } + + /** + * @depends testSimpleDirectoryConstruct + * @expectedException Sabre\DAV\Exception\NotFound + */ + function testSimpleDirectoryGetChild404() { + + $dir = new SimpleCollection('simpledir'); + $dir->getChild('blabla'); + + } +} + +class DirectoryMock extends Collection { + + function getName() { + + return 'mockdir'; + + } + + function getChildren() { + + return [new FileMock()]; + + } + +} + +class FileMock extends File { + + function getName() { + + return 'mockfile'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php new file mode 100644 index 00000000000..54a3053ec9f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/GuessContentTypeTest.php @@ -0,0 +1,70 @@ +server->getPropertiesForPath('/somefile.jpg', $properties); + $this->assertArrayHasKey(0, $result); + $this->assertArrayHasKey(404, $result[0]); + $this->assertArrayHasKey('{DAV:}getcontenttype', $result[0][404]); + + } + + /** + * @depends testGetProperties + */ + function testGetPropertiesPluginEnabled() { + + $this->server->addPlugin(new GuessContentType()); + $properties = [ + '{DAV:}getcontenttype', + ]; + $result = $this->server->getPropertiesForPath('/somefile.jpg', $properties); + $this->assertArrayHasKey(0, $result); + $this->assertArrayHasKey(200, $result[0], 'We received: ' . print_r($result, true)); + $this->assertArrayHasKey('{DAV:}getcontenttype', $result[0][200]); + $this->assertEquals('image/jpeg', $result[0][200]['{DAV:}getcontenttype']); + + } + + /** + * @depends testGetPropertiesPluginEnabled + */ + function testGetPropertiesUnknown() { + + $this->server->addPlugin(new GuessContentType()); + $properties = [ + '{DAV:}getcontenttype', + ]; + $result = $this->server->getPropertiesForPath('/somefile.hoi', $properties); + $this->assertArrayHasKey(0, $result); + $this->assertArrayHasKey(200, $result[0]); + $this->assertArrayHasKey('{DAV:}getcontenttype', $result[0][200]); + $this->assertEquals('application/octet-stream', $result[0][200]['{DAV:}getcontenttype']); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php new file mode 100644 index 00000000000..33c4ede96b4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php @@ -0,0 +1,44 @@ +server->addPlugin(new MapGetToPropFind()); + + } + + function testCollectionGet() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'GET', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody(''); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Incorrect status response received. Full response body: ' . $this->response->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'DAV' => ['1, 3, extended-mkcol'], + 'Vary' => ['Brief,Prefer'], + ], + $this->response->getHeaders() + ); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php new file mode 100644 index 00000000000..f20c50f8637 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/PluginTest.php @@ -0,0 +1,186 @@ +server->addPlugin($this->plugin = new Plugin()); + $this->server->tree->getNodeForPath('')->createDirectory('dir2'); + + } + + function testCollectionGet() { + + $request = new HTTP\Request('GET', '/dir'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(200, $this->response->getStatus(), "Incorrect status received. Full response body: " . $this->response->getBodyAsString()); + $this->assertEquals( + [ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['text/html; charset=utf-8'], + 'Content-Security-Policy' => ["default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"] + ], + $this->response->getHeaders() + ); + + $body = $this->response->getBodyAsString(); + $this->assertTrue(strpos($body, 'dir') !== false, $body); + $this->assertTrue(strpos($body, '<a href="/dir/child.txt">') !== false); + + } + + /** + * Adding the If-None-Match should have 0 effect, but it threw an error. + */ + function testCollectionGetIfNoneMatch() { + + $request = new HTTP\Request('GET', '/dir'); + $request->setHeader('If-None-Match', '"foo-bar"'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(200, $this->response->getStatus(), "Incorrect status received. Full response body: " . $this->response->getBodyAsString()); + $this->assertEquals( + [ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['text/html; charset=utf-8'], + 'Content-Security-Policy' => ["default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"] + ], + $this->response->getHeaders() + ); + + $body = $this->response->getBodyAsString(); + $this->assertTrue(strpos($body, '<title>dir') !== false, $body); + $this->assertTrue(strpos($body, '<a href="/dir/child.txt">') !== false); + + } + function testCollectionGetRoot() { + + $request = new HTTP\Request('GET', '/'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(200, $this->response->status, "Incorrect status received. Full response body: " . $this->response->getBodyAsString()); + $this->assertEquals( + [ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['text/html; charset=utf-8'], + 'Content-Security-Policy' => ["default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"] + ], + $this->response->getHeaders() + ); + + $body = $this->response->getBodyAsString(); + $this->assertTrue(strpos($body, '<title>/') !== false, $body); + $this->assertTrue(strpos($body, '<a href="/dir/">') !== false); + $this->assertTrue(strpos($body, '<span class="btn disabled">') !== false); + + } + + function testGETPassthru() { + + $request = new HTTP\Request('GET', '/random'); + $response = new HTTP\Response(); + $this->assertNull( + $this->plugin->httpGet($request, $response) + ); + + } + + function testPostOtherContentType() { + + $request = new HTTP\Request('POST', '/', ['Content-Type' => 'text/xml']); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(501, $this->response->status); + + } + + function testPostNoSabreAction() { + + $request = new HTTP\Request('POST', '/', ['Content-Type' => 'application/x-www-form-urlencoded']); + $request->setPostData([]); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(501, $this->response->status); + + } + + function testPostMkCol() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'POST', + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', + ]; + $postVars = [ + 'sabreAction' => 'mkcol', + 'name' => 'new_collection', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setPostData($postVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(302, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Location' => ['/'], + ], $this->response->getHeaders()); + + $this->assertTrue(is_dir(SABRE_TEMPDIR . '/new_collection')); + + } + + function testGetAsset() { + + $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=favicon.ico'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(200, $this->response->getStatus(), 'Error: ' . $this->response->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['image/vnd.microsoft.icon'], + 'Content-Length' => ['4286'], + 'Cache-Control' => ['public, max-age=1209600'], + 'Content-Security-Policy' => ["default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"] + ], $this->response->getHeaders()); + + } + + function testGetAsset404() { + + $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=flavicon.ico'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(404, $this->response->getStatus(), 'Error: ' . $this->response->body); + + } + + function testGetAssetEscapeBasePath() { + + $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=./../assets/favicon.ico'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(404, $this->response->getStatus(), 'Error: ' . $this->response->body); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/PropFindAllTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/PropFindAllTest.php new file mode 100644 index 00000000000..08c2080bbab --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Browser/PropFindAllTest.php @@ -0,0 +1,70 @@ +<?php + +namespace Sabre\DAV\Browser; + +class PropFindAllTest extends \PHPUnit_Framework_TestCase { + + function testHandleSimple() { + + $pf = new PropFindAll('foo'); + $pf->handle('{DAV:}displayname', 'foo'); + + $this->assertEquals(200, $pf->getStatus('{DAV:}displayname')); + $this->assertEquals('foo', $pf->get('{DAV:}displayname')); + + + } + + function testHandleCallBack() { + + $pf = new PropFindAll('foo'); + $pf->handle('{DAV:}displayname', function() { return 'foo'; }); + + $this->assertEquals(200, $pf->getStatus('{DAV:}displayname')); + $this->assertEquals('foo', $pf->get('{DAV:}displayname')); + + } + + function testSet() { + + $pf = new PropFindAll('foo'); + $pf->set('{DAV:}displayname', 'foo'); + + $this->assertEquals(200, $pf->getStatus('{DAV:}displayname')); + $this->assertEquals('foo', $pf->get('{DAV:}displayname')); + + } + + function testSetNull() { + + $pf = new PropFindAll('foo'); + $pf->set('{DAV:}displayname', null); + + $this->assertEquals(404, $pf->getStatus('{DAV:}displayname')); + $this->assertEquals(null, $pf->get('{DAV:}displayname')); + + } + + function testGet404Properties() { + + $pf = new PropFindAll('foo'); + $pf->set('{DAV:}displayname', null); + $this->assertEquals( + ['{DAV:}displayname'], + $pf->get404Properties() + ); + + } + + function testGet404PropertiesNothing() { + + $pf = new PropFindAll('foo'); + $pf->set('{DAV:}displayname', 'foo'); + $this->assertEquals( + ['{http://sabredav.org/ns}idk'], + $pf->get404Properties() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ClientMock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ClientMock.php new file mode 100644 index 00000000000..5a48b063ce8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ClientMock.php @@ -0,0 +1,34 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP\RequestInterface; + +class ClientMock extends Client { + + public $request; + public $response; + + public $url; + public $curlSettings; + + /** + * Just making this method public + * + * @param string $url + * @return string + */ + function getAbsoluteUrl($url) { + + return parent::getAbsoluteUrl($url); + + } + + function doRequest(RequestInterface $request) { + + $this->request = $request; + return $this->response; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ClientTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ClientTest.php new file mode 100644 index 00000000000..687f61e2fd2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ClientTest.php @@ -0,0 +1,306 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +require_once 'Sabre/DAV/ClientMock.php'; + +class ClientTest extends \PHPUnit_Framework_TestCase { + + function setUp() { + + if (!function_exists('curl_init')) { + $this->markTestSkipped('CURL must be installed to test the client'); + } + + } + + function testConstruct() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + $this->assertInstanceOf('Sabre\DAV\ClientMock', $client); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testConstructNoBaseUri() { + + $client = new ClientMock([]); + + } + + function testAuth() { + + $client = new ClientMock([ + 'baseUri' => '/', + 'userName' => 'foo', + 'password' => 'bar', + ]); + + $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); + $this->assertEquals(CURLAUTH_BASIC | CURLAUTH_DIGEST, $client->curlSettings[CURLOPT_HTTPAUTH]); + + } + + function testBasicAuth() { + + $client = new ClientMock([ + 'baseUri' => '/', + 'userName' => 'foo', + 'password' => 'bar', + 'authType' => Client::AUTH_BASIC + ]); + + $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); + $this->assertEquals(CURLAUTH_BASIC, $client->curlSettings[CURLOPT_HTTPAUTH]); + + } + + function testDigestAuth() { + + $client = new ClientMock([ + 'baseUri' => '/', + 'userName' => 'foo', + 'password' => 'bar', + 'authType' => Client::AUTH_DIGEST + ]); + + $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); + $this->assertEquals(CURLAUTH_DIGEST, $client->curlSettings[CURLOPT_HTTPAUTH]); + + } + + function testNTLMAuth() { + + $client = new ClientMock([ + 'baseUri' => '/', + 'userName' => 'foo', + 'password' => 'bar', + 'authType' => Client::AUTH_NTLM + ]); + + $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); + $this->assertEquals(CURLAUTH_NTLM, $client->curlSettings[CURLOPT_HTTPAUTH]); + + } + + function testProxy() { + + $client = new ClientMock([ + 'baseUri' => '/', + 'proxy' => 'localhost:8888', + ]); + + $this->assertEquals("localhost:8888", $client->curlSettings[CURLOPT_PROXY]); + + } + + function testEncoding() { + + $client = new ClientMock([ + 'baseUri' => '/', + 'encoding' => Client::ENCODING_IDENTITY | Client::ENCODING_GZIP | Client::ENCODING_DEFLATE, + ]); + + $this->assertEquals("identity,deflate,gzip", $client->curlSettings[CURLOPT_ENCODING]); + + } + + function testPropFind() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = <<<XML +<?xml version="1.0"?> +<multistatus xmlns="DAV:"> + <response> + <href>/foo</href> + <propstat> + <prop> + <displayname>bar</displayname> + </prop> + <status>HTTP/1.1 200 OK</status> + </propstat> + </response> +</multistatus> +XML; + + $client->response = new Response(207, [], $responseBody); + $result = $client->propFind('foo', ['{DAV:}displayname', '{urn:zim}gir']); + + $this->assertEquals(['{DAV:}displayname' => 'bar'], $result); + + $request = $client->request; + $this->assertEquals('PROPFIND', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'Depth' => ['0'], + 'Content-Type' => ['application/xml'], + ], $request->getHeaders()); + + } + + /** + * @expectedException \Sabre\HTTP\ClientHttpException + */ + function testPropFindError() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $client->response = new Response(405, []); + $client->propFind('foo', ['{DAV:}displayname', '{urn:zim}gir']); + + } + + function testPropFindDepth1() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = <<<XML +<?xml version="1.0"?> +<multistatus xmlns="DAV:"> + <response> + <href>/foo</href> + <propstat> + <prop> + <displayname>bar</displayname> + </prop> + <status>HTTP/1.1 200 OK</status> + </propstat> + </response> +</multistatus> +XML; + + $client->response = new Response(207, [], $responseBody); + $result = $client->propFind('foo', ['{DAV:}displayname', '{urn:zim}gir'], 1); + + $this->assertEquals([ + '/foo' => [ + '{DAV:}displayname' => 'bar' + ], + ], $result); + + $request = $client->request; + $this->assertEquals('PROPFIND', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'Depth' => ['1'], + 'Content-Type' => ['application/xml'], + ], $request->getHeaders()); + + } + + function testPropPatch() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = <<<XML +<?xml version="1.0"?> +<multistatus xmlns="DAV:"> + <response> + <href>/foo</href> + <propstat> + <prop> + <displayname>bar</displayname> + </prop> + <status>HTTP/1.1 200 OK</status> + </propstat> + </response> +</multistatus> +XML; + + $client->response = new Response(207, [], $responseBody); + $result = $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null]); + $this->assertTrue($result); + $request = $client->request; + $this->assertEquals('PROPPATCH', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'Content-Type' => ['application/xml'], + ], $request->getHeaders()); + + } + + /** + * @depends testPropPatch + * @expectedException \Sabre\HTTP\ClientHttpException + */ + function testPropPatchHTTPError() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $client->response = new Response(403, [], ''); + $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null]); + + } + + /** + * @depends testPropPatch + * @expectedException Sabre\HTTP\ClientException + */ + function testPropPatchMultiStatusError() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $responseBody = <<<XML +<?xml version="1.0"?> +<multistatus xmlns="DAV:"> +<response> + <href>/foo</href> + <propstat> + <prop> + <displayname /> + </prop> + <status>HTTP/1.1 403 Forbidden</status> + </propstat> +</response> +</multistatus> +XML; + + $client->response = new Response(207, [], $responseBody); + $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null]); + + } + + function testOPTIONS() { + + $client = new ClientMock([ + 'baseUri' => '/', + ]); + + $client->response = new Response(207, [ + 'DAV' => 'calendar-access, extended-mkcol', + ]); + $result = $client->options(); + + $this->assertEquals( + ['calendar-access', 'extended-mkcol'], + $result + ); + + $request = $client->request; + $this->assertEquals('OPTIONS', $request->getMethod()); + $this->assertEquals('/', $request->getUrl()); + $this->assertEquals([ + ], $request->getHeaders()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/CorePluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/CorePluginTest.php new file mode 100644 index 00000000000..5c6f07ae80a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/CorePluginTest.php @@ -0,0 +1,14 @@ +<?php + +namespace Sabre\DAV; + +class CorePluginTest extends \PHPUnit_Framework_TestCase { + + function testGetInfo() { + + $corePlugin = new CorePlugin(); + $this->assertEquals('core', $corePlugin->getPluginInfo()['name']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/DbTestHelperTrait.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/DbTestHelperTrait.php new file mode 100644 index 00000000000..63e35fb88ee --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/DbTestHelperTrait.php @@ -0,0 +1,143 @@ +<?php + +namespace Sabre\DAV; + +use PDO; +use PDOException; + +class DbCache { + + static $cache = []; + +} + +trait DbTestHelperTrait { + + /** + * Should be "mysql", "pgsql", "sqlite". + */ + public $driver = null; + + /** + * Returns a fully configured PDO object. + * + * @return PDO + */ + function getDb() { + + if (!$this->driver) { + throw new \Exception('You must set the $driver public property'); + } + + if (array_key_exists($this->driver, DbCache::$cache)) { + $pdo = DbCache::$cache[$this->driver]; + if ($pdo === null) { + $this->markTestSkipped($this->driver . ' was not enabled, not correctly configured or of the wrong version'); + } + return $pdo; + } + + try { + + switch ($this->driver) { + + case 'mysql' : + $pdo = new PDO(SABRE_MYSQLDSN, SABRE_MYSQLUSER, SABRE_MYSQLPASS); + break; + case 'sqlite' : + $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/testdb'); + break; + case 'pgsql' : + $pdo = new \PDO(SABRE_PGSQLDSN); + $version = $pdo->query('SELECT VERSION()')->fetchColumn(); + preg_match('|([0-9\.]){5,}|', $version, $matches); + $version = $matches[0]; + if (version_compare($version, '9.5.0', '<')) { + DbCache::$cache[$this->driver] = null; + $this->markTestSkipped('We require at least Postgres 9.5. This server is running ' . $version); + } + break; + + + + } + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + } catch (PDOException $e) { + + $this->markTestSkipped($this->driver . ' was not enabled or not correctly configured. Error message: ' . $e->getMessage()); + + } + + DbCache::$cache[$this->driver] = $pdo; + return $pdo; + + } + + /** + * Alias for getDb + * + * @return PDO + */ + function getPDO() { + + return $this->getDb(); + + } + + /** + * Uses .sql files from the examples directory to initialize the database. + * + * @param string $schemaName + * @return void + */ + function createSchema($schemaName) { + + $db = $this->getDb(); + + $queries = file_get_contents( + __DIR__ . '/../../../examples/sql/' . $this->driver . '.' . $schemaName . '.sql' + ); + + foreach (explode(';', $queries) as $query) { + + if (trim($query) === '') { + continue; + } + + $db->exec($query); + + } + + } + + /** + * Drops tables, if they exist + * + * @param string|string[] $tableNames + * @return void + */ + function dropTables($tableNames) { + + $tableNames = (array)$tableNames; + $db = $this->getDb(); + foreach ($tableNames as $tableName) { + $db->exec('DROP TABLE IF EXISTS ' . $tableName); + } + + + } + + function tearDown() { + + switch ($this->driver) { + + case 'sqlite' : + // Recreating sqlite, just in case + unset(DbCache::$cache[$this->driver]); + unlink(SABRE_TEMPDIR . '/testdb'); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/LockedTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/LockedTest.php new file mode 100644 index 00000000000..174a561b537 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/LockedTest.php @@ -0,0 +1,67 @@ +<?php + +namespace Sabre\DAV\Exception; + +use DOMDocument; +use Sabre\DAV; + +class LockedTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $dom = new DOMDocument('1.0'); + $dom->formatOutput = true; + $root = $dom->createElement('d:root'); + + $dom->appendChild($root); + $root->setAttribute('xmlns:d', 'DAV:'); + + $lockInfo = new DAV\Locks\LockInfo(); + $lockInfo->uri = '/foo'; + $locked = new Locked($lockInfo); + + $locked->serialize(new DAV\Server(), $root); + + $output = $dom->saveXML(); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:lock-token-submitted xmlns:d="DAV:"> + <d:href>/foo</d:href> + </d:lock-token-submitted> +</d:root> +'; + + $this->assertEquals($expected, $output); + + } + + function testSerializeAmpersand() { + + $dom = new DOMDocument('1.0'); + $dom->formatOutput = true; + $root = $dom->createElement('d:root'); + + $dom->appendChild($root); + $root->setAttribute('xmlns:d', 'DAV:'); + + $lockInfo = new DAV\Locks\LockInfo(); + $lockInfo->uri = '/foo&bar'; + $locked = new Locked($lockInfo); + + $locked->serialize(new DAV\Server(), $root); + + $output = $dom->saveXML(); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:lock-token-submitted xmlns:d="DAV:"> + <d:href>/foo&bar</d:href> + </d:lock-token-submitted> +</d:root> +'; + + $this->assertEquals($expected, $output); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/PaymentRequiredTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/PaymentRequiredTest.php new file mode 100644 index 00000000000..7142937b4c0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/PaymentRequiredTest.php @@ -0,0 +1,14 @@ +<?php + +namespace Sabre\DAV\Exception; + +class PaymentRequiredTest extends \PHPUnit_Framework_TestCase { + + function testGetHTTPCode() { + + $ex = new PaymentRequired(); + $this->assertEquals(402, $ex->getHTTPCode()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/ServiceUnavailableTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/ServiceUnavailableTest.php new file mode 100644 index 00000000000..1cc691e6d38 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/ServiceUnavailableTest.php @@ -0,0 +1,14 @@ +<?php + +namespace Sabre\DAV\Exception; + +class ServiceUnavailableTest extends \PHPUnit_Framework_TestCase { + + function testGetHTTPCode() { + + $ex = new ServiceUnavailable(); + $this->assertEquals(503, $ex->getHTTPCode()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/TooManyMatchesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/TooManyMatchesTest.php new file mode 100644 index 00000000000..0f58e8726be --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Exception/TooManyMatchesTest.php @@ -0,0 +1,35 @@ +<?php + +namespace Sabre\DAV\Exception; + +use DOMDocument; +use Sabre\DAV; + +class TooManyMatchesTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $dom = new DOMDocument('1.0'); + $dom->formatOutput = true; + $root = $dom->createElement('d:root'); + + $dom->appendChild($root); + $root->setAttribute('xmlns:d', 'DAV:'); + + $locked = new TooManyMatches(); + + $locked->serialize(new DAV\Server(), $root); + + $output = $dom->saveXML(); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:number-of-matches-within-limits xmlns:d="DAV:"/> +</d:root> +'; + + $this->assertEquals($expected, $output); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ExceptionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ExceptionTest.php new file mode 100644 index 00000000000..0eb4f3dd878 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ExceptionTest.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\DAV; + +class ExceptionTest extends \PHPUnit_Framework_TestCase { + + function testStatus() { + + $e = new Exception(); + $this->assertEquals(500, $e->getHTTPCode()); + + } + + function testExceptionStatuses() { + + $c = [ + 'Sabre\\DAV\\Exception\\NotAuthenticated' => 401, + 'Sabre\\DAV\\Exception\\InsufficientStorage' => 507, + ]; + + foreach ($c as $class => $status) { + + $obj = new $class(); + $this->assertEquals($status, $obj->getHTTPCode()); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/DirectoryTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/DirectoryTest.php new file mode 100644 index 00000000000..097ebd26b92 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/DirectoryTest.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\DAV\FSExt; + +class DirectoryTest extends \PHPUnit_Framework_TestCase { + + function create() { + + return new Directory(SABRE_TEMPDIR); + + } + + function testCreate() { + + $dir = $this->create(); + $this->assertEquals(basename(SABRE_TEMPDIR), $dir->getName()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testChildExistDot() { + + $dir = $this->create(); + $dir->childExists('..'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php new file mode 100644 index 00000000000..f5d65a44f96 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/FileTest.php @@ -0,0 +1,110 @@ +<?php + +namespace Sabre\DAV\FSExt; + +require_once 'Sabre/TestUtil.php'; + +class FileTest extends \PHPUnit_Framework_TestCase { + + function setUp() { + + file_put_contents(SABRE_TEMPDIR . '/file.txt', 'Contents'); + + } + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + function testPut() { + + $filename = SABRE_TEMPDIR . '/file.txt'; + $file = new File($filename); + $result = $file->put('New contents'); + + $this->assertEquals('New contents', file_get_contents(SABRE_TEMPDIR . '/file.txt')); + $this->assertEquals( + '"' . + sha1( + fileinode($filename) . + filesize($filename) . + filemtime($filename) + ) . '"', + $result + ); + + } + + function testRange() { + + $file = new File(SABRE_TEMPDIR . '/file.txt'); + $file->put('0000000'); + $file->patch('111', 2, 3); + + $this->assertEquals('0001110', file_get_contents(SABRE_TEMPDIR . '/file.txt')); + + } + + function testRangeStream() { + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, "222"); + rewind($stream); + + $file = new File(SABRE_TEMPDIR . '/file.txt'); + $file->put('0000000'); + $file->patch($stream, 2, 3); + + $this->assertEquals('0002220', file_get_contents(SABRE_TEMPDIR . '/file.txt')); + + } + + + function testGet() { + + $file = new File(SABRE_TEMPDIR . '/file.txt'); + $this->assertEquals('Contents', stream_get_contents($file->get())); + + } + + function testDelete() { + + $file = new File(SABRE_TEMPDIR . '/file.txt'); + $file->delete(); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/file.txt')); + + } + + function testGetETag() { + + $filename = SABRE_TEMPDIR . '/file.txt'; + $file = new File($filename); + $this->assertEquals( + '"' . + sha1( + fileinode($filename) . + filesize($filename) . + filemtime($filename) + ) . '"', + $file->getETag() + ); + } + + function testGetContentType() { + + $file = new File(SABRE_TEMPDIR . '/file.txt'); + $this->assertNull($file->getContentType()); + + } + + function testGetSize() { + + $file = new File(SABRE_TEMPDIR . '/file.txt'); + $this->assertEquals(8, $file->getSize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php new file mode 100644 index 00000000000..20fca490a35 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/FSExt/ServerTest.php @@ -0,0 +1,246 @@ +<?php + +namespace Sabre\DAV\FSExt; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/DAV/AbstractServer.php'; + +class ServerTest extends DAV\AbstractServer{ + + protected function getRootNode() { + + return new Directory($this->tempDir); + + } + + function testGet() { + + $request = new HTTP\Request('GET', '/test.txt'); + $filename = $this->tempDir . '/test.txt'; + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(200, $this->response->getStatus(), 'Invalid status code received.'); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [13], + 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($filename)))], + 'ETag' => ['"' . sha1(fileinode($filename) . filesize($filename) . filemtime($filename)) . '"'], + ], + $this->response->getHeaders() + ); + + + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + function testHEAD() { + + $request = new HTTP\Request('HEAD', '/test.txt'); + $filename = $this->tempDir . '/test.txt'; + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [13], + 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], + 'ETag' => ['"' . sha1(fileinode($filename) . filesize($filename) . filemtime($filename)) . '"'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals('', $this->response->body); + + } + + function testPut() { + + $request = new HTTP\Request('PUT', '/testput.txt'); + $filename = $this->tempDir . '/testput.txt'; + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . sha1(fileinode($filename) . filesize($filename) . filemtime($filename)) . '"'], + ], $this->response->getHeaders()); + + $this->assertEquals(201, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('Testing new file', file_get_contents($filename)); + + } + + function testPutAlreadyExists() { + + $request = new HTTP\Request('PUT', '/test.txt', ['If-None-Match' => '*']); + $request->setBody('Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(412, $this->response->status); + $this->assertNotEquals('Testing new file', file_get_contents($this->tempDir . '/test.txt')); + + } + + function testMkcol() { + + $request = new HTTP\Request('MKCOL', '/testcol'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + ], $this->response->getHeaders()); + + $this->assertEquals(201, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertTrue(is_dir($this->tempDir . '/testcol')); + + } + + function testPutUpdate() { + + $request = new HTTP\Request('PUT', '/test.txt'); + $request->setBody('Testing updated file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('0', $this->response->getHeader('Content-Length')); + + $this->assertEquals(204, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('Testing updated file', file_get_contents($this->tempDir . '/test.txt')); + + } + + function testDelete() { + + $request = new HTTP\Request('DELETE', '/test.txt'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + ], $this->response->getHeaders()); + + $this->assertEquals(204, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertFalse(file_exists($this->tempDir . '/test.txt')); + + } + + function testDeleteDirectory() { + + mkdir($this->tempDir . '/testcol'); + file_put_contents($this->tempDir . '/testcol/test.txt', 'Hi! I\'m a file with a short lifespan'); + + $request = new HTTP\Request('DELETE', '/testcol'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + ], $this->response->getHeaders()); + $this->assertEquals(204, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertFalse(file_exists($this->tempDir . '/testcol')); + + } + + function testOptions() { + + $request = new HTTP\Request('OPTIONS', '/'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'DAV' => ['1, 3, extended-mkcol'], + 'MS-Author-Via' => ['DAV'], + 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'], + 'Accept-Ranges' => ['bytes'], + 'Content-Length' => ['0'], + 'X-Sabre-Version' => [DAV\Version::VERSION], + ], $this->response->getHeaders()); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals('', $this->response->body); + + } + + function testMove() { + + mkdir($this->tempDir . '/testcol'); + + $request = new HTTP\Request('MOVE', '/test.txt', ['Destination' => '/testcol/test2.txt']); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(201, $this->response->status); + $this->assertEquals('', $this->response->body); + + $this->assertEquals([ + 'Content-Length' => ['0'], + 'X-Sabre-Version' => [DAV\Version::VERSION], + ], $this->response->getHeaders()); + + $this->assertTrue( + is_file($this->tempDir . '/testcol/test2.txt') + ); + + + } + + /** + * This test checks if it's possible to move a non-FSExt collection into a + * FSExt collection. + * + * The moveInto function *should* ignore the object and let sabredav itself + * execute the slow move. + */ + function testMoveOtherObject() { + + mkdir($this->tempDir . '/tree1'); + mkdir($this->tempDir . '/tree2'); + + $tree = new DAV\Tree(new DAV\SimpleCollection('root', [ + new DAV\FS\Directory($this->tempDir . '/tree1'), + new DAV\FSExt\Directory($this->tempDir . '/tree2'), + ])); + $this->server->tree = $tree; + + $request = new HTTP\Request('MOVE', '/tree1', ['Destination' => '/tree2/tree1']); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(201, $this->response->status); + $this->assertEquals('', $this->response->body); + + $this->assertEquals([ + 'Content-Length' => ['0'], + 'X-Sabre-Version' => [DAV\Version::VERSION], + ], $this->response->getHeaders()); + + $this->assertTrue( + is_dir($this->tempDir . '/tree2/tree1') + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/GetIfConditionsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/GetIfConditionsTest.php new file mode 100644 index 00000000000..d41abc00a2b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/GetIfConditionsTest.php @@ -0,0 +1,337 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; +require_once 'Sabre/DAV/AbstractServer.php'; + +class GetIfConditionsTest extends AbstractServer { + + function testNoConditions() { + + $request = new HTTP\Request(); + + $conditions = $this->server->getIfConditions($request); + $this->assertEquals([], $conditions); + + } + + function testLockToken() { + + $request = new HTTP\Request('GET', '/path/', ['If' => '(<opaquelocktoken:token1>)']); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'path', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token1', + 'etag' => '', + ], + ], + + ], + + ]; + + $this->assertEquals($compare, $conditions); + + } + + function testNotLockToken() { + + $serverVars = [ + 'HTTP_IF' => '(Not <opaquelocktoken:token1>)', + 'REQUEST_URI' => '/bla' + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'bla', + 'tokens' => [ + [ + 'negate' => true, + 'token' => 'opaquelocktoken:token1', + 'etag' => '', + ], + ], + + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function testLockTokenUrl() { + + $serverVars = [ + 'HTTP_IF' => '<http://www.example.com/> (<opaquelocktoken:token1>)', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => '', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token1', + 'etag' => '', + ], + ], + + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function test2LockTokens() { + + $serverVars = [ + 'HTTP_IF' => '(<opaquelocktoken:token1>) (Not <opaquelocktoken:token2>)', + 'REQUEST_URI' => '/bla', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'bla', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token1', + 'etag' => '', + ], + [ + 'negate' => true, + 'token' => 'opaquelocktoken:token2', + 'etag' => '', + ], + ], + + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function test2UriLockTokens() { + + $serverVars = [ + 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1>) <http://www.example.org/node2> (Not <opaquelocktoken:token2>)', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'node1', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token1', + 'etag' => '', + ], + ], + ], + [ + 'uri' => 'node2', + 'tokens' => [ + [ + 'negate' => true, + 'token' => 'opaquelocktoken:token2', + 'etag' => '', + ], + ], + + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function test2UriMultiLockTokens() { + + $serverVars = [ + 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1>) (<opaquelocktoken:token2>) <http://www.example.org/node2> (Not <opaquelocktoken:token3>)', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'node1', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token1', + 'etag' => '', + ], + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token2', + 'etag' => '', + ], + ], + ], + [ + 'uri' => 'node2', + 'tokens' => [ + [ + 'negate' => true, + 'token' => 'opaquelocktoken:token3', + 'etag' => '', + ], + ], + + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function testEtag() { + + $serverVars = [ + 'HTTP_IF' => '(["etag1"])', + 'REQUEST_URI' => '/foo', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'foo', + 'tokens' => [ + [ + 'negate' => false, + 'token' => '', + 'etag' => '"etag1"', + ], + ], + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function test2Etags() { + + $serverVars = [ + 'HTTP_IF' => '<http://www.example.org/> (["etag1"]) (["etag2"])', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => '', + 'tokens' => [ + [ + 'negate' => false, + 'token' => '', + 'etag' => '"etag1"', + ], + [ + 'negate' => false, + 'token' => '', + 'etag' => '"etag2"', + ], + ], + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + + function testComplexIf() { + + $serverVars = [ + 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1> ["etag1"]) ' . + '(Not <opaquelocktoken:token2>) (["etag2"]) <http://www.example.org/node2> ' . + '(<opaquelocktoken:token3>) (Not <opaquelocktoken:token4>) (["etag3"])', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $conditions = $this->server->getIfConditions($request); + + $compare = [ + + [ + 'uri' => 'node1', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token1', + 'etag' => '"etag1"', + ], + [ + 'negate' => true, + 'token' => 'opaquelocktoken:token2', + 'etag' => '', + ], + [ + 'negate' => false, + 'token' => '', + 'etag' => '"etag2"', + ], + ], + ], + [ + 'uri' => 'node2', + 'tokens' => [ + [ + 'negate' => false, + 'token' => 'opaquelocktoken:token3', + 'etag' => '', + ], + [ + 'negate' => true, + 'token' => 'opaquelocktoken:token4', + 'etag' => '', + ], + [ + 'negate' => false, + 'token' => '', + 'etag' => '"etag3"', + ], + ], + ], + + ]; + $this->assertEquals($compare, $conditions); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php new file mode 100644 index 00000000000..cd8bee9686d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HTTPPreferParsingTest.php @@ -0,0 +1,188 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +class HTTPPreferParsingTest extends \Sabre\DAVServerTest { + + function testParseSimple() { + + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_PREFER' => 'return-asynch', + ]); + + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals([ + 'respond-async' => true, + 'return' => null, + 'handling' => null, + 'wait' => null, + ], $server->getHTTPPrefer()); + + } + + function testParseValue() { + + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_PREFER' => 'wait=10', + ]); + + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals([ + 'respond-async' => false, + 'return' => null, + 'handling' => null, + 'wait' => '10', + ], $server->getHTTPPrefer()); + + } + + function testParseMultiple() { + + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_PREFER' => 'return-minimal, strict,lenient', + ]); + + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals([ + 'respond-async' => false, + 'return' => 'minimal', + 'handling' => 'lenient', + 'wait' => null, + ], $server->getHTTPPrefer()); + + } + + function testParseWeirdValue() { + + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_PREFER' => 'BOOOH', + ]); + + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals([ + 'respond-async' => false, + 'return' => null, + 'handling' => null, + 'wait' => null, + 'boooh' => true, + ], $server->getHTTPPrefer()); + + } + + function testBrief() { + + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_BRIEF' => 't', + ]); + + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals([ + 'respond-async' => false, + 'return' => 'minimal', + 'handling' => null, + 'wait' => null, + ], $server->getHTTPPrefer()); + + } + + /** + * propfindMinimal + * + * @return void + */ + function testpropfindMinimal() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PROPFIND', + 'REQUEST_URI' => '/', + 'HTTP_PREFER' => 'return-minimal', + ]); + $request->setBody(<<<BLA +<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:something /> + <d:resourcetype /> + </d:prop> +</d:propfind> +BLA + ); + + $response = $this->request($request); + + $body = $response->getBodyAsString(); + + $this->assertEquals(207, $response->getStatus(), $body); + + $this->assertTrue(strpos($body, 'resourcetype') !== false, $body); + $this->assertTrue(strpos($body, 'something') === false, $body); + + } + + function testproppatchMinimal() { + + $request = new HTTP\Request('PROPPATCH', '/', ['Prefer' => 'return-minimal']); + $request->setBody(<<<BLA +<?xml version="1.0"?> +<d:propertyupdate xmlns:d="DAV:"> + <d:set> + <d:prop> + <d:something>nope!</d:something> + </d:prop> + </d:set> +</d:propertyupdate> +BLA + ); + + $this->server->on('propPatch', function($path, PropPatch $propPatch) { + + $propPatch->handle('{DAV:}something', function($props) { + return true; + }); + + }); + + $response = $this->request($request); + + $this->assertEquals(0, strlen($response->body), 'Expected empty body: ' . $response->body); + $this->assertEquals(204, $response->status); + + } + + function testproppatchMinimalError() { + + $request = new HTTP\Request('PROPPATCH', '/', ['Prefer' => 'return-minimal']); + $request->setBody(<<<BLA +<?xml version="1.0"?> +<d:propertyupdate xmlns:d="DAV:"> + <d:set> + <d:prop> + <d:something>nope!</d:something> + </d:prop> + </d:set> +</d:propertyupdate> +BLA + ); + + $response = $this->request($request); + + $body = $response->getBodyAsString(); + + $this->assertEquals(207, $response->status); + $this->assertTrue(strpos($body, 'something') !== false); + $this->assertTrue(strpos($body, '403 Forbidden') !== false, $body); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpCopyTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpCopyTest.php new file mode 100644 index 00000000000..b5e64369ef9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpCopyTest.php @@ -0,0 +1,199 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\DAVServerTest; +use Sabre\HTTP; + +/** + * Tests related to the COPY request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class HttpCopyTest extends DAVServerTest { + + /** + * Sets up the DAV tree. + * + * @return void + */ + function setUpTree() { + + $this->tree = new Mock\Collection('root', [ + 'file1' => 'content1', + 'file2' => 'content2', + 'coll1' => [ + 'file3' => 'content3', + 'file4' => 'content4', + ] + ]); + + } + + function testCopyFile() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file5' + ]); + $response = $this->request($request); + $this->assertEquals(201, $response->getStatus()); + $this->assertEquals('content1', $this->tree->getChild('file5')->get()); + + } + + function testCopyFileToSelf() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file1' + ]); + $response = $this->request($request); + $this->assertEquals(403, $response->getStatus()); + + } + + function testCopyFileToExisting() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file2' + ]); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus()); + $this->assertEquals('content1', $this->tree->getChild('file2')->get()); + + } + + function testCopyFileToExistingOverwriteT() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file2', + 'Overwrite' => 'T', + ]); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus()); + $this->assertEquals('content1', $this->tree->getChild('file2')->get()); + + } + + function testCopyFileToExistingOverwriteBadValue() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file2', + 'Overwrite' => 'B', + ]); + $response = $this->request($request); + $this->assertEquals(400, $response->getStatus()); + + } + + function testCopyFileNonExistantParent() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/notfound/file2', + ]); + $response = $this->request($request); + $this->assertEquals(409, $response->getStatus()); + + } + + function testCopyFileToExistingOverwriteF() { + + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file2', + 'Overwrite' => 'F', + ]); + $response = $this->request($request); + $this->assertEquals(412, $response->getStatus()); + $this->assertEquals('content2', $this->tree->getChild('file2')->get()); + + } + + function testCopyFileToExistinBlockedCreateDestination() { + + $this->server->on('beforeBind', function($path) { + + if ($path === 'file2') { + return false; + } + + }); + $request = new HTTP\Request('COPY', '/file1', [ + 'Destination' => '/file2', + 'Overwrite' => 'T', + ]); + $response = $this->request($request); + + // This checks if the destination file is intact. + $this->assertEquals('content2', $this->tree->getChild('file2')->get()); + + } + + function testCopyColl() { + + $request = new HTTP\Request('COPY', '/coll1', [ + 'Destination' => '/coll2' + ]); + $response = $this->request($request); + $this->assertEquals(201, $response->getStatus()); + $this->assertEquals('content3', $this->tree->getChild('coll2')->getChild('file3')->get()); + + } + + function testCopyCollToSelf() { + + $request = new HTTP\Request('COPY', '/coll1', [ + 'Destination' => '/coll1' + ]); + $response = $this->request($request); + $this->assertEquals(403, $response->getStatus()); + + } + + function testCopyCollToExisting() { + + $request = new HTTP\Request('COPY', '/coll1', [ + 'Destination' => '/file2' + ]); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus()); + $this->assertEquals('content3', $this->tree->getChild('file2')->getChild('file3')->get()); + + } + + function testCopyCollToExistingOverwriteT() { + + $request = new HTTP\Request('COPY', '/coll1', [ + 'Destination' => '/file2', + 'Overwrite' => 'T', + ]); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus()); + $this->assertEquals('content3', $this->tree->getChild('file2')->getChild('file3')->get()); + + } + + function testCopyCollToExistingOverwriteF() { + + $request = new HTTP\Request('COPY', '/coll1', [ + 'Destination' => '/file2', + 'Overwrite' => 'F', + ]); + $response = $this->request($request); + $this->assertEquals(412, $response->getStatus()); + $this->assertEquals('content2', $this->tree->getChild('file2')->get()); + + } + + function testCopyCollIntoSubtree() { + + $request = new HTTP\Request('COPY', '/coll1', [ + 'Destination' => '/coll1/subcol', + ]); + $response = $this->request($request); + $this->assertEquals(409, $response->getStatus()); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php new file mode 100644 index 00000000000..bd1b3315057 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpDeleteTest.php @@ -0,0 +1,137 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\DAVServerTest; +use Sabre\HTTP; + +/** + * Tests related to the PUT request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class HttpDeleteTest extends DAVServerTest { + + /** + * Sets up the DAV tree. + * + * @return void + */ + function setUpTree() { + + $this->tree = new Mock\Collection('root', [ + 'file1' => 'foo', + 'dir' => [ + 'subfile' => 'bar', + 'subfile2' => 'baz', + ], + ]); + + } + + /** + * A successful DELETE + */ + function testDelete() { + + $request = new HTTP\Request('DELETE', '/file1'); + + $response = $this->request($request); + + $this->assertEquals( + 204, + $response->getStatus(), + "Incorrect status code. Response body: " . $response->getBodyAsString() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + ], + $response->getHeaders() + ); + + } + + /** + * Deleting a Directory + */ + function testDeleteDirectory() { + + $request = new HTTP\Request('DELETE', '/dir'); + + $response = $this->request($request); + + $this->assertEquals( + 204, + $response->getStatus(), + "Incorrect status code. Response body: " . $response->getBodyAsString() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + ], + $response->getHeaders() + ); + + } + + /** + * DELETE on a node that does not exist + */ + function testDeleteNotFound() { + + $request = new HTTP\Request('DELETE', '/file2'); + $response = $this->request($request); + + $this->assertEquals( + 404, + $response->getStatus(), + "Incorrect status code. Response body: " . $response->getBodyAsString() + ); + + } + + /** + * DELETE with preconditions + */ + function testDeletePreconditions() { + + $request = new HTTP\Request('DELETE', '/file1', [ + 'If-Match' => '"' . md5('foo') . '"', + ]); + + $response = $this->request($request); + + $this->assertEquals( + 204, + $response->getStatus(), + "Incorrect status code. Response body: " . $response->getBodyAsString() + ); + + } + + /** + * DELETE with incorrect preconditions + */ + function testDeletePreconditionsFailed() { + + $request = new HTTP\Request('DELETE', '/file1', [ + 'If-Match' => '"' . md5('bar') . '"', + ]); + + $response = $this->request($request); + + $this->assertEquals( + 412, + $response->getStatus(), + "Incorrect status code. Response body: " . $response->getBodyAsString() + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpGetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpGetTest.php new file mode 100644 index 00000000000..1eefba70657 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpGetTest.php @@ -0,0 +1,158 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\DAVServerTest; +use Sabre\HTTP; + +/** + * Tests related to the GET request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class HttpGetTest extends DAVServerTest { + + /** + * Sets up the DAV tree. + * + * @return void + */ + function setUpTree() { + + $this->tree = new Mock\Collection('root', [ + 'file1' => 'foo', + new Mock\Collection('dir', []), + new Mock\StreamingFile('streaming', 'stream') + ]); + + } + + function testGet() { + + $request = new HTTP\Request('GET', '/file1'); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + // Removing Last-Modified because it keeps changing. + $response->removeHeader('Last-Modified'); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [3], + 'ETag' => ['"' . md5('foo') . '"'], + ], + $response->getHeaders() + ); + + $this->assertEquals('foo', $response->getBodyAsString()); + + } + + function testGetHttp10() { + + $request = new HTTP\Request('GET', '/file1'); + $request->setHttpVersion('1.0'); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + // Removing Last-Modified because it keeps changing. + $response->removeHeader('Last-Modified'); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [3], + 'ETag' => ['"' . md5('foo') . '"'], + ], + $response->getHeaders() + ); + + $this->assertEquals('1.0', $response->getHttpVersion()); + + $this->assertEquals('foo', $response->getBodyAsString()); + + } + + function testGet404() { + + $request = new HTTP\Request('GET', '/notfound'); + $response = $this->request($request); + + $this->assertEquals(404, $response->getStatus()); + + } + + function testGet404_aswell() { + + $request = new HTTP\Request('GET', '/file1/subfile'); + $response = $this->request($request); + + $this->assertEquals(404, $response->getStatus()); + + } + + /** + * We automatically normalize double slashes. + */ + function testGetDoubleSlash() { + + $request = new HTTP\Request('GET', '//file1'); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + // Removing Last-Modified because it keeps changing. + $response->removeHeader('Last-Modified'); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [3], + 'ETag' => ['"' . md5('foo') . '"'], + ], + $response->getHeaders() + ); + + $this->assertEquals('foo', $response->getBodyAsString()); + + } + + function testGetCollection() { + + $request = new HTTP\Request('GET', '/dir'); + $response = $this->request($request); + + $this->assertEquals(501, $response->getStatus()); + + } + + function testGetStreaming() { + + $request = new HTTP\Request('GET', '/streaming'); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + // Removing Last-Modified because it keeps changing. + $response->removeHeader('Last-Modified'); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + ], + $response->getHeaders() + ); + + $this->assertEquals('stream', $response->getBodyAsString()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpHeadTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpHeadTest.php new file mode 100644 index 00000000000..2cd1c5ea3c1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpHeadTest.php @@ -0,0 +1,97 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\DAVServerTest; +use Sabre\HTTP; + +/** + * Tests related to the HEAD request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class HttpHeadTest extends DAVServerTest { + + /** + * Sets up the DAV tree. + * + * @return void + */ + function setUpTree() { + + $this->tree = new Mock\Collection('root', [ + 'file1' => 'foo', + new Mock\Collection('dir', []), + new Mock\StreamingFile('streaming', 'stream') + ]); + + } + + function testHEAD() { + + $request = new HTTP\Request('HEAD', '//file1'); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + // Removing Last-Modified because it keeps changing. + $response->removeHeader('Last-Modified'); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [3], + 'ETag' => ['"' . md5('foo') . '"'], + ], + $response->getHeaders() + ); + + $this->assertEquals('', $response->getBodyAsString()); + + } + + /** + * According to the specs, HEAD should behave identical to GET. But, broken + * clients needs HEAD requests on collections to respond with a 200, so + * that's what we do. + */ + function testHEADCollection() { + + $request = new HTTP\Request('HEAD', '/dir'); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + } + + /** + * HEAD automatically internally maps to GET via a sub-request. + * The Auth plugin must not be triggered twice for these, so we'll + * test for that. + */ + function testDoubleAuth() { + + $count = 0; + + $authBackend = new Auth\Backend\BasicCallBack(function($userName, $password) use (&$count) { + $count++; + return true; + }); + $this->server->addPlugin( + new Auth\Plugin( + $authBackend + ) + ); + $request = new HTTP\Request('HEAD', '/file1', ['Authorization' => 'Basic ' . base64_encode('user:pass')]); + $response = $this->request($request); + + $this->assertEquals(200, $response->getStatus()); + + $this->assertEquals(1, $count, 'Auth was triggered twice :('); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpMoveTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpMoveTest.php new file mode 100644 index 00000000000..52f7c674ee9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpMoveTest.php @@ -0,0 +1,119 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\DAVServerTest; +use Sabre\HTTP; + +/** + * Tests related to the MOVE request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class HttpMoveTest extends DAVServerTest { + + /** + * Sets up the DAV tree. + * + * @return void + */ + function setUpTree() { + + $this->tree = new Mock\Collection('root', [ + 'file1' => 'content1', + 'file2' => 'content2', + ]); + + } + + function testMoveToSelf() { + + $request = new HTTP\Request('MOVE', '/file1', [ + 'Destination' => '/file1' + ]); + $response = $this->request($request); + $this->assertEquals(403, $response->getStatus()); + $this->assertEquals('content1', $this->tree->getChild('file1')->get()); + + } + + function testMove() { + + $request = new HTTP\Request('MOVE', '/file1', [ + 'Destination' => '/file3' + ]); + $response = $this->request($request); + $this->assertEquals(201, $response->getStatus(), print_r($response, true)); + $this->assertEquals('content1', $this->tree->getChild('file3')->get()); + $this->assertFalse($this->tree->childExists('file1')); + + } + + function testMoveToExisting() { + + $request = new HTTP\Request('MOVE', '/file1', [ + 'Destination' => '/file2' + ]); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus(), print_r($response, true)); + $this->assertEquals('content1', $this->tree->getChild('file2')->get()); + $this->assertFalse($this->tree->childExists('file1')); + + } + + function testMoveToExistingOverwriteT() { + + $request = new HTTP\Request('MOVE', '/file1', [ + 'Destination' => '/file2', + 'Overwrite' => 'T', + ]); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus(), print_r($response, true)); + $this->assertEquals('content1', $this->tree->getChild('file2')->get()); + $this->assertFalse($this->tree->childExists('file1')); + + } + + function testMoveToExistingOverwriteF() { + + $request = new HTTP\Request('MOVE', '/file1', [ + 'Destination' => '/file2', + 'Overwrite' => 'F', + ]); + $response = $this->request($request); + $this->assertEquals(412, $response->getStatus(), print_r($response, true)); + $this->assertEquals('content1', $this->tree->getChild('file1')->get()); + $this->assertEquals('content2', $this->tree->getChild('file2')->get()); + $this->assertTrue($this->tree->childExists('file1')); + $this->assertTrue($this->tree->childExists('file2')); + + } + + /** + * If we MOVE to an existing file, but a plugin prevents the original from + * being deleted, we need to make sure that the server does not delete + * the destination. + */ + function testMoveToExistingBlockedDeleteSource() { + + $this->server->on('beforeUnbind', function($path) { + + if ($path === 'file1') { + throw new \Sabre\DAV\Exception\Forbidden('uh oh'); + } + + }); + $request = new HTTP\Request('MOVE', '/file1', [ + 'Destination' => '/file2' + ]); + $response = $this->request($request); + $this->assertEquals(403, $response->getStatus(), print_r($response, true)); + $this->assertEquals('content1', $this->tree->getChild('file1')->get()); + $this->assertEquals('content2', $this->tree->getChild('file2')->get()); + $this->assertTrue($this->tree->childExists('file1')); + $this->assertTrue($this->tree->childExists('file2')); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpPutTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpPutTest.php new file mode 100644 index 00000000000..86480b1c22a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/HttpPutTest.php @@ -0,0 +1,349 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\DAVServerTest; +use Sabre\HTTP; + +/** + * Tests related to the PUT request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class HttpPutTest extends DAVServerTest { + + /** + * Sets up the DAV tree. + * + * @return void + */ + function setUpTree() { + + $this->tree = new Mock\Collection('root', [ + 'file1' => 'foo', + ]); + + } + + /** + * A successful PUT of a new file. + */ + function testPut() { + + $request = new HTTP\Request('PUT', '/file2', [], 'hello'); + + $response = $this->request($request); + + $this->assertEquals(201, $response->getStatus(), 'Incorrect status code received. Full response body:' . $response->getBodyAsString()); + + $this->assertEquals( + 'hello', + $this->server->tree->getNodeForPath('file2')->get() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5('hello') . '"'] + ], + $response->getHeaders() + ); + + } + + /** + * A successful PUT on an existing file. + * + * @depends testPut + */ + function testPutExisting() { + + $request = new HTTP\Request('PUT', '/file1', [], 'bar'); + + $response = $this->request($request); + + $this->assertEquals(204, $response->getStatus()); + + $this->assertEquals( + 'bar', + $this->server->tree->getNodeForPath('file1')->get() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5('bar') . '"'] + ], + $response->getHeaders() + ); + + } + + /** + * PUT on existing file with If-Match: * + * + * @depends testPutExisting + */ + function testPutExistingIfMatchStar() { + + $request = new HTTP\Request( + 'PUT', + '/file1', + ['If-Match' => '*'], + 'hello' + ); + + $response = $this->request($request); + + $this->assertEquals(204, $response->getStatus()); + + $this->assertEquals( + 'hello', + $this->server->tree->getNodeForPath('file1')->get() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5('hello') . '"'] + ], + $response->getHeaders() + ); + + } + + /** + * PUT on existing file with If-Match: with a correct etag + * + * @depends testPutExisting + */ + function testPutExistingIfMatchCorrect() { + + $request = new HTTP\Request( + 'PUT', + '/file1', + ['If-Match' => '"' . md5('foo') . '"'], + 'hello' + ); + + $response = $this->request($request); + + $this->assertEquals(204, $response->status); + + $this->assertEquals( + 'hello', + $this->server->tree->getNodeForPath('file1')->get() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5('hello') . '"'], + ], + $response->getHeaders() + ); + + } + + /** + * PUT with Content-Range should be rejected. + * + * @depends testPut + */ + function testPutContentRange() { + + $request = new HTTP\Request( + 'PUT', + '/file2', + ['Content-Range' => 'bytes/100-200'], + 'hello' + ); + + $response = $this->request($request); + $this->assertEquals(400, $response->getStatus()); + + } + + /** + * PUT on non-existing file with If-None-Match: * should work. + * + * @depends testPut + */ + function testPutIfNoneMatchStar() { + + $request = new HTTP\Request( + 'PUT', + '/file2', + ['If-None-Match' => '*'], + 'hello' + ); + + $response = $this->request($request); + + $this->assertEquals(201, $response->getStatus()); + + $this->assertEquals( + 'hello', + $this->server->tree->getNodeForPath('file2')->get() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5('hello') . '"'] + ], + $response->getHeaders() + ); + + } + + /** + * PUT on non-existing file with If-Match: * should fail. + * + * @depends testPut + */ + function testPutIfMatchStar() { + + $request = new HTTP\Request( + 'PUT', + '/file2', + ['If-Match' => '*'], + 'hello' + ); + + $response = $this->request($request); + + $this->assertEquals(412, $response->getStatus()); + + } + + /** + * PUT on existing file with If-None-Match: * should fail. + * + * @depends testPut + */ + function testPutExistingIfNoneMatchStar() { + + $request = new HTTP\Request( + 'PUT', + '/file1', + ['If-None-Match' => '*'], + 'hello' + ); + $request->setBody('hello'); + + $response = $this->request($request); + + $this->assertEquals(412, $response->getStatus()); + + } + + /** + * PUT thats created in a non-collection should be rejected. + * + * @depends testPut + */ + function testPutNoParent() { + + $request = new HTTP\Request( + 'PUT', + '/file1/file2', + [], + 'hello' + ); + + $response = $this->request($request); + $this->assertEquals(409, $response->getStatus()); + + } + + /** + * Finder may sometimes make a request, which gets its content-body + * stripped. We can't always prevent this from happening, but in some cases + * we can detected this and return an error instead. + * + * @depends testPut + */ + function testFinderPutSuccess() { + + $request = new HTTP\Request( + 'PUT', + '/file2', + ['X-Expected-Entity-Length' => '5'], + 'hello' + ); + $response = $this->request($request); + + $this->assertEquals(201, $response->getStatus()); + + $this->assertEquals( + 'hello', + $this->server->tree->getNodeForPath('file2')->get() + ); + + $this->assertEquals( + [ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + 'ETag' => ['"' . md5('hello') . '"'], + ], + $response->getHeaders() + ); + + } + + /** + * Same as the last one, but in this case we're mimicing a failed request. + * + * @depends testFinderPutSuccess + */ + function testFinderPutFail() { + + $request = new HTTP\Request( + 'PUT', + '/file2', + ['X-Expected-Entity-Length' => '5'], + '' + ); + + $response = $this->request($request); + + $this->assertEquals(403, $response->getStatus()); + + } + + /** + * Plugins can intercept PUT. We need to make sure that works. + * + * @depends testPut + */ + function testPutIntercept() { + + $this->server->on('beforeBind', function($uri) { + $this->server->httpResponse->setStatus(418); + return false; + }); + + $request = new HTTP\Request('PUT', '/file2', [], 'hello'); + $response = $this->request($request); + + $this->assertEquals(418, $response->getStatus(), 'Incorrect status code received. Full response body: ' . $response->getBodyAsString()); + + $this->assertFalse( + $this->server->tree->nodeExists('file2') + ); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + ], $response->getHeaders()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Issue33Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Issue33Test.php new file mode 100644 index 00000000000..ba2cf3dc152 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Issue33Test.php @@ -0,0 +1,106 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/TestUtil.php'; + +class Issue33Test extends \PHPUnit_Framework_TestCase { + + function setUp() { + + \Sabre\TestUtil::clearTempDir(); + + } + + function testCopyMoveInfo() { + + $bar = new SimpleCollection('bar'); + $root = new SimpleCollection('webdav', [$bar]); + + $server = new Server($root); + $server->setBaseUri('/webdav/'); + + $serverVars = [ + 'REQUEST_URI' => '/webdav/bar', + 'HTTP_DESTINATION' => 'http://dev2.tribalos.com/webdav/%C3%A0fo%C3%B3', + 'HTTP_OVERWRITE' => 'F', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + + $server->httpRequest = $request; + + $info = $server->getCopyAndMoveInfo($request); + + $this->assertEquals('%C3%A0fo%C3%B3', urlencode($info['destination'])); + $this->assertFalse($info['destinationExists']); + $this->assertFalse($info['destinationNode']); + + } + + function testTreeMove() { + + mkdir(SABRE_TEMPDIR . '/issue33'); + $dir = new FS\Directory(SABRE_TEMPDIR . '/issue33'); + + $dir->createDirectory('bar'); + + $tree = new Tree($dir); + $tree->move('bar', urldecode('%C3%A0fo%C3%B3')); + + $node = $tree->getNodeForPath(urldecode('%C3%A0fo%C3%B3')); + $this->assertEquals(urldecode('%C3%A0fo%C3%B3'), $node->getName()); + + } + + function testDirName() { + + $dirname1 = 'bar'; + $dirname2 = urlencode('%C3%A0fo%C3%B3'); + + $this->assertTrue(dirname($dirname1) == dirname($dirname2)); + + } + + /** + * @depends testTreeMove + * @depends testCopyMoveInfo + */ + function testEverything() { + + // Request object + $serverVars = [ + 'REQUEST_METHOD' => 'MOVE', + 'REQUEST_URI' => '/webdav/bar', + 'HTTP_DESTINATION' => 'http://dev2.tribalos.com/webdav/%C3%A0fo%C3%B3', + 'HTTP_OVERWRITE' => 'F', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody(''); + + $response = new HTTP\ResponseMock(); + + // Server setup + mkdir(SABRE_TEMPDIR . '/issue33'); + $dir = new FS\Directory(SABRE_TEMPDIR . '/issue33'); + + $dir->createDirectory('bar'); + + $tree = new Tree($dir); + + $server = new Server($tree); + $server->setBaseUri('/webdav/'); + + $server->httpRequest = $request; + $server->httpResponse = $response; + $server->sapi = new HTTP\SapiMock(); + $server->exec(); + + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/issue33/' . urldecode('%C3%A0fo%C3%B3'))); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/AbstractTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/AbstractTest.php new file mode 100644 index 00000000000..bbde69097a5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/AbstractTest.php @@ -0,0 +1,196 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +use Sabre\DAV; + +abstract class AbstractTest extends \PHPUnit_Framework_TestCase { + + /** + * @abstract + * @return AbstractBackend + */ + abstract function getBackend(); + + function testSetup() { + + $backend = $this->getBackend(); + $this->assertInstanceOf('Sabre\\DAV\\Locks\\Backend\\AbstractBackend', $backend); + + } + + /** + * @depends testSetup + */ + function testGetLocks() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + $lock->uri = 'someuri'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + + $this->assertEquals(1, count($locks)); + $this->assertEquals('Sinterklaas', $locks[0]->owner); + $this->assertEquals('someuri', $locks[0]->uri); + + } + + /** + * @depends testGetLocks + */ + function testGetLocksParent() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->depth = DAV\Server::DEPTH_INFINITY; + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri/child', false); + + $this->assertEquals(1, count($locks)); + $this->assertEquals('Sinterklaas', $locks[0]->owner); + $this->assertEquals('someuri', $locks[0]->uri); + + } + + + /** + * @depends testGetLocks + */ + function testGetLocksParentDepth0() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->depth = 0; + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri/child', false); + + $this->assertEquals(0, count($locks)); + + } + + function testGetLocksChildren() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->depth = 0; + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri/child', $lock)); + + $locks = $backend->getLocks('someuri/child', false); + $this->assertEquals(1, count($locks)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(0, count($locks)); + + $locks = $backend->getLocks('someuri', true); + $this->assertEquals(1, count($locks)); + + } + + /** + * @depends testGetLocks + */ + function testLockRefresh() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + /* Second time */ + + $lock->owner = 'Santa Clause'; + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + + $this->assertEquals(1, count($locks)); + + $this->assertEquals('Santa Clause', $locks[0]->owner); + $this->assertEquals('someuri', $locks[0]->uri); + + } + + /** + * @depends testGetLocks + */ + function testUnlock() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(1, count($locks)); + + $this->assertTrue($backend->unlock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(0, count($locks)); + + } + + /** + * @depends testUnlock + */ + function testUnlockUnknownToken() { + + $backend = $this->getBackend(); + + $lock = new DAV\Locks\LockInfo(); + $lock->owner = 'Sinterklaas'; + $lock->timeout = 60; + $lock->created = time(); + $lock->token = 'MY-UNIQUE-TOKEN'; + + $this->assertTrue($backend->lock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(1, count($locks)); + + $lock->token = 'SOME-OTHER-TOKEN'; + $this->assertFalse($backend->unlock('someuri', $lock)); + + $locks = $backend->getLocks('someuri', false); + $this->assertEquals(1, count($locks)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/FileTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/FileTest.php new file mode 100644 index 00000000000..537996f3bbf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/FileTest.php @@ -0,0 +1,24 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +require_once 'Sabre/TestUtil.php'; + +class FileTest extends AbstractTest { + + function getBackend() { + + \Sabre\TestUtil::clearTempDir(); + $backend = new File(SABRE_TEMPDIR . '/lockdb'); + return $backend; + + } + + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/Mock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/Mock.php new file mode 100644 index 00000000000..dd475807178 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/Mock.php @@ -0,0 +1,139 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +use Sabre\DAV\Locks\LockInfo; + +/** + * Locks Mock backend. + * + * This backend stores lock information in memory. Mainly useful for testing. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Mock extends AbstractBackend { + + /** + * Returns a list of Sabre\DAV\Locks\LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * If returnChildLocks is set to true, this method should also look for + * any locks in the subtree of the uri for locks. + * + * @param string $uri + * @param bool $returnChildLocks + * @return array + */ + function getLocks($uri, $returnChildLocks) { + + $newLocks = []; + + $locks = $this->getData(); + + foreach ($locks as $lock) { + + if ($lock->uri === $uri || + //deep locks on parents + ($lock->depth != 0 && strpos($uri, $lock->uri . '/') === 0) || + + // locks on children + ($returnChildLocks && (strpos($lock->uri, $uri . '/') === 0))) { + + $newLocks[] = $lock; + + } + + } + + // Checking if we can remove any of these locks + foreach ($newLocks as $k => $lock) { + if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); + } + return $newLocks; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function lock($uri, LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getData(); + + foreach ($locks as $k => $lock) { + if ( + ($lock->token == $lockInfo->token) || + (time() > $lock->timeout + $lock->created) + ) { + unset($locks[$k]); + } + } + $locks[] = $lockInfo; + $this->putData($locks); + return true; + + } + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param LockInfo $lockInfo + * @return bool + */ + function unlock($uri, LockInfo $lockInfo) { + + $locks = $this->getData(); + foreach ($locks as $k => $lock) { + + if ($lock->token == $lockInfo->token) { + + unset($locks[$k]); + $this->putData($locks); + return true; + + } + } + return false; + + } + + protected $data = []; + + /** + * Loads the lockdata from the filesystem. + * + * @return array + */ + protected function getData() { + + return $this->data; + + } + + /** + * Saves the lockdata + * + * @param array $newData + * @return void + */ + protected function putData(array $newData) { + + $this->data = $newData; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOMySQLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOMySQLTest.php new file mode 100644 index 00000000000..0ba02fc8b53 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOMySQLTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +class PDOMySQLTest extends PDOTest { + + public $driver = 'mysql'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOPgSqlTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOPgSqlTest.php new file mode 100644 index 00000000000..39ee56419a2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOPgSqlTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +class PDOPgSqlTest extends PDOTest { + + public $driver = 'pgsql'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOSqliteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOSqliteTest.php new file mode 100644 index 00000000000..4b126dcf359 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOSqliteTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +class PDOSqliteTest extends PDOTest { + + public $driver = 'sqlite'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOTest.php new file mode 100644 index 00000000000..a27eae93cef --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Backend/PDOTest.php @@ -0,0 +1,20 @@ +<?php + +namespace Sabre\DAV\Locks\Backend; + +abstract class PDOTest extends AbstractTest { + + use \Sabre\DAV\DbTestHelperTrait; + + function getBackend() { + + $this->dropTables('locks'); + $this->createSchema('locks'); + + $pdo = $this->getPDO(); + + return new PDO($pdo); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php new file mode 100644 index 00000000000..1111db5b5c2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/MSWordTest.php @@ -0,0 +1,124 @@ +<?php + +namespace Sabre\DAV\Locks; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; +require_once 'Sabre/TestUtil.php'; + +class MSWordTest extends \PHPUnit_Framework_TestCase { + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + function testLockEtc() { + + mkdir(SABRE_TEMPDIR . '/mstest'); + $tree = new DAV\FS\Directory(SABRE_TEMPDIR . '/mstest'); + + $server = new DAV\Server($tree); + $server->debugExceptions = true; + $locksBackend = new Backend\File(SABRE_TEMPDIR . '/locksdb'); + $locksPlugin = new Plugin($locksBackend); + $server->addPlugin($locksPlugin); + + $response1 = new HTTP\ResponseMock(); + + $server->httpRequest = $this->getLockRequest(); + $server->httpResponse = $response1; + $server->sapi = new HTTP\SapiMock(); + $server->exec(); + + $this->assertEquals(201, $server->httpResponse->getStatus(), 'Full response body:' . $response1->getBodyAsString()); + $this->assertTrue(!!$server->httpResponse->getHeaders('Lock-Token')); + $lockToken = $server->httpResponse->getHeader('Lock-Token'); + + //sleep(10); + + $response2 = new HTTP\ResponseMock(); + + $server->httpRequest = $this->getLockRequest2(); + $server->httpResponse = $response2; + $server->exec(); + + $this->assertEquals(201, $server->httpResponse->status); + $this->assertTrue(!!$server->httpResponse->getHeaders('Lock-Token')); + + //sleep(10); + + $response3 = new HTTP\ResponseMock(); + $server->httpRequest = $this->getPutRequest($lockToken); + $server->httpResponse = $response3; + $server->exec(); + + $this->assertEquals(204, $server->httpResponse->status); + + } + + function getLockRequest() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'HTTP_TIMEOUT' => 'Second-3600', + 'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', + ]); + + $request->setBody('<D:lockinfo xmlns:D="DAV:"> + <D:lockscope> + <D:exclusive /> + </D:lockscope> + <D:locktype> + <D:write /> + </D:locktype> + <D:owner> + <D:href>PC-Vista\User</D:href> + </D:owner> +</D:lockinfo>'); + + return $request; + + } + function getLockRequest2() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_CONTENT_TYPE' => 'application/xml', + 'HTTP_TIMEOUT' => 'Second-3600', + 'REQUEST_URI' => '/~$Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', + ]); + + $request->setBody('<D:lockinfo xmlns:D="DAV:"> + <D:lockscope> + <D:exclusive /> + </D:lockscope> + <D:locktype> + <D:write /> + </D:locktype> + <D:owner> + <D:href>PC-Vista\User</D:href> + </D:owner> +</D:lockinfo>'); + + return $request; + + } + + function getPutRequest($lockToken) { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', + 'HTTP_IF' => 'If: (' . $lockToken . ')', + ]); + $request->setBody('FAKE BODY'); + return $request; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Plugin2Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Plugin2Test.php new file mode 100644 index 00000000000..7af49079574 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/Plugin2Test.php @@ -0,0 +1,69 @@ +<?php + +namespace Sabre\DAV\Locks; + +use Sabre\HTTP\Request; + +class Plugin2Test extends \Sabre\DAVServerTest { + + public $setupLocks = true; + + function setUpTree() { + + $this->tree = new \Sabre\DAV\FS\Directory(SABRE_TEMPDIR); + + } + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + /** + * This test first creates a file with LOCK and then deletes it. + * + * After deleting the file, the lock should no longer be in the lock + * backend. + * + * Reported in ticket #487 + */ + function testUnlockAfterDelete() { + + $body = '<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> +</D:lockinfo>'; + + $request = new Request( + 'LOCK', + '/file.txt', + [], + $body + ); + $response = $this->request($request); + $this->assertEquals(201, $response->getStatus(), $response->getBodyAsString()); + + $this->assertEquals( + 1, + count($this->locksBackend->getLocks('file.txt', true)) + ); + + $request = new Request( + 'DELETE', + '/file.txt', + [ + 'If' => '(' . $response->getHeader('Lock-Token') . ')', + ] + ); + $response = $this->request($request); + $this->assertEquals(204, $response->getStatus(), $response->getBodyAsString()); + + $this->assertEquals( + 0, + count($this->locksBackend->getLocks('file.txt', true)) + ); + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php new file mode 100644 index 00000000000..dbbf6757aad --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Locks/PluginTest.php @@ -0,0 +1,1003 @@ +<?php + +namespace Sabre\DAV\Locks; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/DAV/AbstractServer.php'; + +class PluginTest extends DAV\AbstractServer { + + /** + * @var Plugin + */ + protected $locksPlugin; + + function setUp() { + + parent::setUp(); + $locksBackend = new Backend\File(SABRE_TEMPDIR . '/locksdb'); + $locksPlugin = new Plugin($locksBackend); + $this->server->addPlugin($locksPlugin); + $this->locksPlugin = $locksPlugin; + + } + + function testGetInfo() { + + $this->assertArrayHasKey( + 'name', + $this->locksPlugin->getPluginInfo() + ); + + } + + function testGetFeatures() { + + $this->assertEquals([2], $this->locksPlugin->getFeatures()); + + } + + function testGetHTTPMethods() { + + $this->assertEquals(['LOCK', 'UNLOCK'], $this->locksPlugin->getHTTPMethods('')); + + } + + function testLockNoBody() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(400, $this->response->status); + + } + + function testLock() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status, 'Got an incorrect status back. Response body: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + $elements = [ + '/d:prop', + '/d:prop/d:lockdiscovery', + '/d:prop/d:lockdiscovery/d:activelock', + '/d:prop/d:lockdiscovery/d:activelock/d:locktype', + '/d:prop/d:lockdiscovery/d:activelock/d:lockroot', + '/d:prop/d:lockdiscovery/d:activelock/d:lockroot/d:href', + '/d:prop/d:lockdiscovery/d:activelock/d:locktype/d:write', + '/d:prop/d:lockdiscovery/d:activelock/d:lockscope', + '/d:prop/d:lockdiscovery/d:activelock/d:lockscope/d:exclusive', + '/d:prop/d:lockdiscovery/d:activelock/d:depth', + '/d:prop/d:lockdiscovery/d:activelock/d:owner', + '/d:prop/d:lockdiscovery/d:activelock/d:timeout', + '/d:prop/d:lockdiscovery/d:activelock/d:locktoken', + '/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href', + ]; + + foreach ($elements as $elem) { + $data = $xml->xpath($elem); + $this->assertEquals(1, count($data), 'We expected 1 match for the xpath expression "' . $elem . '". ' . count($data) . ' were found. Full response body: ' . $this->response->body); + } + + $depth = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:depth'); + $this->assertEquals('infinity', (string)$depth[0]); + + $token = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href'); + $this->assertEquals($this->response->getHeader('Lock-Token'), '<' . (string)$token[0] . '>', 'Token in response body didn\'t match token in response header.'); + + } + + /** + * @depends testLock + */ + function testDoubleLock() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->response = new HTTP\ResponseMock(); + $this->server->httpResponse = $this->response; + + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + $this->assertEquals(423, $this->response->status, 'Full response: ' . $this->response->body); + + } + + /** + * @depends testLock + */ + function testLockRefresh() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $lockToken = $this->response->getHeader('Lock-Token'); + + $this->response = new HTTP\ResponseMock(); + $this->server->httpResponse = $this->response; + + $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . ')']); + $request->setBody(''); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + $this->assertEquals(200, $this->response->status, 'We received an incorrect status code. Full response body: ' . $this->response->getBody()); + + } + + /** + * @depends testLock + */ + function testLockRefreshBadToken() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $lockToken = $this->response->getHeader('Lock-Token'); + + $this->response = new HTTP\ResponseMock(); + $this->server->httpResponse = $this->response; + + $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . 'foobar) (<opaquelocktoken:anotherbadtoken>)']); + $request->setBody(''); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + $this->assertEquals(423, $this->response->getStatus(), 'We received an incorrect status code. Full response body: ' . $this->response->getBody()); + + } + + /** + * @depends testLock + */ + function testLockNoFile() { + + $request = new HTTP\Request('LOCK', '/notfound.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(201, $this->response->status); + + } + + /** + * @depends testLock + */ + function testUnlockNoToken() { + + $request = new HTTP\Request('UNLOCK', '/test.txt'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(400, $this->response->status); + + } + + /** + * @depends testLock + */ + function testUnlockBadToken() { + + $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => '<opaquelocktoken:blablabla>']); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(409, $this->response->status, 'Got an incorrect status code. Full response body: ' . $this->response->body); + + } + + /** + * @depends testLock + */ + function testLockPutNoToken() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $request = new HTTP\Request('PUT', '/test.txt'); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(423, $this->response->status); + + } + + /** + * @depends testLock + */ + function testUnlock() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $this->server->httpRequest = $request; + + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->invokeMethod($request, $this->server->httpResponse); + $lockToken = $this->server->httpResponse->getHeader('Lock-Token'); + + $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => $lockToken]); + $this->server->httpRequest = $request; + $this->server->httpResponse = new HTTP\ResponseMock(); + $this->server->invokeMethod($request, $this->server->httpResponse); + + $this->assertEquals(204, $this->server->httpResponse->status, 'Got an incorrect status code. Full response body: ' . $this->response->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + ], + $this->server->httpResponse->getHeaders() + ); + + + } + + /** + * @depends testLock + */ + function testUnlockWindowsBug() { + + $request = new HTTP\Request('LOCK', '/test.txt'); + $this->server->httpRequest = $request; + + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->invokeMethod($request, $this->server->httpResponse); + $lockToken = $this->server->httpResponse->getHeader('Lock-Token'); + + // See Issue 123 + $lockToken = trim($lockToken, '<>'); + + $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => $lockToken]); + $this->server->httpRequest = $request; + $this->server->httpResponse = new HTTP\ResponseMock(); + $this->server->invokeMethod($request, $this->server->httpResponse); + + $this->assertEquals(204, $this->server->httpResponse->status, 'Got an incorrect status code. Full response body: ' . $this->response->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Length' => ['0'], + ], + $this->server->httpResponse->getHeaders() + ); + + + } + + /** + * @depends testLock + */ + function testLockRetainOwner() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ]); + $this->server->httpRequest = $request; + + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner>Evert</D:owner> +</D:lockinfo>'); + + $this->server->invokeMethod($request, $this->server->httpResponse); + $lockToken = $this->server->httpResponse->getHeader('Lock-Token'); + + $locks = $this->locksPlugin->getLocks('test.txt'); + $this->assertEquals(1, count($locks)); + $this->assertEquals('Evert', $locks[0]->owner); + + + } + + /** + * @depends testLock + */ + function testLockPutBadToken() { + + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '(<opaquelocktoken:token1>)', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + // $this->assertEquals('412 Precondition failed',$this->response->status); + $this->assertEquals(423, $this->response->status); + + } + + /** + * @depends testLock + */ + function testLockDeleteParent() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir', + 'REQUEST_METHOD' => 'DELETE', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(423, $this->response->status); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + /** + * @depends testLock + */ + function testLockDeleteSucceed() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'DELETE', + 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(204, $this->response->status); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + + /** + * @depends testLock + */ + function testLockCopyLockSource() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(201, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + /** + * @depends testLock + */ + function testLockCopyLockDestination() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child2.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(201, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'COPY', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(423, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + + /** + * @depends testLock + */ + function testLockMoveLockSourceLocked() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(423, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + + /** + * @depends testLock + */ + function testLockMoveLockSourceSucceed() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(201, $this->response->status, 'A valid lock-token was provided for the source, so this MOVE operation must succeed. Full response body: ' . $this->response->body); + + } + + /** + * @depends testLock + */ + function testLockMoveLockDestination() { + + $serverVars = [ + 'REQUEST_URI' => '/dir/child2.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(201, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(423, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination'); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + /** + * @depends testLock + */ + function testLockMoveLockParent() { + + $serverVars = [ + 'REQUEST_URI' => '/dir', + 'REQUEST_METHOD' => 'LOCK', + 'HTTP_DEPTH' => 'infinite', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/dir/child.txt', + 'REQUEST_METHOD' => 'MOVE', + 'HTTP_DESTINATION' => '/dir/child2.txt', + 'HTTP_IF' => '</dir> (' . $this->response->getHeader('Lock-Token') . ')', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(201, $this->response->status, 'We locked the parent of both the source and destination, but the move didn\'t succeed.'); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + + } + + /** + * @depends testLock + */ + function testLockPutGoodToken() { + + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'LOCK', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(200, $this->response->status); + + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(204, $this->response->status); + + } + + /** + * @depends testLock + */ + function testLockPutUnrelatedToken() { + + $request = new HTTP\Request('LOCK', '/unrelated.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(201, $this->response->getStatus()); + + $request = new HTTP\Request( + 'PUT', + '/test.txt', + ['If' => '</unrelated.txt> (' . $this->response->getHeader('Lock-Token') . ')'] + ); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + + $this->assertEquals(204, $this->response->status); + + } + + function testPutWithIncorrectETag() { + + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '(["etag1"])', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + $this->assertEquals(412, $this->response->status); + + } + + /** + * @depends testPutWithIncorrectETag + */ + function testPutWithCorrectETag() { + + // We need an ETag-enabled file node. + $tree = new DAV\Tree(new DAV\FSExt\Directory(SABRE_TEMPDIR)); + $this->server->tree = $tree; + + $filename = SABRE_TEMPDIR . '/test.txt'; + $etag = sha1( + fileinode($filename) . + filesize($filename) . + filemtime($filename) + ); + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'PUT', + 'HTTP_IF' => '(["' . $etag . '"])', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('newbody'); + $this->server->httpRequest = $request; + $this->server->exec(); + $this->assertEquals(204, $this->response->status, 'Incorrect status received. Full response body:' . $this->response->body); + + } + + function testDeleteWithETagOnCollection() { + + $serverVars = [ + 'REQUEST_URI' => '/dir', + 'REQUEST_METHOD' => 'DELETE', + 'HTTP_IF' => '(["etag1"])', + ]; + $request = HTTP\Sapi::createFromServerArray($serverVars); + + $this->server->httpRequest = $request; + $this->server->exec(); + $this->assertEquals(412, $this->response->status); + + } + + function testGetTimeoutHeader() { + + $request = HTTP\Sapi::createFromServerArray([ + 'HTTP_TIMEOUT' => 'second-100', + ]); + + $this->server->httpRequest = $request; + $this->assertEquals(100, $this->locksPlugin->getTimeoutHeader()); + + } + + function testGetTimeoutHeaderTwoItems() { + + $request = HTTP\Sapi::createFromServerArray([ + 'HTTP_TIMEOUT' => 'second-5, infinite', + ]); + + $this->server->httpRequest = $request; + $this->assertEquals(5, $this->locksPlugin->getTimeoutHeader()); + + } + + function testGetTimeoutHeaderInfinite() { + + $request = HTTP\Sapi::createFromServerArray([ + 'HTTP_TIMEOUT' => 'infinite, second-5', + ]); + + $this->server->httpRequest = $request; + $this->assertEquals(LockInfo::TIMEOUT_INFINITE, $this->locksPlugin->getTimeoutHeader()); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testGetTimeoutHeaderInvalid() { + + $request = HTTP\Sapi::createFromServerArray([ + 'HTTP_TIMEOUT' => 'yourmom', + ]); + + $this->server->httpRequest = $request; + $this->locksPlugin->getTimeoutHeader(); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/Collection.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/Collection.php new file mode 100644 index 00000000000..fded5e474a6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/Collection.php @@ -0,0 +1,168 @@ +<?php + +namespace Sabre\DAV\Mock; + +use Sabre\DAV; + +/** + * Mock Collection. + * + * This collection quickly allows you to create trees of nodes. + * Children are specified as an array. + * + * Every key a filename, every array value is either: + * * an array, for a sub-collection + * * a string, for a file + * * An instance of \Sabre\DAV\INode. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Collection extends DAV\Collection { + + protected $name; + protected $children; + protected $parent; + + /** + * Creates the object + * + * @param string $name + * @param array $children + * @param Collection $parent + * @return void + */ + function __construct($name, array $children = [], Collection $parent = null) { + + $this->name = $name; + foreach ($children as $key => $value) { + if (is_string($value)) { + $this->children[] = new File($key, $value, $this); + } elseif (is_array($value)) { + $this->children[] = new self($key, $value, $this); + } elseif ($value instanceof \Sabre\DAV\INode) { + $this->children[] = $value; + } else { + throw new \InvalidArgumentException('Unknown value passed in $children'); + } + } + $this->parent = $parent; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + + return $this->name; + + } + + /** + * Creates a new file in the directory + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After successful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string + */ + function createFile($name, $data = null) { + + if (is_resource($data)) { + $data = stream_get_contents($data); + } + $this->children[] = new File($name, $data, $this); + return '"' . md5($data) . '"'; + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + function createDirectory($name) { + + $this->children[] = new self($name); + + } + + /** + * Returns an array with all the child nodes + * + * @return \Sabre\DAV\INode[] + */ + function getChildren() { + + return $this->children; + + } + + /** + * Adds an already existing node to this collection. + * + * @param \Sabre\DAV\INode $node + */ + function addNode(\Sabre\DAV\INode $node) { + + $this->children[] = $node; + + } + + /** + * Removes a childnode from this node. + * + * @param string $name + * @return void + */ + function deleteChild($name) { + + foreach ($this->children as $key => $value) { + + if ($value->getName() == $name) { + unset($this->children[$key]); + return; + } + + } + + } + + /** + * Deletes this collection and all its children,. + * + * @return void + */ + function delete() { + + foreach ($this->getChildren() as $child) { + $this->deleteChild($child->getName()); + } + $this->parent->deleteChild($this->getName()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/File.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/File.php new file mode 100644 index 00000000000..a624b6b6bf1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/File.php @@ -0,0 +1,163 @@ +<?php + +namespace Sabre\DAV\Mock; + +use Sabre\DAV; + +/** + * Mock File + * + * See the Collection in this directory for more details. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class File extends DAV\File { + + protected $name; + protected $contents; + protected $parent; + protected $lastModified; + + /** + * Creates the object + * + * @param string $name + * @param resource $contents + * @param Collection $parent + * @param int $lastModified + * @return void + */ + function __construct($name, $contents, Collection $parent = null, $lastModified = -1) { + + $this->name = $name; + $this->put($contents); + $this->parent = $parent; + + if ($lastModified === -1) { + $lastModified = time(); + } + + $this->lastModified = $lastModified; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + function getName() { + + return $this->name; + + } + + /** + * Changes the name of the node. + * + * @param string $name + * @return void + */ + function setName($name) { + + $this->name = $name; + + } + + /** + * Updates the data + * + * The data argument is a readable stream resource. + * + * After a successful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * If you don't plan to store the file byte-by-byte, and you return a + * different object on a subsequent GET you are strongly recommended to not + * return an ETag, and just return null. + * + * @param resource $data + * @return string|null + */ + function put($data) { + + if (is_resource($data)) { + $data = stream_get_contents($data); + } + $this->contents = $data; + return '"' . md5($data) . '"'; + + } + + /** + * Returns the data + * + * This method may either return a string or a readable stream resource + * + * @return mixed + */ + function get() { + + return $this->contents; + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * + * Return null if the ETag can not effectively be determined + * + * @return void + */ + function getETag() { + + return '"' . md5($this->contents) . '"'; + + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + function getSize() { + + return strlen($this->contents); + + } + + /** + * Delete the node + * + * @return void + */ + function delete() { + + $this->parent->deleteChild($this->name); + + } + + /** + * Returns the last modification time as a unix timestamp. + * If the information is not available, return null. + * + * @return int + */ + function getLastModified() { + + return $this->lastModified; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/PropertiesCollection.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/PropertiesCollection.php new file mode 100644 index 00000000000..af3fd2d3f2f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/PropertiesCollection.php @@ -0,0 +1,94 @@ +<?php + +namespace Sabre\DAV\Mock; + +use Sabre\DAV\IProperties; +use Sabre\DAV\PropPatch; + +/** + * A node specifically for testing property-related operations + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class PropertiesCollection extends Collection implements IProperties { + + public $failMode = false; + + public $properties; + + /** + * Creates the object + * + * @param string $name + * @param array $children + * @param array $properties + * @return void + */ + function __construct($name, array $children, array $properties = []) { + + parent::__construct($name, $children, null); + $this->properties = $properties; + + } + + /** + * Updates properties on this node. + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * To update specific properties, call the 'handle' method on this object. + * Read the PropPatch documentation for more information. + * + * @param PropPatch $proppatch + * @return bool|array + */ + function propPatch(PropPatch $proppatch) { + + $proppatch->handleRemaining(function($updateProperties) { + + switch ($this->failMode) { + case 'updatepropsfalse' : return false; + case 'updatepropsarray' : + $r = []; + foreach ($updateProperties as $k => $v) $r[$k] = 402; + return $r; + case 'updatepropsobj' : + return new \STDClass(); + } + + }); + + } + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname + * + * If the array is empty, it means 'all properties' were requested. + * + * Note that it's fine to liberally give properties back, instead of + * conforming to the list of requested properties. + * The Server class will filter out the extra. + * + * @param array $requestedProperties + * @return array + */ + function getProperties($requestedProperties) { + + $returnedProperties = []; + foreach ($requestedProperties as $requestedProperty) { + if (isset($this->properties[$requestedProperty])) { + $returnedProperties[$requestedProperty] = + $this->properties[$requestedProperty]; + } + } + return $returnedProperties; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/SharedNode.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/SharedNode.php new file mode 100644 index 00000000000..503d6407095 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/SharedNode.php @@ -0,0 +1,125 @@ +<?php + +namespace Sabre\DAV\Mock; + +use Sabre\DAV\Sharing\ISharedNode; +use Sabre\DAV\Sharing\Sharee; + +class SharedNode extends \Sabre\DAV\Node implements ISharedNode { + + protected $name; + protected $access; + protected $invites = []; + + function __construct($name, $access) { + + $this->name = $name; + $this->access = $access; + + } + + function getName() { + + return $this->name; + + } + + /** + * Returns the 'access level' for the instance of this shared resource. + * + * The value should be one of the Sabre\DAV\Sharing\Plugin::ACCESS_ + * constants. + * + * @return int + */ + function getShareAccess() { + + return $this->access; + + } + + /** + * This function must return a URI that uniquely identifies the shared + * resource. This URI should be identical across instances, and is + * also used in several other XML bodies to connect invites to + * resources. + * + * This may simply be a relative reference to the original shared instance, + * but it could also be a urn. As long as it's a valid URI and unique. + * + * @return string + */ + function getShareResourceUri() { + + return 'urn:example:bar'; + + } + + /** + * Updates the list of sharees. + * + * Every item must be a Sharee object. + * + * @param Sharee[] $sharees + * @return void + */ + function updateInvites(array $sharees) { + + foreach ($sharees as $sharee) { + + if ($sharee->access === \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS) { + // Removal + foreach ($this->invites as $k => $invitee) { + + if ($invitee->href = $sharee->href) { + unset($this->invites[$k]); + } + + } + + } else { + foreach ($this->invites as $k => $invitee) { + + if ($invitee->href = $sharee->href) { + if (!$sharee->inviteStatus) { + $sharee->inviteStatus = $invitee->inviteStatus; + } + // Overwriting an existing invitee + $this->invites[$k] = $sharee; + continue 2; + } + + } + if (!$sharee->inviteStatus) { + $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE; + } + // Adding a new invitee + $this->invites[] = $sharee; + } + + } + + } + + /** + * Returns the list of people whom this resource is shared with. + * + * Every item in the returned array must be a Sharee object with + * at least the following properties set: + * + * * $href + * * $shareAccess + * * $inviteStatus + * + * and optionally: + * + * * $properties + * + * @return \Sabre\DAV\Xml\Element\Sharee[] + */ + function getInvites() { + + return $this->invites; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/StreamingFile.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/StreamingFile.php new file mode 100644 index 00000000000..d60a49d092e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mock/StreamingFile.php @@ -0,0 +1,102 @@ +<?php + +namespace Sabre\DAV\Mock; + +/** + * Mock Streaming File File + * + * Works similar to the mock file, but this one works with streams and has no + * content-length or etags. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class StreamingFile extends File { + + protected $size; + + /** + * Updates the data + * + * The data argument is a readable stream resource. + * + * After a successful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * If you don't plan to store the file byte-by-byte, and you return a + * different object on a subsequent GET you are strongly recommended to not + * return an ETag, and just return null. + * + * @param resource $data + * @return string|null + */ + function put($data) { + + if (is_string($data)) { + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $data); + rewind($stream); + $data = $stream; + } + $this->contents = $data; + + } + + /** + * Returns the data + * + * This method may either return a string or a readable stream resource + * + * @return mixed + */ + function get() { + + return $this->contents; + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * + * Return null if the ETag can not effectively be determined + * + * @return void + */ + function getETag() { + + return null; + + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + function getSize() { + + return $this->size; + + } + + /** + * Allows testing scripts to set the resource's file size. + * + * @param int $size + * @return void + */ + function setSize($size) { + + $this->size = $size; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/MockLogger.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/MockLogger.php new file mode 100644 index 00000000000..03332569300 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/MockLogger.php @@ -0,0 +1,36 @@ +<?php + +namespace Sabre\DAV; + +use Psr\Log\AbstractLogger; + +/** + * The MockLogger is a simple PSR-3 implementation that we can use to test + * whether things get logged correctly. + * + * @copyright Copyright (C) fruux GmbH. (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class MockLogger extends AbstractLogger { + + public $logs = []; + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + function log($level, $message, array $context = []) { + + $this->logs[] = [ + $level, + $message, + $context + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php new file mode 100644 index 00000000000..3213fcb1b45 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Mount/PluginTest.php @@ -0,0 +1,58 @@ +<?php + +namespace Sabre\DAV\Mount; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/DAV/AbstractServer.php'; + +class PluginTest extends DAV\AbstractServer { + + function setUp() { + + parent::setUp(); + $this->server->addPlugin(new Plugin()); + + } + + function testPassThrough() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'GET', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(501, $this->response->status, 'We expected GET to not be implemented for Directories. Response body: ' . $this->response->body); + + } + + function testMountResponse() { + + $serverVars = [ + 'REQUEST_URI' => '/?mount', + 'REQUEST_METHOD' => 'GET', + 'QUERY_STRING' => 'mount', + 'HTTP_HOST' => 'example.org', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(200, $this->response->status); + + $xml = simplexml_load_string($this->response->body); + $this->assertInstanceOf('SimpleXMLElement', $xml, 'Response was not a valid xml document. The list of errors:' . print_r(libxml_get_errors(), true) . '. xml body: ' . $this->response->body . '. What type we got: ' . gettype($xml) . ' class, if object: ' . get_class($xml)); + + $xml->registerXPathNamespace('dm', 'http://purl.org/NET/webdav/mount'); + $url = $xml->xpath('//dm:url'); + $this->assertEquals('http://example.org/', (string)$url[0]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php new file mode 100644 index 00000000000..15289ce528c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ObjectTreeTest.php @@ -0,0 +1,100 @@ +<?php + +namespace Sabre\DAV; + +require_once 'Sabre/TestUtil.php'; + +class ObjectTreeTest extends \PHPUnit_Framework_TestCase { + + protected $tree; + + function setup() { + + \Sabre\TestUtil::clearTempDir(); + mkdir(SABRE_TEMPDIR . '/root'); + mkdir(SABRE_TEMPDIR . '/root/subdir'); + file_put_contents(SABRE_TEMPDIR . '/root/file.txt', 'contents'); + file_put_contents(SABRE_TEMPDIR . '/root/subdir/subfile.txt', 'subcontents'); + $rootNode = new FSExt\Directory(SABRE_TEMPDIR . '/root'); + $this->tree = new Tree($rootNode); + + } + + function teardown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + function testGetRootNode() { + + $root = $this->tree->getNodeForPath(''); + $this->assertInstanceOf('Sabre\\DAV\\FSExt\\Directory', $root); + + } + + function testGetSubDir() { + + $root = $this->tree->getNodeForPath('subdir'); + $this->assertInstanceOf('Sabre\\DAV\\FSExt\\Directory', $root); + + } + + function testCopyFile() { + + $this->tree->copy('file.txt', 'file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/file2.txt')); + $this->assertEquals('contents', file_get_contents(SABRE_TEMPDIR . '/root/file2.txt')); + + } + + /** + * @depends testCopyFile + */ + function testCopyDirectory() { + + $this->tree->copy('subdir', 'subdir2'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/subdir2')); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/subdir2/subfile.txt')); + $this->assertEquals('subcontents', file_get_contents(SABRE_TEMPDIR . '/root/subdir2/subfile.txt')); + + } + + /** + * @depends testCopyFile + */ + function testMoveFile() { + + $this->tree->move('file.txt', 'file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/file2.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/root/file.txt')); + $this->assertEquals('contents', file_get_contents(SABRE_TEMPDIR . '/root/file2.txt')); + + } + + /** + * @depends testMoveFile + */ + function testMoveFileNewParent() { + + $this->tree->move('file.txt', 'subdir/file2.txt'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/subdir/file2.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/root/file.txt')); + $this->assertEquals('contents', file_get_contents(SABRE_TEMPDIR . '/root/subdir/file2.txt')); + + } + + /** + * @depends testCopyDirectory + */ + function testMoveDirectory() { + + $this->tree->move('subdir', 'subdir2'); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/subdir2')); + $this->assertTrue(file_exists(SABRE_TEMPDIR . '/root/subdir2/subfile.txt')); + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/root/subdir')); + $this->assertEquals('subcontents', file_get_contents(SABRE_TEMPDIR . '/root/subdir2/subfile.txt')); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PSR3Test.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PSR3Test.php new file mode 100644 index 00000000000..d30fde128a7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PSR3Test.php @@ -0,0 +1,87 @@ +<?php + +namespace Sabre\DAV; + +class PSR3Test extends \PHPUnit_Framework_TestCase { + + function testIsLoggerAware() { + + $server = new Server(); + $this->assertInstanceOf( + 'Psr\Log\LoggerAwareInterface', + $server + ); + + } + + function testGetNullLoggerByDefault() { + + $server = new Server(); + $this->assertInstanceOf( + 'Psr\Log\NullLogger', + $server->getLogger() + ); + + } + + function testSetLogger() { + + $server = new Server(); + $logger = new MockLogger(); + + $server->setLogger($logger); + + $this->assertEquals( + $logger, + $server->getLogger() + ); + + } + + /** + * Start the server, trigger an exception and see if the logger captured + * it. + */ + function testLogException() { + + $server = new Server(); + $logger = new MockLogger(); + + $server->setLogger($logger); + + // Creating a fake environment to execute http requests in. + $request = new \Sabre\HTTP\Request( + 'GET', + '/not-found', + [] + ); + $response = new \Sabre\HTTP\Response(); + + $server->httpRequest = $request; + $server->httpResponse = $response; + $server->sapi = new \Sabre\HTTP\SapiMock(); + + // Executing the request. + $server->exec(); + + // The request should have triggered a 404 status. + $this->assertEquals(404, $response->getStatus()); + + // We should also see this in the PSR-3 log. + $this->assertEquals(1, count($logger->logs)); + + $logItem = $logger->logs[0]; + + $this->assertEquals( + \Psr\Log\LogLevel::INFO, + $logItem[0] + ); + + $this->assertInstanceOf( + 'Exception', + $logItem[2]['exception'] + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php new file mode 100644 index 00000000000..eff1e7d6738 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/FileMock.php @@ -0,0 +1,122 @@ +<?php + +namespace Sabre\DAV\PartialUpdate; + +use Sabre\DAV; + +class FileMock implements IPatchSupport { + + protected $data = ''; + + function put($str) { + + if (is_resource($str)) { + $str = stream_get_contents($str); + } + $this->data = $str; + + } + + /** + * Updates the file based on a range specification. + * + * The first argument is the data, which is either a readable stream + * resource or a string. + * + * The second argument is the type of update we're doing. + * This is either: + * * 1. append + * * 2. update based on a start byte + * * 3. update based on an end byte + *; + * The third argument is the start or end byte. + * + * After a successful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * @param resource|string $data + * @param int $rangeType + * @param int $offset + * @return string|null + */ + function patch($data, $rangeType, $offset = null) { + + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + switch ($rangeType) { + + case 1 : + $this->data .= $data; + break; + case 3 : + // Turn the offset into an offset-offset. + $offset = strlen($this->data) - $offset; + // No break is intentional + case 2 : + $this->data = + substr($this->data, 0, $offset) . + $data . + substr($this->data, $offset + strlen($data)); + break; + + } + + } + + function get() { + + return $this->data; + + } + + function getContentType() { + + return 'text/plain'; + + } + + function getSize() { + + return strlen($this->data); + + } + + function getETag() { + + return '"' . $this->data . '"'; + + } + + function delete() { + + throw new DAV\Exception\MethodNotAllowed(); + + } + + function setName($name) { + + throw new DAV\Exception\MethodNotAllowed(); + + } + + function getName() { + + return 'partial'; + + } + + function getLastModified() { + + return null; + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php new file mode 100644 index 00000000000..5bd696416bb --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/PluginTest.php @@ -0,0 +1,135 @@ +<?php + +namespace Sabre\DAV\PartialUpdate; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/DAV/PartialUpdate/FileMock.php'; + +class PluginTest extends \Sabre\DAVServerTest { + + protected $node; + protected $plugin; + + function setUp() { + + $this->node = new FileMock(); + $this->tree[] = $this->node; + + parent::setUp(); + + $this->plugin = new Plugin(); + $this->server->addPlugin($this->plugin); + + + + } + + function testInit() { + + $this->assertEquals('partialupdate', $this->plugin->getPluginName()); + $this->assertEquals(['sabredav-partialupdate'], $this->plugin->getFeatures()); + $this->assertEquals([ + 'PATCH' + ], $this->plugin->getHTTPMethods('partial')); + $this->assertEquals([ + ], $this->plugin->getHTTPMethods('')); + + } + + function testPatchNoRange() { + + $this->node->put('aaaaaaaa'); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PATCH', + 'REQUEST_URI' => '/partial', + ]); + $response = $this->request($request); + + $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); + + } + + function testPatchNotSupported() { + + $this->node->put('aaaaaaaa'); + $request = new HTTP\Request('PATCH', '/', ['X-Update-Range' => '3-4']); + $request->setBody( + 'bbb' + ); + $response = $this->request($request); + + $this->assertEquals(405, $response->status, 'Full response body:' . $response->body); + + } + + function testPatchNoContentType() { + + $this->node->put('aaaaaaaa'); + $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-4']); + $request->setBody( + 'bbb' + ); + $response = $this->request($request); + + $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); + + } + + function testPatchBadRange() { + + $this->node->put('aaaaaaaa'); + $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-4', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => '3']); + $request->setBody( + 'bbb' + ); + $response = $this->request($request); + + $this->assertEquals(416, $response->status, 'Full response body:' . $response->body); + + } + + function testPatchNoLength() { + + $this->node->put('aaaaaaaa'); + $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-5', 'Content-Type' => 'application/x-sabredav-partialupdate']); + $request->setBody( + 'bbb' + ); + $response = $this->request($request); + + $this->assertEquals(411, $response->status, 'Full response body:' . $response->body); + + } + + function testPatchSuccess() { + + $this->node->put('aaaaaaaa'); + $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-5', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => 3]); + $request->setBody( + 'bbb' + ); + $response = $this->request($request); + + $this->assertEquals(204, $response->status, 'Full response body:' . $response->body); + $this->assertEquals('aaabbbaa', $this->node->get()); + + } + + function testPatchNoEndRange() { + + $this->node->put('aaaaa'); + $request = new HTTP\Request('PATCH', '/partial', ['X-Update-Range' => 'bytes=3-', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => '3']); + $request->setBody( + 'bbb' + ); + + $response = $this->request($request); + + $this->assertEquals(204, $response->getStatus(), 'Full response body:' . $response->getBodyAsString()); + $this->assertEquals('aaabbb', $this->node->get()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php new file mode 100644 index 00000000000..2c627417330 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php @@ -0,0 +1,94 @@ +<?php + +namespace Sabre\DAV\PartialUpdate; + +use Sabre\DAV\FSExt\File; +use Sabre\DAV\Server; +use Sabre\HTTP; + +/** + * This test is an end-to-end sabredav test that goes through all + * the cases in the specification. + * + * See: http://sabre.io/dav/http-patch/ + */ +class SpecificationTest extends \PHPUnit_Framework_TestCase { + + protected $server; + + function setUp() { + + $tree = [ + new File(SABRE_TEMPDIR . '/foobar.txt') + ]; + $server = new Server($tree); + $server->debugExceptions = true; + $server->addPlugin(new Plugin()); + + $tree[0]->put('1234567890'); + + $this->server = $server; + + } + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + /** + * @param string $headerValue + * @param string $httpStatus + * @param string $endResult + * @param int $contentLength + * + * @dataProvider data + */ + function testUpdateRange($headerValue, $httpStatus, $endResult, $contentLength = 4) { + + $headers = [ + 'Content-Type' => 'application/x-sabredav-partialupdate', + 'X-Update-Range' => $headerValue, + ]; + + if ($contentLength) { + $headers['Content-Length'] = (string)$contentLength; + } + + $request = new HTTP\Request('PATCH', '/foobar.txt', $headers, '----'); + + $request->setBody('----'); + $this->server->httpRequest = $request; + $this->server->httpResponse = new HTTP\ResponseMock(); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->exec(); + + $this->assertEquals($httpStatus, $this->server->httpResponse->status, 'Incorrect http status received: ' . $this->server->httpResponse->body); + if (!is_null($endResult)) { + $this->assertEquals($endResult, file_get_contents(SABRE_TEMPDIR . '/foobar.txt')); + } + + } + + function data() { + + return [ + // Problems + ['foo', 400, null], + ['bytes=0-3', 411, null, 0], + ['bytes=4-1', 416, null], + + ['bytes=0-3', 204, '----567890'], + ['bytes=1-4', 204, '1----67890'], + ['bytes=0-', 204, '----567890'], + ['bytes=-4', 204, '123456----'], + ['bytes=-2', 204, '12345678----'], + ['bytes=2-', 204, '12----7890'], + ['append', 204, '1234567890----'], + + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropFindTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropFindTest.php new file mode 100644 index 00000000000..ec1d616cbb9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropFindTest.php @@ -0,0 +1,76 @@ +<?php + +namespace Sabre\DAV; + +class PropFindTest extends \PHPUnit_Framework_TestCase { + + function testHandle() { + + $propFind = new PropFind('foo', ['{DAV:}displayname']); + $propFind->handle('{DAV:}displayname', 'foobar'); + + $this->assertEquals([ + 200 => ['{DAV:}displayname' => 'foobar'], + 404 => [], + ], $propFind->getResultForMultiStatus()); + + } + + function testHandleCallBack() { + + $propFind = new PropFind('foo', ['{DAV:}displayname']); + $propFind->handle('{DAV:}displayname', function() { return 'foobar'; }); + + $this->assertEquals([ + 200 => ['{DAV:}displayname' => 'foobar'], + 404 => [], + ], $propFind->getResultForMultiStatus()); + + } + + function testAllPropDefaults() { + + $propFind = new PropFind('foo', ['{DAV:}displayname'], 0, PropFind::ALLPROPS); + + $this->assertEquals([ + 200 => [], + ], $propFind->getResultForMultiStatus()); + + } + + function testSet() { + + $propFind = new PropFind('foo', ['{DAV:}displayname']); + $propFind->set('{DAV:}displayname', 'bar'); + + $this->assertEquals([ + 200 => ['{DAV:}displayname' => 'bar'], + 404 => [], + ], $propFind->getResultForMultiStatus()); + + } + + function testSetAllpropCustom() { + + $propFind = new PropFind('foo', ['{DAV:}displayname'], 0, PropFind::ALLPROPS); + $propFind->set('{DAV:}customproperty', 'bar'); + + $this->assertEquals([ + 200 => ['{DAV:}customproperty' => 'bar'], + ], $propFind->getResultForMultiStatus()); + + } + + function testSetUnset() { + + $propFind = new PropFind('foo', ['{DAV:}displayname']); + $propFind->set('{DAV:}displayname', 'bar'); + $propFind->set('{DAV:}displayname', null); + + $this->assertEquals([ + 200 => [], + 404 => ['{DAV:}displayname' => null], + ], $propFind->getResultForMultiStatus()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropPatchTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropPatchTest.php new file mode 100644 index 00000000000..72dbf5345b5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropPatchTest.php @@ -0,0 +1,351 @@ +<?php + +namespace Sabre\DAV; + +class PropPatchTest extends \PHPUnit_Framework_TestCase { + + protected $propPatch; + + function setUp() { + + $this->propPatch = new PropPatch([ + '{DAV:}displayname' => 'foo', + ]); + $this->assertEquals(['{DAV:}displayname' => 'foo'], $this->propPatch->getMutations()); + + } + + function testHandleSingleSuccess() { + + $hasRan = false; + + $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { + $hasRan = true; + $this->assertEquals('foo', $value); + return true; + }); + + $this->assertTrue($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 200], $result); + + $this->assertTrue($hasRan); + + } + + function testHandleSingleFail() { + + $hasRan = false; + + $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { + $hasRan = true; + $this->assertEquals('foo', $value); + return false; + }); + + $this->assertFalse($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 403], $result); + + $this->assertTrue($hasRan); + + } + + function testHandleSingleCustomResult() { + + $hasRan = false; + + $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { + $hasRan = true; + $this->assertEquals('foo', $value); + return 201; + }); + + $this->assertTrue($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 201], $result); + + $this->assertTrue($hasRan); + + } + + function testHandleSingleDeleteSuccess() { + + $hasRan = false; + + $this->propPatch = new PropPatch(['{DAV:}displayname' => null]); + $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { + $hasRan = true; + $this->assertNull($value); + return true; + }); + + $this->assertTrue($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 204], $result); + + $this->assertTrue($hasRan); + + } + + + function testHandleNothing() { + + $hasRan = false; + + $this->propPatch->handle('{DAV:}foobar', function($value) use (&$hasRan) { + $hasRan = true; + }); + + $this->assertFalse($hasRan); + + } + + /** + * @depends testHandleSingleSuccess + */ + function testHandleRemaining() { + + $hasRan = false; + + $this->propPatch->handleRemaining(function($mutations) use (&$hasRan) { + $hasRan = true; + $this->assertEquals(['{DAV:}displayname' => 'foo'], $mutations); + return true; + }); + + $this->assertTrue($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 200], $result); + + $this->assertTrue($hasRan); + + } + function testHandleRemainingNothingToDo() { + + $hasRan = false; + + $this->propPatch->handle('{DAV:}displayname', function() {}); + $this->propPatch->handleRemaining(function($mutations) use (&$hasRan) { + $hasRan = true; + }); + + $this->assertFalse($hasRan); + + } + + function testSetResultCode() { + + $this->propPatch->setResultCode('{DAV:}displayname', 201); + $this->assertTrue($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 201], $result); + + } + + function testSetResultCodeFail() { + + $this->propPatch->setResultCode('{DAV:}displayname', 402); + $this->assertFalse($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 402], $result); + + } + + function testSetRemainingResultCode() { + + $this->propPatch->setRemainingResultCode(204); + $this->assertTrue($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 204], $result); + + } + + function testCommitNoHandler() { + + $this->assertFalse($this->propPatch->commit()); + $result = $this->propPatch->getResult(); + $this->assertEquals(['{DAV:}displayname' => 403], $result); + + } + + function testHandlerNotCalled() { + + $hasRan = false; + + $this->propPatch->setResultCode('{DAV:}displayname', 402); + $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { + $hasRan = true; + }); + + $this->propPatch->commit(); + + // The handler is not supposed to have ran + $this->assertFalse($hasRan); + + } + + function testDependencyFail() { + + $propPatch = new PropPatch([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + ]); + + $calledA = false; + $calledB = false; + + $propPatch->handle('{DAV:}a', function() use (&$calledA) { + $calledA = true; + return false; + }); + $propPatch->handle('{DAV:}b', function() use (&$calledB) { + $calledB = true; + return false; + }); + + $result = $propPatch->commit(); + $this->assertTrue($calledA); + $this->assertFalse($calledB); + + $this->assertFalse($result); + + $this->assertEquals([ + '{DAV:}a' => 403, + '{DAV:}b' => 424, + ], $propPatch->getResult()); + + } + + /** + * @expectedException \UnexpectedValueException + */ + function testHandleSingleBrokenResult() { + + $propPatch = new PropPatch([ + '{DAV:}a' => 'foo', + ]); + + $propPatch->handle('{DAV:}a', function() { + return []; + }); + $propPatch->commit(); + + } + + function testHandleMultiValueSuccess() { + + $propPatch = new PropPatch([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ]); + + $calledA = false; + + $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { + $calledA = true; + $this->assertEquals([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ], $properties); + return true; + }); + $result = $propPatch->commit(); + $this->assertTrue($calledA); + $this->assertTrue($result); + + $this->assertEquals([ + '{DAV:}a' => 200, + '{DAV:}b' => 200, + '{DAV:}c' => 204, + ], $propPatch->getResult()); + + } + + + function testHandleMultiValueFail() { + + $propPatch = new PropPatch([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ]); + + $calledA = false; + + $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { + $calledA = true; + $this->assertEquals([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ], $properties); + return false; + }); + $result = $propPatch->commit(); + $this->assertTrue($calledA); + $this->assertFalse($result); + + $this->assertEquals([ + '{DAV:}a' => 403, + '{DAV:}b' => 403, + '{DAV:}c' => 403, + ], $propPatch->getResult()); + + } + + function testHandleMultiValueCustomResult() { + + $propPatch = new PropPatch([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ]); + + $calledA = false; + + $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { + $calledA = true; + $this->assertEquals([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ], $properties); + + return [ + '{DAV:}a' => 201, + '{DAV:}b' => 204, + ]; + }); + $result = $propPatch->commit(); + $this->assertTrue($calledA); + $this->assertFalse($result); + + $this->assertEquals([ + '{DAV:}a' => 201, + '{DAV:}b' => 204, + '{DAV:}c' => 500, + ], $propPatch->getResult()); + + } + + /** + * @expectedException \UnexpectedValueException + */ + function testHandleMultiValueBroken() { + + $propPatch = new PropPatch([ + '{DAV:}a' => 'foo', + '{DAV:}b' => 'bar', + '{DAV:}c' => null, + ]); + + $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) { + return 'hi'; + }); + $propPatch->commit(); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/AbstractPDOTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/AbstractPDOTest.php new file mode 100644 index 00000000000..a2b9987b7e5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/AbstractPDOTest.php @@ -0,0 +1,193 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +use Sabre\DAV\PropFind; +use Sabre\DAV\PropPatch; +use Sabre\DAV\Xml\Property\Complex; +use Sabre\DAV\Xml\Property\Href; + +abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase { + + use \Sabre\DAV\DbTestHelperTrait; + + function getBackend() { + + $this->dropTables('propertystorage'); + $this->createSchema('propertystorage'); + + $pdo = $this->getPDO(); + + $pdo->exec("INSERT INTO propertystorage (path, name, valuetype, value) VALUES ('dir', '{DAV:}displayname', 1, 'Directory')"); + + return new PDO($this->getPDO()); + + } + + function testPropFind() { + + $backend = $this->getBackend(); + + $propFind = new PropFind('dir', ['{DAV:}displayname']); + $backend->propFind('dir', $propFind); + + $this->assertEquals('Directory', $propFind->get('{DAV:}displayname')); + + } + + function testPropFindNothingToDo() { + + $backend = $this->getBackend(); + + $propFind = new PropFind('dir', ['{DAV:}displayname']); + $propFind->set('{DAV:}displayname', 'foo'); + $backend->propFind('dir', $propFind); + + $this->assertEquals('foo', $propFind->get('{DAV:}displayname')); + + } + + /** + * @depends testPropFind + */ + function testPropPatchUpdate() { + + $backend = $this->getBackend(); + + $propPatch = new PropPatch(['{DAV:}displayname' => 'bar']); + $backend->propPatch('dir', $propPatch); + $propPatch->commit(); + + $propFind = new PropFind('dir', ['{DAV:}displayname']); + $backend->propFind('dir', $propFind); + + $this->assertEquals('bar', $propFind->get('{DAV:}displayname')); + + } + + /** + * @depends testPropPatchUpdate + */ + function testPropPatchComplex() { + + $backend = $this->getBackend(); + + $complex = new Complex('<foo xmlns="DAV:">somevalue</foo>'); + + $propPatch = new PropPatch(['{DAV:}complex' => $complex]); + $backend->propPatch('dir', $propPatch); + $propPatch->commit(); + + $propFind = new PropFind('dir', ['{DAV:}complex']); + $backend->propFind('dir', $propFind); + + $this->assertEquals($complex, $propFind->get('{DAV:}complex')); + + } + + + /** + * @depends testPropPatchComplex + */ + function testPropPatchCustom() { + + $backend = $this->getBackend(); + + $custom = new Href('/foo/bar/'); + + $propPatch = new PropPatch(['{DAV:}custom' => $custom]); + $backend->propPatch('dir', $propPatch); + $propPatch->commit(); + + $propFind = new PropFind('dir', ['{DAV:}custom']); + $backend->propFind('dir', $propFind); + + $this->assertEquals($custom, $propFind->get('{DAV:}custom')); + + } + + /** + * @depends testPropFind + */ + function testPropPatchRemove() { + + $backend = $this->getBackend(); + + $propPatch = new PropPatch(['{DAV:}displayname' => null]); + $backend->propPatch('dir', $propPatch); + $propPatch->commit(); + + $propFind = new PropFind('dir', ['{DAV:}displayname']); + $backend->propFind('dir', $propFind); + + $this->assertEquals(null, $propFind->get('{DAV:}displayname')); + + } + + /** + * @depends testPropFind + */ + function testDelete() { + + $backend = $this->getBackend(); + $backend->delete('dir'); + + $propFind = new PropFind('dir', ['{DAV:}displayname']); + $backend->propFind('dir', $propFind); + + $this->assertEquals(null, $propFind->get('{DAV:}displayname')); + + } + + /** + * @depends testPropFind + */ + function testMove() { + + $backend = $this->getBackend(); + // Creating a new child property. + $propPatch = new PropPatch(['{DAV:}displayname' => 'child']); + $backend->propPatch('dir/child', $propPatch); + $propPatch->commit(); + + $backend->move('dir', 'dir2'); + + // Old 'dir' + $propFind = new PropFind('dir', ['{DAV:}displayname']); + $backend->propFind('dir', $propFind); + $this->assertEquals(null, $propFind->get('{DAV:}displayname')); + + // Old 'dir/child' + $propFind = new PropFind('dir/child', ['{DAV:}displayname']); + $backend->propFind('dir/child', $propFind); + $this->assertEquals(null, $propFind->get('{DAV:}displayname')); + + // New 'dir2' + $propFind = new PropFind('dir2', ['{DAV:}displayname']); + $backend->propFind('dir2', $propFind); + $this->assertEquals('Directory', $propFind->get('{DAV:}displayname')); + + // New 'dir2/child' + $propFind = new PropFind('dir2/child', ['{DAV:}displayname']); + $backend->propFind('dir2/child', $propFind); + $this->assertEquals('child', $propFind->get('{DAV:}displayname')); + } + + /** + * @depends testPropFind + */ + function testDeepDelete() { + + $backend = $this->getBackend(); + $propPatch = new PropPatch(['{DAV:}displayname' => 'child']); + $backend->propPatch('dir/child', $propPatch); + $propPatch->commit(); + $backend->delete('dir'); + + $propFind = new PropFind('dir/child', ['{DAV:}displayname']); + $backend->propFind('dir/child', $propFind); + + $this->assertEquals(null, $propFind->get('{DAV:}displayname')); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/Mock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/Mock.php new file mode 100644 index 00000000000..cf4c88fb85f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/Mock.php @@ -0,0 +1,117 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +use Sabre\DAV\PropFind; +use Sabre\DAV\PropPatch; + +class Mock implements BackendInterface { + + public $data = []; + + /** + * Fetches properties for a path. + * + * This method received a PropFind object, which contains all the + * information about the properties that need to be fetched. + * + * Usually you would just want to call 'get404Properties' on this object, + * as this will give you the _exact_ list of properties that need to be + * fetched, and haven't yet. + * + * @param string $path + * @param PropFind $propFind + * @return void + */ + function propFind($path, PropFind $propFind) { + + if (!isset($this->data[$path])) { + return; + } + + foreach ($this->data[$path] as $name => $value) { + $propFind->set($name, $value); + } + + } + + /** + * Updates properties for a path + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * Usually you would want to call 'handleRemaining' on this object, to get; + * a list of all properties that need to be stored. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch) { + + if (!isset($this->data[$path])) { + $this->data[$path] = []; + } + $propPatch->handleRemaining(function($properties) use ($path) { + + foreach ($properties as $propName => $propValue) { + + if (is_null($propValue)) { + unset($this->data[$path][$propName]); + } else { + $this->data[$path][$propName] = $propValue; + } + return true; + + } + + }); + + } + + /** + * This method is called after a node is deleted. + * + * This allows a backend to clean up all associated properties. + * + * @param string $path + * @return void + */ + function delete($path) { + + unset($this->data[$path]); + + } + + /** + * This method is called after a successful MOVE + * + * This should be used to migrate all properties from one path to another. + * Note that entire collections may be moved, so ensure that all properties + * for children are also moved along. + * + * @param string $source + * @param string $destination + * @return void + */ + function move($source, $destination) { + + foreach ($this->data as $path => $props) { + + if ($path === $source) { + $this->data[$destination] = $props; + unset($this->data[$path]); + continue; + } + + if (strpos($path, $source . '/') === 0) { + $this->data[$destination . substr($path, strlen($source) + 1)] = $props; + unset($this->data[$path]); + } + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOMysqlTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOMysqlTest.php new file mode 100644 index 00000000000..b92b034df84 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOMysqlTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +class PDOMysqlTest extends AbstractPDOTest { + + public $driver = 'mysql'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOPgSqlTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOPgSqlTest.php new file mode 100644 index 00000000000..616c2e67a6c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOPgSqlTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +class PDOPgSqlTest extends AbstractPDOTest { + + public $driver = 'pgsql'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOSqliteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOSqliteTest.php new file mode 100644 index 00000000000..20a6a09e5ba --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/Backend/PDOSqliteTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +class PDOSqliteTest extends AbstractPDOTest { + + public $driver = 'sqlite'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/PluginTest.php new file mode 100644 index 00000000000..130f1490f17 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/PropertyStorage/PluginTest.php @@ -0,0 +1,117 @@ +<?php + +namespace Sabre\DAV\PropertyStorage; + +class PluginTest extends \Sabre\DAVServerTest { + + protected $backend; + protected $plugin; + + protected $setupFiles = true; + + function setUp() { + + parent::setUp(); + $this->backend = new Backend\Mock(); + $this->plugin = new Plugin( + $this->backend + ); + + $this->server->addPlugin($this->plugin); + + } + + function testGetInfo() { + + $this->assertArrayHasKey( + 'name', + $this->plugin->getPluginInfo() + ); + + } + + function testSetProperty() { + + $this->server->updateProperties('', ['{DAV:}displayname' => 'hi']); + $this->assertEquals([ + '' => [ + '{DAV:}displayname' => 'hi', + ] + ], $this->backend->data); + + } + + /** + * @depends testSetProperty + */ + function testGetProperty() { + + $this->testSetProperty(); + $result = $this->server->getProperties('', ['{DAV:}displayname']); + + $this->assertEquals([ + '{DAV:}displayname' => 'hi', + ], $result); + + } + + /** + * @depends testSetProperty + */ + function testDeleteProperty() { + + $this->testSetProperty(); + $this->server->emit('afterUnbind', ['']); + $this->assertEquals([], $this->backend->data); + + } + + function testMove() { + + $this->server->tree->getNodeForPath('files')->createFile('source'); + $this->server->updateProperties('files/source', ['{DAV:}displayname' => 'hi']); + + $request = new \Sabre\HTTP\Request('MOVE', '/files/source', ['Destination' => '/files/dest']); + $this->assertHTTPStatus(201, $request); + + $result = $this->server->getProperties('/files/dest', ['{DAV:}displayname']); + + $this->assertEquals([ + '{DAV:}displayname' => 'hi', + ], $result); + + $this->server->tree->getNodeForPath('files')->createFile('source'); + $result = $this->server->getProperties('/files/source', ['{DAV:}displayname']); + + $this->assertEquals([], $result); + + } + + /** + * @depends testDeleteProperty + */ + function testSetPropertyInFilteredPath() { + + $this->plugin->pathFilter = function($path) { + + return false; + + }; + + $this->server->updateProperties('', ['{DAV:}displayname' => 'hi']); + $this->assertEquals([], $this->backend->data); + + } + + /** + * @depends testSetPropertyInFilteredPath + */ + function testGetPropertyInFilteredPath() { + + $this->testSetPropertyInFilteredPath(); + $result = $this->server->getProperties('', ['{DAV:}displayname']); + + $this->assertEquals([], $result); + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php new file mode 100644 index 00000000000..42759647ab0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerEventsTest.php @@ -0,0 +1,126 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/DAV/AbstractServer.php'; + +class ServerEventsTest extends AbstractServer { + + private $tempPath; + + private $exception; + + function testAfterBind() { + + $this->server->on('afterBind', [$this, 'afterBindHandler']); + $newPath = 'afterBind'; + + $this->tempPath = ''; + $this->server->createFile($newPath, 'body'); + $this->assertEquals($newPath, $this->tempPath); + + } + + function afterBindHandler($path) { + + $this->tempPath = $path; + + } + + function testAfterResponse() { + + $mock = $this->getMockBuilder('stdClass') + ->setMethods(['afterResponseCallback']) + ->getMock(); + $mock->expects($this->once())->method('afterResponseCallback'); + + $this->server->on('afterResponse', [$mock, 'afterResponseCallback']); + + $this->server->httpRequest = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => '/test.txt', + ]); + + $this->server->exec(); + + } + + function testBeforeBindCancel() { + + $this->server->on('beforeBind', [$this, 'beforeBindCancelHandler']); + $this->assertFalse($this->server->createFile('bla', 'body')); + + // Also testing put() + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'PUT', + 'REQUEST_URI' => '/barbar', + ]); + + $this->server->httpRequest = $req; + $this->server->exec(); + + $this->assertEquals(500, $this->server->httpResponse->getStatus()); + + } + + function beforeBindCancelHandler($path) { + + return false; + + } + + function testException() { + + $this->server->on('exception', [$this, 'exceptionHandler']); + + $req = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => '/not/exisitng', + ]); + $this->server->httpRequest = $req; + $this->server->exec(); + + $this->assertInstanceOf('Sabre\\DAV\\Exception\\NotFound', $this->exception); + + } + + function exceptionHandler(Exception $exception) { + + $this->exception = $exception; + + } + + function testMethod() { + + $k = 1; + $this->server->on('method', function($request, $response) use (&$k) { + + $k += 1; + + return false; + + }); + $this->server->on('method', function($request, $response) use (&$k) { + + $k += 2; + + return false; + + }); + + try { + $this->server->invokeMethod( + new HTTP\Request('BLABLA', '/'), + new HTTP\Response(), + false + ); + } catch (Exception $e) {} + + // Fun fact, PHP 7.1 changes the order when sorting-by-callback. + $this->assertTrue($k >= 2 && $k <= 3); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php new file mode 100644 index 00000000000..557eddbbcf6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerMKCOLTest.php @@ -0,0 +1,366 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +class ServerMKCOLTest extends AbstractServer { + + function testMkcol() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody(""); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + ], $this->response->getHeaders()); + + $this->assertEquals(201, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertTrue(is_dir($this->tempDir . '/testcol')); + + } + + /** + * @depends testMkcol + */ + function testMKCOLUnknownBody() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody("Hello"); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(415, $this->response->status); + + } + + /** + * @depends testMkcol + */ + function testMKCOLBrokenXML() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody("Hello"); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(400, $this->response->getStatus(), $this->response->getBodyAsString()); + + } + + /** + * @depends testMkcol + */ + function testMKCOLUnknownXML() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?><html></html>'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(400, $this->response->getStatus()); + + } + + /** + * @depends testMkcol + */ + function testMKCOLNoResourceType() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<mkcol xmlns="DAV:"> + <set> + <prop> + <displayname>Evert</displayname> + </prop> + </set> +</mkcol>'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(400, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMkcol + */ + function testMKCOLIncorrectResourceType() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<mkcol xmlns="DAV:"> + <set> + <prop> + <resourcetype><collection /><blabla /></resourcetype> + </prop> + </set> +</mkcol>'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(403, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType + */ + function testMKCOLSuccess() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<mkcol xmlns="DAV:"> + <set> + <prop> + <resourcetype><collection /></resourcetype> + </prop> + </set> +</mkcol>'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + ], $this->response->getHeaders()); + + $this->assertEquals(201, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType + */ + function testMKCOLWhiteSpaceResourceType() { + + $serverVars = [ + 'REQUEST_URI' => '/testcol', + 'REQUEST_METHOD' => 'MKCOL', + 'HTTP_CONTENT_TYPE' => 'application/xml', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody('<?xml version="1.0"?> +<mkcol xmlns="DAV:"> + <set> + <prop> + <resourcetype> + <collection /> + </resourcetype> + </prop> + </set> +</mkcol>'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Length' => ['0'], + ], $this->response->getHeaders()); + + $this->assertEquals(201, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType + */ + function testMKCOLNoParent() { + + $serverVars = [ + 'REQUEST_URI' => '/testnoparent/409me', + 'REQUEST_METHOD' => 'MKCOL', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody(''); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(409, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType + */ + function testMKCOLParentIsNoCollection() { + + $serverVars = [ + 'REQUEST_URI' => '/test.txt/409me', + 'REQUEST_METHOD' => 'MKCOL', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody(''); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(409, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMKCOLIncorrectResourceType + */ + function testMKCOLAlreadyExists() { + + $serverVars = [ + 'REQUEST_URI' => '/test.txt', + 'REQUEST_METHOD' => 'MKCOL', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody(''); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'], + ], $this->response->getHeaders()); + + $this->assertEquals(405, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + } + + /** + * @depends testMKCOLSuccess + * @depends testMKCOLAlreadyExists + */ + function testMKCOLAndProps() { + + $request = new HTTP\Request( + 'MKCOL', + '/testcol', + ['Content-Type' => 'application/xml'] + ); + $request->setBody('<?xml version="1.0"?> +<mkcol xmlns="DAV:"> + <set> + <prop> + <resourcetype><collection /></resourcetype> + <displayname>my new collection</displayname> + </prop> + </set> +</mkcol>'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Wrong statuscode received. Full response body: ' . $this->response->body); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $responseBody = $this->response->getBodyAsString(); + + $expected = <<<XML +<?xml version="1.0"?> +<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:response> + <d:href>/testcol</d:href> + <d:propstat> + <d:prop> + <d:displayname /> + </d:prop> + <d:status>HTTP/1.1 403 Forbidden</d:status> + </d:propstat> + </d:response> +</d:multistatus> +XML; + + $this->assertXmlStringEqualsXmlString( + $expected, + $responseBody + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php new file mode 100644 index 00000000000..fa67102cc43 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPluginTest.php @@ -0,0 +1,108 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/DAV/AbstractServer.php'; +require_once 'Sabre/DAV/TestPlugin.php'; + +class ServerPluginTest extends AbstractServer { + + /** + * @var Sabre\DAV\TestPlugin + */ + protected $testPlugin; + + function setUp() { + + parent::setUp(); + + $testPlugin = new TestPlugin(); + $this->server->addPlugin($testPlugin); + $this->testPlugin = $testPlugin; + + } + + /** + */ + function testBaseClass() { + + $p = new ServerPluginMock(); + $this->assertEquals([], $p->getFeatures()); + $this->assertEquals([], $p->getHTTPMethods('')); + $this->assertEquals( + [ + 'name' => 'Sabre\DAV\ServerPluginMock', + 'description' => null, + 'link' => null + ], $p->getPluginInfo() + ); + + } + + function testOptions() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'OPTIONS', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'DAV' => ['1, 3, extended-mkcol, drinking'], + 'MS-Author-Via' => ['DAV'], + 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, BEER, WINE'], + 'Accept-Ranges' => ['bytes'], + 'Content-Length' => ['0'], + 'X-Sabre-Version' => [Version::VERSION], + ], $this->response->getHeaders()); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals('', $this->response->body); + $this->assertEquals('OPTIONS', $this->testPlugin->beforeMethod); + + + } + + function testGetPlugin() { + + $this->assertEquals($this->testPlugin, $this->server->getPlugin(get_class($this->testPlugin))); + + } + + function testUnknownPlugin() { + + $this->assertNull($this->server->getPlugin('SomeRandomClassName')); + + } + + function testGetSupportedReportSet() { + + $this->assertEquals([], $this->testPlugin->getSupportedReportSet('/')); + + } + + function testGetPlugins() { + + $this->assertEquals( + [ + get_class($this->testPlugin) => $this->testPlugin, + 'core' => $this->server->getPlugin('core'), + ], + $this->server->getPlugins() + ); + + } + + +} + +class ServerPluginMock extends ServerPlugin { + + function initialize(Server $s) { } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php new file mode 100644 index 00000000000..203cf26d9d4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPreconditionTest.php @@ -0,0 +1,344 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; + +class ServerPreconditionsTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException Sabre\DAV\Exception\PreconditionFailed + */ + function testIfMatchNoNode() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/bar', ['If-Match' => '*']); + $httpResponse = new HTTP\Response(); + $server->checkPreconditions($httpRequest, $httpResponse); + + } + + /** + */ + function testIfMatchHasNode() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '*']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + * @expectedException Sabre\DAV\Exception\PreconditionFailed + */ + function testIfMatchWrongEtag() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '1234']); + $httpResponse = new HTTP\Response(); + $server->checkPreconditions($httpRequest, $httpResponse); + + } + + /** + */ + function testIfMatchCorrectEtag() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '"abc123"']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + * Evolution sometimes uses \" instead of " for If-Match headers. + * + * @depends testIfMatchCorrectEtag + */ + function testIfMatchEvolutionEtag() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '\\"abc123\\"']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + */ + function testIfMatchMultiple() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '"hellothere", "abc123"']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + */ + function testIfNoneMatchNoNode() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/bar', ['If-None-Match' => '*']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + * @expectedException Sabre\DAV\Exception\PreconditionFailed + */ + function testIfNoneMatchHasNode() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '*']); + $httpResponse = new HTTP\Response(); + $server->checkPreconditions($httpRequest, $httpResponse); + + } + + /** + */ + function testIfNoneMatchWrongEtag() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234"']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + */ + function testIfNoneMatchWrongEtagMultiple() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234", "5678"']); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + * @expectedException Sabre\DAV\Exception\PreconditionFailed + */ + function testIfNoneMatchCorrectEtag() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"abc123"']); + $httpResponse = new HTTP\Response(); + $server->checkPreconditions($httpRequest, $httpResponse); + + } + + /** + * @expectedException Sabre\DAV\Exception\PreconditionFailed + */ + function testIfNoneMatchCorrectEtagMultiple() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234, "abc123"']); + $httpResponse = new HTTP\Response(); + $server->checkPreconditions($httpRequest, $httpResponse); + + } + + /** + */ + function testIfNoneMatchCorrectEtagAsGet() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = new HTTP\Request('GET', '/foo', ['If-None-Match' => '"abc123"']); + $server->httpResponse = new HTTP\ResponseMock(); + + $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse)); + $this->assertEquals(304, $server->httpResponse->getStatus()); + $this->assertEquals(['ETag' => ['"abc123"']], $server->httpResponse->getHeaders()); + + } + + /** + * This was a test written for issue #515. + */ + function testNoneMatchCorrectEtagEnsureSapiSent() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $server->sapi = new HTTP\SapiMock(); + HTTP\SapiMock::$sent = 0; + $httpRequest = new HTTP\Request('GET', '/foo', ['If-None-Match' => '"abc123"']); + $server->httpRequest = $httpRequest; + $server->httpResponse = new HTTP\ResponseMock(); + + $server->exec(); + + $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse)); + $this->assertEquals(304, $server->httpResponse->getStatus()); + $this->assertEquals([ + 'ETag' => ['"abc123"'], + 'X-Sabre-Version' => [Version::VERSION], + ], $server->httpResponse->getHeaders()); + $this->assertEquals(1, HTTP\SapiMock::$sent); + + } + + /** + */ + function testIfModifiedSinceUnModified() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + ]); + $server->httpResponse = new HTTP\ResponseMock(); + $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse)); + + $this->assertEquals(304, $server->httpResponse->status); + $this->assertEquals([ + 'Last-Modified' => ['Sat, 06 Apr 1985 23:30:00 GMT'], + ], $server->httpResponse->getHeaders()); + + } + + + /** + */ + function testIfModifiedSinceModified() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_MODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + ]); + + $httpResponse = new HTTP\ResponseMock(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + */ + function testIfModifiedSinceInvalidDate() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_MODIFIED_SINCE' => 'Your mother', + 'REQUEST_URI' => '/foo' + ]); + $httpResponse = new HTTP\ResponseMock(); + + // Invalid dates must be ignored, so this should return true + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + /** + */ + function testIfModifiedSinceInvalidDate2() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 EST', + 'REQUEST_URI' => '/foo' + ]); + $httpResponse = new HTTP\ResponseMock(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + + /** + */ + function testIfUnmodifiedSinceUnModified() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + ]); + $httpResponse = new HTTP\Response(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + + /** + * @expectedException Sabre\DAV\Exception\PreconditionFailed + */ + function testIfUnmodifiedSinceModified() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_UNMODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT', + 'REQUEST_URI' => '/foo' + ]); + $httpResponse = new HTTP\ResponseMock(); + $server->checkPreconditions($httpRequest, $httpResponse); + + } + + /** + */ + function testIfUnmodifiedSinceInvalidDate() { + + $root = new SimpleCollection('root', [new ServerPreconditionsNode()]); + $server = new Server($root); + $httpRequest = HTTP\Sapi::createFromServerArray([ + 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1984 08:49:37 CET', + 'REQUEST_URI' => '/foo' + ]); + $httpResponse = new HTTP\ResponseMock(); + $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); + + } + + +} + +class ServerPreconditionsNode extends File { + + function getETag() { + + return '"abc123"'; + + } + + function getLastModified() { + + /* my birthday & time, I believe */ + return strtotime('1985-04-07 01:30 +02:00'); + + } + + function getName() { + + return 'foo'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPropsInfiniteDepthTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPropsInfiniteDepthTest.php new file mode 100644 index 00000000000..c968e72008a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPropsInfiniteDepthTest.php @@ -0,0 +1,163 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/DAV/AbstractServer.php'; + +class ServerPropsInfiniteDepthTest extends AbstractServer { + + protected function getRootNode() { + + return new FSExt\Directory(SABRE_TEMPDIR); + + } + + function setUp() { + + if (file_exists(SABRE_TEMPDIR . '../.sabredav')) unlink(SABRE_TEMPDIR . '../.sabredav'); + parent::setUp(); + file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2'); + mkdir(SABRE_TEMPDIR . '/col'); + mkdir(SABRE_TEMPDIR . '/col/col'); + file_put_contents(SABRE_TEMPDIR . 'col/col/test.txt', 'Test contents'); + $this->server->addPlugin(new Locks\Plugin(new Locks\Backend\File(SABRE_TEMPDIR . '/.locksdb'))); + $this->server->enablePropfindDepthInfinity = true; + + } + + function tearDown() { + + parent::tearDown(); + if (file_exists(SABRE_TEMPDIR . '../.locksdb')) unlink(SABRE_TEMPDIR . '../.locksdb'); + + } + + private function sendRequest($body) { + + $request = new HTTP\Request('PROPFIND', '/', ['Depth' => 'infinity']); + $request->setBody($body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + } + + function testPropFindEmptyBody() { + + $this->sendRequest(""); + + $this->assertEquals(207, $this->response->status, 'Incorrect status received. Full response body: ' . $this->response->getBodyAsString()); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'DAV' => ['1, 3, extended-mkcol, 2'], + 'Vary' => ['Brief,Prefer'], + ], + $this->response->getHeaders() + ); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); + $this->assertEquals('/', (string)$data, 'href element should have been /'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); + // 8 resources are to be returned: /, col, col/col, col/col/test.txt, dir, dir/child.txt, test.txt and test2.txt + $this->assertEquals(8, count($data)); + + } + + function testSupportedLocks() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:supportedlock /> + </d:prop> +</d:propfind>'; + + $this->sendRequest($xml); + + $body = $this->response->getBodyAsString(); + $this->assertEquals(207, $this->response->getStatus(), $body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry'); + $this->assertEquals(16, count($data), 'We expected sixteen \'d:lockentry\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope'); + $this->assertEquals(16, count($data), 'We expected sixteen \'d:lockscope\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype'); + $this->assertEquals(16, count($data), 'We expected sixteen \'d:locktype\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:shared'); + $this->assertEquals(8, count($data), 'We expected eight \'d:shared\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:exclusive'); + $this->assertEquals(8, count($data), 'We expected eight \'d:exclusive\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype/d:write'); + $this->assertEquals(16, count($data), 'We expected sixteen \'d:write\' tags'); + } + + function testLockDiscovery() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:lockdiscovery /> + </d:prop> +</d:propfind>'; + + $this->sendRequest($xml); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery'); + $this->assertEquals(8, count($data), 'We expected eight \'d:lockdiscovery\' tags'); + + } + + function testUnknownProperty() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:macaroni /> + </d:prop> +</d:propfind>'; + + $this->sendRequest($xml); + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + $pathTests = [ + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:status', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:macaroni', + ]; + foreach ($pathTests as $test) { + $this->assertTrue(count($xml->xpath($test)) == true, 'We expected the ' . $test . ' element to appear in the response, we got: ' . $body); + } + + $val = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(8, count($val), $body); + $this->assertEquals('HTTP/1.1 404 Not Found', (string)$val[0]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php new file mode 100644 index 00000000000..253200be7cf --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerPropsTest.php @@ -0,0 +1,201 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; +require_once 'Sabre/DAV/AbstractServer.php'; + +class ServerPropsTest extends AbstractServer { + + protected function getRootNode() { + + return new FSExt\Directory(SABRE_TEMPDIR); + + } + + function setUp() { + + if (file_exists(SABRE_TEMPDIR . '../.sabredav')) unlink(SABRE_TEMPDIR . '../.sabredav'); + parent::setUp(); + file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2'); + mkdir(SABRE_TEMPDIR . '/col'); + file_put_contents(SABRE_TEMPDIR . 'col/test.txt', 'Test contents'); + $this->server->addPlugin(new Locks\Plugin(new Locks\Backend\File(SABRE_TEMPDIR . '/.locksdb'))); + + } + + function tearDown() { + + parent::tearDown(); + if (file_exists(SABRE_TEMPDIR . '../.locksdb')) unlink(SABRE_TEMPDIR . '../.locksdb'); + + } + + private function sendRequest($body, $path = '/', $headers = ['Depth' => '0']) { + + $request = new HTTP\Request('PROPFIND', $path, $headers, $body); + + $this->server->httpRequest = $request; + $this->server->exec(); + + } + + function testPropFindEmptyBody() { + + $this->sendRequest(""); + $this->assertEquals(207, $this->response->status); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'DAV' => ['1, 3, extended-mkcol, 2'], + 'Vary' => ['Brief,Prefer'], + ], + $this->response->getHeaders() + ); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); + $this->assertEquals('/', (string)$data, 'href element should have been /'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); + $this->assertEquals(1, count($data)); + + } + + function testPropFindEmptyBodyFile() { + + $this->sendRequest("", '/test2.txt', []); + $this->assertEquals(207, $this->response->status); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'DAV' => ['1, 3, extended-mkcol, 2'], + 'Vary' => ['Brief,Prefer'], + ], + $this->response->getHeaders() + ); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); + $this->assertEquals('/test2.txt', (string)$data, 'href element should have been /test2.txt'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength'); + $this->assertEquals(1, count($data)); + + } + + function testSupportedLocks() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:supportedlock /> + </d:prop> +</d:propfind>'; + + $this->sendRequest($xml); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry'); + $this->assertEquals(2, count($data), 'We expected two \'d:lockentry\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope'); + $this->assertEquals(2, count($data), 'We expected two \'d:lockscope\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype'); + $this->assertEquals(2, count($data), 'We expected two \'d:locktype\' tags'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:shared'); + $this->assertEquals(1, count($data), 'We expected a \'d:shared\' tag'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:exclusive'); + $this->assertEquals(1, count($data), 'We expected a \'d:exclusive\' tag'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype/d:write'); + $this->assertEquals(2, count($data), 'We expected two \'d:write\' tags'); + } + + function testLockDiscovery() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:lockdiscovery /> + </d:prop> +</d:propfind>'; + + $this->sendRequest($xml); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery'); + $this->assertEquals(1, count($data), 'We expected a \'d:lockdiscovery\' tag'); + + } + + function testUnknownProperty() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:macaroni /> + </d:prop> +</d:propfind>'; + + $this->sendRequest($xml); + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + $pathTests = [ + '/d:multistatus', + '/d:multistatus/d:response', + '/d:multistatus/d:response/d:propstat', + '/d:multistatus/d:response/d:propstat/d:status', + '/d:multistatus/d:response/d:propstat/d:prop', + '/d:multistatus/d:response/d:propstat/d:prop/d:macaroni', + ]; + foreach ($pathTests as $test) { + $this->assertTrue(count($xml->xpath($test)) == true, 'We expected the ' . $test . ' element to appear in the response, we got: ' . $body); + } + + $val = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1, count($val), $body); + $this->assertEquals('HTTP/1.1 404 Not Found', (string)$val[0]); + + } + + function testParsePropPatchRequest() { + + $body = '<?xml version="1.0"?> +<d:propertyupdate xmlns:d="DAV:" xmlns:s="http://sabredav.org/NS/test"> + <d:set><d:prop><s:someprop>somevalue</s:someprop></d:prop></d:set> + <d:remove><d:prop><s:someprop2 /></d:prop></d:remove> + <d:set><d:prop><s:someprop3>removeme</s:someprop3></d:prop></d:set> + <d:remove><d:prop><s:someprop3 /></d:prop></d:remove> +</d:propertyupdate>'; + + $result = $this->server->xml->parse($body); + $this->assertEquals([ + '{http://sabredav.org/NS/test}someprop' => 'somevalue', + '{http://sabredav.org/NS/test}someprop2' => null, + '{http://sabredav.org/NS/test}someprop3' => null, + ], $result->properties); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php new file mode 100644 index 00000000000..81224d687c3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerRangeTest.php @@ -0,0 +1,262 @@ +<?php + +namespace Sabre\DAV; + +use DateTime; +use Sabre\HTTP; + +/** + * This file tests HTTP requests that use the Range: header. + * + * @copyright Copyright (C) fruux GmbH. (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ServerRangeTest extends \Sabre\DAVServerTest { + + protected $setupFiles = true; + + /** + * We need this string a lot + */ + protected $lastModified; + + function setUp() { + + parent::setUp(); + $this->server->createFile('files/test.txt', 'Test contents'); + + $this->lastModified = HTTP\Util::toHTTPDate( + new DateTime('@' . $this->server->tree->getNodeForPath('files/test.txt')->getLastModified()) + ); + + $stream = popen('echo "Test contents"', 'r'); + $streamingFile = new Mock\StreamingFile( + 'no-seeking.txt', + $stream + ); + $streamingFile->setSize(12); + $this->server->tree->getNodeForPath('files')->addNode($streamingFile); + + } + + function testRange() { + + $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=2-5']); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [4], + 'Content-Range' => ['bytes 2-5/13'], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + $this->assertEquals(206, $response->getStatus()); + $this->assertEquals('st c', $response->getBodyAsString()); + + } + + /** + * @depends testRange + */ + function testStartRange() { + + $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=2-']); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [11], + 'Content-Range' => ['bytes 2-12/13'], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals(206, $response->getStatus()); + $this->assertEquals('st contents', $response->getBodyAsString()); + + } + + /** + * @depends testRange + */ + function testEndRange() { + + $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=-8']); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [8], + 'Content-Range' => ['bytes 5-12/13'], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals(206, $response->getStatus()); + $this->assertEquals('contents', $response->getBodyAsString()); + + } + + /** + * @depends testRange + */ + function testTooHighRange() { + + $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=100-200']); + $response = $this->request($request); + + $this->assertEquals(416, $response->getStatus()); + + } + + /** + * @depends testRange + */ + function testCrazyRange() { + + $request = new HTTP\Request('GET', '/files/test.txt', ['Range' => 'bytes=8-4']); + $response = $this->request($request); + + $this->assertEquals(416, $response->getStatus()); + + } + + function testNonSeekableStream() { + + $request = new HTTP\Request('GET', '/files/no-seeking.txt', ['Range' => 'bytes=2-5']); + $response = $this->request($request); + + $this->assertEquals(206, $response->getStatus(), $response); + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [4], + 'Content-Range' => ['bytes 2-5/12'], + // 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals('st c', $response->getBodyAsString()); + + } + + /** + * @depends testRange + */ + function testIfRangeEtag() { + + $request = new HTTP\Request('GET', '/files/test.txt', [ + 'Range' => 'bytes=2-5', + 'If-Range' => '"' . md5('Test contents') . '"', + ]); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [4], + 'Content-Range' => ['bytes 2-5/13'], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals(206, $response->getStatus()); + $this->assertEquals('st c', $response->getBodyAsString()); + + } + + /** + * @depends testIfRangeEtag + */ + function testIfRangeEtagIncorrect() { + + $request = new HTTP\Request('GET', '/files/test.txt', [ + 'Range' => 'bytes=2-5', + 'If-Range' => '"foobar"', + ]); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [13], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals('Test contents', $response->getBodyAsString()); + + } + + /** + * @depends testIfRangeEtag + */ + function testIfRangeModificationDate() { + + $request = new HTTP\Request('GET', '/files/test.txt', [ + 'Range' => 'bytes=2-5', + 'If-Range' => 'tomorrow', + ]); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [4], + 'Content-Range' => ['bytes 2-5/13'], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals(206, $response->getStatus()); + $this->assertEquals('st c', $response->getBodyAsString()); + + } + + /** + * @depends testIfRangeModificationDate + */ + function testIfRangeModificationDateModified() { + + $request = new HTTP\Request('GET', '/files/test.txt', [ + 'Range' => 'bytes=2-5', + 'If-Range' => '-2 years', + ]); + $response = $this->request($request); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [13], + 'ETag' => ['"' . md5('Test contents') . '"'], + 'Last-Modified' => [$this->lastModified], + ], + $response->getHeaders() + ); + + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals('Test contents', $response->getBodyAsString()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php new file mode 100644 index 00000000000..043179a0051 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerSimpleTest.php @@ -0,0 +1,475 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +class ServerSimpleTest extends AbstractServer{ + + function testConstructArray() { + + $nodes = [ + new SimpleCollection('hello') + ]; + + $server = new Server($nodes); + $this->assertEquals($nodes[0], $server->tree->getNodeForPath('hello')); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testConstructIncorrectObj() { + + $nodes = [ + new SimpleCollection('hello'), + new \STDClass(), + ]; + + $server = new Server($nodes); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testConstructInvalidArg() { + + $server = new Server(1); + + } + + function testOptions() { + + $request = new HTTP\Request('OPTIONS', '/'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals([ + 'DAV' => ['1, 3, extended-mkcol'], + 'MS-Author-Via' => ['DAV'], + 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'], + 'Accept-Ranges' => ['bytes'], + 'Content-Length' => ['0'], + 'X-Sabre-Version' => [Version::VERSION], + ], $this->response->getHeaders()); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals('', $this->response->body); + + } + + function testOptionsUnmapped() { + + $request = new HTTP\Request('OPTIONS', '/unmapped'); + $this->server->httpRequest = $request; + + $this->server->exec(); + + $this->assertEquals([ + 'DAV' => ['1, 3, extended-mkcol'], + 'MS-Author-Via' => ['DAV'], + 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, MKCOL'], + 'Accept-Ranges' => ['bytes'], + 'Content-Length' => ['0'], + 'X-Sabre-Version' => [Version::VERSION], + ], $this->response->getHeaders()); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals('', $this->response->body); + + } + + function testNonExistantMethod() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'BLABLA', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(501, $this->response->status); + + + } + + function testBaseUri() { + + $serverVars = [ + 'REQUEST_URI' => '/blabla/test.txt', + 'REQUEST_METHOD' => 'GET', + ]; + $filename = $this->tempDir . '/test.txt'; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->setBaseUri('/blabla/'); + $this->assertEquals('/blabla/', $this->server->getBaseUri()); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/octet-stream'], + 'Content-Length' => [13], + 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($filename)))], + 'ETag' => ['"' . sha1(fileinode($filename) . filesize($filename) . filemtime($filename)) . '"'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals('Test contents', stream_get_contents($this->response->body)); + + } + + function testBaseUriAddSlash() { + + $tests = [ + '/' => '/', + '/foo' => '/foo/', + '/foo/' => '/foo/', + '/foo/bar' => '/foo/bar/', + '/foo/bar/' => '/foo/bar/', + ]; + + foreach ($tests as $test => $result) { + $this->server->setBaseUri($test); + + $this->assertEquals($result, $this->server->getBaseUri()); + + } + + } + + function testCalculateUri() { + + $uris = [ + 'http://www.example.org/root/somepath', + '/root/somepath', + '/root/somepath/', + ]; + + $this->server->setBaseUri('/root/'); + + foreach ($uris as $uri) { + + $this->assertEquals('somepath', $this->server->calculateUri($uri)); + + } + + $this->server->setBaseUri('/root'); + + foreach ($uris as $uri) { + + $this->assertEquals('somepath', $this->server->calculateUri($uri)); + + } + + $this->assertEquals('', $this->server->calculateUri('/root')); + + } + + function testCalculateUriSpecialChars() { + + $uris = [ + 'http://www.example.org/root/%C3%A0fo%C3%B3', + '/root/%C3%A0fo%C3%B3', + '/root/%C3%A0fo%C3%B3/' + ]; + + $this->server->setBaseUri('/root/'); + + foreach ($uris as $uri) { + + $this->assertEquals("\xc3\xa0fo\xc3\xb3", $this->server->calculateUri($uri)); + + } + + $this->server->setBaseUri('/root'); + + foreach ($uris as $uri) { + + $this->assertEquals("\xc3\xa0fo\xc3\xb3", $this->server->calculateUri($uri)); + + } + + $this->server->setBaseUri('/'); + + foreach ($uris as $uri) { + + $this->assertEquals("root/\xc3\xa0fo\xc3\xb3", $this->server->calculateUri($uri)); + + } + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testCalculateUriBreakout() { + + $uri = '/path1/'; + + $this->server->setBaseUri('/path2/'); + $this->server->calculateUri($uri); + + } + + /** + */ + function testGuessBaseUri() { + + $serverVars = [ + 'REQUEST_URI' => '/index.php/root', + 'PATH_INFO' => '/root', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + /** + * @depends testGuessBaseUri + */ + function testGuessBaseUriPercentEncoding() { + + $serverVars = [ + 'REQUEST_URI' => '/index.php/dir/path2/path%20with%20spaces', + 'PATH_INFO' => '/dir/path2/path with spaces', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + /** + * @depends testGuessBaseUri + */ + /* + function testGuessBaseUriPercentEncoding2() { + + $this->markTestIncomplete('This behaviour is not yet implemented'); + $serverVars = [ + 'REQUEST_URI' => '/some%20directory+mixed/index.php/dir/path2/path%20with%20spaces', + 'PATH_INFO' => '/dir/path2/path with spaces', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/some%20directory+mixed/index.php/', $server->guessBaseUri()); + + }*/ + + function testGuessBaseUri2() { + + $serverVars = [ + 'REQUEST_URI' => '/index.php/root/', + 'PATH_INFO' => '/root/', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + function testGuessBaseUriNoPathInfo() { + + $serverVars = [ + 'REQUEST_URI' => '/index.php/root', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/', $server->guessBaseUri()); + + } + + function testGuessBaseUriNoPathInfo2() { + + $serverVars = [ + 'REQUEST_URI' => '/a/b/c/test.php', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/', $server->guessBaseUri()); + + } + + + /** + * @depends testGuessBaseUri + */ + function testGuessBaseUriQueryString() { + + $serverVars = [ + 'REQUEST_URI' => '/index.php/root?query_string=blabla', + 'PATH_INFO' => '/root', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $this->assertEquals('/index.php/', $server->guessBaseUri()); + + } + + /** + * @depends testGuessBaseUri + * @expectedException \Sabre\DAV\Exception + */ + function testGuessBaseUriBadConfig() { + + $serverVars = [ + 'REQUEST_URI' => '/index.php/root/heyyy', + 'PATH_INFO' => '/root', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $server = new Server(); + $server->httpRequest = $httpRequest; + + $server->guessBaseUri(); + + } + + function testTriggerException() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'FOO', + ]; + + $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = $httpRequest; + $this->server->on('beforeMethod', [$this, 'exceptionTrigger']); + $this->server->exec(); + + $this->assertEquals([ + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $this->assertEquals(500, $this->response->status); + + } + + function exceptionTrigger($request, $response) { + + throw new Exception('Hola'); + + } + + function testReportNotFound() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'REPORT', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = ($request); + $this->server->httpRequest->setBody('<?xml version="1.0"?><bla:myreport xmlns:bla="http://www.rooftopsolutions.nl/NS"></bla:myreport>'); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(415, $this->response->status, 'We got an incorrect status back. Full response body follows: ' . $this->response->body); + + } + + function testReportIntercepted() { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'REPORT', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $this->server->httpRequest = ($request); + $this->server->httpRequest->setBody('<?xml version="1.0"?><bla:myreport xmlns:bla="http://www.rooftopsolutions.nl/NS"></bla:myreport>'); + $this->server->on('report', [$this, 'reportHandler']); + $this->server->exec(); + + $this->assertEquals([ + 'X-Sabre-Version' => [Version::VERSION], + 'testheader' => ['testvalue'], + ], + $this->response->getHeaders() + ); + + $this->assertEquals(418, $this->response->status, 'We got an incorrect status back. Full response body follows: ' . $this->response->body); + + } + + function reportHandler($reportName, $result, $path) { + + if ($reportName == '{http://www.rooftopsolutions.nl/NS}myreport') { + $this->server->httpResponse->setStatus(418); + $this->server->httpResponse->setHeader('testheader', 'testvalue'); + return false; + } + else return; + + } + + function testGetPropertiesForChildren() { + + $result = $this->server->getPropertiesForChildren('', [ + '{DAV:}getcontentlength', + ]); + + $expected = [ + 'test.txt' => ['{DAV:}getcontentlength' => 13], + 'dir/' => [], + ]; + + $this->assertEquals($expected, $result); + + } + + /** + * There are certain cases where no HTTP status may be set. We need to + * intercept these and set it to a default error message. + */ + function testNoHTTPStatusSet() { + + $this->server->on('method:GET', function() { return false; }, 1); + $this->server->httpRequest = new HTTP\Request('GET', '/'); + $this->server->exec(); + $this->assertEquals(500, $this->response->getStatus()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php new file mode 100644 index 00000000000..383f8e657ef --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/ServerUpdatePropertiesTest.php @@ -0,0 +1,102 @@ +<?php + +namespace Sabre\DAV; + +class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase { + + function testUpdatePropertiesFail() { + + $tree = [ + new SimpleCollection('foo'), + ]; + $server = new Server($tree); + + $result = $server->updateProperties('foo', [ + '{DAV:}foo' => 'bar' + ]); + + $expected = [ + '{DAV:}foo' => 403, + ]; + $this->assertEquals($expected, $result); + + } + + function testUpdatePropertiesProtected() { + + $tree = [ + new SimpleCollection('foo'), + ]; + $server = new Server($tree); + + $server->on('propPatch', function($path, PropPatch $propPatch) { + $propPatch->handleRemaining(function() { return true; }); + }); + $result = $server->updateProperties('foo', [ + '{DAV:}getetag' => 'bla', + '{DAV:}foo' => 'bar' + ]); + + $expected = [ + '{DAV:}getetag' => 403, + '{DAV:}foo' => 424, + ]; + $this->assertEquals($expected, $result); + + } + + function testUpdatePropertiesEventFail() { + + $tree = [ + new SimpleCollection('foo'), + ]; + $server = new Server($tree); + $server->on('propPatch', function($path, PropPatch $propPatch) { + $propPatch->setResultCode('{DAV:}foo', 404); + $propPatch->handleRemaining(function() { return true; }); + }); + + $result = $server->updateProperties('foo', [ + '{DAV:}foo' => 'bar', + '{DAV:}foo2' => 'bla', + ]); + + $expected = [ + '{DAV:}foo' => 404, + '{DAV:}foo2' => 424, + ]; + $this->assertEquals($expected, $result); + + } + + function testUpdatePropertiesEventSuccess() { + + $tree = [ + new SimpleCollection('foo'), + ]; + $server = new Server($tree); + $server->on('propPatch', function($path, PropPatch $propPatch) { + + $propPatch->handle(['{DAV:}foo', '{DAV:}foo2'], function() { + return [ + '{DAV:}foo' => 200, + '{DAV:}foo2' => 201, + ]; + }); + + }); + + $result = $server->updateProperties('foo', [ + '{DAV:}foo' => 'bar', + '{DAV:}foo2' => 'bla', + ]); + + $expected = [ + '{DAV:}foo' => 200, + '{DAV:}foo2' => 201, + ]; + $this->assertEquals($expected, $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sharing/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sharing/PluginTest.php new file mode 100644 index 00000000000..6aa09cac072 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sharing/PluginTest.php @@ -0,0 +1,190 @@ +<?php + +namespace Sabre\DAV\Sharing; + +use Sabre\DAV\Mock; +use Sabre\DAV\Xml\Property; + +class PluginTest extends \Sabre\DAVServerTest { + + protected $setupSharing = true; + protected $setupACL = true; + protected $autoLogin = 'admin'; + + function setUpTree() { + + $this->tree[] = new Mock\SharedNode( + 'shareable', + Plugin::ACCESS_READWRITE + ); + + } + + function testFeatures() { + + $this->assertEquals( + ['resource-sharing'], + $this->sharingPlugin->getFeatures() + ); + + } + + function testProperties() { + + $result = $this->server->getPropertiesForPath( + 'shareable', + ['{DAV:}share-access'] + ); + + $expected = [ + [ + 200 => [ + '{DAV:}share-access' => new Property\ShareAccess(Plugin::ACCESS_READWRITE) + ], + 404 => [], + 'href' => 'shareable', + ] + ]; + + $this->assertEquals( + $expected, + $result + ); + + } + + function testGetPluginInfo() { + + $result = $this->sharingPlugin->getPluginInfo(); + $this->assertInternalType('array', $result); + $this->assertEquals('sharing', $result['name']); + + } + + function testHtmlActionsPanel() { + + $node = new \Sabre\DAV\Mock\Collection('foo'); + $html = ''; + + $this->assertNull( + $this->sharingPlugin->htmlActionsPanel($node, $html, 'foo/bar') + ); + + $this->assertEquals( + '', + $html + ); + + $node = new \Sabre\DAV\Mock\SharedNode('foo', \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER); + $html = ''; + + $this->assertNull( + $this->sharingPlugin->htmlActionsPanel($node, $html, 'shareable') + ); + $this->assertContains( + 'Share this resource', + $html + ); + + } + + function testBrowserPostActionUnknownAction() { + + $this->assertNull($this->sharingPlugin->browserPostAction( + 'shareable', + 'foo', + [] + )); + + } + + function testBrowserPostActionSuccess() { + + $this->assertFalse($this->sharingPlugin->browserPostAction( + 'shareable', + 'share', + [ + 'access' => 'read', + 'href' => 'mailto:foo@example.org', + ] + )); + + $expected = [ + new \Sabre\DAV\Xml\Element\Sharee([ + 'href' => 'mailto:foo@example.org', + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_READ, + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE, + ]) + ]; + $this->assertEquals( + $expected, + $this->tree[0]->getInvites() + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testBrowserPostActionNoHref() { + + $this->sharingPlugin->browserPostAction( + 'shareable', + 'share', + [ + 'access' => 'read', + ] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testBrowserPostActionNoAccess() { + + $this->sharingPlugin->browserPostAction( + 'shareable', + 'share', + [ + 'href' => 'mailto:foo@example.org', + ] + ); + + } + + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testBrowserPostActionBadAccess() { + + $this->sharingPlugin->browserPostAction( + 'shareable', + 'share', + [ + 'href' => 'mailto:foo@example.org', + 'access' => 'bleed', + ] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testBrowserPostActionAccessDenied() { + + $this->aclPlugin->setDefaultAcl([]); + $this->sharingPlugin->browserPostAction( + 'shareable', + 'share', + [ + 'access' => 'read', + 'href' => 'mailto:foo@example.org', + ] + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sharing/ShareResourceTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sharing/ShareResourceTest.php new file mode 100644 index 00000000000..959811166eb --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sharing/ShareResourceTest.php @@ -0,0 +1,210 @@ +<?php + +namespace Sabre\DAV\Sharing; + +use Sabre\DAV\Mock; +use Sabre\DAV\Xml\Element\Sharee; +use Sabre\HTTP\Request; + +class ShareResourceTest extends \Sabre\DAVServerTest { + + protected $setupSharing = true; + protected $sharingNodeMock; + + function setUpTree() { + + $this->tree[] = $this->sharingNodeMock = new Mock\SharedNode( + 'shareable', + Plugin::ACCESS_SHAREDOWNER + ); + + } + + function testShareResource() { + + $body = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:share-resource xmlns:D="DAV:"> + <D:sharee> + <D:href>mailto:eric@example.com</D:href> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> + <D:share-access> + <D:read-write /> + </D:share-access> + </D:sharee> +</D:share-resource> +XML; + $request = new Request('POST', '/shareable', ['Content-Type' => 'application/davsharing+xml; charset="utf-8"'], $body); + + $response = $this->request($request); + $this->assertEquals(200, $response->getStatus(), (string)$response->getBodyAsString()); + + $expected = [ + new Sharee([ + 'href' => 'mailto:eric@example.com', + 'properties' => [ + '{DAV:}displayname' => 'Eric York', + ], + 'access' => Plugin::ACCESS_READWRITE, + 'comment' => 'Shared workspace', + 'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE, + ]) + ]; + + $this->assertEquals( + $expected, + $this->sharingNodeMock->getInvites() + ); + + } + + /** + * @depends testShareResource + */ + function testShareResourceRemoveAccess() { + + // First we just want to execute all the actions from the first + // test. + $this->testShareResource(); + + $body = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:share-resource xmlns:D="DAV:"> + <D:sharee> + <D:href>mailto:eric@example.com</D:href> + <D:share-access> + <D:no-access /> + </D:share-access> + </D:sharee> +</D:share-resource> +XML; + $request = new Request('POST', '/shareable', ['Content-Type' => 'application/davsharing+xml; charset="utf-8"'], $body); + + $response = $this->request($request); + $this->assertEquals(200, $response->getStatus(), (string)$response->getBodyAsString()); + + $expected = []; + + $this->assertEquals( + $expected, + $this->sharingNodeMock->getInvites() + ); + + + } + + /** + * @depends testShareResource + */ + function testShareResourceInviteProperty() { + + // First we just want to execute all the actions from the first + // test. + $this->testShareResource(); + + $body = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:propfind xmlns:D="DAV:"> + <D:prop> + <D:invite /> + <D:share-access /> + <D:share-resource-uri /> + </D:prop> +</D:propfind> +XML; + $request = new Request('PROPFIND', '/shareable', ['Content-Type' => 'application/xml'], $body); + $response = $this->request($request); + + $this->assertEquals(207, $response->getStatus()); + + $expected = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:response> + <d:href>/shareable</d:href> + <d:propstat> + <d:prop> + <d:invite> + <d:sharee> + <d:href>mailto:eric@example.com</d:href> + <d:prop> + <d:displayname>Eric York</d:displayname> + </d:prop> + <d:share-access><d:read-write /></d:share-access> + <d:invite-noresponse /> + </d:sharee> + </d:invite> + <d:share-access><d:shared-owner /></d:share-access> + <d:share-resource-uri><d:href>urn:example:bar</d:href></d:share-resource-uri> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + </d:response> +</d:multistatus> +XML; + + $this->assertXmlStringEqualsXmlString($expected, $response->getBodyAsString()); + + } + + function testShareResourceNotFound() { + + $body = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:share-resource xmlns:D="DAV:"> + <D:sharee> + <D:href>mailto:eric@example.com</D:href> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> + <D:share-access> + <D:read-write /> + </D:share-access> + </D:sharee> +</D:share-resource> +XML; + $request = new Request('POST', '/not-found', ['Content-Type' => 'application/davsharing+xml; charset="utf-8"'], $body); + + $response = $this->request($request, 404); + + } + + function testShareResourceNotISharedNode() { + + $body = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:share-resource xmlns:D="DAV:"> + <D:sharee> + <D:href>mailto:eric@example.com</D:href> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> + <D:share-access> + <D:read-write /> + </D:share-access> + </D:sharee> +</D:share-resource> +XML; + $request = new Request('POST', '/', ['Content-Type' => 'application/davsharing+xml; charset="utf-8"'], $body); + + $response = $this->request($request, 403); + + } + + function testShareResourceUnknownDoc() { + + $body = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:blablabla xmlns:D="DAV:" /> +XML; + $request = new Request('POST', '/shareable', ['Content-Type' => 'application/davsharing+xml; charset="utf-8"'], $body); + $response = $this->request($request, 400); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php new file mode 100644 index 00000000000..15ccfaf9e36 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/SimpleFileTest.php @@ -0,0 +1,19 @@ +<?php + +namespace Sabre\DAV; + +class SimpleFileTest extends \PHPUnit_Framework_TestCase { + + function testAll() { + + $file = new SimpleFile('filename.txt', 'contents', 'text/plain'); + + $this->assertEquals('filename.txt', $file->getName()); + $this->assertEquals('contents', $file->get()); + $this->assertEquals(8, $file->getSize()); + $this->assertEquals('"' . sha1('contents') . '"', $file->getETag()); + $this->assertEquals('text/plain', $file->getContentType()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/StringUtilTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/StringUtilTest.php new file mode 100644 index 00000000000..e98fe904884 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/StringUtilTest.php @@ -0,0 +1,129 @@ +<?php + +namespace Sabre\DAV; + +class StringUtilTest extends \PHPUnit_Framework_TestCase { + + /** + * @param string $haystack + * @param string $needle + * @param string $collation + * @param string $matchType + * @param string $result + * @throws Exception\BadRequest + * + * @dataProvider dataset + */ + function testTextMatch($haystack, $needle, $collation, $matchType, $result) { + + $this->assertEquals($result, StringUtil::textMatch($haystack, $needle, $collation, $matchType)); + + } + + function dataset() { + + return [ + ['FOOBAR', 'FOO', 'i;octet', 'contains', true], + ['FOOBAR', 'foo', 'i;octet', 'contains', false], + ['FÖÖBAR', 'FÖÖ', 'i;octet', 'contains', true], + ['FÖÖBAR', 'föö', 'i;octet', 'contains', false], + ['FOOBAR', 'FOOBAR', 'i;octet', 'equals', true], + ['FOOBAR', 'fooBAR', 'i;octet', 'equals', false], + ['FOOBAR', 'FOO', 'i;octet', 'starts-with', true], + ['FOOBAR', 'foo', 'i;octet', 'starts-with', false], + ['FOOBAR', 'BAR', 'i;octet', 'starts-with', false], + ['FOOBAR', 'bar', 'i;octet', 'starts-with', false], + ['FOOBAR', 'FOO', 'i;octet', 'ends-with', false], + ['FOOBAR', 'foo', 'i;octet', 'ends-with', false], + ['FOOBAR', 'BAR', 'i;octet', 'ends-with', true], + ['FOOBAR', 'bar', 'i;octet', 'ends-with', false], + + ['FOOBAR', 'FOO', 'i;ascii-casemap', 'contains', true], + ['FOOBAR', 'foo', 'i;ascii-casemap', 'contains', true], + ['FÖÖBAR', 'FÖÖ', 'i;ascii-casemap', 'contains', true], + ['FÖÖBAR', 'föö', 'i;ascii-casemap', 'contains', false], + ['FOOBAR', 'FOOBAR', 'i;ascii-casemap', 'equals', true], + ['FOOBAR', 'fooBAR', 'i;ascii-casemap', 'equals', true], + ['FOOBAR', 'FOO', 'i;ascii-casemap', 'starts-with', true], + ['FOOBAR', 'foo', 'i;ascii-casemap', 'starts-with', true], + ['FOOBAR', 'BAR', 'i;ascii-casemap', 'starts-with', false], + ['FOOBAR', 'bar', 'i;ascii-casemap', 'starts-with', false], + ['FOOBAR', 'FOO', 'i;ascii-casemap', 'ends-with', false], + ['FOOBAR', 'foo', 'i;ascii-casemap', 'ends-with', false], + ['FOOBAR', 'BAR', 'i;ascii-casemap', 'ends-with', true], + ['FOOBAR', 'bar', 'i;ascii-casemap', 'ends-with', true], + + ['FOOBAR', 'FOO', 'i;unicode-casemap', 'contains', true], + ['FOOBAR', 'foo', 'i;unicode-casemap', 'contains', true], + ['FÖÖBAR', 'FÖÖ', 'i;unicode-casemap', 'contains', true], + ['FÖÖBAR', 'föö', 'i;unicode-casemap', 'contains', true], + ['FOOBAR', 'FOOBAR', 'i;unicode-casemap', 'equals', true], + ['FOOBAR', 'fooBAR', 'i;unicode-casemap', 'equals', true], + ['FOOBAR', 'FOO', 'i;unicode-casemap', 'starts-with', true], + ['FOOBAR', 'foo', 'i;unicode-casemap', 'starts-with', true], + ['FOOBAR', 'BAR', 'i;unicode-casemap', 'starts-with', false], + ['FOOBAR', 'bar', 'i;unicode-casemap', 'starts-with', false], + ['FOOBAR', 'FOO', 'i;unicode-casemap', 'ends-with', false], + ['FOOBAR', 'foo', 'i;unicode-casemap', 'ends-with', false], + ['FOOBAR', 'BAR', 'i;unicode-casemap', 'ends-with', true], + ['FOOBAR', 'bar', 'i;unicode-casemap', 'ends-with', true], + ]; + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testBadCollation() { + + StringUtil::textMatch('foobar', 'foo', 'blabla', 'contains'); + + } + + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testBadMatchType() { + + StringUtil::textMatch('foobar', 'foo', 'i;octet', 'booh'); + + } + + function testEnsureUTF8_ascii() { + + $inputString = "harkema"; + $outputString = "harkema"; + + $this->assertEquals( + $outputString, + StringUtil::ensureUTF8($inputString) + ); + + } + + function testEnsureUTF8_latin1() { + + $inputString = "m\xfcnster"; + $outputString = "münster"; + + $this->assertEquals( + $outputString, + StringUtil::ensureUTF8($inputString) + ); + + } + + function testEnsureUTF8_utf8() { + + $inputString = "m\xc3\xbcnster"; + $outputString = "münster"; + + $this->assertEquals( + $outputString, + StringUtil::ensureUTF8($inputString) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sync/MockSyncCollection.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sync/MockSyncCollection.php new file mode 100644 index 00000000000..aac1dee489b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sync/MockSyncCollection.php @@ -0,0 +1,169 @@ +<?php + +namespace Sabre\DAV\Sync; + +use Sabre\DAV; + +/** + * This mocks a ISyncCollection, for unittesting. + * + * This object behaves the same as SimpleCollection. Call addChange to update + * the 'changelog' that this class uses for the collection. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class MockSyncCollection extends DAV\SimpleCollection implements ISyncCollection { + + public $changeLog = []; + + public $token = null; + + /** + * This method returns the current sync-token for this collection. + * This can be any string. + * + * If null is returned from this function, the plugin assumes there's no + * sync information available. + * + * @return string|null + */ + function getSyncToken() { + + // Will be 'null' in the first round, and will increment ever after. + return $this->token; + + } + + function addChange(array $added, array $modified, array $deleted) { + + $this->token++; + $this->changeLog[$this->token] = [ + 'added' => $added, + 'modified' => $modified, + 'deleted' => $deleted, + ]; + + } + + /** + * The getChanges method returns all the changes that have happened, since + * the specified syncToken and the current collection. + * + * This function should return an array, such as the following: + * + * array( + * 'syncToken' => 'The current synctoken', + * 'modified' => array( + * 'new.txt', + * ), + * 'deleted' => array( + * 'foo.php.bak', + * 'old.txt' + * ) + * ); + * + * The syncToken property should reflect the *current* syncToken of the + * collection, as reported getSyncToken(). This is needed here too, to + * ensure the operation is atomic. + * + * If the syncToken is specified as null, this is an initial sync, and all + * members should be reported. + * + * The modified property is an array of nodenames that have changed since + * the last token. + * + * The deleted property is an array with nodenames, that have been deleted + * from collection. + * + * The second argument is basically the 'depth' of the report. If it's 1, + * you only have to report changes that happened only directly in immediate + * descendants. If it's 2, it should also include changes from the nodes + * below the child collections. (grandchildren) + * + * The third (optional) argument allows a client to specify how many + * results should be returned at most. If the limit is not specified, it + * should be treated as infinite. + * + * If the limit (infinite or not) is higher than you're willing to return, + * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. + * + * If the syncToken is expired (due to data cleanup) or unknown, you must + * return null. + * + * The limit is 'suggestive'. You are free to ignore it. + * + * @param string $syncToken + * @param int $syncLevel + * @param int $limit + * @return array + */ + function getChanges($syncToken, $syncLevel, $limit = null) { + + // This is an initial sync + if (is_null($syncToken)) { + return [ + 'added' => array_map( + function($item) { + return $item->getName(); + }, $this->getChildren() + ), + 'modified' => [], + 'deleted' => [], + 'syncToken' => $this->getSyncToken(), + ]; + } + + if (!is_int($syncToken) && !ctype_digit($syncToken)) { + + return null; + + } + if (is_null($this->token)) return null; + + $added = []; + $modified = []; + $deleted = []; + + foreach ($this->changeLog as $token => $change) { + + if ($token > $syncToken) { + + $added = array_merge($added, $change['added']); + $modified = array_merge($modified, $change['modified']); + $deleted = array_merge($deleted, $change['deleted']); + + if ($limit) { + // If there's a limit, we may need to cut things off. + // This alghorithm is weird and stupid, but it works. + $left = $limit - (count($modified) + count($deleted)); + if ($left > 0) continue; + if ($left === 0) break; + if ($left < 0) { + $modified = array_slice($modified, 0, $left); + } + $left = $limit - (count($modified) + count($deleted)); + if ($left === 0) break; + if ($left < 0) { + $deleted = array_slice($deleted, 0, $left); + } + break; + + } + + } + + } + + return [ + 'syncToken' => $this->token, + 'added' => $added, + 'modified' => $modified, + 'deleted' => $deleted, + ]; + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sync/PluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sync/PluginTest.php new file mode 100644 index 00000000000..6bcec8b7535 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Sync/PluginTest.php @@ -0,0 +1,523 @@ +<?php + +namespace Sabre\DAV\Sync; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once __DIR__ . '/MockSyncCollection.php'; + +class PluginTest extends \Sabre\DAVServerTest { + + protected $collection; + + function setUp() { + + parent::setUp(); + $this->server->addPlugin(new Plugin()); + + } + + function testGetInfo() { + + $this->assertArrayHasKey( + 'name', + (new Plugin())->getPluginInfo() + ); + + } + + function setUpTree() { + + $this->collection = + new MockSyncCollection('coll', [ + new DAV\SimpleFile('file1.txt', 'foo'), + new DAV\SimpleFile('file2.txt', 'bar'), + ]); + $this->tree = [ + $this->collection, + new DAV\SimpleCollection('normalcoll', []) + ]; + + } + + function testSupportedReportSet() { + + $result = $this->server->getProperties('/coll', ['{DAV:}supported-report-set']); + $this->assertFalse($result['{DAV:}supported-report-set']->has('{DAV:}sync-collection')); + + // Making a change + $this->collection->addChange(['file1.txt'], [], []); + + $result = $this->server->getProperties('/coll', ['{DAV:}supported-report-set']); + $this->assertTrue($result['{DAV:}supported-report-set']->has('{DAV:}sync-collection')); + + } + + function testGetSyncToken() { + + $result = $this->server->getProperties('/coll', ['{DAV:}sync-token']); + $this->assertFalse(isset($result['{DAV:}sync-token'])); + + // Making a change + $this->collection->addChange(['file1.txt'], [], []); + + $result = $this->server->getProperties('/coll', ['{DAV:}sync-token']); + $this->assertTrue(isset($result['{DAV:}sync-token'])); + + // non-sync-enabled collection + $this->collection->addChange(['file1.txt'], [], []); + + $result = $this->server->getProperties('/normalcoll', ['{DAV:}sync-token']); + $this->assertFalse(isset($result['{DAV:}sync-token'])); + } + + function testSyncInitialSyncCollection() { + + // Making a change + $this->collection->addChange(['file1.txt'], [], []); + + $request = new HTTP\Request('REPORT', '/coll/', ['Content-Type' => 'application/xml']); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token/> + <D:sync-level>1</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); + + $multiStatus = $this->server->xml->parse($response->getBodyAsString()); + + // Checking the sync-token + $this->assertEquals( + 'http://sabre.io/ns/sync/1', + $multiStatus->getSyncToken() + ); + + $responses = $multiStatus->getResponses(); + $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); + + $response = $responses[0]; + + $this->assertNull($response->getHttpStatus()); + $this->assertEquals('/coll/file1.txt', $response->getHref()); + $this->assertEquals([ + 200 => [ + '{DAV:}getcontentlength' => 3, + ] + ], $response->getResponseProperties()); + + $response = $responses[1]; + + $this->assertNull($response->getHttpStatus()); + $this->assertEquals('/coll/file2.txt', $response->getHref()); + $this->assertEquals([ + 200 => [ + '{DAV:}getcontentlength' => 3, + ] + ], $response->getResponseProperties()); + + } + + function testSubsequentSyncSyncCollection() { + + // Making a change + $this->collection->addChange(['file1.txt'], [], []); + // Making another change + $this->collection->addChange([], ['file2.txt'], ['file3.txt']); + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> + <D:sync-level>infinite</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); + + $multiStatus = $this->server->xml->parse($response->getBodyAsString()); + + // Checking the sync-token + $this->assertEquals( + 'http://sabre.io/ns/sync/2', + $multiStatus->getSyncToken() + ); + + $responses = $multiStatus->getResponses(); + $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); + + $response = $responses[0]; + + $this->assertNull($response->getHttpStatus()); + $this->assertEquals('/coll/file2.txt', $response->getHref()); + $this->assertEquals([ + 200 => [ + '{DAV:}getcontentlength' => 3, + ] + ], $response->getResponseProperties()); + + $response = $responses[1]; + + $this->assertEquals('404', $response->getHttpStatus()); + $this->assertEquals('/coll/file3.txt', $response->getHref()); + $this->assertEquals([], $response->getResponseProperties()); + + } + + function testSubsequentSyncSyncCollectionLimit() { + + // Making a change + $this->collection->addChange(['file1.txt'], [], []); + // Making another change + $this->collection->addChange([], ['file2.txt'], ['file3.txt']); + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> + <D:sync-level>infinite</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> + <D:limit><D:nresults>1</D:nresults></D:limit> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); + + $multiStatus = $this->server->xml->parse( + $response->getBodyAsString() + ); + + // Checking the sync-token + $this->assertEquals( + 'http://sabre.io/ns/sync/2', + $multiStatus->getSyncToken() + ); + + $responses = $multiStatus->getResponses(); + $this->assertEquals(1, count($responses), 'We expected exactly 1 {DAV:}response'); + + $response = $responses[0]; + + $this->assertEquals('404', $response->getHttpStatus()); + $this->assertEquals('/coll/file3.txt', $response->getHref()); + $this->assertEquals([], $response->getResponseProperties()); + + } + + function testSubsequentSyncSyncCollectionDepthFallBack() { + + // Making a change + $this->collection->addChange(['file1.txt'], [], []); + // Making another change + $this->collection->addChange([], ['file2.txt'], ['file3.txt']); + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + 'HTTP_DEPTH' => "1", + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); + + $multiStatus = $this->server->xml->parse( + $response->getBodyAsString() + ); + + // Checking the sync-token + $this->assertEquals( + 'http://sabre.io/ns/sync/2', + $multiStatus->getSyncToken() + ); + + $responses = $multiStatus->getResponses(); + $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); + + $response = $responses[0]; + + $this->assertNull($response->getHttpStatus()); + $this->assertEquals('/coll/file2.txt', $response->getHref()); + $this->assertEquals([ + 200 => [ + '{DAV:}getcontentlength' => 3, + ] + ], $response->getResponseProperties()); + + $response = $responses[1]; + + $this->assertEquals('404', $response->getHttpStatus()); + $this->assertEquals('/coll/file3.txt', $response->getHref()); + $this->assertEquals([], $response->getResponseProperties()); + + } + + function testSyncNoSyncInfo() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token/> + <D:sync-level>1</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + // The default state has no sync-token, so this report should not yet + // be supported. + $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); + + } + + function testSyncNoSyncCollection() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/normalcoll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token/> + <D:sync-level>1</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + // The default state has no sync-token, so this report should not yet + // be supported. + $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); + + } + + function testSyncInvalidToken() { + + $this->collection->addChange(['file1.txt'], [], []); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token>http://sabre.io/ns/sync/invalid</D:sync-token> + <D:sync-level>1</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + // The default state has no sync-token, so this report should not yet + // be supported. + $this->assertEquals(403, $response->status, 'Full response body:' . $response->body); + + } + function testSyncInvalidTokenNoPrefix() { + + $this->collection->addChange(['file1.txt'], [], []); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token>invalid</D:sync-token> + <D:sync-level>1</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + // The default state has no sync-token, so this report should not yet + // be supported. + $this->assertEquals(403, $response->status, 'Full response body:' . $response->body); + + } + + function testSyncNoSyncToken() { + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-level>1</D:sync-level> + <D:prop> + <D:getcontentlength/> + </D:prop> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + // The default state has no sync-token, so this report should not yet + // be supported. + $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); + + } + + function testSyncNoProp() { + + $this->collection->addChange(['file1.txt'], [], []); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/coll/', + 'CONTENT_TYPE' => 'application/xml', + ]); + + $body = <<<BLA +<?xml version="1.0" encoding="utf-8" ?> +<D:sync-collection xmlns:D="DAV:"> + <D:sync-token /> + <D:sync-level>1</D:sync-level> +</D:sync-collection> +BLA; + + $request->setBody($body); + + $response = $this->request($request); + + // The default state has no sync-token, so this report should not yet + // be supported. + $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); + + } + + function testIfConditions() { + + $this->collection->addChange(['file1.txt'], [], []); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'DELETE', + 'REQUEST_URI' => '/coll/file1.txt', + 'HTTP_IF' => '</coll> (<http://sabre.io/ns/sync/1>)', + ]); + $response = $this->request($request); + + // If a 403 is thrown this works correctly. The file in questions + // doesn't allow itself to be deleted. + // If the If conditions failed, it would have been a 412 instead. + $this->assertEquals(403, $response->status); + + } + + function testIfConditionsNot() { + + $this->collection->addChange(['file1.txt'], [], []); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'DELETE', + 'REQUEST_URI' => '/coll/file1.txt', + 'HTTP_IF' => '</coll> (Not <http://sabre.io/ns/sync/2>)', + ]); + $response = $this->request($request); + + // If a 403 is thrown this works correctly. The file in questions + // doesn't allow itself to be deleted. + // If the If conditions failed, it would have been a 412 instead. + $this->assertEquals(403, $response->status); + + } + + function testIfConditionsNoSyncToken() { + + $this->collection->addChange(['file1.txt'], [], []); + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'DELETE', + 'REQUEST_URI' => '/coll/file1.txt', + 'HTTP_IF' => '</coll> (<opaquelocktoken:foo>)', + ]); + $response = $this->request($request); + + $this->assertEquals(412, $response->status); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/SyncTokenPropertyTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/SyncTokenPropertyTest.php new file mode 100644 index 00000000000..ff139f78c41 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/SyncTokenPropertyTest.php @@ -0,0 +1,106 @@ +<?php + +namespace Sabre\DAV; + +class SyncTokenPropertyTest extends \Sabre\DAVServerTest { + + /** + * The assumption in these tests is that a PROPFIND is going on, and to + * fetch the sync-token, the event handler is just able to use the existing + * result. + * + * @param string $name + * @param mixed $value + * + * @dataProvider data + */ + function testAlreadyThere1($name, $value) { + + $propFind = new PropFind('foo', [ + '{http://calendarserver.org/ns/}getctag', + $name, + ]); + + $propFind->set($name, $value); + $corePlugin = new CorePlugin(); + $corePlugin->propFindLate($propFind, new SimpleCollection('hi')); + + $this->assertEquals("hello", $propFind->get('{http://calendarserver.org/ns/}getctag')); + + } + + /** + * In these test-cases, the plugin is forced to do a local propfind to + * fetch the items. + * + * @param string $name + * @param mixed $value + * + * @dataProvider data + */ + function testRefetch($name, $value) { + + $this->server->tree = new Tree( + new SimpleCollection('root', [ + new Mock\PropertiesCollection( + 'foo', + [], + [$name => $value] + ) + ]) + ); + $propFind = new PropFind('foo', [ + '{http://calendarserver.org/ns/}getctag', + $name, + ]); + + $corePlugin = $this->server->getPlugin('core'); + $corePlugin->propFindLate($propFind, new SimpleCollection('hi')); + + $this->assertEquals("hello", $propFind->get('{http://calendarserver.org/ns/}getctag')); + + } + + function testNoData() { + + $this->server->tree = new Tree( + new SimpleCollection('root', [ + new Mock\PropertiesCollection( + 'foo', + [], + [] + ) + ]) + ); + + $propFind = new PropFind('foo', [ + '{http://calendarserver.org/ns/}getctag', + ]); + + $corePlugin = $this->server->getPlugin('core'); + $corePlugin->propFindLate($propFind, new SimpleCollection('hi')); + + $this->assertNull($propFind->get('{http://calendarserver.org/ns/}getctag')); + + } + + function data() { + + return [ + [ + '{http://sabredav.org/ns}sync-token', + "hello" + ], + [ + '{DAV:}sync-token', + "hello" + ], + [ + '{DAV:}sync-token', + new Xml\Property\Href(Sync\Plugin::SYNCTOKEN_PREFIX . "hello", false) + ] + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php new file mode 100644 index 00000000000..6acd6b077b8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TemporaryFileFilterTest.php @@ -0,0 +1,199 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP; + +class TemporaryFileFilterTest extends AbstractServer { + + function setUp() { + + parent::setUp(); + $plugin = new TemporaryFileFilterPlugin(SABRE_TEMPDIR . '/tff'); + $this->server->addPlugin($plugin); + + } + + function testPutNormal() { + + $request = new HTTP\Request('PUT', '/testput.txt', [], 'Testing new file'); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals(201, $this->response->status); + $this->assertEquals('0', $this->response->getHeader('Content-Length')); + + $this->assertEquals('Testing new file', file_get_contents(SABRE_TEMPDIR . '/testput.txt')); + + } + + function testPutTemp() { + + // mimicking an OS/X resource fork + $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file'); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals(201, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + ], $this->response->getHeaders()); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'), '._testput.txt should not exist in the regular file structure.'); + + } + + function testPutTempIfNoneMatch() { + + // mimicking an OS/X resource fork + $request = new HTTP\Request('PUT', '/._testput.txt', ['If-None-Match' => '*'], 'Testing new file'); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals(201, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + ], $this->response->getHeaders()); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'), '._testput.txt should not exist in the regular file structure.'); + + + $this->server->exec(); + + $this->assertEquals(412, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + } + + function testPutGet() { + + // mimicking an OS/X resource fork + $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file'); + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals(201, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + ], $this->response->getHeaders()); + + $request = new HTTP\Request('GET', '/._testput.txt'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(200, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + 'Content-Length' => [16], + 'Content-Type' => ['application/octet-stream'], + ], $this->response->getHeaders()); + + $this->assertEquals('Testing new file', stream_get_contents($this->response->body)); + + } + + function testLockNonExistant() { + + mkdir(SABRE_TEMPDIR . '/locksdir'); + $locksBackend = new Locks\Backend\File(SABRE_TEMPDIR . '/locks'); + $locksPlugin = new Locks\Plugin($locksBackend); + $this->server->addPlugin($locksPlugin); + + // mimicking an OS/X resource fork + $request = new HTTP\Request('LOCK', '/._testput.txt'); + $request->setBody('<?xml version="1.0"?> +<D:lockinfo xmlns:D="DAV:"> + <D:lockscope><D:exclusive/></D:lockscope> + <D:locktype><D:write/></D:locktype> + <D:owner> + <D:href>http://example.org/~ejw/contact.html</D:href> + </D:owner> +</D:lockinfo>'); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(201, $this->response->status); + $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type')); + $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); + $this->assertEquals('true', $this->response->getHeader('X-Sabre-Temp')); + + $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testlock.txt'), '._testlock.txt should not exist in the regular file structure.'); + + } + + function testPutDelete() { + + // mimicking an OS/X resource fork + $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file'); + + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals(201, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + ], $this->response->getHeaders()); + + $request = new HTTP\Request('DELETE', '/._testput.txt'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals(204, $this->response->status, "Incorrect status code received. Full body:\n" . $this->response->body); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + ], $this->response->getHeaders()); + + $this->assertEquals('', $this->response->body); + + } + + function testPutPropfind() { + + // mimicking an OS/X resource fork + $request = new HTTP\Request('PUT', '/._testput.txt', [], 'Testing new file'); + $this->server->httpRequest = $request; + $this->server->exec(); + + $this->assertEquals('', $this->response->body); + $this->assertEquals(201, $this->response->status); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + ], $this->response->getHeaders()); + + $request = new HTTP\Request('PROPFIND', '/._testput.txt'); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + $this->assertEquals(207, $this->response->status, 'Incorrect status code returned. Body: ' . $this->response->body); + $this->assertEquals([ + 'X-Sabre-Temp' => ['true'], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $this->response->getHeaders()); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); + $this->assertEquals('/._testput.txt', (string)$data, 'href element should have been /._testput.txt'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); + $this->assertEquals(1, count($data)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TestPlugin.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TestPlugin.php new file mode 100644 index 00000000000..619ac03fd73 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TestPlugin.php @@ -0,0 +1,37 @@ +<?php + +namespace Sabre\DAV; + +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +class TestPlugin extends ServerPlugin { + + public $beforeMethod; + + function getFeatures() { + + return ['drinking']; + + } + + function getHTTPMethods($uri) { + + return ['BEER','WINE']; + + } + + function initialize(Server $server) { + + $server->on('beforeMethod', [$this, 'beforeMethod']); + + } + + function beforeMethod(RequestInterface $request, ResponseInterface $response) { + + $this->beforeMethod = $request->getMethod(); + return true; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TreeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TreeTest.php new file mode 100644 index 00000000000..e719e38d59d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/TreeTest.php @@ -0,0 +1,242 @@ +<?php + +namespace Sabre\DAV; + +class TreeTest extends \PHPUnit_Framework_TestCase { + + function testNodeExists() { + + $tree = new TreeMock(); + + $this->assertTrue($tree->nodeExists('hi')); + $this->assertFalse($tree->nodeExists('hello')); + + } + + function testCopy() { + + $tree = new TreeMock(); + $tree->copy('hi', 'hi2'); + + $this->assertArrayHasKey('hi2', $tree->getNodeForPath('')->newDirectories); + $this->assertEquals('foobar', $tree->getNodeForPath('hi/file')->get()); + $this->assertEquals(['test1' => 'value'], $tree->getNodeForPath('hi/file')->getProperties([])); + + } + + function testMove() { + + $tree = new TreeMock(); + $tree->move('hi', 'hi2'); + + $this->assertEquals('hi2', $tree->getNodeForPath('hi')->getName()); + $this->assertTrue($tree->getNodeForPath('hi')->isRenamed); + + } + + function testDeepMove() { + + $tree = new TreeMock(); + $tree->move('hi/sub', 'hi2'); + + $this->assertArrayHasKey('hi2', $tree->getNodeForPath('')->newDirectories); + $this->assertTrue($tree->getNodeForPath('hi/sub')->isDeleted); + + } + + function testDelete() { + + $tree = new TreeMock(); + $tree->delete('hi'); + $this->assertTrue($tree->getNodeForPath('hi')->isDeleted); + + } + + function testGetChildren() { + + $tree = new TreeMock(); + $children = $tree->getChildren(''); + $this->assertEquals(2, count($children)); + $this->assertEquals('hi', $children[0]->getName()); + + } + + function testGetMultipleNodes() { + + $tree = new TreeMock(); + $result = $tree->getMultipleNodes(['hi/sub', 'hi/file']); + $this->assertArrayHasKey('hi/sub', $result); + $this->assertArrayHasKey('hi/file', $result); + + $this->assertEquals('sub', $result['hi/sub']->getName()); + $this->assertEquals('file', $result['hi/file']->getName()); + + } + function testGetMultipleNodes2() { + + $tree = new TreeMock(); + $result = $tree->getMultipleNodes(['multi/1', 'multi/2']); + $this->assertArrayHasKey('multi/1', $result); + $this->assertArrayHasKey('multi/2', $result); + + } + +} + +class TreeMock extends Tree { + + private $nodes = []; + + function __construct() { + + $file = new TreeFileTester('file'); + $file->properties = ['test1' => 'value']; + $file->data = 'foobar'; + + parent::__construct( + new TreeDirectoryTester('root', [ + new TreeDirectoryTester('hi', [ + new TreeDirectoryTester('sub'), + $file, + ]), + new TreeMultiGetTester('multi', [ + new TreeFileTester('1'), + new TreeFileTester('2'), + new TreeFileTester('3'), + ]) + ]) + ); + + } + +} + +class TreeDirectoryTester extends SimpleCollection { + + public $newDirectories = []; + public $newFiles = []; + public $isDeleted = false; + public $isRenamed = false; + + function createDirectory($name) { + + $this->newDirectories[$name] = true; + + } + + function createFile($name, $data = null) { + + $this->newFiles[$name] = $data; + + } + + function getChild($name) { + + if (isset($this->newDirectories[$name])) return new self($name); + if (isset($this->newFiles[$name])) return new TreeFileTester($name, $this->newFiles[$name]); + return parent::getChild($name); + + } + + function childExists($name) { + + return !!$this->getChild($name); + + } + + function delete() { + + $this->isDeleted = true; + + } + + function setName($name) { + + $this->isRenamed = true; + $this->name = $name; + + } + +} + +class TreeFileTester extends File implements IProperties { + + public $name; + public $data; + public $properties; + + function __construct($name, $data = null) { + + $this->name = $name; + if (is_null($data)) $data = 'bla'; + $this->data = $data; + + } + + function getName() { + + return $this->name; + + } + + function get() { + + return $this->data; + + } + + function getProperties($properties) { + + return $this->properties; + + } + + /** + * Updates properties on this node. + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * To update specific properties, call the 'handle' method on this object. + * Read the PropPatch documentation for more information. + * + * @param PropPatch $propPatch + * @return void + */ + function propPatch(PropPatch $propPatch) { + + $this->properties = $propPatch->getMutations(); + $propPatch->setRemainingResultCode(200); + + } + +} + +class TreeMultiGetTester extends TreeDirectoryTester implements IMultiGet { + + /** + * This method receives a list of paths in it's first argument. + * It must return an array with Node objects. + * + * If any children are not found, you do not have to return them. + * + * @param array $paths + * @return array + */ + function getMultipleChildren(array $paths) { + + $result = []; + foreach ($paths as $path) { + try { + $child = $this->getChild($path); + $result[] = $child; + } catch (Exception\NotFound $e) { + // Do nothing + } + } + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/UUIDUtilTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/UUIDUtilTest.php new file mode 100644 index 00000000000..f005ecc75f0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/UUIDUtilTest.php @@ -0,0 +1,25 @@ +<?php + +namespace Sabre\DAV; + +class UUIDUtilTest extends \PHPUnit_Framework_TestCase { + + function testValidateUUID() { + + $this->assertTrue( + UUIDUtil::validateUUID('11111111-2222-3333-4444-555555555555') + ); + $this->assertFalse( + UUIDUtil::validateUUID(' 11111111-2222-3333-4444-555555555555') + ); + $this->assertTrue( + UUIDUtil::validateUUID('ffffffff-2222-3333-4444-555555555555') + ); + $this->assertFalse( + UUIDUtil::validateUUID('fffffffg-2222-3333-4444-555555555555') + ); + + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/PropTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/PropTest.php new file mode 100644 index 00000000000..7cc10650cba --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/PropTest.php @@ -0,0 +1,154 @@ +<?php + +namespace Sabre\DAV\Xml\Element; + +use Sabre\DAV\Xml\Property\Complex; +use Sabre\DAV\Xml\Property\Href; +use Sabre\DAV\Xml\XmlTest; + +class PropTest extends XmlTest { + + function testDeserializeSimple() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:"> + <foo>bar</foo> +</root> +XML; + + $expected = [ + '{DAV:}foo' => 'bar', + ]; + + $this->assertDecodeProp($input, $expected); + + } + function testDeserializeEmpty() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:" /> +XML; + + $expected = [ + ]; + + $this->assertDecodeProp($input, $expected); + + } + function testDeserializeComplex() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:"> + <foo><no>yes</no></foo> +</root> +XML; + + $expected = [ + '{DAV:}foo' => new Complex('<no xmlns="DAV:">yes</no>') + ]; + + $this->assertDecodeProp($input, $expected); + + } + function testDeserializeCustom() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:"> + <foo><href>/hello</href></foo> +</root> +XML; + + $expected = [ + '{DAV:}foo' => new Href('/hello', false) + ]; + + $elementMap = [ + '{DAV:}foo' => 'Sabre\DAV\Xml\Property\Href' + ]; + + $this->assertDecodeProp($input, $expected, $elementMap); + + } + function testDeserializeCustomCallback() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:"> + <foo>blabla</foo> +</root> +XML; + + $expected = [ + '{DAV:}foo' => 'zim', + ]; + + $elementMap = [ + '{DAV:}foo' => function($reader) { + $reader->next(); + return 'zim'; + } + ]; + + $this->assertDecodeProp($input, $expected, $elementMap); + + } + + /** + * @expectedException \LogicException + */ + function testDeserializeCustomBad() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:"> + <foo>blabla</foo> +</root> +XML; + + $expected = []; + + $elementMap = [ + '{DAV:}foo' => 'idk?', + ]; + + $this->assertDecodeProp($input, $expected, $elementMap); + + } + + /** + * @expectedException \LogicException + */ + function testDeserializeCustomBadObj() { + + $input = <<<XML +<?xml version="1.0"?> +<root xmlns="DAV:"> + <foo>blabla</foo> +</root> +XML; + + $expected = []; + + $elementMap = [ + '{DAV:}foo' => new \StdClass(), + ]; + + $this->assertDecodeProp($input, $expected, $elementMap); + + } + + function assertDecodeProp($input, array $expected, array $elementMap = []) { + + $elementMap['{DAV:}root'] = 'Sabre\DAV\Xml\Element\Prop'; + + $result = $this->parse($input, $elementMap); + $this->assertInternalType('array', $result); + $this->assertEquals($expected, $result['value']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/ResponseTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/ResponseTest.php new file mode 100644 index 00000000000..f19e7df7c0c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/ResponseTest.php @@ -0,0 +1,313 @@ +<?php + +namespace Sabre\DAV\Xml\Element; + +use Sabre\DAV; + +class ResponseTest extends DAV\Xml\XmlTest { + + function testSimple() { + + $innerProps = [ + 200 => [ + '{DAV:}displayname' => 'my file', + ], + 404 => [ + '{DAV:}owner' => null, + ] + ]; + + $property = new Response('uri', $innerProps); + + $this->assertEquals('uri', $property->getHref()); + $this->assertEquals($innerProps, $property->getResponseProperties()); + + + } + + /** + * @depends testSimple + */ + function testSerialize() { + + $innerProps = [ + 200 => [ + '{DAV:}displayname' => 'my file', + ], + 404 => [ + '{DAV:}owner' => null, + ] + ]; + + $property = new Response('uri', $innerProps); + + $xml = $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:response> + <d:href>/uri</d:href> + <d:propstat> + <d:prop> + <d:displayname>my file</d:displayname> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + <d:propstat> + <d:prop> + <d:owner/> + </d:prop> + <d:status>HTTP/1.1 404 Not Found</d:status> + </d:propstat> + </d:response> +</d:root> +', $xml); + + } + + /** + * This one is specifically for testing properties with no namespaces, which is legal xml + * + * @depends testSerialize + */ + function testSerializeEmptyNamespace() { + + $innerProps = [ + 200 => [ + '{}propertyname' => 'value', + ], + ]; + + $property = new Response('uri', $innerProps); + + $xml = $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + $this->assertEquals( +'<d:root xmlns:d="DAV:"> + <d:response> + <d:href>/uri</d:href> + <d:propstat> + <d:prop> + <propertyname xmlns="">value</propertyname> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + </d:response> +</d:root> +', $xml); + + } + + /** + * This one is specifically for testing properties with no namespaces, which is legal xml + * + * @depends testSerialize + */ + function testSerializeCustomNamespace() { + + $innerProps = [ + 200 => [ + '{http://sabredav.org/NS/example}propertyname' => 'value', + ], + ]; + + $property = new Response('uri', $innerProps); + $xml = $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:response> + <d:href>/uri</d:href> + <d:propstat> + <d:prop> + <x1:propertyname xmlns:x1="http://sabredav.org/NS/example">value</x1:propertyname> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + </d:response> +</d:root>', $xml); + + } + + /** + * @depends testSerialize + */ + function testSerializeComplexProperty() { + + $innerProps = [ + 200 => [ + '{DAV:}link' => new DAV\Xml\Property\Href('http://sabredav.org/', false) + ], + ]; + + $property = new Response('uri', $innerProps); + $xml = $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:response> + <d:href>/uri</d:href> + <d:propstat> + <d:prop> + <d:link><d:href>http://sabredav.org/</d:href></d:link> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + </d:response> +</d:root> +', $xml); + + } + + /** + * @depends testSerialize + * @expectedException \InvalidArgumentException + */ + function testSerializeBreak() { + + $innerProps = [ + 200 => [ + '{DAV:}link' => new \STDClass() + ], + ]; + + $property = new Response('uri', $innerProps); + $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + } + + function testDeserializeComplexProperty() { + + $xml = '<?xml version="1.0"?> +<d:response xmlns:d="DAV:"> + <d:href>/uri</d:href> + <d:propstat> + <d:prop> + <d:foo>hello</d:foo> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> +</d:response> +'; + + $result = $this->parse($xml, [ + '{DAV:}response' => 'Sabre\DAV\Xml\Element\Response', + '{DAV:}foo' => function($reader) { + + $reader->next(); + return 'world'; + }, + ]); + $this->assertEquals( + new Response('/uri', [ + '200' => [ + '{DAV:}foo' => 'world', + ] + ]), + $result['value'] + ); + + } + + /** + * @depends testSimple + */ + function testSerializeUrlencoding() { + + $innerProps = [ + 200 => [ + '{DAV:}displayname' => 'my file', + ], + ]; + + $property = new Response('space here', $innerProps); + + $xml = $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:response> + <d:href>/space%20here</d:href> + <d:propstat> + <d:prop> + <d:displayname>my file</d:displayname> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + </d:response> +</d:root> +', $xml); + + } + + /** + * @depends testSerialize + * + * The WebDAV spec _requires_ at least one DAV:propstat to appear for + * every DAV:response. In some circumstances however, there are no + * properties to encode. + * + * In those cases we MUST specify at least one DAV:propstat anyway, with + * no properties. + */ + function testSerializeNoProperties() { + + $innerProps = []; + + $property = new Response('uri', $innerProps); + $xml = $this->write(['{DAV:}root' => ['{DAV:}response' => $property]]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:response> + <d:href>/uri</d:href> + <d:propstat> + <d:prop /> + <d:status>HTTP/1.1 418 I\'m a teapot</d:status> + </d:propstat> + </d:response> +</d:root> +', $xml); + + } + + /** + * In the case of {DAV:}prop, a deserializer should never get called, if + * the property element is empty. + */ + function testDeserializeComplexPropertyEmpty() { + + $xml = '<?xml version="1.0"?> +<d:response xmlns:d="DAV:"> + <d:href>/uri</d:href> + <d:propstat> + <d:prop> + <d:foo /> + </d:prop> + <d:status>HTTP/1.1 404 Not Found</d:status> + </d:propstat> +</d:response> +'; + + $result = $this->parse($xml, [ + '{DAV:}response' => 'Sabre\DAV\Xml\Element\Response', + '{DAV:}foo' => function($reader) { + throw new \LogicException('This should never happen'); + }, + ]); + $this->assertEquals( + new Response('/uri', [ + '404' => [ + '{DAV:}foo' => null + ] + ]), + $result['value'] + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/ShareeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/ShareeTest.php new file mode 100644 index 00000000000..3704d878244 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Element/ShareeTest.php @@ -0,0 +1,98 @@ +<?php + +namespace Sabre\DAV\Xml\Element; + +use Sabre\DAV\Sharing\Plugin; +use Sabre\DAV\Xml\XmlTest; + +class ShareeTest extends XmlTest { + + /** + * @expectedException \InvalidArgumentException + */ + function testShareeUnknownPropertyInConstructor() { + + new Sharee(['foo' => 'bar']); + + } + + function testDeserialize() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:sharee xmlns:D="DAV:"> + <D:href>mailto:eric@example.com</D:href> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> + <D:share-access> + <D:read-write /> + </D:share-access> +</D:sharee> +XML; + + $result = $this->parse($xml, [ + '{DAV:}sharee' => 'Sabre\\DAV\\Xml\\Element\\Sharee' + ]); + + $expected = new Sharee([ + 'href' => 'mailto:eric@example.com', + 'properties' => ['{DAV:}displayname' => 'Eric York'], + 'comment' => 'Shared workspace', + 'access' => Plugin::ACCESS_READWRITE, + ]); + $this->assertEquals( + $expected, + $result['value'] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeNoHref() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:sharee xmlns:D="DAV:"> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> + <D:share-access> + <D:read-write /> + </D:share-access> +</D:sharee> +XML; + + $this->parse($xml, [ + '{DAV:}sharee' => 'Sabre\\DAV\\Xml\\Element\\Sharee' + ]); + + } + + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeNoShareeAccess() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:sharee xmlns:D="DAV:"> + <D:href>mailto:eric@example.com</D:href> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> +</D:sharee> +XML; + + $this->parse($xml, [ + '{DAV:}sharee' => 'Sabre\\DAV\\Xml\\Element\\Sharee' + ]); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/HrefTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/HrefTest.php new file mode 100644 index 00000000000..bf58853371e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/HrefTest.php @@ -0,0 +1,109 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use Sabre\DAV; +use Sabre\DAV\Browser\HtmlOutputHelper; +use Sabre\DAV\Xml\XmlTest; + +class HrefTest extends XmlTest { + + function testConstruct() { + + $href = new Href('path'); + $this->assertEquals('path', $href->getHref()); + + } + + function testSerialize() { + + $href = new Href('path'); + $this->assertEquals('path', $href->getHref()); + + $this->contextUri = '/bla/'; + + $xml = $this->write(['{DAV:}anything' => $href]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"><d:href>/bla/path</d:href></d:anything> +', $xml); + + } + + function testUnserialize() { + + $xml = '<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"><d:href>/bla/path</d:href></d:anything> +'; + + $result = $this->parse($xml, ['{DAV:}anything' => 'Sabre\\DAV\\Xml\\Property\\Href']); + + $href = $result['value']; + + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $href); + + $this->assertEquals('/bla/path', $href->getHref()); + + } + + function testUnserializeIncompatible() { + + $xml = '<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"><d:href2>/bla/path</d:href2></d:anything> +'; + $result = $this->parse($xml, ['{DAV:}anything' => 'Sabre\\DAV\\Xml\\Property\\Href']); + $href = $result['value']; + $this->assertNull($href); + + } + function testUnserializeEmpty() { + + $xml = '<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"></d:anything> +'; + $result = $this->parse($xml, ['{DAV:}anything' => 'Sabre\\DAV\\Xml\\Property\\Href']); + $href = $result['value']; + $this->assertNull($href); + + } + + /** + * This method tests if hrefs containing & are correctly encoded. + */ + function testSerializeEntity() { + + $href = new Href('http://example.org/?a&b', false); + $this->assertEquals('http://example.org/?a&b', $href->getHref()); + + $xml = $this->write(['{DAV:}anything' => $href]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"><d:href>http://example.org/?a&b</d:href></d:anything> +', $xml); + + } + + function testToHtml() { + + $href = new Href([ + '/foo/bar', + 'foo/bar', + 'http://example.org/bar' + ]); + + $html = new HtmlOutputHelper( + '/base/', + [] + ); + + $expected = + '<a href="/foo/bar">/foo/bar</a><br />' . + '<a href="/base/foo/bar">/base/foo/bar</a><br />' . + '<a href="http://example.org/bar">http://example.org/bar</a>'; + $this->assertEquals($expected, $href->toHtml($html)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/InviteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/InviteTest.php new file mode 100644 index 00000000000..6f8d6cc6c30 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/InviteTest.php @@ -0,0 +1,76 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use Sabre\DAV\Sharing\Plugin; +use Sabre\DAV\Xml\Element\Sharee; +use Sabre\DAV\Xml\XmlTest; + +class InviteTest extends XmlTest { + + function testSerialize() { + + $sharees = [ + new Sharee(), + new Sharee(), + new Sharee(), + new Sharee() + ]; + $sharees[0]->href = 'mailto:foo@example.org'; + $sharees[0]->properties['{DAV:}displayname'] = 'Foo Bar'; + $sharees[0]->access = Plugin::ACCESS_SHAREDOWNER; + $sharees[0]->inviteStatus = Plugin::INVITE_ACCEPTED; + + $sharees[1]->href = 'mailto:bar@example.org'; + $sharees[1]->access = Plugin::ACCESS_READ; + $sharees[1]->inviteStatus = Plugin::INVITE_DECLINED; + + $sharees[2]->href = 'mailto:baz@example.org'; + $sharees[2]->access = Plugin::ACCESS_READWRITE; + $sharees[2]->inviteStatus = Plugin::INVITE_NORESPONSE; + + $sharees[3]->href = 'mailto:zim@example.org'; + $sharees[3]->access = Plugin::ACCESS_READWRITE; + $sharees[3]->inviteStatus = Plugin::INVITE_INVALID; + + $invite = new Invite($sharees); + + $xml = $this->write(['{DAV:}root' => $invite]); + + $expected = <<<XML +<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> +<d:sharee> + <d:href>mailto:foo@example.org</d:href> + <d:prop> + <d:displayname>Foo Bar</d:displayname> + </d:prop> + <d:share-access><d:shared-owner /></d:share-access> + <d:invite-accepted/> +</d:sharee> +<d:sharee> + <d:href>mailto:bar@example.org</d:href> + <d:prop /> + <d:share-access><d:read /></d:share-access> + <d:invite-declined/> +</d:sharee> +<d:sharee> + <d:href>mailto:baz@example.org</d:href> + <d:prop /> + <d:share-access><d:read-write /></d:share-access> + <d:invite-noresponse/> +</d:sharee> +<d:sharee> + <d:href>mailto:zim@example.org</d:href> + <d:prop /> + <d:share-access><d:read-write /></d:share-access> + <d:invite-invalid/> +</d:sharee> +</d:root> +XML; + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LastModifiedTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LastModifiedTest.php new file mode 100644 index 00000000000..669efbd4534 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LastModifiedTest.php @@ -0,0 +1,59 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use DateTime; +use DateTimeZone; +use Sabre\DAV\Xml\XmlTest; + +class LastModifiedTest extends XmlTest { + + function testSerializeDateTime() { + + $dt = new DateTime('2015-03-24 11:47:00', new DateTimeZone('America/Vancouver')); + $val = ['{DAV:}getlastmodified' => new GetLastModified($dt)]; + + $result = $this->write($val); + $expected = <<<XML +<?xml version="1.0"?> +<d:getlastmodified xmlns:d="DAV:">Tue, 24 Mar 2015 18:47:00 GMT</d:getlastmodified> +XML; + + $this->assertXmlStringEqualsXmlString($expected, $result); + + } + + function testSerializeTimeStamp() { + + $dt = new DateTime('2015-03-24 11:47:00', new DateTimeZone('America/Vancouver')); + $dt = $dt->getTimeStamp(); + $val = ['{DAV:}getlastmodified' => new GetLastModified($dt)]; + + $result = $this->write($val); + $expected = <<<XML +<?xml version="1.0"?> +<d:getlastmodified xmlns:d="DAV:">Tue, 24 Mar 2015 18:47:00 GMT</d:getlastmodified> +XML; + + $this->assertXmlStringEqualsXmlString($expected, $result); + + } + + function testDeserialize() { + + $input = <<<XML +<?xml version="1.0"?> +<d:getlastmodified xmlns:d="DAV:">Tue, 24 Mar 2015 18:47:00 GMT</d:getlastmodified> +XML; + + $elementMap = ['{DAV:}getlastmodified' => 'Sabre\DAV\Xml\Property\GetLastModified']; + $result = $this->parse($input, $elementMap); + + $this->assertEquals( + new DateTime('2015-03-24 18:47:00', new DateTimeZone('UTC')), + $result['value']->getTime() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LocalHrefTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LocalHrefTest.php new file mode 100644 index 00000000000..c3f69c929f6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LocalHrefTest.php @@ -0,0 +1,69 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use Sabre\DAV; +use Sabre\DAV\Browser\HtmlOutputHelper; +use Sabre\DAV\Xml\XmlTest; + +class LocalHrefTest extends XmlTest { + + function testConstruct() { + + $href = new LocalHref('path'); + $this->assertEquals('path', $href->getHref()); + + } + + function testSerialize() { + + $href = new LocalHref('path'); + $this->assertEquals('path', $href->getHref()); + + $this->contextUri = '/bla/'; + + $xml = $this->write(['{DAV:}anything' => $href]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"><d:href>/bla/path</d:href></d:anything> +', $xml); + + } + function testSerializeSpace() { + + $href = new LocalHref('path alsopath'); + $this->assertEquals('path%20alsopath', $href->getHref()); + + $this->contextUri = '/bla/'; + + $xml = $this->write(['{DAV:}anything' => $href]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:anything xmlns:d="DAV:"><d:href>/bla/path%20alsopath</d:href></d:anything> +', $xml); + + } + function testToHtml() { + + $href = new LocalHref([ + '/foo/bar', + 'foo/bar', + 'http://example.org/bar' + ]); + + $html = new HtmlOutputHelper( + '/base/', + [] + ); + + $expected = + '<a href="/foo/bar">/foo/bar</a><br />' . + '<a href="/base/foo/bar">/base/foo/bar</a><br />' . + '<a href="http://example.org/bar">http://example.org/bar</a>'; + $this->assertEquals($expected, $href->toHtml($html)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LockDiscoveryTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LockDiscoveryTest.php new file mode 100644 index 00000000000..0ad069c4723 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/LockDiscoveryTest.php @@ -0,0 +1,86 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use Sabre\DAV\Locks\LockInfo; +use Sabre\DAV\Xml\XmlTest; + +class LockDiscoveryTest extends XmlTest { + + function testSerialize() { + + $lock = new LockInfo(); + $lock->owner = 'hello'; + $lock->token = 'blabla'; + $lock->timeout = 600; + $lock->created = strtotime('2015-03-25 19:21:00'); + $lock->scope = LockInfo::EXCLUSIVE; + $lock->depth = 0; + $lock->uri = 'hi'; + + $prop = new LockDiscovery([$lock]); + + $xml = $this->write(['{DAV:}root' => $prop]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:activelock> + <d:lockscope><d:exclusive /></d:lockscope> + <d:locktype><d:write /></d:locktype> + <d:lockroot> + <d:href>/hi</d:href> + </d:lockroot> + <d:depth>0</d:depth> + <d:timeout>Second-600</d:timeout> + <d:locktoken> + <d:href>opaquelocktoken:blabla</d:href> + </d:locktoken> + <d:owner>hello</d:owner> + + +</d:activelock> +</d:root> +', $xml); + + } + + function testSerializeShared() { + + $lock = new LockInfo(); + $lock->owner = 'hello'; + $lock->token = 'blabla'; + $lock->timeout = 600; + $lock->created = strtotime('2015-03-25 19:21:00'); + $lock->scope = LockInfo::SHARED; + $lock->depth = 0; + $lock->uri = 'hi'; + + $prop = new LockDiscovery([$lock]); + + $xml = $this->write(['{DAV:}root' => $prop]); + + $this->assertXmlStringEqualsXmlString( +'<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:activelock> + <d:lockscope><d:shared /></d:lockscope> + <d:locktype><d:write /></d:locktype> + <d:lockroot> + <d:href>/hi</d:href> + </d:lockroot> + <d:depth>0</d:depth> + <d:timeout>Second-600</d:timeout> + <d:locktoken> + <d:href>opaquelocktoken:blabla</d:href> + </d:locktoken> + <d:owner>hello</d:owner> + + +</d:activelock> +</d:root> +', $xml); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/ShareAccessTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/ShareAccessTest.php new file mode 100644 index 00000000000..6e733dded21 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/ShareAccessTest.php @@ -0,0 +1,121 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use Sabre\DAV\Sharing\Plugin; +use Sabre\DAV\Xml\XmlTest; + +class ShareAccessTest extends XmlTest { + + function testSerialize() { + + $data = ['{DAV:}root' => [ + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_READ), + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_READWRITE), + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_NOTSHARED), + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_NOACCESS), + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_SHAREDOWNER), + ], + + ]]; + + $xml = $this->write($data); + + $expected = <<<XML +<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:share-access><d:read /></d:share-access> + <d:share-access><d:read-write /></d:share-access> + <d:share-access><d:not-shared /></d:share-access> + <d:share-access><d:no-access /></d:share-access> + <d:share-access><d:shared-owner /></d:share-access> +</d:root> +XML; + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + function testDeserialize() { + + $input = <<<XML +<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:share-access><d:read /></d:share-access> + <d:share-access><d:read-write /></d:share-access> + <d:share-access><d:not-shared /></d:share-access> + <d:share-access><d:no-access /></d:share-access> + <d:share-access><d:shared-owner /></d:share-access> +</d:root> +XML; + + $data = [ + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_READ), + 'attributes' => [], + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_READWRITE), + 'attributes' => [], + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_NOTSHARED), + 'attributes' => [], + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_NOACCESS), + 'attributes' => [], + ], + [ + 'name' => '{DAV:}share-access', + 'value' => new ShareAccess(Plugin::ACCESS_SHAREDOWNER), + 'attributes' => [], + ], + + ]; + + $this->assertParsedValue( + $data, + $input, + ['{DAV:}share-access' => ShareAccess::class] + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeInvalid() { + + $input = <<<XML +<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:share-access><d:foo /></d:share-access> +</d:root> +XML; + + $this->parse( + $input, + ['{DAV:}share-access' => ShareAccess::class] + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/SupportedMethodSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/SupportedMethodSetTest.php new file mode 100644 index 00000000000..3d54acd2d9d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/SupportedMethodSetTest.php @@ -0,0 +1,45 @@ +<?php + +namespace Sabre\DAV\Xml\Property; + +use Sabre\DAV\Xml\XmlTest; + +class SupportedMethodSetTest extends XmlTest { + + function testSimple() { + + $cus = new SupportedMethodSet(['GET', 'PUT']); + $this->assertEquals(['GET', 'PUT'], $cus->getValue()); + + $this->assertTrue($cus->has('GET')); + $this->assertFalse($cus->has('HEAD')); + + } + + function testSerialize() { + + $cus = new SupportedMethodSet(['GET', 'PUT']); + $xml = $this->write(['{DAV:}foo' => $cus]); + + $expected = '<?xml version="1.0"?> +<d:foo xmlns:d="DAV:"> + <d:supported-method name="GET"/> + <d:supported-method name="PUT"/> +</d:foo>'; + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + function testSerializeHtml() { + + $cus = new SupportedMethodSet(['GET', 'PUT']); + $result = $cus->toHtml( + new \Sabre\DAV\Browser\HtmlOutputHelper('/', []) + ); + + $this->assertEquals('GET, PUT', $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/SupportedReportSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/SupportedReportSetTest.php new file mode 100644 index 00000000000..cc25697f68d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Property/SupportedReportSetTest.php @@ -0,0 +1,115 @@ +<?php + +namespace Sabre\DAV\Property; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; +require_once 'Sabre/DAV/AbstractServer.php'; + +class SupportedReportSetTest extends DAV\AbstractServer { + + function sendPROPFIND($body) { + + $serverVars = [ + 'REQUEST_URI' => '/', + 'REQUEST_METHOD' => 'PROPFIND', + 'HTTP_DEPTH' => '0', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($body); + + $this->server->httpRequest = ($request); + $this->server->exec(); + + } + + /** + */ + function testNoReports() { + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:supported-report-set /> + </d:prop> +</d:propfind>'; + + $this->sendPROPFIND($xml); + + $this->assertEquals(207, $this->response->status, 'We expected a multi-status response. Full response body: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:prop\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:supported-report-set\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:status\' element'); + + $this->assertEquals('HTTP/1.1 200 OK', (string)$data[0], 'The status for this property should have been 200'); + + } + + /** + * @depends testNoReports + */ + function testCustomReport() { + + // Intercepting the report property + $this->server->on('propFind', function(DAV\PropFind $propFind, DAV\INode $node) { + if ($prop = $propFind->get('{DAV:}supported-report-set')) { + $prop->addReport('{http://www.rooftopsolutions.nl/testnamespace}myreport'); + $prop->addReport('{DAV:}anotherreport'); + } + }, 200); + + $xml = '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:"> + <d:prop> + <d:supported-report-set /> + </d:prop> +</d:propfind>'; + + $this->sendPROPFIND($xml); + + $this->assertEquals(207, $this->response->status, 'We expected a multi-status response. Full response body: ' . $this->response->body); + + $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body); + $xml = simplexml_load_string($body); + $xml->registerXPathNamespace('d', 'urn:DAV'); + $xml->registerXPathNamespace('x', 'http://www.rooftopsolutions.nl/testnamespace'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:prop\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:supported-report-set\' element'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report'); + $this->assertEquals(2, count($data), 'We expected 2 \'d:supported-report\' elements'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report'); + $this->assertEquals(2, count($data), 'We expected 2 \'d:report\' elements'); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/x:myreport'); + $this->assertEquals(1, count($data), 'We expected 1 \'x:myreport\' element. Full body: ' . $this->response->body); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/d:anotherreport'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:anotherreport\' element. Full body: ' . $this->response->body); + + $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); + $this->assertEquals(1, count($data), 'We expected 1 \'d:status\' element'); + + $this->assertEquals('HTTP/1.1 200 OK', (string)$data[0], 'The status for this property should have been 200'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/PropFindTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/PropFindTest.php new file mode 100644 index 00000000000..c11668b6188 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/PropFindTest.php @@ -0,0 +1,48 @@ +<?php + +namespace Sabre\DAV\Xml\Request; + +use Sabre\DAV\Xml\XmlTest; + +class PropFindTest extends XmlTest { + + function testDeserializeProp() { + + $xml = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:prop> + <d:hello /> + </d:prop> +</d:root> +'; + + $result = $this->parse($xml, ['{DAV:}root' => 'Sabre\\DAV\\Xml\\Request\PropFind']); + + $propFind = new PropFind(); + $propFind->properties = ['{DAV:}hello']; + + $this->assertEquals($propFind, $result['value']); + + + } + + function testDeserializeAllProp() { + + $xml = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:allprop /> +</d:root> +'; + + $result = $this->parse($xml, ['{DAV:}root' => 'Sabre\\DAV\\Xml\\Request\PropFind']); + + $propFind = new PropFind(); + $propFind->allProp = true; + + $this->assertEquals($propFind, $result['value']); + + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/PropPatchTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/PropPatchTest.php new file mode 100644 index 00000000000..03514da5cb6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/PropPatchTest.php @@ -0,0 +1,53 @@ +<?php + +namespace Sabre\DAV\Xml\Request; + +use Sabre\DAV\Xml\Property\Href; +use Sabre\DAV\Xml\XmlTest; + +class PropPatchTest extends XmlTest { + + function testSerialize() { + + $propPatch = new PropPatch(); + $propPatch->properties = [ + '{DAV:}displayname' => 'Hello!', + '{DAV:}delete-me' => null, + '{DAV:}some-url' => new Href('foo/bar') + ]; + + $result = $this->write( + ['{DAV:}propertyupdate' => $propPatch] + ); + + $expected = <<<XML +<?xml version="1.0"?> +<d:propertyupdate xmlns:d="DAV:"> + <d:set> + <d:prop> + <d:displayname>Hello!</d:displayname> + </d:prop> + </d:set> + <d:remove> + <d:prop> + <d:delete-me /> + </d:prop> + </d:remove> + <d:set> + <d:prop> + <d:some-url> + <d:href>/foo/bar</d:href> + </d:some-url> + </d:prop> + </d:set> +</d:propertyupdate> +XML; + + $this->assertXmlStringEqualsXmlString( + $expected, + $result + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/ShareResourceTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/ShareResourceTest.php new file mode 100644 index 00000000000..1e6b5602de0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/ShareResourceTest.php @@ -0,0 +1,75 @@ +<?php + +namespace Sabre\DAV\Xml\Request; + +use Sabre\DAV\Sharing\Plugin; +use Sabre\DAV\Xml\Element\Sharee; +use Sabre\DAV\Xml\XmlTest; + +class ShareResourceTest extends XmlTest { + + function testDeserialize() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:share-resource xmlns:D="DAV:"> + <D:sharee> + <D:href>mailto:eric@example.com</D:href> + <D:prop> + <D:displayname>Eric York</D:displayname> + </D:prop> + <D:comment>Shared workspace</D:comment> + <D:share-access> + <D:read-write /> + </D:share-access> + </D:sharee> + <D:sharee> + <D:href>mailto:eric@example.com</D:href> + <D:share-access> + <D:read /> + </D:share-access> + </D:sharee> + <D:sharee> + <D:href>mailto:wilfredo@example.com</D:href> + <D:share-access> + <D:no-access /> + </D:share-access> + </D:sharee> +</D:share-resource> +XML; + + $result = $this->parse($xml, [ + '{DAV:}share-resource' => 'Sabre\\DAV\\Xml\\Request\\ShareResource' + ]); + + $this->assertInstanceOf( + 'Sabre\\DAV\\Xml\\Request\\ShareResource', + $result['value'] + ); + + $expected = [ + new Sharee(), + new Sharee(), + new Sharee(), + ]; + + $expected[0]->href = 'mailto:eric@example.com'; + $expected[0]->properties['{DAV:}displayname'] = 'Eric York'; + $expected[0]->comment = 'Shared workspace'; + $expected[0]->access = Plugin::ACCESS_READWRITE; + + $expected[1]->href = 'mailto:eric@example.com'; + $expected[1]->access = Plugin::ACCESS_READ; + + $expected[2]->href = 'mailto:wilfredo@example.com'; + $expected[2]->access = Plugin::ACCESS_NOACCESS; + + $this->assertEquals( + $expected, + $result['value']->sharees + ); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/SyncCollectionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/SyncCollectionTest.php new file mode 100644 index 00000000000..bde1a103d19 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/Request/SyncCollectionTest.php @@ -0,0 +1,94 @@ +<?php + +namespace Sabre\DAV\Xml\Request; + +use Sabre\DAV\Xml\XmlTest; + +class SyncCollectionTest extends XmlTest { + + function testDeserializeProp() { + + $xml = '<?xml version="1.0"?> +<d:sync-collection xmlns:d="DAV:"> + <d:sync-token /> + <d:sync-level>1</d:sync-level> + <d:prop> + <d:foo /> + </d:prop> +</d:sync-collection> +'; + + $result = $this->parse($xml, ['{DAV:}sync-collection' => 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport']); + + $elem = new SyncCollectionReport(); + $elem->syncLevel = 1; + $elem->properties = ['{DAV:}foo']; + + $this->assertEquals($elem, $result['value']); + + } + + + function testDeserializeLimit() { + + $xml = '<?xml version="1.0"?> +<d:sync-collection xmlns:d="DAV:"> + <d:sync-token /> + <d:sync-level>1</d:sync-level> + <d:prop> + <d:foo /> + </d:prop> + <d:limit><d:nresults>5</d:nresults></d:limit> +</d:sync-collection> +'; + + $result = $this->parse($xml, ['{DAV:}sync-collection' => 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport']); + + $elem = new SyncCollectionReport(); + $elem->syncLevel = 1; + $elem->properties = ['{DAV:}foo']; + $elem->limit = 5; + + $this->assertEquals($elem, $result['value']); + + } + + + function testDeserializeInfinity() { + + $xml = '<?xml version="1.0"?> +<d:sync-collection xmlns:d="DAV:"> + <d:sync-token /> + <d:sync-level>infinity</d:sync-level> + <d:prop> + <d:foo /> + </d:prop> +</d:sync-collection> +'; + + $result = $this->parse($xml, ['{DAV:}sync-collection' => 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport']); + + $elem = new SyncCollectionReport(); + $elem->syncLevel = \Sabre\DAV\Server::DEPTH_INFINITY; + $elem->properties = ['{DAV:}foo']; + + $this->assertEquals($elem, $result['value']); + + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + */ + function testDeserializeMissingElem() { + + $xml = '<?xml version="1.0"?> +<d:sync-collection xmlns:d="DAV:"> + <d:sync-token /> +</d:sync-collection> +'; + + $result = $this->parse($xml, ['{DAV:}sync-collection' => 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/XmlTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/XmlTest.php new file mode 100644 index 00000000000..906a36085bc --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAV/Xml/XmlTest.php @@ -0,0 +1,48 @@ +<?php + +namespace Sabre\DAV\Xml; + +use Sabre\Xml\Reader; +use Sabre\Xml\Writer; + +abstract class XmlTest extends \PHPUnit_Framework_TestCase { + + protected $elementMap = []; + protected $namespaceMap = ['DAV:' => 'd']; + protected $contextUri = '/'; + + function write($input) { + + $writer = new Writer(); + $writer->contextUri = $this->contextUri; + $writer->namespaceMap = $this->namespaceMap; + $writer->openMemory(); + $writer->setIndent(true); + $writer->write($input); + return $writer->outputMemory(); + + } + + function parse($xml, array $elementMap = []) { + + $reader = new Reader(); + $reader->elementMap = array_merge($this->elementMap, $elementMap); + $reader->xml($xml); + return $reader->parse(); + + } + + function assertParsedValue($expected, $xml, array $elementMap = []) { + + $result = $this->parse($xml, $elementMap); + $this->assertEquals($expected, $result['value']); + + } + + function cleanUp() { + + libxml_clear_errors(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php new file mode 100644 index 00000000000..7d7a54d064c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/ACLMethodTest.php @@ -0,0 +1,337 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +class ACLMethodTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testCallback() { + + $acl = new Plugin(); + $server = new DAV\Server(); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpAcl($server->httpRequest, $server->httpResponse); + + } + + /** + /** + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testNotSupportedByNode() { + + $tree = [ + new DAV\SimpleCollection('test'), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request(); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + function testSuccessSimple() { + + $tree = [ + new MockACLNode('test', []), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request(); + $server->httpRequest->setUrl('/test'); + + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $this->assertFalse($acl->httpACL($server->httpRequest, $server->httpResponse)); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NotRecognizedPrincipal + */ + function testUnrecognizedPrincipal() { + + $tree = [ + new MockACLNode('test', []), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:read /></d:privilege></d:grant> + <d:principal><d:href>/principals/notfound</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NotRecognizedPrincipal + */ + function testUnrecognizedPrincipal2() { + + $tree = [ + new MockACLNode('test', []), + new DAV\SimpleCollection('principals', [ + new DAV\SimpleCollection('notaprincipal'), + ]), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:read /></d:privilege></d:grant> + <d:principal><d:href>/principals/notaprincipal</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NotSupportedPrivilege + */ + function testUnknownPrivilege() { + + $tree = [ + new MockACLNode('test', []), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:bananas /></d:privilege></d:grant> + <d:principal><d:href>/principals/notfound</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NoAbstract + */ + function testAbstractPrivilege() { + + $tree = [ + new MockACLNode('test', []), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->on('getSupportedPrivilegeSet', function($node, &$supportedPrivilegeSet) { + $supportedPrivilegeSet['{DAV:}foo'] = ['abstract' => true]; + }); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:foo /></d:privilege></d:grant> + <d:principal><d:href>/principals/foo/</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\AceConflict + */ + function testUpdateProtectedPrivilege() { + + $oldACL = [ + [ + 'principal' => 'principals/notfound', + 'privilege' => '{DAV:}write', + 'protected' => true, + ], + ]; + + $tree = [ + new MockACLNode('test', $oldACL), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:read /></d:privilege></d:grant> + <d:principal><d:href>/principals/notfound</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\AceConflict + */ + function testUpdateProtectedPrivilege2() { + + $oldACL = [ + [ + 'principal' => 'principals/notfound', + 'privilege' => '{DAV:}write', + 'protected' => true, + ], + ]; + + $tree = [ + new MockACLNode('test', $oldACL), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:write /></d:privilege></d:grant> + <d:principal><d:href>/principals/foo</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\AceConflict + */ + function testUpdateProtectedPrivilege3() { + + $oldACL = [ + [ + 'principal' => 'principals/notfound', + 'privilege' => '{DAV:}write', + 'protected' => true, + ], + ]; + + $tree = [ + new MockACLNode('test', $oldACL), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:write /></d:privilege></d:grant> + <d:principal><d:href>/principals/notfound</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + $acl->httpACL($server->httpRequest, $server->httpResponse); + + } + + function testSuccessComplex() { + + $oldACL = [ + [ + 'principal' => 'principals/foo', + 'privilege' => '{DAV:}write', + 'protected' => true, + ], + [ + 'principal' => 'principals/bar', + 'privilege' => '{DAV:}read', + ], + ]; + + $tree = [ + $node = new MockACLNode('test', $oldACL), + new DAV\SimpleCollection('principals', [ + new MockPrincipal('foo', 'principals/foo'), + new MockPrincipal('baz', 'principals/baz'), + ]), + ]; + $acl = new Plugin(); + $server = new DAV\Server($tree); + $server->httpRequest = new HTTP\Request('ACL', '/test'); + $body = '<?xml version="1.0"?> +<d:acl xmlns:d="DAV:"> + <d:ace> + <d:grant><d:privilege><d:write /></d:privilege></d:grant> + <d:principal><d:href>/principals/foo</d:href></d:principal> + <d:protected /> + </d:ace> + <d:ace> + <d:grant><d:privilege><d:write /></d:privilege></d:grant> + <d:principal><d:href>/principals/baz</d:href></d:principal> + </d:ace> +</d:acl>'; + $server->httpRequest->setBody($body); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin($acl); + + + $this->assertFalse($acl->httpAcl($server->httpRequest, $server->httpResponse)); + + $this->assertEquals([ + [ + 'principal' => 'principals/foo', + 'privilege' => '{DAV:}write', + 'protected' => true, + ], + [ + 'principal' => 'principals/baz', + 'privilege' => '{DAV:}write', + 'protected' => false, + ], + ], $node->getACL()); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/AclPrincipalPropSetReportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/AclPrincipalPropSetReportTest.php new file mode 100644 index 00000000000..338fe36ab0c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/AclPrincipalPropSetReportTest.php @@ -0,0 +1,69 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\HTTP\Request; + +class AclPrincipalPropSetReportTest extends \Sabre\DAVServerTest { + + public $setupACL = true; + public $autoLogin = 'admin'; + + function testReport() { + + $xml = <<<XML +<?xml version="1.0"?> +<acl-principal-prop-set xmlns="DAV:"> + <prop> + <principal-URL /> + <displayname /> + </prop> +</acl-principal-prop-set> +XML; + + $request = new Request('REPORT', '/principals/user1', ['Content-Type' => 'application/xml', 'Depth' => 0]); + $request->setBody($xml); + + $response = $this->request($request, 207); + + $expected = <<<XML +<?xml version="1.0"?> +<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:response> + <d:href>/principals/admin/</d:href> + <d:propstat> + <d:prop> + <d:principal-URL><d:href>/principals/admin/</d:href></d:principal-URL> + <d:displayname>Admin</d:displayname> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> + </d:response> +</d:multistatus> +XML; + + $this->assertXmlStringEqualsXmlString( + $expected, + $response->getBodyAsString() + ); + + } + + function testReportDepth1() { + + $xml = <<<XML +<?xml version="1.0"?> +<acl-principal-prop-set xmlns="DAV:"> + <principal-URL /> + <displayname /> +</acl-principal-prop-set> +XML; + + $request = new Request('REPORT', '/principals/user1', ['Content-Type' => 'application/xml', 'Depth' => 1]); + $request->setBody($xml); + + $this->request($request, 400); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php new file mode 100644 index 00000000000..f16693625b2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/AllowAccessTest.php @@ -0,0 +1,132 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; + +class AllowAccessTest extends \PHPUnit_Framework_TestCase { + + /** + * @var DAV\Server + */ + protected $server; + + function setUp() { + + $nodes = [ + new DAV\Mock\Collection('testdir', [ + 'file1.txt' => 'contents', + ]), + ]; + + $this->server = new DAV\Server($nodes); + $this->server->addPlugin( + new DAV\Auth\Plugin( + new DAV\Auth\Backend\Mock() + ) + ); + // Login + $this->server->getPlugin('auth')->beforeMethod( + new \Sabre\HTTP\Request(), + new \Sabre\HTTP\Response() + ); + $aclPlugin = new Plugin(); + $this->server->addPlugin($aclPlugin); + + } + + function testGet() { + + $this->server->httpRequest->setMethod('GET'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testGetDoesntExist() { + + $this->server->httpRequest->setMethod('GET'); + $this->server->httpRequest->setUrl('/foo'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testHEAD() { + + $this->server->httpRequest->setMethod('HEAD'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testOPTIONS() { + + $this->server->httpRequest->setMethod('OPTIONS'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testPUT() { + + $this->server->httpRequest->setMethod('PUT'); + $this->server->httpRequest->setUrl('/testdir/file1.txt'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testPROPPATCH() { + + $this->server->httpRequest->setMethod('PROPPATCH'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testCOPY() { + + $this->server->httpRequest->setMethod('COPY'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testMOVE() { + + $this->server->httpRequest->setMethod('MOVE'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testLOCK() { + + $this->server->httpRequest->setMethod('LOCK'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); + + } + + function testBeforeBind() { + + $this->assertTrue($this->server->emit('beforeBind', ['testdir/file'])); + + } + + + function testBeforeUnbind() { + + $this->assertTrue($this->server->emit('beforeUnbind', ['testdir'])); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php new file mode 100644 index 00000000000..ceae9aed059 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/BlockAccessTest.php @@ -0,0 +1,215 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; + +class BlockAccessTest extends \PHPUnit_Framework_TestCase { + + /** + * @var DAV\Server + */ + protected $server; + protected $plugin; + + function setUp() { + + $nodes = [ + new DAV\SimpleCollection('testdir'), + ]; + + $this->server = new DAV\Server($nodes); + $this->plugin = new Plugin(); + $this->plugin->setDefaultAcl([]); + $this->server->addPlugin( + new DAV\Auth\Plugin( + new DAV\Auth\Backend\Mock() + ) + ); + // Login + $this->server->getPlugin('auth')->beforeMethod( + new \Sabre\HTTP\Request(), + new \Sabre\HTTP\Response() + ); + $this->server->addPlugin($this->plugin); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testGet() { + + $this->server->httpRequest->setMethod('GET'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + function testGetDoesntExist() { + + $this->server->httpRequest->setMethod('GET'); + $this->server->httpRequest->setUrl('/foo'); + + $r = $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + $this->assertTrue($r); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testHEAD() { + + $this->server->httpRequest->setMethod('HEAD'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testOPTIONS() { + + $this->server->httpRequest->setMethod('OPTIONS'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testPUT() { + + $this->server->httpRequest->setMethod('PUT'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testPROPPATCH() { + + $this->server->httpRequest->setMethod('PROPPATCH'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testCOPY() { + + $this->server->httpRequest->setMethod('COPY'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testMOVE() { + + $this->server->httpRequest->setMethod('MOVE'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testACL() { + + $this->server->httpRequest->setMethod('ACL'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testLOCK() { + + $this->server->httpRequest->setMethod('LOCK'); + $this->server->httpRequest->setUrl('/testdir'); + + $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testBeforeBind() { + + $this->server->emit('beforeBind', ['testdir/file']); + + } + + /** + * @expectedException Sabre\DAVACL\Exception\NeedPrivileges + */ + function testBeforeUnbind() { + + $this->server->emit('beforeUnbind', ['testdir']); + + } + + function testPropFind() { + + $propFind = new DAV\PropFind('testdir', [ + '{DAV:}displayname', + '{DAV:}getcontentlength', + '{DAV:}bar', + '{DAV:}owner', + ]); + + $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]); + $this->assertTrue($r); + + $expected = [ + 200 => [], + 404 => [], + 403 => [ + '{DAV:}displayname' => null, + '{DAV:}getcontentlength' => null, + '{DAV:}bar' => null, + '{DAV:}owner' => null, + ], + ]; + + $this->assertEquals($expected, $propFind->getResultForMultiStatus()); + + } + + function testBeforeGetPropertiesNoListing() { + + $this->plugin->hideNodesFromListings = true; + $propFind = new DAV\PropFind('testdir', [ + '{DAV:}displayname', + '{DAV:}getcontentlength', + '{DAV:}bar', + '{DAV:}owner', + ]); + + $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]); + $this->assertFalse($r); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/AceConflictTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/AceConflictTest.php new file mode 100644 index 00000000000..1cdf2949f2e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/AceConflictTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\DAVACL\Exception; + +use Sabre\DAV; + +class AceConflictTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $ex = new AceConflict('message'); + + $server = new DAV\Server(); + $dom = new \DOMDocument('1.0', 'utf-8'); + $root = $dom->createElementNS('DAV:', 'd:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = [ + '/d:root' => 1, + '/d:root/d:no-ace-conflict' => 1, + ]; + + // Reloading because PHP DOM sucks + $dom2 = new \DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new \DOMXPath($dom2); + $dxpath->registerNamespace('d', 'DAV:'); + foreach ($xpaths as $xpath => $count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php new file mode 100644 index 00000000000..b13e7722d89 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php @@ -0,0 +1,49 @@ +<?php + +namespace Sabre\DAVACL\Exception; + +use Sabre\DAV; + +class NeedPrivilegesExceptionTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $uri = 'foo'; + $privileges = [ + '{DAV:}read', + '{DAV:}write', + ]; + $ex = new NeedPrivileges($uri, $privileges); + + $server = new DAV\Server(); + $dom = new \DOMDocument('1.0', 'utf-8'); + $root = $dom->createElementNS('DAV:', 'd:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = [ + '/d:root' => 1, + '/d:root/d:need-privileges' => 1, + '/d:root/d:need-privileges/d:resource' => 2, + '/d:root/d:need-privileges/d:resource/d:href' => 2, + '/d:root/d:need-privileges/d:resource/d:privilege' => 2, + '/d:root/d:need-privileges/d:resource/d:privilege/d:read' => 1, + '/d:root/d:need-privileges/d:resource/d:privilege/d:write' => 1, + ]; + + // Reloading because PHP DOM sucks + $dom2 = new \DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new \DOMXPath($dom2); + $dxpath->registerNamespace('d', 'DAV:'); + foreach ($xpaths as $xpath => $count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NoAbstractTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NoAbstractTest.php new file mode 100644 index 00000000000..f52b1737140 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NoAbstractTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\DAVACL\Exception; + +use Sabre\DAV; + +class NoAbstractTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $ex = new NoAbstract('message'); + + $server = new DAV\Server(); + $dom = new \DOMDocument('1.0', 'utf-8'); + $root = $dom->createElementNS('DAV:', 'd:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = [ + '/d:root' => 1, + '/d:root/d:no-abstract' => 1, + ]; + + // Reloading because PHP DOM sucks + $dom2 = new \DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new \DOMXPath($dom2); + $dxpath->registerNamespace('d', 'DAV:'); + foreach ($xpaths as $xpath => $count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php new file mode 100644 index 00000000000..df89aaf84d1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\DAVACL\Exception; + +use Sabre\DAV; + +class NotRecognizedPrincipalTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $ex = new NotRecognizedPrincipal('message'); + + $server = new DAV\Server(); + $dom = new \DOMDocument('1.0', 'utf-8'); + $root = $dom->createElementNS('DAV:', 'd:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = [ + '/d:root' => 1, + '/d:root/d:recognized-principal' => 1, + ]; + + // Reloading because PHP DOM sucks + $dom2 = new \DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new \DOMXPath($dom2); + $dxpath->registerNamespace('d', 'DAV:'); + foreach ($xpaths as $xpath => $count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php new file mode 100644 index 00000000000..50623952be7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\DAVACL\Exception; + +use Sabre\DAV; + +class NotSupportedPrivilegeTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $ex = new NotSupportedPrivilege('message'); + + $server = new DAV\Server(); + $dom = new \DOMDocument('1.0', 'utf-8'); + $root = $dom->createElementNS('DAV:', 'd:root'); + $dom->appendChild($root); + + $ex->serialize($server, $root); + + $xpaths = [ + '/d:root' => 1, + '/d:root/d:not-supported-privilege' => 1, + ]; + + // Reloading because PHP DOM sucks + $dom2 = new \DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($dom->saveXML()); + + $dxpath = new \DOMXPath($dom2); + $dxpath->registerNamespace('d', 'DAV:'); + foreach ($xpaths as $xpath => $count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php new file mode 100644 index 00000000000..91de64372ab --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/ExpandPropertiesTest.php @@ -0,0 +1,317 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; + +class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase { + + function getServer() { + + $tree = [ + new DAV\Mock\PropertiesCollection('node1', [], [ + '{http://sabredav.org/ns}simple' => 'foo', + '{http://sabredav.org/ns}href' => new DAV\Xml\Property\Href('node2'), + '{DAV:}displayname' => 'Node 1', + ]), + new DAV\Mock\PropertiesCollection('node2', [], [ + '{http://sabredav.org/ns}simple' => 'simple', + '{http://sabredav.org/ns}hreflist' => new DAV\Xml\Property\Href(['node1', 'node3']), + '{DAV:}displayname' => 'Node 2', + ]), + new DAV\Mock\PropertiesCollection('node3', [], [ + '{http://sabredav.org/ns}simple' => 'simple', + '{DAV:}displayname' => 'Node 3', + ]), + ]; + + $fakeServer = new DAV\Server($tree); + $fakeServer->sapi = new HTTP\SapiMock(); + $fakeServer->debugExceptions = true; + $fakeServer->httpResponse = new HTTP\ResponseMock(); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + // Anyone can do anything + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ] + ]); + $this->assertTrue($plugin instanceof Plugin); + + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); + + return $fakeServer; + + } + + function testSimple() { + + $xml = '<?xml version="1.0"?> +<d:expand-property xmlns:d="DAV:"> + <d:property name="displayname" /> + <d:property name="foo" namespace="http://www.sabredav.org/NS/2010/nonexistant" /> + <d:property name="simple" namespace="http://sabredav.org/ns" /> + <d:property name="href" namespace="http://sabredav.org/ns" /> +</d:expand-property>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node1', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status, 'Incorrect status code received. Full body: ' . $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 2, + '/d:multistatus/d:response/d:propstat/d:prop' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:simple' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:href' => 1, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + $xml->registerXPathNamespace('s', 'http://sabredav.org/ns'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response: ' . $server->httpResponse->body); + + } + + } + + /** + * @depends testSimple + */ + function testExpand() { + + $xml = '<?xml version="1.0"?> +<d:expand-property xmlns:d="DAV:"> + <d:property name="href" namespace="http://sabredav.org/ns"> + <d:property name="displayname" /> + </d:property> +</d:expand-property>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node1', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status, 'Incorrect response status received. Full response body: ' . $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop/d:displayname' => 1, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + $xml->registerXPathNamespace('s', 'http://sabredav.org/ns'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . ' Full response body: ' . $server->httpResponse->getBodyAsString()); + + } + + } + + /** + * @depends testSimple + */ + function testExpandHrefList() { + + $xml = '<?xml version="1.0"?> +<d:expand-property xmlns:d="DAV:"> + <d:property name="hreflist" namespace="http://sabredav.org/ns"> + <d:property name="displayname" /> + </d:property> +</d:expand-property>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node2', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/d:displayname' => 2, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + $xml->registerXPathNamespace('s', 'http://sabredav.org/ns'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); + + } + + } + + /** + * @depends testExpand + */ + function testExpandDeep() { + + $xml = '<?xml version="1.0"?> +<d:expand-property xmlns:d="DAV:"> + <d:property name="hreflist" namespace="http://sabredav.org/ns"> + <d:property name="href" namespace="http://sabredav.org/ns"> + <d:property name="displayname" /> + </d:property> + <d:property name="displayname" /> + </d:property> +</d:expand-property>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/node2', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 1, + '/d:multistatus/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat' => 3, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop' => 3, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/d:displayname' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:href' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop' => 1, + '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop/d:displayname' => 1, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + $xml->registerXPathNamespace('s', 'http://sabredav.org/ns'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); + + } + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/CollectionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/CollectionTest.php new file mode 100644 index 00000000000..af18e7cc057 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/CollectionTest.php @@ -0,0 +1,44 @@ +<?php + +namespace Sabre\DAVACL\FS; + +class CollectionTest extends FileTest { + + function setUp() { + + $this->path = SABRE_TEMPDIR; + $this->sut = new Collection($this->path, $this->acl, $this->owner); + + } + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + function testGetChildFile() { + + file_put_contents(SABRE_TEMPDIR . '/file.txt', 'hello'); + $child = $this->sut->getChild('file.txt'); + $this->assertInstanceOf('Sabre\\DAVACL\\FS\\File', $child); + + $this->assertEquals('file.txt', $child->getName()); + $this->assertEquals($this->acl, $child->getACL()); + $this->assertEquals($this->owner, $child->getOwner()); + + } + + function testGetChildDirectory() { + + mkdir(SABRE_TEMPDIR . '/dir'); + $child = $this->sut->getChild('dir'); + $this->assertInstanceOf('Sabre\\DAVACL\\FS\\Collection', $child); + + $this->assertEquals('dir', $child->getName()); + $this->assertEquals($this->acl, $child->getACL()); + $this->assertEquals($this->owner, $child->getOwner()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/FileTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/FileTest.php new file mode 100644 index 00000000000..f57b2fa1d16 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/FileTest.php @@ -0,0 +1,73 @@ +<?php + +namespace Sabre\DAVACL\FS; + +class FileTest extends \PHPUnit_Framework_TestCase { + + /** + * System under test + * + * @var File + */ + protected $sut; + + protected $path = 'foo'; + protected $acl = [ + [ + 'privilege' => '{DAV:}read', + 'principal' => '{DAV:}authenticated', + ] + ]; + + protected $owner = 'principals/evert'; + + function setUp() { + + $this->sut = new File($this->path, $this->acl, $this->owner); + + } + + function testGetOwner() { + + $this->assertEquals( + $this->owner, + $this->sut->getOwner() + ); + + } + + function testGetGroup() { + + $this->assertNull( + $this->sut->getGroup() + ); + + } + + function testGetACL() { + + $this->assertEquals( + $this->acl, + $this->sut->getACL() + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetAcl() { + + $this->sut->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->sut->getSupportedPrivilegeSet() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/HomeCollectionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/HomeCollectionTest.php new file mode 100644 index 00000000000..87cfc83e92c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/FS/HomeCollectionTest.php @@ -0,0 +1,116 @@ +<?php + +namespace Sabre\DAVACL\FS; + +use Sabre\DAVACL\PrincipalBackend\Mock as PrincipalBackend; + +class HomeCollectionTest extends \PHPUnit_Framework_TestCase { + + /** + * System under test + * + * @var HomeCollection + */ + protected $sut; + + protected $path; + protected $name = 'thuis'; + + function setUp() { + + $principalBackend = new PrincipalBackend(); + + $this->path = SABRE_TEMPDIR . '/home'; + + $this->sut = new HomeCollection($principalBackend, $this->path); + $this->sut->collectionName = $this->name; + + + } + + function tearDown() { + + \Sabre\TestUtil::clearTempDir(); + + } + + function testGetName() { + + $this->assertEquals( + $this->name, + $this->sut->getName() + ); + + } + + function testGetChild() { + + $child = $this->sut->getChild('user1'); + $this->assertInstanceOf('Sabre\\DAVACL\\FS\\Collection', $child); + $this->assertEquals('user1', $child->getName()); + + $owner = 'principals/user1'; + $acl = [ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ], + ]; + + $this->assertEquals($acl, $child->getACL()); + $this->assertEquals($owner, $child->getOwner()); + + } + + function testGetOwner() { + + $this->assertNull( + $this->sut->getOwner() + ); + + } + + function testGetGroup() { + + $this->assertNull( + $this->sut->getGroup() + ); + + } + + function testGetACL() { + + $acl = [ + [ + 'principal' => '{DAV:}authenticated', + 'privilege' => '{DAV:}read', + 'protected' => true, + ] + ]; + + $this->assertEquals( + $acl, + $this->sut->getACL() + ); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetAcl() { + + $this->sut->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $this->assertNull( + $this->sut->getSupportedPrivilegeSet() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/MockACLNode.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/MockACLNode.php new file mode 100644 index 00000000000..2d9744e293f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/MockACLNode.php @@ -0,0 +1,55 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; + +class MockACLNode extends DAV\Node implements IACL { + + public $name; + public $acl; + + function __construct($name, array $acl = []) { + + $this->name = $name; + $this->acl = $acl; + + } + + function getName() { + + return $this->name; + + } + + function getOwner() { + + return null; + + } + + function getGroup() { + + return null; + + } + + function getACL() { + + return $this->acl; + + } + + function setACL(array $acl) { + + $this->acl = $acl; + + } + + function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/MockPrincipal.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/MockPrincipal.php new file mode 100644 index 00000000000..934906802ed --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/MockPrincipal.php @@ -0,0 +1,64 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; + +class MockPrincipal extends DAV\Node implements IPrincipal { + + public $name; + public $principalUrl; + public $groupMembership = []; + public $groupMemberSet = []; + + function __construct($name, $principalUrl, array $groupMembership = [], array $groupMemberSet = []) { + + $this->name = $name; + $this->principalUrl = $principalUrl; + $this->groupMembership = $groupMembership; + $this->groupMemberSet = $groupMemberSet; + + } + + function getName() { + + return $this->name; + + } + + function getDisplayName() { + + return $this->getName(); + + } + + function getAlternateUriSet() { + + return []; + + } + + function getPrincipalUrl() { + + return $this->principalUrl; + + } + + function getGroupMemberSet() { + + return $this->groupMemberSet; + + } + + function getGroupMemberShip() { + + return $this->groupMembership; + + } + + function setGroupMemberSet(array $groupMemberSet) { + + $this->groupMemberSet = $groupMemberSet; + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php new file mode 100644 index 00000000000..8552448f54b --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginAdminTest.php @@ -0,0 +1,79 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/DAVACL/MockACLNode.php'; +require_once 'Sabre/HTTP/ResponseMock.php'; + +class PluginAdminTest extends \PHPUnit_Framework_TestCase { + + public $server; + + function setUp() { + + $principalBackend = new PrincipalBackend\Mock(); + + $tree = [ + new MockACLNode('adminonly', []), + new PrincipalCollection($principalBackend), + ]; + + $this->server = new DAV\Server($tree); + $this->server->sapi = new HTTP\SapiMock(); + $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $this->server->addPlugin($plugin); + } + + function testNoAdminAccess() { + + $plugin = new Plugin(); + $this->server->addPlugin($plugin); + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'OPTIONS', + 'HTTP_DEPTH' => 1, + 'REQUEST_URI' => '/adminonly', + ]); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(403, $response->status); + + } + + /** + * @depends testNoAdminAccess + */ + function testAdminAccess() { + + $plugin = new Plugin(); + $plugin->adminPrincipals = [ + 'principals/admin', + ]; + $this->server->addPlugin($plugin); + + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'OPTIONS', + 'HTTP_DEPTH' => 1, + 'REQUEST_URI' => '/adminonly', + ]); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $this->assertEquals(200, $response->status); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php new file mode 100644 index 00000000000..fb42efba719 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginPropertiesTest.php @@ -0,0 +1,415 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +class PluginPropertiesTest extends \PHPUnit_Framework_TestCase { + + function testPrincipalCollectionSet() { + + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + //Anyone can do anything + $plugin->principalCollectionSet = [ + 'principals1', + 'principals2', + ]; + + $requestedProperties = [ + '{DAV:}principal-collection-set', + ]; + + $server = new DAV\Server(new DAV\SimpleCollection('root')); + $server->addPlugin($plugin); + + $result = $server->getPropertiesForPath('', $requestedProperties); + $result = $result[0]; + + $this->assertEquals(1, count($result[200])); + $this->assertArrayHasKey('{DAV:}principal-collection-set', $result[200]); + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}principal-collection-set']); + + $expected = [ + 'principals1/', + 'principals2/', + ]; + + + $this->assertEquals($expected, $result[200]['{DAV:}principal-collection-set']->getHrefs()); + + + } + + function testCurrentUserPrincipal() { + + $fakeServer = new DAV\Server(); + $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $fakeServer->addPlugin($plugin); + $plugin = new Plugin(); + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + $fakeServer->addPlugin($plugin); + + + $requestedProperties = [ + '{DAV:}current-user-principal', + ]; + + $result = $fakeServer->getPropertiesForPath('', $requestedProperties); + $result = $result[0]; + + $this->assertEquals(1, count($result[200])); + $this->assertArrayHasKey('{DAV:}current-user-principal', $result[200]); + $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\Principal', $result[200]['{DAV:}current-user-principal']); + $this->assertEquals(Xml\Property\Principal::UNAUTHENTICATED, $result[200]['{DAV:}current-user-principal']->getType()); + + // This will force the login + $fakeServer->emit('beforeMethod', [$fakeServer->httpRequest, $fakeServer->httpResponse]); + + $result = $fakeServer->getPropertiesForPath('', $requestedProperties); + $result = $result[0]; + + $this->assertEquals(1, count($result[200])); + $this->assertArrayHasKey('{DAV:}current-user-principal', $result[200]); + $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\Principal', $result[200]['{DAV:}current-user-principal']); + $this->assertEquals(Xml\Property\Principal::HREF, $result[200]['{DAV:}current-user-principal']->getType()); + $this->assertEquals('principals/admin/', $result[200]['{DAV:}current-user-principal']->getHref()); + + } + + function testSupportedPrivilegeSet() { + + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + $server = new DAV\Server(); + $server->addPlugin($plugin); + + $requestedProperties = [ + '{DAV:}supported-privilege-set', + ]; + + $result = $server->getPropertiesForPath('', $requestedProperties); + $result = $result[0]; + + $this->assertEquals(1, count($result[200])); + $this->assertArrayHasKey('{DAV:}supported-privilege-set', $result[200]); + $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\SupportedPrivilegeSet', $result[200]['{DAV:}supported-privilege-set']); + + $server = new DAV\Server(); + + $prop = $result[200]['{DAV:}supported-privilege-set']; + $result = $server->xml->write('{DAV:}root', $prop); + + $xpaths = [ + '/d:root' => 1, + '/d:root/d:supported-privilege' => 1, + '/d:root/d:supported-privilege/d:privilege' => 1, + '/d:root/d:supported-privilege/d:privilege/d:all' => 1, + '/d:root/d:supported-privilege/d:abstract' => 0, + '/d:root/d:supported-privilege/d:supported-privilege' => 2, + '/d:root/d:supported-privilege/d:supported-privilege/d:privilege' => 2, + '/d:root/d:supported-privilege/d:supported-privilege/d:privilege/d:read' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:privilege/d:write' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege' => 7, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege' => 7, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:read-acl' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:read-current-user-privilege-set' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-content' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-properties' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:bind' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unbind' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unlock' => 1, + '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:abstract' => 0, + ]; + + + // reloading because php dom sucks + $dom2 = new \DOMDocument('1.0', 'utf-8'); + $dom2->loadXML($result); + + $dxpath = new \DOMXPath($dom2); + $dxpath->registerNamespace('d', 'DAV:'); + foreach ($xpaths as $xpath => $count) { + + $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count . ' Full XML: ' . $result); + + } + + } + + function testACL() { + + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + + $nodes = [ + new MockACLNode('foo', [ + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ] + ]), + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('admin', 'principals/admin'), + ]), + + ]; + + $server = new DAV\Server($nodes); + $server->addPlugin($plugin); + $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $server->addPlugin($authPlugin); + + // Force login + $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $requestedProperties = [ + '{DAV:}acl', + ]; + + $result = $server->getPropertiesForPath('foo', $requestedProperties); + $result = $result[0]; + + $this->assertEquals(1, count($result[200]), 'The {DAV:}acl property did not return from the list. Full list: ' . print_r($result, true)); + $this->assertArrayHasKey('{DAV:}acl', $result[200]); + $this->assertInstanceOf('Sabre\\DAVACL\\Xml\Property\\Acl', $result[200]['{DAV:}acl']); + + } + + function testACLRestrictions() { + + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + + $nodes = [ + new MockACLNode('foo', [ + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ] + ]), + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('admin', 'principals/admin'), + ]), + + ]; + + $server = new DAV\Server($nodes); + $server->addPlugin($plugin); + $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $server->addPlugin($authPlugin); + + // Force login + $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $requestedProperties = [ + '{DAV:}acl-restrictions', + ]; + + $result = $server->getPropertiesForPath('foo', $requestedProperties); + $result = $result[0]; + + $this->assertEquals(1, count($result[200]), 'The {DAV:}acl-restrictions property did not return from the list. Full list: ' . print_r($result, true)); + $this->assertArrayHasKey('{DAV:}acl-restrictions', $result[200]); + $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\AclRestrictions', $result[200]['{DAV:}acl-restrictions']); + + } + + function testAlternateUriSet() { + + $tree = [ + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('user', 'principals/user'), + ]) + ]; + + $fakeServer = new DAV\Server($tree); + //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend()) + //$fakeServer->addPlugin($plugin); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + $fakeServer->addPlugin($plugin); + + $requestedProperties = [ + '{DAV:}alternate-URI-set', + ]; + $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); + $result = $result[0]; + + $this->assertTrue(isset($result[200])); + $this->assertTrue(isset($result[200]['{DAV:}alternate-URI-set'])); + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}alternate-URI-set']); + + $this->assertEquals([], $result[200]['{DAV:}alternate-URI-set']->getHrefs()); + + } + + function testPrincipalURL() { + + $tree = [ + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('user', 'principals/user'), + ]), + ]; + + $fakeServer = new DAV\Server($tree); + //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend()); + //$fakeServer->addPlugin($plugin); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + $fakeServer->addPlugin($plugin); + + $requestedProperties = [ + '{DAV:}principal-URL', + ]; + + $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); + $result = $result[0]; + + $this->assertTrue(isset($result[200])); + $this->assertTrue(isset($result[200]['{DAV:}principal-URL'])); + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}principal-URL']); + + $this->assertEquals('principals/user/', $result[200]['{DAV:}principal-URL']->getHref()); + + } + + function testGroupMemberSet() { + + $tree = [ + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('user', 'principals/user'), + ]), + ]; + + $fakeServer = new DAV\Server($tree); + //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend()); + //$fakeServer->addPlugin($plugin); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + $fakeServer->addPlugin($plugin); + + $requestedProperties = [ + '{DAV:}group-member-set', + ]; + + $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); + $result = $result[0]; + + $this->assertTrue(isset($result[200])); + $this->assertTrue(isset($result[200]['{DAV:}group-member-set'])); + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}group-member-set']); + + $this->assertEquals([], $result[200]['{DAV:}group-member-set']->getHrefs()); + + } + + function testGroupMemberShip() { + + $tree = [ + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('user', 'principals/user'), + ]), + ]; + + $fakeServer = new DAV\Server($tree); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $fakeServer->addPlugin($plugin); + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + + $requestedProperties = [ + '{DAV:}group-membership', + ]; + + $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); + $result = $result[0]; + + $this->assertTrue(isset($result[200])); + $this->assertTrue(isset($result[200]['{DAV:}group-membership'])); + $this->assertInstanceOf('Sabre\\DAV\\Xml\\Property\\Href', $result[200]['{DAV:}group-membership']); + + $this->assertEquals([], $result[200]['{DAV:}group-membership']->getHrefs()); + + } + + function testGetDisplayName() { + + $tree = [ + new DAV\SimpleCollection('principals', [ + $principal = new MockPrincipal('user', 'principals/user'), + ]), + ]; + + $fakeServer = new DAV\Server($tree); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $fakeServer->addPlugin($plugin); + $plugin->setDefaultACL([ + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}all', + ], + ]); + + $requestedProperties = [ + '{DAV:}displayname', + ]; + + $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); + $result = $result[0]; + + $this->assertTrue(isset($result[200])); + $this->assertTrue(isset($result[200]['{DAV:}displayname'])); + + $this->assertEquals('user', $result[200]['{DAV:}displayname']); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php new file mode 100644 index 00000000000..0147e6a6177 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php @@ -0,0 +1,116 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; + +class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase { + + function testUpdatePropertiesPassthrough() { + + $tree = [ + new DAV\SimpleCollection('foo'), + ]; + $server = new DAV\Server($tree); + $server->addPlugin(new DAV\Auth\Plugin()); + $server->addPlugin(new Plugin()); + + $result = $server->updateProperties('foo', [ + '{DAV:}foo' => 'bar', + ]); + + $expected = [ + '{DAV:}foo' => 403, + ]; + + $this->assertEquals($expected, $result); + + } + + function testRemoveGroupMembers() { + + $tree = [ + new MockPrincipal('foo', 'foo'), + ]; + $server = new DAV\Server($tree); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $server->addPlugin($plugin); + + $result = $server->updateProperties('foo', [ + '{DAV:}group-member-set' => null, + ]); + + $expected = [ + '{DAV:}group-member-set' => 204 + ]; + + $this->assertEquals($expected, $result); + $this->assertEquals([], $tree[0]->getGroupMemberSet()); + + } + + function testSetGroupMembers() { + + $tree = [ + new MockPrincipal('foo', 'foo'), + ]; + $server = new DAV\Server($tree); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $server->addPlugin($plugin); + + $result = $server->updateProperties('foo', [ + '{DAV:}group-member-set' => new DAV\Xml\Property\Href(['/bar', '/baz'], true), + ]); + + $expected = [ + '{DAV:}group-member-set' => 200 + ]; + + $this->assertEquals($expected, $result); + $this->assertEquals(['bar', 'baz'], $tree[0]->getGroupMemberSet()); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testSetBadValue() { + + $tree = [ + new MockPrincipal('foo', 'foo'), + ]; + $server = new DAV\Server($tree); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $server->addPlugin($plugin); + + $result = $server->updateProperties('foo', [ + '{DAV:}group-member-set' => new \StdClass(), + ]); + + } + + function testSetBadNode() { + + $tree = [ + new DAV\SimpleCollection('foo'), + ]; + $server = new DAV\Server($tree); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $server->addPlugin($plugin); + + $result = $server->updateProperties('foo', [ + '{DAV:}group-member-set' => new DAV\Xml\Property\Href(['/bar', '/baz'], false), + ]); + + $expected = [ + '{DAV:}group-member-set' => 403, + ]; + + $this->assertEquals($expected, $result); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php new file mode 100644 index 00000000000..9fef3018df9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php @@ -0,0 +1,217 @@ +<?php + +namespace Sabre\DAVACL\PrincipalBackend; + +use Sabre\DAV; +use Sabre\HTTP; + +abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase { + + use DAV\DbTestHelperTrait; + + function setUp() { + + $this->dropTables(['principals', 'groupmembers']); + $this->createSchema('principals'); + + $pdo = $this->getPDO(); + + $pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/user','user@example.org','User')"); + $pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/group','group@example.org','Group')"); + + $pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (5,4)"); + + } + + + function testConstruct() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + $this->assertTrue($backend instanceof PDO); + + } + + /** + * @depends testConstruct + */ + function testGetPrincipalsByPrefix() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + + $expected = [ + [ + 'uri' => 'principals/admin', + '{http://sabredav.org/ns}email-address' => 'admin@example.org', + '{DAV:}displayname' => 'Administrator', + ], + [ + 'uri' => 'principals/user', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + '{DAV:}displayname' => 'User', + ], + [ + 'uri' => 'principals/group', + '{http://sabredav.org/ns}email-address' => 'group@example.org', + '{DAV:}displayname' => 'Group', + ], + ]; + + $this->assertEquals($expected, $backend->getPrincipalsByPrefix('principals')); + $this->assertEquals([], $backend->getPrincipalsByPrefix('foo')); + + } + + /** + * @depends testConstruct + */ + function testGetPrincipalByPath() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + + $expected = [ + 'id' => 4, + 'uri' => 'principals/user', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + '{DAV:}displayname' => 'User', + ]; + + $this->assertEquals($expected, $backend->getPrincipalByPath('principals/user')); + $this->assertEquals(null, $backend->getPrincipalByPath('foo')); + + } + + function testGetGroupMemberSet() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + $expected = ['principals/user']; + + $this->assertEquals($expected, $backend->getGroupMemberSet('principals/group')); + + } + + function testGetGroupMembership() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + $expected = ['principals/group']; + + $this->assertEquals($expected, $backend->getGroupMembership('principals/user')); + + } + + function testSetGroupMemberSet() { + + $pdo = $this->getPDO(); + + // Start situation + $backend = new PDO($pdo); + $this->assertEquals(['principals/user'], $backend->getGroupMemberSet('principals/group')); + + // Removing all principals + $backend->setGroupMemberSet('principals/group', []); + $this->assertEquals([], $backend->getGroupMemberSet('principals/group')); + + // Adding principals again + $backend->setGroupMemberSet('principals/group', ['principals/user']); + $this->assertEquals(['principals/user'], $backend->getGroupMemberSet('principals/group')); + + + } + + function testSearchPrincipals() { + + $pdo = $this->getPDO(); + + $backend = new PDO($pdo); + + $result = $backend->searchPrincipals('principals', ['{DAV:}blabla' => 'foo']); + $this->assertEquals([], $result); + + $result = $backend->searchPrincipals('principals', ['{DAV:}displayname' => 'ou']); + $this->assertEquals(['principals/group'], $result); + + $result = $backend->searchPrincipals('principals', ['{DAV:}displayname' => 'UsEr', '{http://sabredav.org/ns}email-address' => 'USER@EXAMPLE']); + $this->assertEquals(['principals/user'], $result); + + $result = $backend->searchPrincipals('mom', ['{DAV:}displayname' => 'UsEr', '{http://sabredav.org/ns}email-address' => 'USER@EXAMPLE']); + $this->assertEquals([], $result); + + } + + function testUpdatePrincipal() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + + $propPatch = new DAV\PropPatch([ + '{DAV:}displayname' => 'pietje', + ]); + + $backend->updatePrincipal('principals/user', $propPatch); + $result = $propPatch->commit(); + + $this->assertTrue($result); + + $this->assertEquals([ + 'id' => 4, + 'uri' => 'principals/user', + '{DAV:}displayname' => 'pietje', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + ], $backend->getPrincipalByPath('principals/user')); + + } + + function testUpdatePrincipalUnknownField() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + + $propPatch = new DAV\PropPatch([ + '{DAV:}displayname' => 'pietje', + '{DAV:}unknown' => 'foo', + ]); + + $backend->updatePrincipal('principals/user', $propPatch); + $result = $propPatch->commit(); + + $this->assertFalse($result); + + $this->assertEquals([ + '{DAV:}displayname' => 424, + '{DAV:}unknown' => 403 + ], $propPatch->getResult()); + + $this->assertEquals([ + 'id' => '4', + 'uri' => 'principals/user', + '{DAV:}displayname' => 'User', + '{http://sabredav.org/ns}email-address' => 'user@example.org', + ], $backend->getPrincipalByPath('principals/user')); + + } + + function testFindByUriUnknownScheme() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + $this->assertNull($backend->findByUri('http://foo', 'principals')); + + } + + + function testFindByUri() { + + $pdo = $this->getPDO(); + $backend = new PDO($pdo); + $this->assertEquals( + 'principals/user', + $backend->findByUri('mailto:user@example.org', 'principals') + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php new file mode 100644 index 00000000000..1464f4c26ab --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/Mock.php @@ -0,0 +1,168 @@ +<?php + +namespace Sabre\DAVACL\PrincipalBackend; + +class Mock extends AbstractBackend { + + public $groupMembers = []; + public $principals; + + function __construct(array $principals = null) { + + $this->principals = $principals; + + if (is_null($principals)) { + + $this->principals = [ + [ + 'uri' => 'principals/user1', + '{DAV:}displayname' => 'User 1', + '{http://sabredav.org/ns}email-address' => 'user1.sabredav@sabredav.org', + '{http://sabredav.org/ns}vcard-url' => 'addressbooks/user1/book1/vcard1.vcf', + ], + [ + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Admin', + ], + [ + 'uri' => 'principals/user2', + '{DAV:}displayname' => 'User 2', + '{http://sabredav.org/ns}email-address' => 'user2.sabredav@sabredav.org', + ], + ]; + + } + + } + + function getPrincipalsByPrefix($prefix) { + + $prefix = trim($prefix, '/'); + if ($prefix) $prefix .= '/'; + $return = []; + + foreach ($this->principals as $principal) { + + if ($prefix && strpos($principal['uri'], $prefix) !== 0) continue; + + $return[] = $principal; + + } + + return $return; + + } + + function addPrincipal(array $principal) { + + $this->principals[] = $principal; + + } + + function getPrincipalByPath($path) { + + foreach ($this->getPrincipalsByPrefix('principals') as $principal) { + if ($principal['uri'] === $path) return $principal; + } + + } + + function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { + + $matches = []; + foreach ($this->getPrincipalsByPrefix($prefixPath) as $principal) { + + foreach ($searchProperties as $key => $value) { + + if (!isset($principal[$key])) { + continue 2; + } + if (mb_stripos($principal[$key], $value, 0, 'UTF-8') === false) { + continue 2; + } + + // We have a match for this searchProperty! + if ($test === 'allof') { + continue; + } else { + break; + } + + } + $matches[] = $principal['uri']; + + } + return $matches; + + } + + function getGroupMemberSet($path) { + + return isset($this->groupMembers[$path]) ? $this->groupMembers[$path] : []; + + } + + function getGroupMembership($path) { + + $membership = []; + foreach ($this->groupMembers as $group => $members) { + if (in_array($path, $members)) $membership[] = $group; + } + return $membership; + + } + + function setGroupMemberSet($path, array $members) { + + $this->groupMembers[$path] = $members; + + } + + /** + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is stored in a Sabre\DAV\PropPatch object. + * To do the actual updates, you must tell this object which properties + * you're going to process with the handle() method. + * + * Calling the handle method is like telling the PropPatch object "I + * promise I can handle updating this property". + * + * Read the PropPatch documentation for more info and examples. + * + * @param string $path + * @param \Sabre\DAV\PropPatch $propPatch + */ + function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { + + $value = null; + foreach ($this->principals as $principalIndex => $value) { + if ($value['uri'] === $path) { + $principal = $value; + break; + } + } + if (!$principal) return; + + $propPatch->handleRemaining(function($mutations) use ($principal, $principalIndex) { + + foreach ($mutations as $prop => $value) { + + if (is_null($value) && isset($principal[$prop])) { + unset($principal[$prop]); + } else { + $principal[$prop] = $value; + } + + } + + $this->principals[$principalIndex] = $principal; + + return true; + + }); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php new file mode 100644 index 00000000000..8779eb69f53 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAVACL\PrincipalBackend; + +class PDOMySQLTest extends AbstractPDOTest { + + public $driver = 'mysql'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOPgSqlTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOPgSqlTest.php new file mode 100644 index 00000000000..302616e785c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOPgSqlTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAVACL\PrincipalBackend; + +class PDOPgSqlTest extends AbstractPDOTest { + + public $driver = 'pgsql'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php new file mode 100644 index 00000000000..48454981d54 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php @@ -0,0 +1,9 @@ +<?php + +namespace Sabre\DAVACL\PrincipalBackend; + +class PDOSqliteTest extends AbstractPDOTest { + + public $driver = 'sqlite'; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php new file mode 100644 index 00000000000..bcf78821bc9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalCollectionTest.php @@ -0,0 +1,57 @@ +<?php + +namespace Sabre\DAVACL; + +class PrincipalCollectionTest extends \PHPUnit_Framework_TestCase { + + function testBasic() { + + $backend = new PrincipalBackend\Mock(); + $pc = new PrincipalCollection($backend); + $this->assertTrue($pc instanceof PrincipalCollection); + + $this->assertEquals('principals', $pc->getName()); + + } + + /** + * @depends testBasic + */ + function testGetChildren() { + + $backend = new PrincipalBackend\Mock(); + $pc = new PrincipalCollection($backend); + + $children = $pc->getChildren(); + $this->assertTrue(is_array($children)); + + foreach ($children as $child) { + $this->assertTrue($child instanceof IPrincipal); + } + + } + + /** + * @depends testBasic + * @expectedException Sabre\DAV\Exception\MethodNotAllowed + */ + function testGetChildrenDisable() { + + $backend = new PrincipalBackend\Mock(); + $pc = new PrincipalCollection($backend); + $pc->disableListing = true; + + $children = $pc->getChildren(); + + } + + function testFindByUri() { + + $backend = new PrincipalBackend\Mock(); + $pc = new PrincipalCollection($backend); + $this->assertEquals('principals/user1', $pc->findByUri('mailto:user1.sabredav@sabredav.org')); + $this->assertNull($pc->findByUri('mailto:fake.user.sabredav@sabredav.org')); + $this->assertNull($pc->findByUri('')); + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalMatchTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalMatchTest.php new file mode 100644 index 00000000000..427e37972db --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalMatchTest.php @@ -0,0 +1,123 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\HTTP\Request; + +class PrincipalMatchTest extends \Sabre\DAVServerTest { + + public $setupACL = true; + public $autoLogin = 'user1'; + + function testPrincipalMatch() { + + $xml = <<<XML +<?xml version="1.0"?> +<principal-match xmlns="DAV:"> + <self /> +</principal-match> +XML; + + $request = new Request('REPORT', '/principals', ['Content-Type' => 'application/xml']); + $request->setBody($xml); + + $response = $this->request($request, 207); + + $expected = <<<XML +<?xml version="1.0"?> +<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:status>HTTP/1.1 200 OK</d:status> + <d:href>/principals/user1</d:href> + <d:propstat> + <d:prop/> + <d:status>HTTP/1.1 418 I'm a teapot</d:status> + </d:propstat> +</d:multistatus> +XML; + + $this->assertXmlStringEqualsXmlString( + $expected, + $response->getBodyAsString() + ); + + } + + function testPrincipalMatchProp() { + + $xml = <<<XML +<?xml version="1.0"?> +<principal-match xmlns="DAV:"> + <self /> + <prop> + <resourcetype /> + </prop> +</principal-match> +XML; + + $request = new Request('REPORT', '/principals', ['Content-Type' => 'application/xml']); + $request->setBody($xml); + + $response = $this->request($request, 207); + + $expected = <<<XML +<?xml version="1.0"?> +<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:status>HTTP/1.1 200 OK</d:status> + <d:href>/principals/user1/</d:href> + <d:propstat> + <d:prop> + <d:resourcetype><d:principal/></d:resourcetype> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> +</d:multistatus> +XML; + + $this->assertXmlStringEqualsXmlString( + $expected, + $response->getBodyAsString() + ); + + } + + function testPrincipalMatchPrincipalProperty() { + + $xml = <<<XML +<?xml version="1.0"?> +<principal-match xmlns="DAV:"> + <principal-property> + <principal-URL /> + </principal-property> + <prop> + <resourcetype /> + </prop> +</principal-match> +XML; + + $request = new Request('REPORT', '/principals', ['Content-Type' => 'application/xml']); + $request->setBody($xml); + + $response = $this->request($request, 207); + + $expected = <<<XML +<?xml version="1.0"?> +<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:status>HTTP/1.1 200 OK</d:status> + <d:href>/principals/user1/</d:href> + <d:propstat> + <d:prop> + <d:resourcetype><d:principal/></d:resourcetype> + </d:prop> + <d:status>HTTP/1.1 200 OK</d:status> + </d:propstat> +</d:multistatus> +XML; + + $this->assertXmlStringEqualsXmlString( + $expected, + $response->getBodyAsString() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php new file mode 100644 index 00000000000..60e156d9a0a --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php @@ -0,0 +1,397 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; + +class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase { + + function getServer() { + + $backend = new PrincipalBackend\Mock(); + + $dir = new DAV\SimpleCollection('root'); + $principals = new PrincipalCollection($backend); + $dir->addChild($principals); + + $fakeServer = new DAV\Server($dir); + $fakeServer->sapi = new HTTP\SapiMock(); + $fakeServer->httpResponse = new HTTP\ResponseMock(); + $fakeServer->debugExceptions = true; + $plugin = new MockPlugin(); + $plugin->allowAccessToNodesWithoutACL = true; + $plugin->allowUnauthenticatedAccess = false; + + $this->assertTrue($plugin instanceof Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); + + return $fakeServer; + + } + + function testDepth1() { + + $xml = '<?xml version="1.0"?> +<d:principal-property-search xmlns:d="DAV:"> + <d:property-search> + <d:prop> + <d:displayname /> + </d:prop> + <d:match>user</d:match> + </d:property-search> + <d:prop> + <d:displayname /> + <d:getcontentlength /> + </d:prop> +</d:principal-property-search>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '1', + 'REQUEST_URI' => '/principals', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(400, $server->httpResponse->getStatus(), $server->httpResponse->getBodyAsString()); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + } + + + function testUnknownSearchField() { + + $xml = '<?xml version="1.0"?> +<d:principal-property-search xmlns:d="DAV:"> + <d:property-search> + <d:prop> + <d:yourmom /> + </d:prop> + <d:match>user</d:match> + </d:property-search> + <d:prop> + <d:displayname /> + <d:getcontentlength /> + </d:prop> +</d:principal-property-search>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/principals', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->getStatus(), "Full body: " . $server->httpResponse->getBodyAsString()); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'Vary' => ['Brief,Prefer'], + ], $server->httpResponse->getHeaders()); + + } + + function testCorrect() { + + $xml = '<?xml version="1.0"?> +<d:principal-property-search xmlns:d="DAV:"> + <d:apply-to-principal-collection-set /> + <d:property-search> + <d:prop> + <d:displayname /> + </d:prop> + <d:match>user</d:match> + </d:property-search> + <d:prop> + <d:displayname /> + <d:getcontentlength /> + </d:prop> +</d:principal-property-search>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'Vary' => ['Brief,Prefer'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 2, + '/d:multistatus/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat' => 4, + '/d:multistatus/d:response/d:propstat/d:prop' => 4, + '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 2, + '/d:multistatus/d:response/d:propstat/d:status' => 4, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } + + function testAND() { + + $xml = '<?xml version="1.0"?> +<d:principal-property-search xmlns:d="DAV:"> + <d:apply-to-principal-collection-set /> + <d:property-search> + <d:prop> + <d:displayname /> + </d:prop> + <d:match>user</d:match> + </d:property-search> + <d:property-search> + <d:prop> + <d:foo /> + </d:prop> + <d:match>bar</d:match> + </d:property-search> + <d:prop> + <d:displayname /> + <d:getcontentlength /> + </d:prop> +</d:principal-property-search>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'Vary' => ['Brief,Prefer'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 0, + '/d:multistatus/d:response/d:href' => 0, + '/d:multistatus/d:response/d:propstat' => 0, + '/d:multistatus/d:response/d:propstat/d:prop' => 0, + '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 0, + '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 0, + '/d:multistatus/d:response/d:propstat/d:status' => 0, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } + function testOR() { + + $xml = '<?xml version="1.0"?> +<d:principal-property-search xmlns:d="DAV:" test="anyof"> + <d:apply-to-principal-collection-set /> + <d:property-search> + <d:prop> + <d:displayname /> + </d:prop> + <d:match>user</d:match> + </d:property-search> + <d:property-search> + <d:prop> + <d:foo /> + </d:prop> + <d:match>bar</d:match> + </d:property-search> + <d:prop> + <d:displayname /> + <d:getcontentlength /> + </d:prop> +</d:principal-property-search>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'Vary' => ['Brief,Prefer'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 2, + '/d:multistatus/d:response/d:href' => 2, + '/d:multistatus/d:response/d:propstat' => 4, + '/d:multistatus/d:response/d:propstat/d:prop' => 4, + '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 2, + '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 2, + '/d:multistatus/d:response/d:propstat/d:status' => 4, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } + function testWrongUri() { + + $xml = '<?xml version="1.0"?> +<d:principal-property-search xmlns:d="DAV:"> + <d:property-search> + <d:prop> + <d:displayname /> + </d:prop> + <d:match>user</d:match> + </d:property-search> + <d:prop> + <d:displayname /> + <d:getcontentlength /> + </d:prop> +</d:principal-property-search>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + 'Vary' => ['Brief,Prefer'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:multistatus', + '/d:multistatus/d:response' => 0, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } +} + +class MockPlugin extends Plugin { + + function getCurrentUserPrivilegeSet($node) { + + return [ + '{DAV:}read', + '{DAV:}write', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php new file mode 100644 index 00000000000..fa1314d108c --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php @@ -0,0 +1,140 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/HTTP/ResponseMock.php'; + +class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase { + + function getServer() { + + $backend = new PrincipalBackend\Mock(); + + $dir = new DAV\SimpleCollection('root'); + $principals = new PrincipalCollection($backend); + $dir->addChild($principals); + + $fakeServer = new DAV\Server($dir); + $fakeServer->sapi = new HTTP\SapiMock(); + $fakeServer->httpResponse = new HTTP\ResponseMock(); + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $this->assertTrue($plugin instanceof Plugin); + $fakeServer->addPlugin($plugin); + $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); + + return $fakeServer; + + } + + function testDepth1() { + + $xml = '<?xml version="1.0"?> +<d:principal-search-property-set xmlns:d="DAV:" />'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '1', + 'REQUEST_URI' => '/principals', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(400, $server->httpResponse->status); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + } + + function testDepthIncorrectXML() { + + $xml = '<?xml version="1.0"?> +<d:principal-search-property-set xmlns:d="DAV:"><d:ohell /></d:principal-search-property-set>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/principals', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(400, $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + } + + function testCorrect() { + + $xml = '<?xml version="1.0"?> +<d:principal-search-property-set xmlns:d="DAV:"/>'; + + $serverVars = [ + 'REQUEST_METHOD' => 'REPORT', + 'HTTP_DEPTH' => '0', + 'REQUEST_URI' => '/principals', + ]; + + $request = HTTP\Sapi::createFromServerArray($serverVars); + $request->setBody($xml); + + $server = $this->getServer(); + $server->httpRequest = $request; + + $server->exec(); + + $this->assertEquals(200, $server->httpResponse->status, $server->httpResponse->body); + $this->assertEquals([ + 'X-Sabre-Version' => [DAV\Version::VERSION], + 'Content-Type' => ['application/xml; charset=utf-8'], + ], $server->httpResponse->getHeaders()); + + + $check = [ + '/d:principal-search-property-set', + '/d:principal-search-property-set/d:principal-search-property' => 2, + '/d:principal-search-property-set/d:principal-search-property/d:prop' => 2, + '/d:principal-search-property-set/d:principal-search-property/d:prop/d:displayname' => 1, + '/d:principal-search-property-set/d:principal-search-property/d:prop/s:email-address' => 1, + '/d:principal-search-property-set/d:principal-search-property/d:description' => 2, + ]; + + $xml = simplexml_load_string($server->httpResponse->body); + $xml->registerXPathNamespace('d', 'DAV:'); + $xml->registerXPathNamespace('s', 'http://sabredav.org/ns'); + foreach ($check as $v1 => $v2) { + + $xpath = is_int($v1) ? $v2 : $v1; + + $result = $xml->xpath($xpath); + + $count = 1; + if (!is_int($v1)) $count = $v2; + + $this->assertEquals($count, count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php new file mode 100644 index 00000000000..20622ad1757 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/PrincipalTest.php @@ -0,0 +1,208 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +class PrincipalTest extends \PHPUnit_Framework_TestCase { + + function testConstruct() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertTrue($principal instanceof Principal); + + } + + /** + * @expectedException Sabre\DAV\Exception + */ + function testConstructNoUri() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, []); + + } + + function testGetName() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals('admin', $principal->getName()); + + } + + function testGetDisplayName() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals('admin', $principal->getDisplayname()); + + $principal = new Principal($principalBackend, [ + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Mr. Admin' + ]); + $this->assertEquals('Mr. Admin', $principal->getDisplayname()); + + } + + function testGetProperties() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, [ + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Mr. Admin', + '{http://www.example.org/custom}custom' => 'Custom', + '{http://sabredav.org/ns}email-address' => 'admin@example.org', + ]); + + $keys = [ + '{DAV:}displayname', + '{http://www.example.org/custom}custom', + '{http://sabredav.org/ns}email-address', + ]; + $props = $principal->getProperties($keys); + + foreach ($keys as $key) $this->assertArrayHasKey($key, $props); + + $this->assertEquals('Mr. Admin', $props['{DAV:}displayname']); + + $this->assertEquals('admin@example.org', $props['{http://sabredav.org/ns}email-address']); + } + + function testUpdateProperties() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + + $propPatch = new DAV\PropPatch(['{DAV:}yourmom' => 'test']); + + $result = $principal->propPatch($propPatch); + $result = $propPatch->commit(); + $this->assertTrue($result); + + } + + function testGetPrincipalUrl() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals('principals/admin', $principal->getPrincipalUrl()); + + } + + function testGetAlternateUriSet() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, [ + 'uri' => 'principals/admin', + '{DAV:}displayname' => 'Mr. Admin', + '{http://www.example.org/custom}custom' => 'Custom', + '{http://sabredav.org/ns}email-address' => 'admin@example.org', + '{DAV:}alternate-URI-set' => [ + 'mailto:admin+1@example.org', + 'mailto:admin+2@example.org', + 'mailto:admin@example.org', + ], + ]); + + $expected = [ + 'mailto:admin+1@example.org', + 'mailto:admin+2@example.org', + 'mailto:admin@example.org', + ]; + + $this->assertEquals($expected, $principal->getAlternateUriSet()); + + } + function testGetAlternateUriSetEmpty() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, [ + 'uri' => 'principals/admin', + ]); + + $expected = []; + + $this->assertEquals($expected, $principal->getAlternateUriSet()); + + } + + function testGetGroupMemberSet() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals([], $principal->getGroupMemberSet()); + + } + function testGetGroupMembership() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals([], $principal->getGroupMembership()); + + } + + function testSetGroupMemberSet() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $principal->setGroupMemberSet(['principals/foo']); + + $this->assertEquals([ + 'principals/admin' => ['principals/foo'], + ], $principalBackend->groupMembers); + + } + + function testGetOwner() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals('principals/admin', $principal->getOwner()); + + } + + function testGetGroup() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertNull($principal->getGroup()); + + } + + function testGetACl() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertEquals([ + [ + 'privilege' => '{DAV:}all', + 'principal' => '{DAV:}owner', + 'protected' => true, + ] + ], $principal->getACL()); + + } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + */ + function testSetACl() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $principal->setACL([]); + + } + + function testGetSupportedPrivilegeSet() { + + $principalBackend = new PrincipalBackend\Mock(); + $principal = new Principal($principalBackend, ['uri' => 'principals/admin']); + $this->assertNull($principal->getSupportedPrivilegeSet()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php new file mode 100644 index 00000000000..2de0ba6a853 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/SimplePluginTest.php @@ -0,0 +1,321 @@ +<?php + +namespace Sabre\DAVACL; + +use Sabre\DAV; +use Sabre\HTTP; + +require_once 'Sabre/DAVACL/MockPrincipal.php'; +require_once 'Sabre/DAVACL/MockACLNode.php'; + +class SimplePluginTest extends \PHPUnit_Framework_TestCase { + + function testValues() { + + $aclPlugin = new Plugin(); + $this->assertEquals('acl', $aclPlugin->getPluginName()); + $this->assertEquals( + ['access-control', 'calendarserver-principal-property-search'], + $aclPlugin->getFeatures() + ); + + $this->assertEquals( + [ + '{DAV:}expand-property', + '{DAV:}principal-match', + '{DAV:}principal-property-search', + '{DAV:}principal-search-property-set' + ], + $aclPlugin->getSupportedReportSet('')); + + $this->assertEquals(['ACL'], $aclPlugin->getMethods('')); + + + $this->assertEquals( + 'acl', + $aclPlugin->getPluginInfo()['name'] + ); + } + + function testGetFlatPrivilegeSet() { + + $expected = [ + '{DAV:}all' => [ + 'privilege' => '{DAV:}all', + 'abstract' => false, + 'aggregates' => [ + '{DAV:}read', + '{DAV:}write', + ], + 'concrete' => '{DAV:}all', + ], + '{DAV:}read' => [ + 'privilege' => '{DAV:}read', + 'abstract' => false, + 'aggregates' => [ + '{DAV:}read-acl', + '{DAV:}read-current-user-privilege-set', + ], + 'concrete' => '{DAV:}read', + ], + '{DAV:}read-acl' => [ + 'privilege' => '{DAV:}read-acl', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}read-acl', + ], + '{DAV:}read-current-user-privilege-set' => [ + 'privilege' => '{DAV:}read-current-user-privilege-set', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}read-current-user-privilege-set', + ], + '{DAV:}write' => [ + 'privilege' => '{DAV:}write', + 'abstract' => false, + 'aggregates' => [ + '{DAV:}write-properties', + '{DAV:}write-content', + '{DAV:}unlock', + '{DAV:}bind', + '{DAV:}unbind', + ], + 'concrete' => '{DAV:}write', + ], + '{DAV:}write-properties' => [ + 'privilege' => '{DAV:}write-properties', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}write-properties', + ], + '{DAV:}write-content' => [ + 'privilege' => '{DAV:}write-content', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}write-content', + ], + '{DAV:}unlock' => [ + 'privilege' => '{DAV:}unlock', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}unlock', + ], + '{DAV:}bind' => [ + 'privilege' => '{DAV:}bind', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}bind', + ], + '{DAV:}unbind' => [ + 'privilege' => '{DAV:}unbind', + 'abstract' => false, + 'aggregates' => [], + 'concrete' => '{DAV:}unbind', + ], + + ]; + + $plugin = new Plugin(); + $plugin->allowUnauthenticatedAccess = false; + $server = new DAV\Server(); + $server->addPlugin($plugin); + $this->assertEquals($expected, $plugin->getFlatPrivilegeSet('')); + + } + + function testCurrentUserPrincipalsNotLoggedIn() { + + $acl = new Plugin(); + $acl->allowUnauthenticatedAccess = false; + $server = new DAV\Server(); + $server->addPlugin($acl); + + $this->assertEquals([], $acl->getCurrentUserPrincipals()); + + } + + function testCurrentUserPrincipalsSimple() { + + $tree = [ + + new DAV\SimpleCollection('principals', [ + new MockPrincipal('admin', 'principals/admin'), + ]) + + ]; + + $acl = new Plugin(); + $acl->allowUnauthenticatedAccess = false; + $server = new DAV\Server($tree); + $server->addPlugin($acl); + + $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $server->addPlugin($auth); + + //forcing login + $auth->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $this->assertEquals(['principals/admin'], $acl->getCurrentUserPrincipals()); + + } + + function testCurrentUserPrincipalsGroups() { + + $tree = [ + + new DAV\SimpleCollection('principals', [ + new MockPrincipal('admin', 'principals/admin', ['principals/administrators', 'principals/everyone']), + new MockPrincipal('administrators', 'principals/administrators', ['principals/groups'], ['principals/admin']), + new MockPrincipal('everyone', 'principals/everyone', [], ['principals/admin']), + new MockPrincipal('groups', 'principals/groups', [], ['principals/administrators']), + ]) + + ]; + + $acl = new Plugin(); + $acl->allowUnauthenticatedAccess = false; + $server = new DAV\Server($tree); + $server->addPlugin($acl); + + $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $server->addPlugin($auth); + + //forcing login + $auth->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $expected = [ + 'principals/admin', + 'principals/administrators', + 'principals/everyone', + 'principals/groups', + ]; + + $this->assertEquals($expected, $acl->getCurrentUserPrincipals()); + + // The second one should trigger the cache and be identical + $this->assertEquals($expected, $acl->getCurrentUserPrincipals()); + + } + + function testGetACL() { + + $acl = [ + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ], + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}write', + ], + ]; + + + $tree = [ + new MockACLNode('foo', $acl), + ]; + + $server = new DAV\Server($tree); + $aclPlugin = new Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $server->addPlugin($aclPlugin); + + $this->assertEquals($acl, $aclPlugin->getACL('foo')); + + } + + function testGetCurrentUserPrivilegeSet() { + + $acl = [ + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ], + [ + 'principal' => 'principals/user1', + 'privilege' => '{DAV:}read', + ], + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}write', + ], + ]; + + + $tree = [ + new MockACLNode('foo', $acl), + + new DAV\SimpleCollection('principals', [ + new MockPrincipal('admin', 'principals/admin'), + ]), + + ]; + + $server = new DAV\Server($tree); + $aclPlugin = new Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $server->addPlugin($aclPlugin); + + $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $server->addPlugin($auth); + + //forcing login + $auth->beforeMethod(new HTTP\Request(), new HTTP\Response()); + + $expected = [ + '{DAV:}write', + '{DAV:}write-properties', + '{DAV:}write-content', + '{DAV:}unlock', + '{DAV:}write-acl', + '{DAV:}read', + '{DAV:}read-acl', + '{DAV:}read-current-user-privilege-set', + ]; + + $this->assertEquals($expected, $aclPlugin->getCurrentUserPrivilegeSet('foo')); + + } + + function testCheckPrivileges() { + + $acl = [ + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}read', + ], + [ + 'principal' => 'principals/user1', + 'privilege' => '{DAV:}read', + ], + [ + 'principal' => 'principals/admin', + 'privilege' => '{DAV:}write', + ], + ]; + + + $tree = [ + new MockACLNode('foo', $acl), + + new DAV\SimpleCollection('principals', [ + new MockPrincipal('admin', 'principals/admin'), + ]), + + ]; + + $server = new DAV\Server($tree); + $aclPlugin = new Plugin(); + $aclPlugin->allowUnauthenticatedAccess = false; + $server->addPlugin($aclPlugin); + + $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); + $server->addPlugin($auth); + + //forcing login + //$auth->beforeMethod('GET','/'); + + $this->assertFalse($aclPlugin->checkPrivileges('foo', ['{DAV:}read'], Plugin::R_PARENT, false)); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/ACLTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/ACLTest.php new file mode 100644 index 00000000000..7b9853fe5fe --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/ACLTest.php @@ -0,0 +1,342 @@ +<?php + +namespace Sabre\DAVACL\Xml\Property; + +use Sabre\DAV; +use Sabre\DAV\Browser\HtmlOutputHelper; +use Sabre\HTTP; + +class ACLTest extends \PHPUnit_Framework_TestCase { + + function testConstruct() { + + $acl = new Acl([]); + $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\ACL', $acl); + + } + + function testSerializeEmpty() { + + $acl = new Acl([]); + $xml = (new DAV\Server())->xml->write('{DAV:}root', $acl); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" />'; + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + function testSerialize() { + + $privileges = [ + [ + 'principal' => 'principals/evert', + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => 'principals/foo', + 'privilege' => '{DAV:}read', + 'protected' => true, + ], + ]; + + $acl = new Acl($privileges); + $xml = (new DAV\Server())->xml->write('{DAV:}root', $acl, '/'); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:ace> + <d:principal> + <d:href>/principals/evert/</d:href> + </d:principal> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + </d:ace> + <d:ace> + <d:principal> + <d:href>/principals/foo/</d:href> + </d:principal> + <d:grant> + <d:privilege> + <d:read/> + </d:privilege> + </d:grant> + <d:protected/> + </d:ace> +</d:root> +'; + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + function testSerializeSpecialPrincipals() { + + $privileges = [ + [ + 'principal' => '{DAV:}authenticated', + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => '{DAV:}unauthenticated', + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => '{DAV:}all', + 'privilege' => '{DAV:}write', + ], + + ]; + + $acl = new Acl($privileges); + $xml = (new DAV\Server())->xml->write('{DAV:}root', $acl, '/'); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:ace> + <d:principal> + <d:authenticated/> + </d:principal> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + </d:ace> + <d:ace> + <d:principal> + <d:unauthenticated/> + </d:principal> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + </d:ace> + <d:ace> + <d:principal> + <d:all/> + </d:principal> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + </d:ace> +</d:root> +'; + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + function testUnserialize() { + + $source = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:ace> + <d:principal> + <d:href>/principals/evert/</d:href> + </d:principal> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + </d:ace> + <d:ace> + <d:principal> + <d:href>/principals/foo/</d:href> + </d:principal> + <d:grant> + <d:privilege> + <d:read/> + </d:privilege> + </d:grant> + <d:protected/> + </d:ace> +</d:root> +'; + + $reader = new \Sabre\Xml\Reader(); + $reader->elementMap['{DAV:}root'] = 'Sabre\DAVACL\Xml\Property\Acl'; + $reader->xml($source); + + $result = $reader->parse(); + $result = $result['value']; + + $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\Acl', $result); + + $expected = [ + [ + 'principal' => '/principals/evert/', + 'protected' => false, + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => '/principals/foo/', + 'protected' => true, + 'privilege' => '{DAV:}read', + ], + ]; + + $this->assertEquals($expected, $result->getPrivileges()); + + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testUnserializeNoPrincipal() { + + $source = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:ace> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + </d:ace> +</d:root> +'; + + + $reader = new \Sabre\Xml\Reader(); + $reader->elementMap['{DAV:}root'] = 'Sabre\DAVACL\Xml\Property\Acl'; + $reader->xml($source); + + $result = $reader->parse(); + + } + + function testUnserializeOtherPrincipal() { + + $source = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:ace> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + <d:principal><d:authenticated /></d:principal> + </d:ace> + <d:ace> + <d:grant> + <d:ignoreme /> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + <d:principal><d:unauthenticated /></d:principal> + </d:ace> + <d:ace> + <d:grant> + <d:privilege> + <d:write/> + </d:privilege> + </d:grant> + <d:principal><d:all /></d:principal> + </d:ace> +</d:root> +'; + + $reader = new \Sabre\Xml\Reader(); + $reader->elementMap['{DAV:}root'] = 'Sabre\DAVACL\Xml\Property\Acl'; + $reader->xml($source); + + $result = $reader->parse(); + $result = $result['value']; + + $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\Acl', $result); + + $expected = [ + [ + 'principal' => '{DAV:}authenticated', + 'protected' => false, + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => '{DAV:}unauthenticated', + 'protected' => false, + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => '{DAV:}all', + 'protected' => false, + 'privilege' => '{DAV:}write', + ], + ]; + + $this->assertEquals($expected, $result->getPrivileges()); + + } + + /** + * @expectedException Sabre\DAV\Exception\NotImplemented + */ + function testUnserializeDeny() { + + $source = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:ignore-me /> + <d:ace> + <d:deny> + <d:privilege> + <d:write/> + </d:privilege> + </d:deny> + <d:principal><d:href>/principals/evert</d:href></d:principal> + </d:ace> +</d:root> +'; + + $reader = new \Sabre\Xml\Reader(); + $reader->elementMap['{DAV:}root'] = 'Sabre\DAVACL\Xml\Property\Acl'; + $reader->xml($source); + + $result = $reader->parse(); + + } + + function testToHtml() { + + $privileges = [ + [ + 'principal' => 'principals/evert', + 'privilege' => '{DAV:}write', + ], + [ + 'principal' => 'principals/foo', + 'privilege' => '{http://example.org/ns}read', + 'protected' => true, + ], + [ + 'principal' => '{DAV:}authenticated', + 'privilege' => '{DAV:}write', + ], + ]; + + $acl = new Acl($privileges); + $html = new HtmlOutputHelper( + '/base/', + ['DAV:' => 'd'] + ); + + $expected = + '<table>' . + '<tr><th>Principal</th><th>Privilege</th><th></th></tr>' . + '<tr><td><a href="/base/principals/evert">/base/principals/evert</a></td><td><span title="{DAV:}write">d:write</span></td><td></td></tr>' . + '<tr><td><a href="/base/principals/foo">/base/principals/foo</a></td><td><span title="{http://example.org/ns}read">{http://example.org/ns}read</span></td><td>(protected)</td></tr>' . + '<tr><td><span title="{DAV:}authenticated">d:authenticated</span></td><td><span title="{DAV:}write">d:write</span></td><td></td></tr>' . + '</table>'; + + $this->assertEquals($expected, $acl->toHtml($html)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/AclRestrictionsTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/AclRestrictionsTest.php new file mode 100644 index 00000000000..6d8b83a1285 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/AclRestrictionsTest.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\DAVACL\Xml\Property; + +use Sabre\DAV; +use Sabre\HTTP; + +class AclRestrictionsTest extends \PHPUnit_Framework_TestCase { + + function testConstruct() { + + $prop = new AclRestrictions(); + $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\AclRestrictions', $prop); + + } + + function testSerialize() { + + $prop = new AclRestrictions(); + $xml = (new DAV\Server())->xml->write('{DAV:}root', $prop); + + $expected = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"><d:grant-only/><d:no-invert/></d:root>'; + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/CurrentUserPrivilegeSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/CurrentUserPrivilegeSetTest.php new file mode 100644 index 00000000000..d6e6b2d193f --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/CurrentUserPrivilegeSetTest.php @@ -0,0 +1,86 @@ +<?php + +namespace Sabre\DAVACL\Xml\Property; + +use Sabre\DAV; +use Sabre\DAV\Browser\HtmlOutputHelper; +use Sabre\HTTP; +use Sabre\Xml\Reader; + +class CurrentUserPrivilegeSetTest extends \PHPUnit_Framework_TestCase { + + function testSerialize() { + + $privileges = [ + '{DAV:}read', + '{DAV:}write', + ]; + $prop = new CurrentUserPrivilegeSet($privileges); + $xml = (new DAV\Server())->xml->write('{DAV:}root', $prop); + + $expected = <<<XML +<d:root xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:privilege> + <d:read /> + </d:privilege> + <d:privilege> + <d:write /> + </d:privilege> +</d:root> +XML; + + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + } + + function testUnserialize() { + + $source = '<?xml version="1.0"?> +<d:root xmlns:d="DAV:"> + <d:privilege> + <d:write-properties /> + </d:privilege> + <d:ignoreme /> + <d:privilege> + <d:read /> + </d:privilege> +</d:root> +'; + + $result = $this->parse($source); + $this->assertTrue($result->has('{DAV:}read')); + $this->assertTrue($result->has('{DAV:}write-properties')); + $this->assertFalse($result->has('{DAV:}bind')); + + } + + function parse($xml) { + + $reader = new Reader(); + $reader->elementMap['{DAV:}root'] = 'Sabre\\DAVACL\\Xml\\Property\\CurrentUserPrivilegeSet'; + $reader->xml($xml); + $result = $reader->parse(); + return $result['value']; + + } + + function testToHtml() { + + $privileges = ['{DAV:}read', '{DAV:}write']; + + $prop = new CurrentUserPrivilegeSet($privileges); + $html = new HtmlOutputHelper( + '/base/', + ['DAV:' => 'd'] + ); + + $expected = + '<span title="{DAV:}read">d:read</span>, ' . + '<span title="{DAV:}write">d:write</span>'; + + $this->assertEquals($expected, $prop->toHtml($html)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/PrincipalTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/PrincipalTest.php new file mode 100644 index 00000000000..876d1073aaa --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/PrincipalTest.php @@ -0,0 +1,191 @@ +<?php + +namespace Sabre\DAVACL\Xml\Property; + +use Sabre\DAV; +use Sabre\DAV\Browser\HtmlOutputHelper; +use Sabre\HTTP; +use Sabre\Xml\Reader; + +class PrincipalTest extends \PHPUnit_Framework_TestCase { + + function testSimple() { + + $principal = new Principal(Principal::UNAUTHENTICATED); + $this->assertEquals(Principal::UNAUTHENTICATED, $principal->getType()); + $this->assertNull($principal->getHref()); + + $principal = new Principal(Principal::AUTHENTICATED); + $this->assertEquals(Principal::AUTHENTICATED, $principal->getType()); + $this->assertNull($principal->getHref()); + + $principal = new Principal(Principal::HREF, 'admin'); + $this->assertEquals(Principal::HREF, $principal->getType()); + $this->assertEquals('admin/', $principal->getHref()); + + } + + /** + * @depends testSimple + * @expectedException Sabre\DAV\Exception + */ + function testNoHref() { + + $principal = new Principal(Principal::HREF); + + } + + /** + * @depends testSimple + */ + function testSerializeUnAuthenticated() { + + $prin = new Principal(Principal::UNAUTHENTICATED); + + $xml = (new DAV\Server())->xml->write('{DAV:}principal', $prin); + + $this->assertXmlStringEqualsXmlString(' +<d:principal xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> +<d:unauthenticated/> +</d:principal>', $xml); + + } + + + /** + * @depends testSerializeUnAuthenticated + */ + function testSerializeAuthenticated() { + + $prin = new Principal(Principal::AUTHENTICATED); + $xml = (new DAV\Server())->xml->write('{DAV:}principal', $prin); + + $this->assertXmlStringEqualsXmlString(' +<d:principal xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> +<d:authenticated/> +</d:principal>', $xml); + + } + + + /** + * @depends testSerializeUnAuthenticated + */ + function testSerializeHref() { + + $prin = new Principal(Principal::HREF, 'principals/admin'); + $xml = (new DAV\Server())->xml->write('{DAV:}principal', $prin, '/'); + + $this->assertXmlStringEqualsXmlString(' +<d:principal xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> +<d:href>/principals/admin/</d:href> +</d:principal>', $xml); + + } + + function testUnserializeHref() { + + $xml = '<?xml version="1.0"?> +<d:principal xmlns:d="DAV:">' . +'<d:href>/principals/admin</d:href>' . +'</d:principal>'; + + $principal = $this->parse($xml); + $this->assertEquals(Principal::HREF, $principal->getType()); + $this->assertEquals('/principals/admin/', $principal->getHref()); + + } + + function testUnserializeAuthenticated() { + + $xml = '<?xml version="1.0"?> +<d:principal xmlns:d="DAV:">' . +' <d:authenticated />' . +'</d:principal>'; + + $principal = $this->parse($xml); + $this->assertEquals(Principal::AUTHENTICATED, $principal->getType()); + + } + + function testUnserializeUnauthenticated() { + + $xml = '<?xml version="1.0"?> +<d:principal xmlns:d="DAV:">' . +' <d:unauthenticated />' . +'</d:principal>'; + + $principal = $this->parse($xml); + $this->assertEquals(Principal::UNAUTHENTICATED, $principal->getType()); + + } + + /** + * @expectedException Sabre\DAV\Exception\BadRequest + */ + function testUnserializeUnknown() { + + $xml = '<?xml version="1.0"?> +<d:principal xmlns:d="DAV:">' . +' <d:foo />' . +'</d:principal>'; + + $this->parse($xml); + + } + + function parse($xml) { + + $reader = new Reader(); + $reader->elementMap['{DAV:}principal'] = 'Sabre\\DAVACL\\Xml\\Property\\Principal'; + $reader->xml($xml); + $result = $reader->parse(); + return $result['value']; + + } + + /** + * @depends testSimple + * @dataProvider htmlProvider + */ + function testToHtml($principal, $output) { + + $html = $principal->toHtml(new HtmlOutputHelper('/', [])); + + $this->assertXmlStringEqualsXmlString( + $output, + $html + ); + + } + + /** + * Provides data for the html tests + * + * @return array + */ + function htmlProvider() { + + return [ + [ + new Principal(Principal::UNAUTHENTICATED), + '<em>unauthenticated</em>', + ], + [ + new Principal(Principal::AUTHENTICATED), + '<em>authenticated</em>', + ], + [ + new Principal(Principal::ALL), + '<em>all</em>', + ], + [ + new Principal(Principal::HREF, 'principals/admin'), + '<a href="/principals/admin/">/principals/admin/</a>', + ], + + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/SupportedPrivilegeSetTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/SupportedPrivilegeSetTest.php new file mode 100644 index 00000000000..749d349fc7d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Property/SupportedPrivilegeSetTest.php @@ -0,0 +1,103 @@ +<?php + +namespace Sabre\DAVACL\Xml\Property; + +use Sabre\DAV; +use Sabre\DAV\Browser\HtmlOutputHelper; +use Sabre\HTTP; + +class SupportedPrivilegeSetTest extends \PHPUnit_Framework_TestCase { + + function testSimple() { + + $prop = new SupportedPrivilegeSet([ + 'privilege' => '{DAV:}all', + ]); + $this->assertInstanceOf('Sabre\DAVACL\Xml\Property\SupportedPrivilegeSet', $prop); + + } + + + /** + * @depends testSimple + */ + function testSerializeSimple() { + + $prop = new SupportedPrivilegeSet([]); + + $xml = (new DAV\Server())->xml->write('{DAV:}supported-privilege-set', $prop); + + $this->assertXmlStringEqualsXmlString(' +<d:supported-privilege-set xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:supported-privilege> + <d:privilege> + <d:all/> + </d:privilege> + </d:supported-privilege> +</d:supported-privilege-set>', $xml); + + } + + /** + * @depends testSimple + */ + function testSerializeAggregate() { + + $prop = new SupportedPrivilegeSet([ + '{DAV:}read' => [], + '{DAV:}write' => [ + 'description' => 'booh', + ] + ]); + + $xml = (new DAV\Server())->xml->write('{DAV:}supported-privilege-set', $prop); + + $this->assertXmlStringEqualsXmlString(' +<d:supported-privilege-set xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> + <d:supported-privilege> + <d:privilege> + <d:all/> + </d:privilege> + <d:supported-privilege> + <d:privilege> + <d:read/> + </d:privilege> + </d:supported-privilege> + <d:supported-privilege> + <d:privilege> + <d:write/> + </d:privilege> + <d:description>booh</d:description> + </d:supported-privilege> + </d:supported-privilege> +</d:supported-privilege-set>', $xml); + + } + + function testToHtml() { + + $prop = new SupportedPrivilegeSet([ + '{DAV:}read' => [], + '{DAV:}write' => [ + 'description' => 'booh', + ], + ]); + $html = new HtmlOutputHelper( + '/base/', + ['DAV:' => 'd'] + ); + + $expected = <<<HTML +<ul class="tree"><li><span title="{DAV:}all">d:all</span> +<ul> +<li><span title="{DAV:}read">d:read</span></li> +<li><span title="{DAV:}write">d:write</span> booh</li> +</ul></li> +</ul> + +HTML; + + $this->assertEquals($expected, $prop->toHtml($html)); + + } +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Request/AclPrincipalPropSetReportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Request/AclPrincipalPropSetReportTest.php new file mode 100644 index 00000000000..bae682f2155 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Request/AclPrincipalPropSetReportTest.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\DAVACL\Xml\Request; + +class AclPrincipalPropSetReportTest extends \Sabre\DAV\Xml\XmlTest { + + protected $elementMap = [ + + '{DAV:}acl-principal-prop-set' => 'Sabre\DAVACL\Xml\Request\AclPrincipalPropSetReport', + + ]; + + function testDeserialize() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> +<D:acl-principal-prop-set xmlns:D="DAV:"> + <D:prop> + <D:displayname/> + </D:prop> +</D:acl-principal-prop-set> +XML; + + $result = $this->parse($xml); + + $this->assertEquals(['{DAV:}displayname'], $result['value']->properties); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Request/PrincipalMatchReportTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Request/PrincipalMatchReportTest.php new file mode 100644 index 00000000000..1431ab34939 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVACL/Xml/Request/PrincipalMatchReportTest.php @@ -0,0 +1,51 @@ +<?php + +namespace Sabre\DAVACL\Xml\Request; + +class PrincipalMatchReportTest extends \Sabre\DAV\Xml\XmlTest { + + protected $elementMap = [ + + '{DAV:}principal-match' => 'Sabre\DAVACL\Xml\Request\PrincipalMatchReport', + + ]; + + function testDeserialize() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> + <D:principal-match xmlns:D="DAV:"> + <D:principal-property> + <D:owner/> + </D:principal-property> + </D:principal-match> +XML; + + $result = $this->parse($xml); + + $this->assertEquals(PrincipalMatchReport::PRINCIPAL_PROPERTY, $result['value']->type); + $this->assertEquals('{DAV:}owner', $result['value']->principalProperty); + + } + + function testDeserializeSelf() { + + $xml = <<<XML +<?xml version="1.0" encoding="utf-8" ?> + <D:principal-match xmlns:D="DAV:"> + <D:self /> + <D:prop> + <D:foo /> + </D:prop> + </D:principal-match> +XML; + + $result = $this->parse($xml); + + $this->assertEquals(PrincipalMatchReport::SELF, $result['value']->type); + $this->assertNull($result['value']->principalProperty); + $this->assertEquals(['{DAV:}foo'], $result['value']->properties); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVServerTest.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVServerTest.php new file mode 100644 index 00000000000..35f240d23fa --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/DAVServerTest.php @@ -0,0 +1,306 @@ +<?php + +namespace Sabre; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; +use Sabre\HTTP\Sapi; + +/** + * This class may be used as a basis for other webdav-related unittests. + * + * This class is supposed to provide a reasonably big framework to quickly get + * a testing environment running. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class DAVServerTest extends \PHPUnit_Framework_TestCase { + + protected $setupCalDAV = false; + protected $setupCardDAV = false; + protected $setupACL = false; + protected $setupCalDAVSharing = false; + protected $setupCalDAVScheduling = false; + protected $setupCalDAVSubscriptions = false; + protected $setupCalDAVICSExport = false; + protected $setupLocks = false; + protected $setupFiles = false; + protected $setupSharing = false; + protected $setupPropertyStorage = false; + + /** + * An array with calendars. Every calendar should have + * - principaluri + * - uri + */ + protected $caldavCalendars = []; + protected $caldavCalendarObjects = []; + + protected $carddavAddressBooks = []; + protected $carddavCards = []; + + /** + * @var Sabre\DAV\Server + */ + protected $server; + protected $tree = []; + + protected $caldavBackend; + protected $carddavBackend; + protected $principalBackend; + protected $locksBackend; + protected $propertyStorageBackend; + + /** + * @var Sabre\CalDAV\Plugin + */ + protected $caldavPlugin; + + /** + * @var Sabre\CardDAV\Plugin + */ + protected $carddavPlugin; + + /** + * @var Sabre\DAVACL\Plugin + */ + protected $aclPlugin; + + /** + * @var Sabre\CalDAV\SharingPlugin + */ + protected $caldavSharingPlugin; + + /** + * CalDAV scheduling plugin + * + * @var CalDAV\Schedule\Plugin + */ + protected $caldavSchedulePlugin; + + /** + * @var Sabre\DAV\Auth\Plugin + */ + protected $authPlugin; + + /** + * @var Sabre\DAV\Locks\Plugin + */ + protected $locksPlugin; + + /** + * Sharing plugin. + * + * @var \Sabre\DAV\Sharing\Plugin + */ + protected $sharingPlugin; + + /* + * @var Sabre\DAV\PropertyStorage\Plugin + */ + protected $propertyStoragePlugin; + + /** + * If this string is set, we will automatically log in the user with this + * name. + */ + protected $autoLogin = null; + + function setUp() { + + $this->initializeEverything(); + + } + + function initializeEverything() { + + $this->setUpBackends(); + $this->setUpTree(); + + $this->server = new DAV\Server($this->tree); + $this->server->sapi = new HTTP\SapiMock(); + $this->server->debugExceptions = true; + + if ($this->setupCalDAV) { + $this->caldavPlugin = new CalDAV\Plugin(); + $this->server->addPlugin($this->caldavPlugin); + } + if ($this->setupCalDAVSharing || $this->setupSharing) { + $this->sharingPlugin = new DAV\Sharing\Plugin(); + $this->server->addPlugin($this->sharingPlugin); + } + if ($this->setupCalDAVSharing) { + $this->caldavSharingPlugin = new CalDAV\SharingPlugin(); + $this->server->addPlugin($this->caldavSharingPlugin); + } + if ($this->setupCalDAVScheduling) { + $this->caldavSchedulePlugin = new CalDAV\Schedule\Plugin(); + $this->server->addPlugin($this->caldavSchedulePlugin); + } + if ($this->setupCalDAVSubscriptions) { + $this->server->addPlugin(new CalDAV\Subscriptions\Plugin()); + } + if ($this->setupCalDAVICSExport) { + $this->caldavICSExportPlugin = new CalDAV\ICSExportPlugin(); + $this->server->addPlugin($this->caldavICSExportPlugin); + } + if ($this->setupCardDAV) { + $this->carddavPlugin = new CardDAV\Plugin(); + $this->server->addPlugin($this->carddavPlugin); + } + if ($this->setupLocks) { + $this->locksPlugin = new DAV\Locks\Plugin( + $this->locksBackend + ); + $this->server->addPlugin($this->locksPlugin); + } + if ($this->setupPropertyStorage) { + $this->propertyStoragePlugin = new DAV\PropertyStorage\Plugin( + $this->propertyStorageBackend + ); + $this->server->addPlugin($this->propertyStoragePlugin); + } + if ($this->autoLogin) { + $this->autoLogin($this->autoLogin); + } + if ($this->setupACL) { + $this->aclPlugin = new DAVACL\Plugin(); + if (!$this->autoLogin) { + $this->aclPlugin->allowUnauthenticatedAccess = false; + } + $this->aclPlugin->adminPrincipals = ['principals/admin']; + $this->server->addPlugin($this->aclPlugin); + } + + } + + /** + * Makes a request, and returns a response object. + * + * You can either pass an instance of Sabre\HTTP\Request, or an array, + * which will then be used as the _SERVER array. + * + * If $expectedStatus is set, we'll compare it with the HTTP status of + * the returned response. If it doesn't match, we'll immediately fail + * the test. + * + * @param array|\Sabre\HTTP\Request $request + * @param int $expectedStatus + * @return \Sabre\HTTP\Response + */ + function request($request, $expectedStatus = null) { + + if (is_array($request)) { + $request = HTTP\Request::createFromServerArray($request); + } + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + $this->server->exec(); + + if ($expectedStatus) { + $responseBody = $expectedStatus !== $response->getStatus() ? $response->getBodyAsString() : ''; + $this->assertEquals($expectedStatus, $response->getStatus(), 'Incorrect HTTP status received for request. Response body: ' . $responseBody); + } + return $this->server->httpResponse; + + } + + /** + * This function takes a username and sets the server in a state where + * this user is logged in, and no longer requires an authentication check. + * + * @param string $userName + */ + function autoLogin($userName) { + $authBackend = new DAV\Auth\Backend\Mock(); + $authBackend->setPrincipal('principals/' . $userName); + $this->authPlugin = new DAV\Auth\Plugin($authBackend); + + // If the auth plugin already exists, we're removing its hooks: + if ($oldAuth = $this->server->getPlugin('auth')) { + $this->server->removeListener('beforeMethod', [$oldAuth, 'beforeMethod']); + } + $this->server->addPlugin($this->authPlugin); + + // This will trigger the actual login procedure + $this->authPlugin->beforeMethod(new Request(), new Response()); + } + + /** + * Override this to provide your own Tree for your test-case. + */ + function setUpTree() { + + if ($this->setupCalDAV) { + $this->tree[] = new CalDAV\CalendarRoot( + $this->principalBackend, + $this->caldavBackend + ); + } + if ($this->setupCardDAV) { + $this->tree[] = new CardDAV\AddressBookRoot( + $this->principalBackend, + $this->carddavBackend + ); + } + + if ($this->setupCalDAV) { + $this->tree[] = new CalDAV\Principal\Collection( + $this->principalBackend + ); + } elseif ($this->setupCardDAV || $this->setupACL) { + $this->tree[] = new DAVACL\PrincipalCollection( + $this->principalBackend + ); + } + if ($this->setupFiles) { + + $this->tree[] = new DAV\Mock\Collection('files'); + + } + + } + + function setUpBackends() { + + if ($this->setupCalDAVSharing && is_null($this->caldavBackend)) { + $this->caldavBackend = new CalDAV\Backend\MockSharing($this->caldavCalendars, $this->caldavCalendarObjects); + } + if ($this->setupCalDAVSubscriptions && is_null($this->caldavBackend)) { + $this->caldavBackend = new CalDAV\Backend\MockSubscriptionSupport($this->caldavCalendars, $this->caldavCalendarObjects); + } + if ($this->setupCalDAV && is_null($this->caldavBackend)) { + if ($this->setupCalDAVScheduling) { + $this->caldavBackend = new CalDAV\Backend\MockScheduling($this->caldavCalendars, $this->caldavCalendarObjects); + } else { + $this->caldavBackend = new CalDAV\Backend\Mock($this->caldavCalendars, $this->caldavCalendarObjects); + } + } + if ($this->setupCardDAV && is_null($this->carddavBackend)) { + $this->carddavBackend = new CardDAV\Backend\Mock($this->carddavAddressBooks, $this->carddavCards); + } + if ($this->setupCardDAV || $this->setupCalDAV || $this->setupACL) { + $this->principalBackend = new DAVACL\PrincipalBackend\Mock(); + } + if ($this->setupLocks) { + $this->locksBackend = new DAV\Locks\Backend\Mock(); + } + if ($this->setupPropertyStorage) { + $this->propertyStorageBackend = new DAV\PropertyStorage\Backend\Mock(); + } + + } + + + function assertHttpStatus($expectedStatus, HTTP\Request $req) { + + $resp = $this->request($req); + $this->assertEquals((int)$expectedStatus, (int)$resp->status, 'Incorrect HTTP status received: ' . $resp->body); + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/HTTP/ResponseMock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/HTTP/ResponseMock.php new file mode 100644 index 00000000000..eb486bf5b89 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/HTTP/ResponseMock.php @@ -0,0 +1,22 @@ +<?php + +namespace Sabre\HTTP; + +/** + * HTTP Response Mock object + * + * This class exists to make the transition to sabre/http easier. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ResponseMock extends Response { + + /** + * Making these public. + */ + public $body; + public $status; + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/HTTP/SapiMock.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/HTTP/SapiMock.php new file mode 100644 index 00000000000..e2888a9da79 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/HTTP/SapiMock.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\HTTP; + +/** + * HTTP Response Mock object + * + * This class exists to make the transition to sabre/http easier. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class SapiMock extends Sapi { + + static $sent = 0; + + /** + * Overriding this so nothing is ever echo'd. + * + * @param ResponseInterface $response + * @return void + */ + static function sendResponse(ResponseInterface $response) { + + self::$sent++; + + } + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/Sabre/TestUtil.php b/htdocs/includes/sabre/sabre/dav/tests/Sabre/TestUtil.php new file mode 100644 index 00000000000..9df94915fb2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/Sabre/TestUtil.php @@ -0,0 +1,71 @@ +<?php + +namespace Sabre; + +class TestUtil { + + /** + * This function deletes all the contents of the temporary directory. + * + * @return void + */ + static function clearTempDir() { + + self::deleteTree(SABRE_TEMPDIR, false); + + } + + + private static function deleteTree($path, $deleteRoot = true) { + + foreach (scandir($path) as $node) { + + if ($node == '.' || $node == '..') continue; + $myPath = $path . '/' . $node; + if (is_file($myPath)) { + unlink($myPath); + } else { + self::deleteTree($myPath); + } + + } + if ($deleteRoot) { + rmdir($path); + } + + } + + static function getMySQLDB() { + + try { + $pdo = new \PDO(SABRE_MYSQLDSN, SABRE_MYSQLUSER, SABRE_MYSQLPASS); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + return $pdo; + } catch (\PDOException $e) { + return null; + } + + } + + static function getSQLiteDB() { + + $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/pdobackend'); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + return $pdo; + + } + + static function getPgSqlDB() { + + //try { + $pdo = new \PDO(SABRE_PGSQLDSN); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + return $pdo; + //} catch (\PDOException $e) { + // return null; + //} + + } + + +} diff --git a/htdocs/includes/sabre/sabre/dav/tests/bootstrap.php b/htdocs/includes/sabre/sabre/dav/tests/bootstrap.php new file mode 100644 index 00000000000..26eb32aa284 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/bootstrap.php @@ -0,0 +1,38 @@ +<?php + +set_include_path(__DIR__ . '/../lib/' . PATH_SEPARATOR . __DIR__ . PATH_SEPARATOR . get_include_path()); + +$autoLoader = include __DIR__ . '/../vendor/autoload.php'; + +// SabreDAV tests auto loading +$autoLoader->add('Sabre\\', __DIR__); +// VObject tests auto loading +$autoLoader->addPsr4('Sabre\\VObject\\', __DIR__ . '/../vendor/sabre/vobject/tests/VObject'); +$autoLoader->addPsr4('Sabre\\Xml\\', __DIR__ . '/../vendor/sabre/xml/tests/Sabre/Xml'); + +date_default_timezone_set('UTC'); + +$config = [ + 'SABRE_TEMPDIR' => dirname(__FILE__) . '/temp/', + 'SABRE_HASSQLITE' => in_array('sqlite', PDO::getAvailableDrivers()), + 'SABRE_HASMYSQL' => in_array('mysql', PDO::getAvailableDrivers()), + 'SABRE_HASPGSQL' => in_array('pgsql', PDO::getAvailableDrivers()), + 'SABRE_MYSQLDSN' => 'mysql:host=127.0.0.1;dbname=sabredav_test', + 'SABRE_MYSQLUSER' => 'sabredav', + 'SABRE_MYSQLPASS' => '', + 'SABRE_PGSQLDSN' => 'pgsql:host=localhost;dbname=sabredav_test;user=sabredav;password=sabredav', +]; + +if (file_exists(__DIR__ . '/config.user.php')) { + include __DIR__ . '/config.user.php'; + foreach ($userConfig as $key => $value) { + $config[$key] = $value; + } +} + +foreach ($config as $key => $value) { + if (!defined($key)) define($key, $value); +} + +if (!file_exists(SABRE_TEMPDIR)) mkdir(SABRE_TEMPDIR); +if (file_exists('.sabredav')) unlink('.sabredav'); diff --git a/htdocs/includes/sabre/sabre/dav/tests/phpcs/ruleset.xml b/htdocs/includes/sabre/sabre/dav/tests/phpcs/ruleset.xml new file mode 100644 index 00000000000..ec2c4c84b1d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/phpcs/ruleset.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<ruleset name="sabre.php"> + <description>sabre.io codesniffer ruleset</description> + + <!-- Include the whole PSR-1 standard --> + <rule ref="PSR1" /> + + <!-- All PHP files MUST use the Unix LF (linefeed) line ending. --> + <rule ref="Generic.Files.LineEndings"> + <properties> + <property name="eolChar" value="\n"/> + </properties> + </rule> + + <!-- The closing ?> tag MUST be omitted from files containing only PHP. --> + <rule ref="Zend.Files.ClosingTag"/> + + <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. --> + <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"> + <properties> + <property name="ignoreBlankLines" value="true"/> + </properties> + </rule> + + <!-- There MUST NOT be more than one statement per line. --> + <rule ref="Generic.Formatting.DisallowMultipleStatements"/> + + <rule ref="Generic.WhiteSpace.ScopeIndent"> + <properties> + <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT"/> + </properties> + </rule> + <rule ref="Generic.WhiteSpace.DisallowTabIndent"/> + + <!-- PHP keywords MUST be in lower case. --> + <rule ref="Generic.PHP.LowerCaseKeyword"/> + + <!-- The PHP constants true, false, and null MUST be in lower case. --> + <rule ref="Generic.PHP.LowerCaseConstant"/> + + <!-- <rule ref="Squiz.Scope.MethodScope"/> --> + <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/> + + <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. --> + <!-- + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing"> + <properties> + <property name="equalsSpacing" value="1"/> + </properties> + </rule> + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint"> + <severity>0</severity> + </rule> + --> + <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/> + +</ruleset> diff --git a/htdocs/includes/sabre/sabre/dav/tests/phpunit.xml.dist b/htdocs/includes/sabre/sabre/dav/tests/phpunit.xml.dist new file mode 100644 index 00000000000..453fabb82ad --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/tests/phpunit.xml.dist @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<phpunit + colors="true" + bootstrap="bootstrap.php" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + beStrictAboutTestsThatDoNotTestAnything="true" + beStrictAboutOutputDuringTests="true" + beStrictAboutTestSize="true"> + + <testsuite name="sabre-event"> + <directory>../vendor/sabre/event/tests/</directory> + </testsuite> + <testsuite name="sabre-uri"> + <directory>../vendor/sabre/uri/tests/</directory> + </testsuite> + <testsuite name="sabre-xml"> + <directory>../vendor/sabre/xml/tests/Sabre/Xml/</directory> + </testsuite> + <testsuite name="sabre-http"> + <directory>../vendor/sabre/http/tests/HTTP</directory> + </testsuite> + <testsuite name="sabre-vobject"> + <directory>../vendor/sabre/vobject/tests/VObject</directory> + </testsuite> + + <testsuite name="sabre-dav"> + <directory>Sabre/DAV</directory> + </testsuite> + <testsuite name="sabre-davacl"> + <directory>Sabre/DAVACL</directory> + </testsuite> + <testsuite name="sabre-caldav"> + <directory>Sabre/CalDAV</directory> + </testsuite> + <testsuite name="sabre-carddav"> + <directory>Sabre/CardDAV</directory> + </testsuite> + + <filter> + <whitelist addUncoveredFilesFromWhitelist="true"> + <directory suffix=".php">../lib/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/htdocs/includes/sabre/sabre/event/.gitignore b/htdocs/includes/sabre/sabre/event/.gitignore new file mode 100644 index 00000000000..d06a78164db --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/.gitignore @@ -0,0 +1,14 @@ +#composer +vendor +composer.lock + +#binaries +bin/sabre-cs-fixer +bin/php-cs-fixer +bin/phpunit + +#vim lock files +.*.swp + +#development stuff +tests/cov diff --git a/htdocs/includes/sabre/sabre/event/.travis.yml b/htdocs/includes/sabre/sabre/event/.travis.yml new file mode 100644 index 00000000000..b6719f591f7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/.travis.yml @@ -0,0 +1,26 @@ +language: php +php: + - 5.5 + - 5.6 + - 7 + - hhvm + +matrix: + allow_failures: + - php: hhvm + +env: + matrix: + - LOWEST_DEPS="" + - LOWEST_DEPS="--prefer-lowest" + +before_script: + - composer update --prefer-source $LOWEST_DEPS + +script: + - ./bin/phpunit + - ./bin/sabre-cs-fixer fix . --dry-run --diff + +sudo: false + +cache: vendor diff --git a/htdocs/includes/sabre/sabre/event/CHANGELOG.md b/htdocs/includes/sabre/sabre/event/CHANGELOG.md new file mode 100644 index 00000000000..9d6d7cfaaf8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/CHANGELOG.md @@ -0,0 +1,78 @@ +ChangeLog +========= + +3.0.0 (2015-11-05) +------------------ + +* Now requires PHP 5.5! +* `Promise::all()` is moved to `Promise\all()`. +* Aside from the `Promise\all()` function, there's now also `Promise\race()`. +* `Promise\reject()` and `Promise\resolve()` have also been added. +* Now 100% compatible with the Ecmascript 6 Promise. + + +3.0.0-alpha1 (2015-10-23) +------------------------- + +* This package now requires PHP 5.5. +* #26: Added an event loop implementation. Also knows as the Reactor Pattern. +* Renamed `Promise::error` to `Promise::otherwise` to be consistent with + ReactPHP and Guzzle. The `error` method is kept for BC but will be removed + in a future version. +* #27: Support for Promise-based coroutines via the `Sabre\Event\coroutine` + function. +* BC Break: Promises now use the EventLoop to run "then"-events in a separate + execution context. In practise that means you need to run the event loop to + wait for any `then`/`otherwise` callbacks to trigger. +* Promises now have a `wait()` method. Allowing you to make a promise + synchronous and simply wait for a result (or exception) to happen. + + +2.0.2 (2015-05-19) +------------------ + +* This release has no functional changes. It's just been brought up to date + with the latest coding standards. + + +2.0.1 (2014-10-06) +------------------ + +* Fixed: `$priority` was ignored in `EventEmitter::once` method. +* Fixed: Breaking the event chain was not possible in `EventEmitter::once`. + + +2.0.0 (2014-06-21) +------------------ + +* Added: When calling emit, it's now possible to specify a callback that will be + triggered after each method handled. This is dubbed the 'continueCallback' and + can be used to implement strategy patterns. +* Added: Promise object! +* Changed: EventEmitter::listeners now returns just the callbacks for an event, + and no longer returns the list by reference. The list is now automatically + sorted by priority. +* Update: Speed improvements. +* Updated: It's now possible to remove all listeners for every event. +* Changed: Now uses psr-4 autoloading. + + +1.0.1 (2014-06-12) +------------------ + +* hhvm compatible! +* Fixed: Issue #4. Compatiblitiy for PHP < 5.4.14. + + +1.0.0 (2013-07-19) +------------------ + +* Added: removeListener, removeAllListeners +* Added: once, to only listen to an event emitting once. +* Added README.md. + + +0.0.1-alpha (2013-06-29) +------------------------ + +* First version! diff --git a/htdocs/includes/sabre/sabre/event/LICENSE b/htdocs/includes/sabre/sabre/event/LICENSE new file mode 100644 index 00000000000..9a495cef0a1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name Sabre nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/htdocs/includes/sabre/sabre/event/README.md b/htdocs/includes/sabre/sabre/event/README.md new file mode 100644 index 00000000000..364906fd496 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/README.md @@ -0,0 +1,50 @@ +sabre/event +=========== + +A lightweight library for event-based development in PHP. + +This library provides the following event-based concepts: + +1. EventEmitter. +2. Promises. +3. An event loop. +4. Co-routines. + +Full documentation can be found on [the website][1]. + +Installation +------------ + +Make sure you have [composer][3] installed, and then run: + + composer require sabre/event "~3.0.0" + +This package requires PHP 5.5. The 2.0 branch is still maintained as well, and +supports PHP 5.4. + +Build status +------------ + +| branch | status | +| ------ | ------ | +| master | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=master)](https://travis-ci.org/fruux/sabre-event) | +| 2.0 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=2.0)](https://travis-ci.org/fruux/sabre-event) | +| 1.0 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=1.0)](https://travis-ci.org/fruux/sabre-event) | +| php53 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=php53)](https://travis-ci.org/fruux/sabre-event) | + + +Questions? +---------- + +Head over to the [sabre/dav mailinglist][4], or you can also just open a ticket +on [GitHub][5]. + +Made at fruux +------------- + +This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. + +[1]: http://sabre.io/event/ +[3]: http://getcomposer.org/ +[4]: http://groups.google.com/group/sabredav-discuss +[5]: https://github.com/fruux/sabre-event/issues/ diff --git a/htdocs/includes/sabre/sabre/event/bin/.empty b/htdocs/includes/sabre/sabre/event/bin/.empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/includes/sabre/sabre/event/composer.json b/htdocs/includes/sabre/sabre/event/composer.json new file mode 100644 index 00000000000..9a11b01aaa4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/composer.json @@ -0,0 +1,47 @@ +{ + "name": "sabre/event", + "description": "sabre/event is a library for lightweight event-based programming", + "keywords": [ + "Events", + "EventEmitter", + "Promise", + "Hooks", + "Plugin", + "Signal", + "Async" + ], + "homepage": "http://sabre.io/event/", + "license": "BSD-3-Clause", + "require": { + "php": ">=5.5" + }, + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "support": { + "forum": "https://groups.google.com/group/sabredav-discuss", + "source": "https://github.com/fruux/sabre-event" + }, + "autoload": { + "psr-4": { + "Sabre\\Event\\": "lib/" + }, + "files" : [ + "lib/coroutine.php", + "lib/Loop/functions.php", + "lib/Promise/functions.php" + ] + }, + "require-dev": { + "sabre/cs": "~0.0.4", + "phpunit/phpunit" : "*" + }, + "config" : { + "bin-dir" : "bin/" + } +} diff --git a/htdocs/includes/sabre/sabre/event/examples/promise.php b/htdocs/includes/sabre/sabre/event/examples/promise.php new file mode 100644 index 00000000000..b40227e15dd --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/examples/promise.php @@ -0,0 +1,100 @@ +#!/usr/bin/env php +<?php + +use Sabre\Event\Promise; +use Sabre\Event\Loop; +use function Sabre\Event\coroutine; + +require __DIR__ . '/../vendor/autoload.php'; + +/** + * This example shows demonstrates the Promise api. + */ + + +/* Creating a new promise */ +$promise = new Promise(); + +/* After 2 seconds we fulfill it */ +Loop\setTimeout(function() use ($promise) { + + echo "Step 1\n"; + $promise->fulfill("hello"); + +}, 2); + + +/* Callback chain */ + +$result = $promise + ->then(function($value) { + + echo "Step 2\n"; + // Immediately returning a new value. + return $value . " world"; + + }) + ->then(function($value) { + + echo "Step 3\n"; + // This 'then' returns a new promise which we resolve later. + $promise = new Promise(); + + // Resolving after 2 seconds + Loop\setTimeout(function() use ($promise, $value) { + + $promise->fulfill($value . ", how are ya?"); + + }, 2); + + return $promise; + }) + ->then(function($value) { + + echo "Step 4\n"; + // This is the final event handler. + return $value . " you rock!"; + + }) + // Making all async calls synchronous by waiting for the final result. + ->wait(); + +echo $result, "\n"; + +/* Now an identical example, this time with coroutines. */ + +$result = coroutine(function() { + + $promise = new Promise(); + + /* After 2 seconds we fulfill it */ + Loop\setTimeout(function() use ($promise) { + + echo "Step 1\n"; + $promise->fulfill("hello"); + + }, 2); + + $value = (yield $promise); + + echo "Step 2\n"; + $value .= ' world'; + + echo "Step 3\n"; + $promise = new Promise(); + Loop\setTimeout(function() use ($promise, $value) { + + $promise->fulfill($value . ", how are ya?"); + + }, 2); + + $value = (yield $promise); + + echo "Step 4\n"; + + // This is the final event handler. + yield $value . " you rock!"; + +})->wait(); + +echo $result, "\n"; diff --git a/htdocs/includes/sabre/sabre/event/examples/tail.php b/htdocs/includes/sabre/sabre/event/examples/tail.php new file mode 100644 index 00000000000..d4e82206b52 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/examples/tail.php @@ -0,0 +1,28 @@ +#!/usr/bin/env php +<?php + +/** + * This example can be used to logfile processing and basically wraps the tail + * command. + * + * The benefit of using this, is that it allows you to tail multiple logs at + * the same time + * + * To stop this application, hit CTRL-C + */ +if ($argc < 2) { + echo "Usage: " . $argv[0] . " filename\n"; + exit(1); +} + +require __DIR__ . '/../vendor/autoload.php'; + +$tail = popen('tail -fn0 ' . escapeshellarg($argv[1]), 'r'); + +\Sabre\Event\Loop\addReadStream($tail, function() use ($tail) { + + echo fread($tail, 4096); + +}); + +$loop->run(); diff --git a/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php b/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php new file mode 100644 index 00000000000..1bb1c3cf92b --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php @@ -0,0 +1,18 @@ +<?php + +namespace Sabre\Event; + +/** + * EventEmitter object. + * + * Instantiate this class, or subclass it for easily creating event emitters. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class EventEmitter implements EventEmitterInterface { + + use EventEmitterTrait; + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/EventEmitterInterface.php b/htdocs/includes/sabre/sabre/event/lib/EventEmitterInterface.php new file mode 100644 index 00000000000..0e2be2cefb5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/EventEmitterInterface.php @@ -0,0 +1,100 @@ +<?php + +namespace Sabre\Event; + +/** + * Event Emitter Interface + * + * Anything that accepts listeners and emits events should implement this + * interface. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface EventEmitterInterface { + + /** + * Subscribe to an event. + * + * @param string $eventName + * @param callable $callBack + * @param int $priority + * @return void + */ + function on($eventName, callable $callBack, $priority = 100); + + /** + * Subscribe to an event exactly once. + * + * @param string $eventName + * @param callable $callBack + * @param int $priority + * @return void + */ + function once($eventName, callable $callBack, $priority = 100); + + /** + * Emits an event. + * + * This method will return true if 0 or more listeners were succesfully + * handled. false is returned if one of the events broke the event chain. + * + * If the continueCallBack is specified, this callback will be called every + * time before the next event handler is called. + * + * If the continueCallback returns false, event propagation stops. This + * allows you to use the eventEmitter as a means for listeners to implement + * functionality in your application, and break the event loop as soon as + * some condition is fulfilled. + * + * Note that returning false from an event subscriber breaks propagation + * and returns false, but if the continue-callback stops propagation, this + * is still considered a 'successful' operation and returns true. + * + * Lastly, if there are 5 event handlers for an event. The continueCallback + * will be called at most 4 times. + * + * @param string $eventName + * @param array $arguments + * @param callback $continueCallBack + * @return bool + */ + function emit($eventName, array $arguments = [], callable $continueCallBack = null); + + /** + * Returns the list of listeners for an event. + * + * The list is returned as an array, and the list of events are sorted by + * their priority. + * + * @param string $eventName + * @return callable[] + */ + function listeners($eventName); + + /** + * Removes a specific listener from an event. + * + * If the listener could not be found, this method will return false. If it + * was removed it will return true. + * + * @param string $eventName + * @param callable $listener + * @return bool + */ + function removeListener($eventName, callable $listener); + + /** + * Removes all listeners. + * + * If the eventName argument is specified, all listeners for that event are + * removed. If it is not specified, every listener for every event is + * removed. + * + * @param string $eventName + * @return void + */ + function removeAllListeners($eventName = null); + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/EventEmitterTrait.php b/htdocs/includes/sabre/sabre/event/lib/EventEmitterTrait.php new file mode 100644 index 00000000000..257629faee2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/EventEmitterTrait.php @@ -0,0 +1,211 @@ +<?php + +namespace Sabre\Event; + +/** + * Event Emitter Trait + * + * This trait contains all the basic functions to implement an + * EventEmitterInterface. + * + * Using the trait + interface allows you to add EventEmitter capabilities + * without having to change your base-class. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +trait EventEmitterTrait { + + /** + * The list of listeners + * + * @var array + */ + protected $listeners = []; + + /** + * Subscribe to an event. + * + * @param string $eventName + * @param callable $callBack + * @param int $priority + * @return void + */ + function on($eventName, callable $callBack, $priority = 100) { + + if (!isset($this->listeners[$eventName])) { + $this->listeners[$eventName] = [ + true, // If there's only one item, it's sorted + [$priority], + [$callBack] + ]; + } else { + $this->listeners[$eventName][0] = false; // marked as unsorted + $this->listeners[$eventName][1][] = $priority; + $this->listeners[$eventName][2][] = $callBack; + } + + } + + /** + * Subscribe to an event exactly once. + * + * @param string $eventName + * @param callable $callBack + * @param int $priority + * @return void + */ + function once($eventName, callable $callBack, $priority = 100) { + + $wrapper = null; + $wrapper = function() use ($eventName, $callBack, &$wrapper) { + + $this->removeListener($eventName, $wrapper); + return call_user_func_array($callBack, func_get_args()); + + }; + + $this->on($eventName, $wrapper, $priority); + + } + + /** + * Emits an event. + * + * This method will return true if 0 or more listeners were succesfully + * handled. false is returned if one of the events broke the event chain. + * + * If the continueCallBack is specified, this callback will be called every + * time before the next event handler is called. + * + * If the continueCallback returns false, event propagation stops. This + * allows you to use the eventEmitter as a means for listeners to implement + * functionality in your application, and break the event loop as soon as + * some condition is fulfilled. + * + * Note that returning false from an event subscriber breaks propagation + * and returns false, but if the continue-callback stops propagation, this + * is still considered a 'successful' operation and returns true. + * + * Lastly, if there are 5 event handlers for an event. The continueCallback + * will be called at most 4 times. + * + * @param string $eventName + * @param array $arguments + * @param callback $continueCallBack + * @return bool + */ + function emit($eventName, array $arguments = [], callable $continueCallBack = null) { + + if (is_null($continueCallBack)) { + + foreach ($this->listeners($eventName) as $listener) { + + $result = call_user_func_array($listener, $arguments); + if ($result === false) { + return false; + } + } + + } else { + + $listeners = $this->listeners($eventName); + $counter = count($listeners); + + foreach ($listeners as $listener) { + + $counter--; + $result = call_user_func_array($listener, $arguments); + if ($result === false) { + return false; + } + + if ($counter > 0) { + if (!$continueCallBack()) break; + } + + } + + } + + return true; + + } + + /** + * Returns the list of listeners for an event. + * + * The list is returned as an array, and the list of events are sorted by + * their priority. + * + * @param string $eventName + * @return callable[] + */ + function listeners($eventName) { + + if (!isset($this->listeners[$eventName])) { + return []; + } + + // The list is not sorted + if (!$this->listeners[$eventName][0]) { + + // Sorting + array_multisort($this->listeners[$eventName][1], SORT_NUMERIC, $this->listeners[$eventName][2]); + + // Marking the listeners as sorted + $this->listeners[$eventName][0] = true; + } + + return $this->listeners[$eventName][2]; + + } + + /** + * Removes a specific listener from an event. + * + * If the listener could not be found, this method will return false. If it + * was removed it will return true. + * + * @param string $eventName + * @param callable $listener + * @return bool + */ + function removeListener($eventName, callable $listener) { + + if (!isset($this->listeners[$eventName])) { + return false; + } + foreach ($this->listeners[$eventName][2] as $index => $check) { + if ($check === $listener) { + unset($this->listeners[$eventName][1][$index]); + unset($this->listeners[$eventName][2][$index]); + return true; + } + } + return false; + + } + + /** + * Removes all listeners. + * + * If the eventName argument is specified, all listeners for that event are + * removed. If it is not specified, every listener for every event is + * removed. + * + * @param string $eventName + * @return void + */ + function removeAllListeners($eventName = null) { + + if (!is_null($eventName)) { + unset($this->listeners[$eventName]); + } else { + $this->listeners = []; + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/Loop/Loop.php b/htdocs/includes/sabre/sabre/event/lib/Loop/Loop.php new file mode 100644 index 00000000000..86ee7c8b084 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/Loop/Loop.php @@ -0,0 +1,386 @@ +<?php + +namespace Sabre\Event\Loop; + +/** + * A simple eventloop implementation. + * + * This eventloop supports: + * * nextTick + * * setTimeout for delayed functions + * * setInterval for repeating functions + * * stream events using stream_select + * + * @copyright Copyright (C) 2007-2015 fruux GmbH. (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Loop { + + /** + * Executes a function after x seconds. + * + * @param callable $cb + * @param float $timeout timeout in seconds + * @return void + */ + function setTimeout(callable $cb, $timeout) { + + $triggerTime = microtime(true) + ($timeout); + + if (!$this->timers) { + // Special case when the timers array was empty. + $this->timers[] = [$triggerTime, $cb]; + return; + } + + // We need to insert these values in the timers array, but the timers + // array must be in reverse-order of trigger times. + // + // So here we search the array for the insertion point. + $index = count($this->timers) - 1; + while (true) { + if ($triggerTime < $this->timers[$index][0]) { + array_splice( + $this->timers, + $index + 1, + 0, + [[$triggerTime, $cb]] + ); + break; + } elseif ($index === 0) { + array_unshift($this->timers, [$triggerTime, $cb]); + break; + } + $index--; + + } + + } + + /** + * Executes a function every x seconds. + * + * The value this function returns can be used to stop the interval with + * clearInterval. + * + * @param callable $cb + * @param float $timeout + * @return array + */ + function setInterval(callable $cb, $timeout) { + + $keepGoing = true; + $f = null; + + $f = function() use ($cb, &$f, $timeout, &$keepGoing) { + if ($keepGoing) { + $cb(); + $this->setTimeout($f, $timeout); + } + }; + $this->setTimeout($f, $timeout); + + // Really the only thing that matters is returning the $keepGoing + // boolean value. + // + // We need to pack it in an array to allow returning by reference. + // Because I'm worried people will be confused by using a boolean as a + // sort of identifier, I added an extra string. + return ['I\'m an implementation detail', &$keepGoing]; + + } + + /** + * Stops a running internval. + * + * @param array $intervalId + * @return void + */ + function clearInterval($intervalId) { + + $intervalId[1] = false; + + } + + /** + * Runs a function immediately at the next iteration of the loop. + * + * @param callable $cb + * @return void + */ + function nextTick(callable $cb) { + + $this->nextTick[] = $cb; + + } + + + /** + * Adds a read stream. + * + * The callback will be called as soon as there is something to read from + * the stream. + * + * You MUST call removeReadStream after you are done with the stream, to + * prevent the eventloop from never stopping. + * + * @param resource $stream + * @param callable $cb + * @return void + */ + function addReadStream($stream, callable $cb) { + + $this->readStreams[(int)$stream] = $stream; + $this->readCallbacks[(int)$stream] = $cb; + + } + + /** + * Adds a write stream. + * + * The callback will be called as soon as the system reports it's ready to + * receive writes on the stream. + * + * You MUST call removeWriteStream after you are done with the stream, to + * prevent the eventloop from never stopping. + * + * @param resource $stream + * @param callable $cb + * @return void + */ + function addWriteStream($stream, callable $cb) { + + $this->writeStreams[(int)$stream] = $stream; + $this->writeCallbacks[(int)$stream] = $cb; + + } + + /** + * Stop watching a stream for reads. + * + * @param resource $stream + * @return void + */ + function removeReadStream($stream) { + + unset( + $this->readStreams[(int)$stream], + $this->readCallbacks[(int)$stream] + ); + + } + + /** + * Stop watching a stream for writes. + * + * @param resource $stream + * @return void + */ + function removeWriteStream($stream) { + + unset( + $this->writeStreams[(int)$stream], + $this->writeCallbacks[(int)$stream] + ); + + } + + + /** + * Runs the loop. + * + * This function will run continiously, until there's no more events to + * handle. + * + * @return void + */ + function run() { + + $this->running = true; + + do { + + $hasEvents = $this->tick(true); + + } while ($this->running && $hasEvents); + $this->running = false; + + } + + /** + * Executes all pending events. + * + * If $block is turned true, this function will block until any event is + * triggered. + * + * If there are now timeouts, nextTick callbacks or events in the loop at + * all, this function will exit immediately. + * + * This function will return true if there are _any_ events left in the + * loop after the tick. + * + * @param bool $block + * @return bool + */ + function tick($block = false) { + + $this->runNextTicks(); + $nextTimeout = $this->runTimers(); + + // Calculating how long runStreams should at most wait. + if (!$block) { + // Don't wait + $streamWait = 0; + } elseif ($this->nextTick) { + // There's a pending 'nextTick'. Don't wait. + $streamWait = 0; + } elseif (is_numeric($nextTimeout)) { + // Wait until the next Timeout should trigger. + $streamWait = $nextTimeout; + } else { + // Wait indefinitely + $streamWait = null; + } + + $this->runStreams($streamWait); + + return ($this->readStreams || $this->writeStreams || $this->nextTick || $this->timers); + + } + + /** + * Stops a running eventloop + * + * @return void + */ + function stop() { + + $this->running = false; + + } + + /** + * Executes all 'nextTick' callbacks. + * + * return void + */ + protected function runNextTicks() { + + $nextTick = $this->nextTick; + $this->nextTick = []; + + foreach ($nextTick as $cb) { + $cb(); + } + + } + + /** + * Runs all pending timers. + * + * After running the timer callbacks, this function returns the number of + * seconds until the next timer should be executed. + * + * If there's no more pending timers, this function returns null. + * + * @return float + */ + protected function runTimers() { + + $now = microtime(true); + while (($timer = array_pop($this->timers)) && $timer[0] < $now) { + $timer[1](); + } + // Add the last timer back to the array. + if ($timer) { + $this->timers[] = $timer; + return $timer[0] - microtime(true); + } + + } + + /** + * Runs all pending stream events. + * + * @param float $timeout + */ + protected function runStreams($timeout) { + + if ($this->readStreams || $this->writeStreams) { + + $read = $this->readStreams; + $write = $this->writeStreams; + $except = null; + if (stream_select($read, $write, $except, null, $timeout)) { + + // See PHP Bug https://bugs.php.net/bug.php?id=62452 + // Fixed in PHP7 + foreach ($read as $readStream) { + $readCb = $this->readCallbacks[(int)$readStream]; + $readCb(); + } + foreach ($write as $writeStream) { + $writeCb = $this->writeCallbacks[(int)$writeStream]; + $writeCb(); + } + + } + + } elseif ($this->running && ($this->nextTick || $this->timers)) { + usleep($timeout !== null ? $timeout * 1000000 : 200000); + } + + } + + /** + * Is the main loop active + * + * @var bool + */ + protected $running = false; + + /** + * A list of timers, added by setTimeout. + * + * @var array + */ + protected $timers = []; + + /** + * A list of 'nextTick' callbacks. + * + * @var callable[] + */ + protected $nextTick = []; + + /** + * List of readable streams for stream_select, indexed by stream id. + * + * @var resource[] + */ + protected $readStreams = []; + + /** + * List of writable streams for stream_select, indexed by stream id. + * + * @var resource[] + */ + protected $writeStreams = []; + + /** + * List of read callbacks, indexed by stream id. + * + * @var callback[] + */ + protected $readCallbacks = []; + + /** + * List of write callbacks, indexed by stream id. + * + * @var callback[] + */ + protected $writeCallbacks = []; + + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php b/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php new file mode 100644 index 00000000000..56c5bc8c74a --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php @@ -0,0 +1,183 @@ +<?php + +namespace Sabre\Event\Loop; + +/** + * Executes a function after x seconds. + * + * @param callable $cb + * @param float $timeout timeout in seconds + * @return void + */ +function setTimeout(callable $cb, $timeout) { + + instance()->setTimeout($cb, $timeout); + +} + +/** + * Executes a function every x seconds. + * + * The value this function returns can be used to stop the interval with + * clearInterval. + * + * @param callable $cb + * @param float $timeout + * @return array + */ +function setInterval(callable $cb, $timeout) { + + return instance()->setInterval($cb, $timeout); + +} + +/** + * Stops a running internval. + * + * @param array $intervalId + * @return void + */ +function clearInterval($intervalId) { + + instance()->clearInterval($intervalId); + +} + +/** + * Runs a function immediately at the next iteration of the loop. + * + * @param callable $cb + * @return void + */ +function nextTick(callable $cb) { + + instance()->nextTick($cb); + +} + + +/** + * Adds a read stream. + * + * The callback will be called as soon as there is something to read from + * the stream. + * + * You MUST call removeReadStream after you are done with the stream, to + * prevent the eventloop from never stopping. + * + * @param resource $stream + * @param callable $cb + * @return void + */ +function addReadStream($stream, callable $cb) { + + instance()->addReadStream($stream, $cb); + +} + +/** + * Adds a write stream. + * + * The callback will be called as soon as the system reports it's ready to + * receive writes on the stream. + * + * You MUST call removeWriteStream after you are done with the stream, to + * prevent the eventloop from never stopping. + * + * @param resource $stream + * @param callable $cb + * @return void + */ +function addWriteStream($stream, callable $cb) { + + instance()->addWriteStream($stream, $cb); + +} + +/** + * Stop watching a stream for reads. + * + * @param resource $stream + * @return void + */ +function removeReadStream($stream) { + + instance()->removeReadStream($stream); + +} + +/** + * Stop watching a stream for writes. + * + * @param resource $stream + * @return void + */ +function removeWriteStream($stream) { + + instance()->removeWriteStream($stream); + +} + + +/** + * Runs the loop. + * + * This function will run continiously, until there's no more events to + * handle. + * + * @return void + */ +function run() { + + instance()->run(); + +} + +/** + * Executes all pending events. + * + * If $block is turned true, this function will block until any event is + * triggered. + * + * If there are now timeouts, nextTick callbacks or events in the loop at + * all, this function will exit immediately. + * + * This function will return true if there are _any_ events left in the + * loop after the tick. + * + * @param bool $block + * @return bool + */ +function tick($block = false) { + + return instance()->tick($block); + +} + +/** + * Stops a running eventloop + * + * @return void + */ +function stop() { + + instance()->stop(); + +} + +/** + * Retrieves or sets the global Loop object. + * + * @param Loop $newLoop + */ +function instance(Loop $newLoop = null) { + + static $loop; + if ($newLoop) { + $loop = $newLoop; + } elseif (!$loop) { + $loop = new Loop(); + } + return $loop; + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/Promise.php b/htdocs/includes/sabre/sabre/event/lib/Promise.php new file mode 100644 index 00000000000..1c874c1bda3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/Promise.php @@ -0,0 +1,320 @@ +<?php + +namespace Sabre\Event; + +use Exception; + +/** + * An implementation of the Promise pattern. + * + * A promise represents the result of an asynchronous operation. + * At any given point a promise can be in one of three states: + * + * 1. Pending (the promise does not have a result yet). + * 2. Fulfilled (the asynchronous operation has completed with a result). + * 3. Rejected (the asynchronous operation has completed with an error). + * + * To get a callback when the operation has finished, use the `then` method. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Promise { + + /** + * The asynchronous operation is pending. + */ + const PENDING = 0; + + /** + * The asynchronous operation has completed, and has a result. + */ + const FULFILLED = 1; + + /** + * The asynchronous operation has completed with an error. + */ + const REJECTED = 2; + + /** + * The current state of this promise. + * + * @var int + */ + public $state = self::PENDING; + + /** + * Creates the promise. + * + * The passed argument is the executor. The executor is automatically + * called with two arguments. + * + * Each are callbacks that map to $this->fulfill and $this->reject. + * Using the executor is optional. + * + * @param callable $executor + */ + function __construct(callable $executor = null) { + + if ($executor) { + $executor( + [$this, 'fulfill'], + [$this, 'reject'] + ); + } + + } + + /** + * This method allows you to specify the callback that will be called after + * the promise has been fulfilled or rejected. + * + * Both arguments are optional. + * + * This method returns a new promise, which can be used for chaining. + * If either the onFulfilled or onRejected callback is called, you may + * return a result from this callback. + * + * If the result of this callback is yet another promise, the result of + * _that_ promise will be used to set the result of the returned promise. + * + * If either of the callbacks return any other value, the returned promise + * is automatically fulfilled with that value. + * + * If either of the callbacks throw an exception, the returned promise will + * be rejected and the exception will be passed back. + * + * @param callable $onFulfilled + * @param callable $onRejected + * @return Promise + */ + function then(callable $onFulfilled = null, callable $onRejected = null) { + + // This new subPromise will be returned from this function, and will + // be fulfilled with the result of the onFulfilled or onRejected event + // handlers. + $subPromise = new self(); + + switch ($this->state) { + case self::PENDING : + // The operation is pending, so we keep a reference to the + // event handlers so we can call them later. + $this->subscribers[] = [$subPromise, $onFulfilled, $onRejected]; + break; + case self::FULFILLED : + // The async operation is already fulfilled, so we trigger the + // onFulfilled callback asap. + $this->invokeCallback($subPromise, $onFulfilled); + break; + case self::REJECTED : + // The async operation failed, so we call teh onRejected + // callback asap. + $this->invokeCallback($subPromise, $onRejected); + break; + } + return $subPromise; + + } + + /** + * Add a callback for when this promise is rejected. + * + * Its usage is identical to then(). However, the otherwise() function is + * preferred. + * + * @param callable $onRejected + * @return Promise + */ + function otherwise(callable $onRejected) { + + return $this->then(null, $onRejected); + + } + + /** + * Marks this promise as fulfilled and sets its return value. + * + * @param mixed $value + * @return void + */ + function fulfill($value = null) { + if ($this->state !== self::PENDING) { + throw new PromiseAlreadyResolvedException('This promise is already resolved, and you\'re not allowed to resolve a promise more than once'); + } + $this->state = self::FULFILLED; + $this->value = $value; + foreach ($this->subscribers as $subscriber) { + $this->invokeCallback($subscriber[0], $subscriber[1]); + } + } + + /** + * Marks this promise as rejected, and set it's rejection reason. + * + * While it's possible to use any PHP value as the reason, it's highly + * recommended to use an Exception for this. + * + * @param mixed $reason + * @return void + */ + function reject($reason = null) { + if ($this->state !== self::PENDING) { + throw new PromiseAlreadyResolvedException('This promise is already resolved, and you\'re not allowed to resolve a promise more than once'); + } + $this->state = self::REJECTED; + $this->value = $reason; + foreach ($this->subscribers as $subscriber) { + $this->invokeCallback($subscriber[0], $subscriber[2]); + } + + } + + /** + * Stops execution until this promise is resolved. + * + * This method stops exection completely. If the promise is successful with + * a value, this method will return this value. If the promise was + * rejected, this method will throw an exception. + * + * This effectively turns the asynchronous operation into a synchronous + * one. In PHP it might be useful to call this on the last promise in a + * chain. + * + * @throws Exception + * @return mixed + */ + function wait() { + + $hasEvents = true; + while ($this->state === self::PENDING) { + + if (!$hasEvents) { + throw new \LogicException('There were no more events in the loop. This promise will never be fulfilled.'); + } + + // As long as the promise is not fulfilled, we tell the event loop + // to handle events, and to block. + $hasEvents = Loop\tick(true); + + } + + if ($this->state === self::FULFILLED) { + // If the state of this promise is fulfilled, we can return the value. + return $this->value; + } else { + // If we got here, it means that the asynchronous operation + // errored. Therefore we need to throw an exception. + $reason = $this->value; + if ($reason instanceof Exception) { + throw $reason; + } elseif (is_scalar($reason)) { + throw new Exception($reason); + } else { + $type = is_object($reason) ? get_class($reason) : gettype($reason); + throw new Exception('Promise was rejected with reason of type: ' . $type); + } + } + + + } + + + /** + * A list of subscribers. Subscribers are the callbacks that want us to let + * them know if the callback was fulfilled or rejected. + * + * @var array + */ + protected $subscribers = []; + + /** + * The result of the promise. + * + * If the promise was fulfilled, this will be the result value. If the + * promise was rejected, this property hold the rejection reason. + * + * @var mixed + */ + protected $value = null; + + /** + * This method is used to call either an onFulfilled or onRejected callback. + * + * This method makes sure that the result of these callbacks are handled + * correctly, and any chained promises are also correctly fulfilled or + * rejected. + * + * @param Promise $subPromise + * @param callable $callBack + * @return void + */ + private function invokeCallback(Promise $subPromise, callable $callBack = null) { + + // We use 'nextTick' to ensure that the event handlers are always + // triggered outside of the calling stack in which they were originally + // passed to 'then'. + // + // This makes the order of execution more predictable. + Loop\nextTick(function() use ($callBack, $subPromise) { + if (is_callable($callBack)) { + try { + + $result = $callBack($this->value); + if ($result instanceof self) { + // If the callback (onRejected or onFulfilled) + // returned a promise, we only fulfill or reject the + // chained promise once that promise has also been + // resolved. + $result->then([$subPromise, 'fulfill'], [$subPromise, 'reject']); + } else { + // If the callback returned any other value, we + // immediately fulfill the chained promise. + $subPromise->fulfill($result); + } + } catch (Exception $e) { + // If the event handler threw an exception, we need to make sure that + // the chained promise is rejected as well. + $subPromise->reject($e); + } + } else { + if ($this->state === self::FULFILLED) { + $subPromise->fulfill($this->value); + } else { + $subPromise->reject($this->value); + } + } + }); + } + + /** + * Alias for 'otherwise'. + * + * This function is now deprecated and will be removed in a future version. + * + * @param callable $onRejected + * @deprecated + * @return Promise + */ + function error(callable $onRejected) { + + return $this->otherwise($onRejected); + + } + + /** + * Deprecated. + * + * Please use Sabre\Event\Promise::all + * + * @param Promise[] $promises + * @deprecated + * @return Promise + */ + static function all(array $promises) { + + return Promise\all($promises); + + } + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php b/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php new file mode 100644 index 00000000000..3604b8aaa95 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php @@ -0,0 +1,135 @@ +<?php + +namespace Sabre\Event\Promise; + +use Sabre\Event\Promise; + +/** + * This file contains a set of functions that are useful for dealing with the + * Promise object. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ + + +/** + * This function takes an array of Promises, and returns a Promise that + * resolves when all of the given arguments have resolved. + * + * The returned Promise will resolve with a value that's an array of all the + * values the given promises have been resolved with. + * + * This array will be in the exact same order as the array of input promises. + * + * If any of the given Promises fails, the returned promise will immidiately + * fail with the first Promise that fails, and its reason. + * + * @param Promise[] $promises + * @return Promise + */ +function all(array $promises) { + + return new Promise(function($success, $fail) use ($promises) { + + $successCount = 0; + $completeResult = []; + + foreach ($promises as $promiseIndex => $subPromise) { + + $subPromise->then( + function($result) use ($promiseIndex, &$completeResult, &$successCount, $success, $promises) { + $completeResult[$promiseIndex] = $result; + $successCount++; + if ($successCount === count($promises)) { + $success($completeResult); + } + return $result; + } + )->error( + function($reason) use ($fail) { + $fail($reason); + } + ); + + } + }); + +} + +/** + * The race function returns a promise that resolves or rejects as soon as + * one of the promises in the argument resolves or rejects. + * + * The returned promise will resolve or reject with the value or reason of + * that first promise. + * + * @param Promise[] $promises + * @return Promise + */ +function race(array $promises) { + + return new Promise(function($success, $fail) use ($promises) { + + $alreadyDone = false; + foreach ($promises as $promise) { + + $promise->then( + function($result) use ($success, &$alreadyDone) { + if ($alreadyDone) { + return; + } + $alreadyDone = true; + $success($result); + }, + function($reason) use ($fail, &$alreadyDone) { + if ($alreadyDone) { + return; + } + $alreadyDone = true; + $fail($reason); + } + ); + + } + + }); + +} + + +/** + * Returns a Promise that resolves with the given value. + * + * If the value is a promise, the returned promise will attach itself to that + * promise and eventually get the same state as the followed promise. + * + * @param mixed $value + * @return Promise + */ +function resolve($value) { + + if ($value instanceof Promise) { + return $value->then(); + } else { + $promise = new Promise(); + $promise->fulfill($value); + return $promise; + } + +} + +/** + * Returns a Promise that will reject with the given reason. + * + * @param mixed $reason + * @return Promise + */ +function reject($reason) { + + $promise = new Promise(); + $promise->reject($reason); + return $promise; + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php b/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php new file mode 100644 index 00000000000..86a6c5b3f1a --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php @@ -0,0 +1,15 @@ +<?php + +namespace Sabre\Event; + +/** + * This exception is thrown when the user tried to reject or fulfill a promise, + * after either of these actions were already performed. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class PromiseAlreadyResolvedException extends \LogicException { + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/Version.php b/htdocs/includes/sabre/sabre/event/lib/Version.php new file mode 100644 index 00000000000..5de22193ff5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/Version.php @@ -0,0 +1,19 @@ +<?php + +namespace Sabre\Event; + +/** + * This class contains the version number for this package. + * + * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Version { + + /** + * Full version number + */ + const VERSION = '3.0.0'; + +} diff --git a/htdocs/includes/sabre/sabre/event/lib/coroutine.php b/htdocs/includes/sabre/sabre/event/lib/coroutine.php new file mode 100644 index 00000000000..19c0ba8a714 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/coroutine.php @@ -0,0 +1,120 @@ +<?php + +namespace Sabre\Event; + +use Generator; +use Exception; + +/** + * Turn asynchronous promise-based code into something that looks synchronous + * again, through the use of generators. + * + * Example without coroutines: + * + * $promise = $httpClient->request('GET', '/foo'); + * $promise->then(function($value) { + * + * return $httpClient->request('DELETE','/foo'); + * + * })->then(function($value) { + * + * return $httpClient->request('PUT', '/foo'); + * + * })->error(function($reason) { + * + * echo "Failed because: $reason\n"; + * + * }); + * + * Example with coroutines: + * + * coroutine(function() { + * + * try { + * yield $httpClient->request('GET', '/foo'); + * yield $httpClient->request('DELETE', /foo'); + * yield $httpClient->request('PUT', '/foo'); + * } catch(\Exception $reason) { + * echo "Failed because: $reason\n"; + * } + * + * }); + * + * @copyright Copyright (C) 2013-2015 fruux GmbH. All rights reserved. + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +function coroutine(callable $gen) { + + $generator = $gen(); + if (!$generator instanceof Generator) { + throw new \InvalidArgumentException('You must pass a generator function'); + } + + // This is the value we're returning. + $promise = new Promise(); + + $lastYieldResult = null; + + /** + * So tempted to use the mythical y-combinator here, but it's not needed in + * PHP. + */ + $advanceGenerator = function() use (&$advanceGenerator, $generator, $promise, &$lastYieldResult) { + + while ($generator->valid()) { + + $yieldedValue = $generator->current(); + if ($yieldedValue instanceof Promise) { + $yieldedValue->then( + function($value) use ($generator, &$advanceGenerator, &$lastYieldResult) { + $lastYieldResult = $value; + $generator->send($value); + $advanceGenerator(); + }, + function($reason) use ($generator, $advanceGenerator) { + if ($reason instanceof Exception) { + $generator->throw($reason); + } elseif (is_scalar($reason)) { + $generator->throw(new Exception($reason)); + } else { + $type = is_object($reason) ? get_class($reason) : gettype($reason); + $generator->throw(new Exception('Promise was rejected with reason of type: ' . $type)); + } + $advanceGenerator(); + } + )->error(function($reason) use ($promise) { + // This error handler would be called, if something in the + // generator throws an exception, and it's not caught + // locally. + $promise->reject($reason); + }); + // We need to break out of the loop, because $advanceGenerator + // will be called asynchronously when the promise has a result. + break; + } else { + // If the value was not a promise, we'll just let it pass through. + $lastYieldResult = $yieldedValue; + $generator->send($yieldedValue); + } + + } + + // If the generator is at the end, and we didn't run into an exception, + // we can fullfill the promise with the last thing that was yielded to + // us. + if (!$generator->valid() && $promise->state === Promise::PENDING) { + $promise->fulfill($lastYieldResult); + } + + }; + + try { + $advanceGenerator(); + } catch (Exception $e) { + $promise->reject($e); + } + + return $promise; + +} diff --git a/htdocs/includes/sabre/sabre/event/phpunit.xml.dist b/htdocs/includes/sabre/sabre/event/phpunit.xml.dist new file mode 100644 index 00000000000..ccd59be9c2a --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/phpunit.xml.dist @@ -0,0 +1,18 @@ +<phpunit + colors="true" + bootstrap="vendor/autoload.php" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + strict="true" + > + <testsuite name="sabre-event"> + <directory>tests/</directory> + </testsuite> + + <filter> + <whitelist addUncoveredFilesFromWhitelist="true"> + <directory suffix=".php">./lib/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/htdocs/includes/sabre/sabre/event/tests/ContinueCallbackTest.php b/htdocs/includes/sabre/sabre/event/tests/ContinueCallbackTest.php new file mode 100644 index 00000000000..c469913795f --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/ContinueCallbackTest.php @@ -0,0 +1,76 @@ +<?php + +namespace Sabre\Event; + +class ContinueCallbackTest extends \PHPUnit_Framework_TestCase { + + function testContinueCallBack() { + + $ee = new EventEmitter(); + + $handlerCounter = 0; + $bla = function() use (&$handlerCounter) { + $handlerCounter++; + }; + $ee->on('foo', $bla); + $ee->on('foo', $bla); + $ee->on('foo', $bla); + + $continueCounter = 0; + $r = $ee->emit('foo', [], function() use (&$continueCounter) { + $continueCounter++; + return true; + }); + $this->assertTrue($r); + $this->assertEquals(3, $handlerCounter); + $this->assertEquals(2, $continueCounter); + + } + + function testContinueCallBackBreak() { + + $ee = new EventEmitter(); + + $handlerCounter = 0; + $bla = function() use (&$handlerCounter) { + $handlerCounter++; + }; + $ee->on('foo', $bla); + $ee->on('foo', $bla); + $ee->on('foo', $bla); + + $continueCounter = 0; + $r = $ee->emit('foo', [], function() use (&$continueCounter) { + $continueCounter++; + return false; + }); + $this->assertTrue($r); + $this->assertEquals(1, $handlerCounter); + $this->assertEquals(1, $continueCounter); + + } + + function testContinueCallBackBreakByHandler() { + + $ee = new EventEmitter(); + + $handlerCounter = 0; + $bla = function() use (&$handlerCounter) { + $handlerCounter++; + return false; + }; + $ee->on('foo', $bla); + $ee->on('foo', $bla); + $ee->on('foo', $bla); + + $continueCounter = 0; + $r = $ee->emit('foo', [], function() use (&$continueCounter) { + $continueCounter++; + return false; + }); + $this->assertFalse($r); + $this->assertEquals(1, $handlerCounter); + $this->assertEquals(0, $continueCounter); + + } +} diff --git a/htdocs/includes/sabre/sabre/event/tests/CoroutineTest.php b/htdocs/includes/sabre/sabre/event/tests/CoroutineTest.php new file mode 100644 index 00000000000..6e4b666b042 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/CoroutineTest.php @@ -0,0 +1,262 @@ +<?php + +namespace Sabre\Event; + +class CoroutineTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \InvalidArgumentException + */ + function testNonGenerator() { + + coroutine(function() {}); + + } + + function testBasicCoroutine() { + + $start = 0; + + coroutine(function() use (&$start) { + + $start += 1; + yield; + + }); + + $this->assertEquals(1, $start); + + } + + function testFulfilledPromise() { + + $start = 0; + $promise = new Promise(function($fulfill, $reject) { + $fulfill(2); + }); + + coroutine(function() use (&$start, $promise) { + + $start += 1; + $start += (yield $promise); + + }); + + Loop\run(); + $this->assertEquals(3, $start); + + } + + function testRejectedPromise() { + + $start = 0; + $promise = new Promise(function($fulfill, $reject) { + $reject(2); + }); + + coroutine(function() use (&$start, $promise) { + + $start += 1; + try { + $start += (yield $promise); + // This line is unreachable, but it's our control + $start += 4; + } catch (\Exception $e) { + $start += $e->getMessage(); + } + + }); + + Loop\run(); + $this->assertEquals(3, $start); + + } + + function testRejectedPromiseException() { + + $start = 0; + $promise = new Promise(function($fulfill, $reject) { + $reject(new \LogicException('2')); + }); + + coroutine(function() use (&$start, $promise) { + + $start += 1; + try { + $start += (yield $promise); + // This line is unreachable, but it's our control + $start += 4; + } catch (\LogicException $e) { + $start += $e->getMessage(); + } + + }); + + Loop\run(); + $this->assertEquals(3, $start); + + } + + function testRejectedPromiseArray() { + + $start = 0; + $promise = new Promise(function($fulfill, $reject) { + $reject([]); + }); + + coroutine(function() use (&$start, $promise) { + + $start += 1; + try { + $start += (yield $promise); + // This line is unreachable, but it's our control + $start += 4; + } catch (\Exception $e) { + $this->assertTrue(strpos($e->getMessage(), 'Promise was rejected with') === 0); + $start += 2; + } + + })->wait(); + + $this->assertEquals(3, $start); + + } + + function testFulfilledPromiseAsync() { + + $start = 0; + $promise = new Promise(); + coroutine(function() use (&$start, $promise) { + + $start += 1; + $start += (yield $promise); + + }); + Loop\run(); + + $this->assertEquals(1, $start); + + $promise->fulfill(2); + Loop\run(); + + $this->assertEquals(3, $start); + + } + + function testRejectedPromiseAsync() { + + $start = 0; + $promise = new Promise(); + coroutine(function() use (&$start, $promise) { + + $start += 1; + try { + $start += (yield $promise); + // This line is unreachable, but it's our control + $start += 4; + } catch (\Exception $e) { + $start += $e->getMessage(); + } + + }); + + $this->assertEquals(1, $start); + + $promise->reject(new \Exception(2)); + Loop\run(); + + $this->assertEquals(3, $start); + + } + + function testCoroutineException() { + + $start = 0; + coroutine(function() use (&$start) { + + $start += 1; + $start += (yield 2); + + throw new \Exception('4'); + + })->error(function($e) use (&$start) { + + $start += $e->getMessage(); + + }); + Loop\run(); + + $this->assertEquals(7, $start); + + } + + function testDeepException() { + + $start = 0; + $promise = new Promise(); + coroutine(function() use (&$start, $promise) { + + $start += 1; + $start += (yield $promise); + + })->error(function($e) use (&$start) { + + $start += $e->getMessage(); + + }); + + $this->assertEquals(1, $start); + + $promise->reject(new \Exception(2)); + Loop\run(); + + $this->assertEquals(3, $start); + + } + + function testResolveToLastYield() { + + $ok = false; + coroutine(function() { + + yield 1; + yield 2; + $hello = 'hi'; + + })->then(function($value) use (&$ok) { + $this->assertEquals(2, $value); + $ok = true; + })->error(function($reason) { + $this->fail($reason); + }); + Loop\run(); + + $this->assertTrue($ok); + + } + + function testResolveToLastYieldPromise() { + + $ok = false; + + $promise = new Promise(); + + coroutine(function() use ($promise) { + + yield 'fail'; + yield $promise; + $hello = 'hi'; + + })->then(function($value) use (&$ok) { + $ok = $value; + $this->fail($reason); + }); + + $promise->fulfill('omg it worked'); + Loop\run(); + + $this->assertEquals('omg it worked', $ok); + + } + +} diff --git a/htdocs/includes/sabre/sabre/event/tests/EventEmitterTest.php b/htdocs/includes/sabre/sabre/event/tests/EventEmitterTest.php new file mode 100644 index 00000000000..df08e9cd84a --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/EventEmitterTest.php @@ -0,0 +1,318 @@ +<?php + +namespace Sabre\Event; + +class EventEmitterTest extends \PHPUnit_Framework_TestCase { + + function testInit() { + + $ee = new EventEmitter(); + $this->assertInstanceOf('Sabre\\Event\\EventEmitter', $ee); + + } + + function testListeners() { + + $ee = new EventEmitter(); + + $callback1 = function() { }; + $callback2 = function() { }; + $ee->on('foo', $callback1, 200); + $ee->on('foo', $callback2, 100); + + $this->assertEquals([$callback2, $callback1], $ee->listeners('foo')); + + } + + /** + * @depends testInit + */ + function testHandleEvent() { + + $argResult = null; + + $ee = new EventEmitter(); + $ee->on('foo', function($arg) use (&$argResult) { + + $argResult = $arg; + + }); + + $this->assertTrue( + $ee->emit('foo', ['bar']) + ); + + $this->assertEquals('bar', $argResult); + + } + + /** + * @depends testHandleEvent + */ + function testCancelEvent() { + + $argResult = 0; + + $ee = new EventEmitter(); + $ee->on('foo', function($arg) use (&$argResult) { + + $argResult = 1; + return false; + + }); + $ee->on('foo', function($arg) use (&$argResult) { + + $argResult = 2; + + }); + + $this->assertFalse( + $ee->emit('foo', ['bar']) + ); + + $this->assertEquals(1, $argResult); + + } + + /** + * @depends testCancelEvent + */ + function testPriority() { + + $argResult = 0; + + $ee = new EventEmitter(); + $ee->on('foo', function($arg) use (&$argResult) { + + $argResult = 1; + return false; + + }); + $ee->on('foo', function($arg) use (&$argResult) { + + $argResult = 2; + return false; + + }, 1); + + $this->assertFalse( + $ee->emit('foo', ['bar']) + ); + + $this->assertEquals(2, $argResult); + + } + + /** + * @depends testPriority + */ + function testPriority2() { + + $result = []; + $ee = new EventEmitter(); + + $ee->on('foo', function() use (&$result) { + + $result[] = 'a'; + + }, 200); + $ee->on('foo', function() use (&$result) { + + $result[] = 'b'; + + }, 50); + $ee->on('foo', function() use (&$result) { + + $result[] = 'c'; + + }, 300); + $ee->on('foo', function() use (&$result) { + + $result[] = 'd'; + + }); + + $ee->emit('foo'); + $this->assertEquals(['b', 'd', 'a', 'c'], $result); + + } + + function testRemoveListener() { + + $result = false; + + $callBack = function() use (&$result) { + + $result = true; + + }; + + $ee = new EventEmitter(); + + $ee->on('foo', $callBack); + + $ee->emit('foo'); + $this->assertTrue($result); + $result = false; + + $this->assertTrue( + $ee->removeListener('foo', $callBack) + ); + + $ee->emit('foo'); + $this->assertFalse($result); + + } + + function testRemoveUnknownListener() { + + $result = false; + + $callBack = function() use (&$result) { + + $result = true; + + }; + + $ee = new EventEmitter(); + + $ee->on('foo', $callBack); + + $ee->emit('foo'); + $this->assertTrue($result); + $result = false; + + $this->assertFalse($ee->removeListener('bar', $callBack)); + + $ee->emit('foo'); + $this->assertTrue($result); + + } + + function testRemoveListenerTwice() { + + $result = false; + + $callBack = function() use (&$result) { + + $result = true; + + }; + + $ee = new EventEmitter(); + + $ee->on('foo', $callBack); + + $ee->emit('foo'); + $this->assertTrue($result); + $result = false; + + $this->assertTrue( + $ee->removeListener('foo', $callBack) + ); + $this->assertFalse( + $ee->removeListener('foo', $callBack) + ); + + $ee->emit('foo'); + $this->assertFalse($result); + + } + + function testRemoveAllListeners() { + + $result = false; + $callBack = function() use (&$result) { + + $result = true; + + }; + + $ee = new EventEmitter(); + $ee->on('foo', $callBack); + + $ee->emit('foo'); + $this->assertTrue($result); + $result = false; + + $ee->removeAllListeners('foo'); + + $ee->emit('foo'); + $this->assertFalse($result); + + } + + function testRemoveAllListenersNoArg() { + + $result = false; + + $callBack = function() use (&$result) { + + $result = true; + + }; + + + $ee = new EventEmitter(); + $ee->on('foo', $callBack); + + $ee->emit('foo'); + $this->assertTrue($result); + $result = false; + + $ee->removeAllListeners(); + + $ee->emit('foo'); + $this->assertFalse($result); + + } + + function testOnce() { + + $result = 0; + + $callBack = function() use (&$result) { + + $result++; + + }; + + $ee = new EventEmitter(); + $ee->once('foo', $callBack); + + $ee->emit('foo'); + $ee->emit('foo'); + + $this->assertEquals(1, $result); + + } + + /** + * @depends testCancelEvent + */ + function testPriorityOnce() { + + $argResult = 0; + + $ee = new EventEmitter(); + $ee->once('foo', function($arg) use (&$argResult) { + + $argResult = 1; + return false; + + }); + $ee->once('foo', function($arg) use (&$argResult) { + + $argResult = 2; + return false; + + }, 1); + + $this->assertFalse( + $ee->emit('foo', ['bar']) + ); + + $this->assertEquals(2, $argResult); + + } +} diff --git a/htdocs/includes/sabre/sabre/event/tests/Loop/FunctionsTest.php b/htdocs/includes/sabre/sabre/event/tests/Loop/FunctionsTest.php new file mode 100644 index 00000000000..08bf306c37f --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/Loop/FunctionsTest.php @@ -0,0 +1,160 @@ +<?php + +namespace Sabre\Event\Loop; + +class FunctionsTest extends \PHPUnit_Framework_TestCase { + + function setUp() { + + // Always creating a fresh loop object. + instance(new Loop()); + + } + + function tearDown() { + + // Removing the global loop object. + instance(null); + + } + + function testNextTick() { + + $check = 0; + nextTick(function() use (&$check) { + + $check++; + + }); + + run(); + + $this->assertEquals(1, $check); + + } + + function testTimeout() { + + $check = 0; + setTimeout(function() use (&$check) { + + $check++; + + }, 0.02); + + run(); + + $this->assertEquals(1, $check); + + } + + function testTimeoutOrder() { + + $check = []; + setTimeout(function() use (&$check) { + + $check[] = 'a'; + + }, 0.2); + setTimeout(function() use (&$check) { + + $check[] = 'b'; + + }, 0.1); + setTimeout(function() use (&$check) { + + $check[] = 'c'; + + }, 0.3); + + run(); + + $this->assertEquals(['b', 'a', 'c'], $check); + + } + + function testSetInterval() { + + $check = 0; + $intervalId = null; + $intervalId = setInterval(function() use (&$check, &$intervalId) { + + $check++; + if ($check > 5) { + clearInterval($intervalId); + } + + }, 0.02); + + run(); + $this->assertEquals(6, $check); + + } + + function testAddWriteStream() { + + $h = fopen('php://temp', 'r+'); + addWriteStream($h, function() use ($h) { + + fwrite($h, 'hello world'); + removeWriteStream($h); + + }); + run(); + rewind($h); + $this->assertEquals('hello world', stream_get_contents($h)); + + } + + function testAddReadStream() { + + $h = fopen('php://temp', 'r+'); + fwrite($h, 'hello world'); + rewind($h); + + $result = null; + + addReadStream($h, function() use ($h, &$result) { + + $result = fgets($h); + removeReadStream($h); + + }); + run(); + $this->assertEquals('hello world', $result); + + } + + function testStop() { + + $check = 0; + setTimeout(function() use (&$check) { + $check++; + }, 200); + + nextTick(function() { + stop(); + }); + run(); + + $this->assertEquals(0, $check); + + } + + function testTick() { + + $check = 0; + setTimeout(function() use (&$check) { + $check++; + }, 1); + + nextTick(function() use (&$check) { + $check++; + }); + tick(); + + $this->assertEquals(1, $check); + + } + +} diff --git a/htdocs/includes/sabre/sabre/event/tests/Loop/LoopTest.php b/htdocs/includes/sabre/sabre/event/tests/Loop/LoopTest.php new file mode 100644 index 00000000000..a9cf551bddb --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/Loop/LoopTest.php @@ -0,0 +1,180 @@ +<?php + +namespace Sabre\Event\Loop; + +class LoopTest extends \PHPUnit_Framework_TestCase { + + function testNextTick() { + + $loop = new Loop(); + $check = 0; + $loop->nextTick(function() use (&$check) { + + $check++; + + }); + + $loop->run(); + + $this->assertEquals(1, $check); + + } + + function testTimeout() { + + $loop = new Loop(); + $check = 0; + $loop->setTimeout(function() use (&$check) { + + $check++; + + }, 0.02); + + $loop->run(); + + $this->assertEquals(1, $check); + + } + + function testTimeoutOrder() { + + $loop = new Loop(); + $check = []; + $loop->setTimeout(function() use (&$check) { + + $check[] = 'a'; + + }, 0.2); + $loop->setTimeout(function() use (&$check) { + + $check[] = 'b'; + + }, 0.1); + $loop->setTimeout(function() use (&$check) { + + $check[] = 'c'; + + }, 0.3); + + $loop->run(); + + $this->assertEquals(['b', 'a', 'c'], $check); + + } + + function testSetInterval() { + + $loop = new Loop(); + $check = 0; + $intervalId = null; + $intervalId = $loop->setInterval(function() use (&$check, &$intervalId, $loop) { + + $check++; + if ($check > 5) { + $loop->clearInterval($intervalId); + } + + }, 0.02); + + $loop->run(); + $this->assertEquals(6, $check); + + } + + function testAddWriteStream() { + + $h = fopen('php://temp', 'r+'); + $loop = new Loop(); + $loop->addWriteStream($h, function() use ($h, $loop) { + + fwrite($h, 'hello world'); + $loop->removeWriteStream($h); + + }); + $loop->run(); + rewind($h); + $this->assertEquals('hello world', stream_get_contents($h)); + + } + + function testAddReadStream() { + + $h = fopen('php://temp', 'r+'); + fwrite($h, 'hello world'); + rewind($h); + + $loop = new Loop(); + + $result = null; + + $loop->addReadStream($h, function() use ($h, $loop, &$result) { + + $result = fgets($h); + $loop->removeReadStream($h); + + }); + $loop->run(); + $this->assertEquals('hello world', $result); + + } + + function testStop() { + + $check = 0; + $loop = new Loop(); + $loop->setTimeout(function() use (&$check) { + $check++; + }, 200); + + $loop->nextTick(function() use ($loop) { + $loop->stop(); + }); + $loop->run(); + + $this->assertEquals(0, $check); + + } + + function testTick() { + + $check = 0; + $loop = new Loop(); + $loop->setTimeout(function() use (&$check) { + $check++; + }, 1); + + $loop->nextTick(function() use ($loop, &$check) { + $check++; + }); + $loop->tick(); + + $this->assertEquals(1, $check); + + } + + /** + * Here we add a new nextTick function as we're in the middle of a current + * nextTick. + */ + function testNextTickStacking() { + + $loop = new Loop(); + $check = 0; + $loop->nextTick(function() use (&$check, $loop) { + + $loop->nextTick(function() use (&$check) { + + $check++; + + }); + $check++; + + }); + + $loop->run(); + + $this->assertEquals(2, $check); + + } + +} diff --git a/htdocs/includes/sabre/sabre/event/tests/Promise/FunctionsTest.php b/htdocs/includes/sabre/sabre/event/tests/Promise/FunctionsTest.php new file mode 100644 index 00000000000..51e47ae297b --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/Promise/FunctionsTest.php @@ -0,0 +1,184 @@ +<?php + +namespace Sabre\Event\Promise; + +use Sabre\Event\Loop; +use Sabre\Event\Promise; + +class FunctionsTest extends \PHPUnit_Framework_TestCase { + + function testAll() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\all([$promise1, $promise2])->then(function($value) use (&$finalValue) { + + $finalValue = $value; + + }); + + $promise1->fulfill(1); + Loop\run(); + $this->assertEquals(0, $finalValue); + + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals([1, 2], $finalValue); + + } + + function testAllReject() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\all([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = 'foo'; + return 'test'; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->reject(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testAllRejectThenResolve() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\all([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = 'foo'; + return 'test'; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testRace() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\race([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = $value; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->fulfill(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testRaceReject() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\race([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = $value; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->reject(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testResolve() { + + $finalValue = 0; + + $promise = resolve(1); + $promise->then(function($value) use (&$finalValue) { + + $finalValue = $value; + + }); + + $this->assertEquals(0, $finalValue); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + /** + * @expectedException \Exception + */ + function testResolvePromise() { + + $finalValue = 0; + + $promise = new Promise(); + $promise->reject(new \Exception('uh oh')); + + $newPromise = resolve($promise); + $newPromise->wait(); + + } + + function testReject() { + + $finalValue = 0; + + $promise = reject(1); + $promise->then(function($value) use (&$finalValue) { + + $finalValue = 'im broken'; + + }, function($reason) use (&$finalValue) { + + $finalValue = $reason; + + }); + + $this->assertEquals(0, $finalValue); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/event/tests/Promise/PromiseTest.php b/htdocs/includes/sabre/sabre/event/tests/Promise/PromiseTest.php new file mode 100644 index 00000000000..69838397881 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/Promise/PromiseTest.php @@ -0,0 +1,341 @@ +<?php + +namespace Sabre\Event\Promise; + +use Sabre\Event\Loop; +use Sabre\Event\Promise; + +class PromiseTest extends \PHPUnit_Framework_TestCase { + + function testSuccess() { + + $finalValue = 0; + $promise = new Promise(); + $promise->fulfill(1); + + $promise->then(function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + Loop\run(); + + $this->assertEquals(3, $finalValue); + + } + + function testFail() { + + $finalValue = 0; + $promise = new Promise(); + $promise->reject(1); + + $promise->then(null, function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + Loop\run(); + + $this->assertEquals(3, $finalValue); + + } + + function testChain() { + + $finalValue = 0; + $promise = new Promise(); + $promise->fulfill(1); + + $promise->then(function($value) use (&$finalValue) { + $finalValue = $value + 2; + return $finalValue; + })->then(function($value) use (&$finalValue) { + $finalValue = $value + 4; + return $finalValue; + }); + Loop\run(); + + $this->assertEquals(7, $finalValue); + + } + function testChainPromise() { + + $finalValue = 0; + $promise = new Promise(); + $promise->fulfill(1); + + $subPromise = new Promise(); + + $promise->then(function($value) use ($subPromise) { + return $subPromise; + })->then(function($value) use (&$finalValue) { + $finalValue = $value + 4; + return $finalValue; + }); + + $subPromise->fulfill(2); + Loop\run(); + + $this->assertEquals(6, $finalValue); + + } + + function testPendingResult() { + + $finalValue = 0; + $promise = new Promise(); + + $promise->then(function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + + $promise->fulfill(4); + Loop\run(); + + $this->assertEquals(6, $finalValue); + + } + + function testPendingFail() { + + $finalValue = 0; + $promise = new Promise(); + + $promise->then(null, function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + + $promise->reject(4); + Loop\run(); + + $this->assertEquals(6, $finalValue); + + } + + function testExecutorSuccess() { + + $promise = (new Promise(function($success, $fail) { + + $success('hi'); + + }))->then(function($result) use (&$realResult) { + + $realResult = $result; + + }); + Loop\run(); + + $this->assertEquals('hi', $realResult); + + } + + function testExecutorFail() { + + $promise = (new Promise(function($success, $fail) { + + $fail('hi'); + + }))->then(function($result) use (&$realResult) { + + $realResult = 'incorrect'; + + }, function($reason) use (&$realResult) { + + $realResult = $reason; + + }); + Loop\run(); + + $this->assertEquals('hi', $realResult); + + } + + /** + * @expectedException \Sabre\Event\PromiseAlreadyResolvedException + */ + function testFulfillTwice() { + + $promise = new Promise(); + $promise->fulfill(1); + $promise->fulfill(1); + + } + + /** + * @expectedException \Sabre\Event\PromiseAlreadyResolvedException + */ + function testRejectTwice() { + + $promise = new Promise(); + $promise->reject(1); + $promise->reject(1); + + } + + function testFromFailureHandler() { + + $ok = 0; + $promise = new Promise(); + $promise->otherwise(function($reason) { + + $this->assertEquals('foo', $reason); + throw new \Exception('hi'); + + })->then(function() use (&$ok) { + + $ok = -1; + + }, function() use (&$ok) { + + $ok = 1; + + }); + + $this->assertEquals(0, $ok); + $promise->reject('foo'); + Loop\run(); + + $this->assertEquals(1, $ok); + + } + + function testAll() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise::all([$promise1, $promise2])->then(function($value) use (&$finalValue) { + + $finalValue = $value; + + }); + + $promise1->fulfill(1); + Loop\run(); + $this->assertEquals(0, $finalValue); + + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals([1, 2], $finalValue); + + } + + function testAllReject() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise::all([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = 'foo'; + return 'test'; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->reject(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testAllRejectThenResolve() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise::all([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = 'foo'; + return 'test'; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testWaitResolve() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->fulfill(1); + }); + $this->assertEquals( + 1, + $promise->wait() + ); + + } + + /** + * @expectedException \LogicException + */ + function testWaitWillNeverResolve() { + + $promise = new Promise(); + $promise->wait(); + + } + + function testWaitRejectedException() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->reject(new \OutOfBoundsException('foo')); + }); + try { + $promise->wait(); + $this->fail('We did not get the expected exception'); + } catch (\Exception $e) { + $this->assertInstanceOf('OutOfBoundsException', $e); + $this->assertEquals('foo', $e->getMessage()); + } + + } + + function testWaitRejectedScalar() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->reject('foo'); + }); + try { + $promise->wait(); + $this->fail('We did not get the expected exception'); + } catch (\Exception $e) { + $this->assertInstanceOf('Exception', $e); + $this->assertEquals('foo', $e->getMessage()); + } + + } + + function testWaitRejectedNonScalar() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->reject([]); + }); + try { + $promise->wait(); + $this->fail('We did not get the expected exception'); + } catch (\Exception $e) { + $this->assertInstanceOf('Exception', $e); + $this->assertEquals('Promise was rejected with reason of type: array', $e->getMessage()); + } + + } +} diff --git a/htdocs/includes/sabre/sabre/event/tests/PromiseTest.php b/htdocs/includes/sabre/sabre/event/tests/PromiseTest.php new file mode 100644 index 00000000000..0029d898e53 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/PromiseTest.php @@ -0,0 +1,386 @@ +<?php + +namespace Sabre\Event; + +class PromiseTest extends \PHPUnit_Framework_TestCase { + + function testSuccess() { + + $finalValue = 0; + $promise = new Promise(); + $promise->fulfill(1); + + $promise->then(function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + Loop\run(); + + $this->assertEquals(3, $finalValue); + + } + + function testFail() { + + $finalValue = 0; + $promise = new Promise(); + $promise->reject(1); + + $promise->then(null, function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + Loop\run(); + + $this->assertEquals(3, $finalValue); + + } + + function testChain() { + + $finalValue = 0; + $promise = new Promise(); + $promise->fulfill(1); + + $promise->then(function($value) use (&$finalValue) { + $finalValue = $value + 2; + return $finalValue; + })->then(function($value) use (&$finalValue) { + $finalValue = $value + 4; + return $finalValue; + }); + Loop\run(); + + $this->assertEquals(7, $finalValue); + + } + function testChainPromise() { + + $finalValue = 0; + $promise = new Promise(); + $promise->fulfill(1); + + $subPromise = new Promise(); + + $promise->then(function($value) use ($subPromise) { + return $subPromise; + })->then(function($value) use (&$finalValue) { + $finalValue = $value + 4; + return $finalValue; + }); + + $subPromise->fulfill(2); + Loop\run(); + + $this->assertEquals(6, $finalValue); + + } + + function testPendingResult() { + + $finalValue = 0; + $promise = new Promise(); + + $promise->then(function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + + $promise->fulfill(4); + Loop\run(); + + $this->assertEquals(6, $finalValue); + + } + + function testPendingFail() { + + $finalValue = 0; + $promise = new Promise(); + + $promise->then(null, function($value) use (&$finalValue) { + $finalValue = $value + 2; + }); + + $promise->reject(4); + Loop\run(); + + $this->assertEquals(6, $finalValue); + + } + + function testExecutorSuccess() { + + $promise = (new Promise(function($success, $fail) { + + $success('hi'); + + }))->then(function($result) use (&$realResult) { + + $realResult = $result; + + }); + Loop\run(); + + $this->assertEquals('hi', $realResult); + + } + + function testExecutorFail() { + + $promise = (new Promise(function($success, $fail) { + + $fail('hi'); + + }))->then(function($result) use (&$realResult) { + + $realResult = 'incorrect'; + + }, function($reason) use (&$realResult) { + + $realResult = $reason; + + }); + Loop\run(); + + $this->assertEquals('hi', $realResult); + + } + + /** + * @expectedException \Sabre\Event\PromiseAlreadyResolvedException + */ + function testFulfillTwice() { + + $promise = new Promise(); + $promise->fulfill(1); + $promise->fulfill(1); + + } + + /** + * @expectedException \Sabre\Event\PromiseAlreadyResolvedException + */ + function testRejectTwice() { + + $promise = new Promise(); + $promise->reject(1); + $promise->reject(1); + + } + + function testFromFailureHandler() { + + $ok = 0; + $promise = new Promise(); + $promise->otherwise(function($reason) { + + $this->assertEquals('foo', $reason); + throw new \Exception('hi'); + + })->then(function() use (&$ok) { + + $ok = -1; + + }, function() use (&$ok) { + + $ok = 1; + + }); + + $this->assertEquals(0, $ok); + $promise->reject('foo'); + Loop\run(); + + $this->assertEquals(1, $ok); + + } + + function testAll() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise::all([$promise1, $promise2])->then(function($value) use (&$finalValue) { + + $finalValue = $value; + + }); + + $promise1->fulfill(1); + Loop\run(); + $this->assertEquals(0, $finalValue); + + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals([1, 2], $finalValue); + + } + + function testAllReject() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise::all([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = 'foo'; + return 'test'; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->reject(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testAllRejectThenResolve() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise::all([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = 'foo'; + return 'test'; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testRace() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\race([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = $value; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->fulfill(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->fulfill(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testRaceReject() { + + $promise1 = new Promise(); + $promise2 = new Promise(); + + $finalValue = 0; + Promise\race([$promise1, $promise2])->then( + function($value) use (&$finalValue) { + $finalValue = $value; + }, + function($value) use (&$finalValue) { + $finalValue = $value; + } + ); + + $promise1->reject(1); + Loop\run(); + $this->assertEquals(1, $finalValue); + $promise2->reject(2); + Loop\run(); + $this->assertEquals(1, $finalValue); + + } + + function testWaitResolve() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->fulfill(1); + }); + $this->assertEquals( + 1, + $promise->wait() + ); + + } + + /** + * @expectedException \LogicException + */ + function testWaitWillNeverResolve() { + + $promise = new Promise(); + $promise->wait(); + + } + + function testWaitRejectedException() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->reject(new \OutOfBoundsException('foo')); + }); + try { + $promise->wait(); + $this->fail('We did not get the expected exception'); + } catch (\Exception $e) { + $this->assertInstanceOf('OutOfBoundsException', $e); + $this->assertEquals('foo', $e->getMessage()); + } + + } + + function testWaitRejectedScalar() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->reject('foo'); + }); + try { + $promise->wait(); + $this->fail('We did not get the expected exception'); + } catch (\Exception $e) { + $this->assertInstanceOf('Exception', $e); + $this->assertEquals('foo', $e->getMessage()); + } + + } + + function testWaitRejectedNonScalar() { + + $promise = new Promise(); + Loop\nextTick(function() use ($promise) { + $promise->reject([]); + }); + try { + $promise->wait(); + $this->fail('We did not get the expected exception'); + } catch (\Exception $e) { + $this->assertInstanceOf('Exception', $e); + $this->assertEquals('Promise was rejected with reason of type: array', $e->getMessage()); + } + + } +} diff --git a/htdocs/includes/sabre/sabre/event/tests/benchmark/bench.php b/htdocs/includes/sabre/sabre/event/tests/benchmark/bench.php new file mode 100644 index 00000000000..b1e6b1d4787 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/tests/benchmark/bench.php @@ -0,0 +1,116 @@ +<?php + +use Sabre\Event\EventEmitter; + +include __DIR__ . '/../../vendor/autoload.php'; + +abstract class BenchMark { + + protected $startTime; + protected $iterations = 10000; + protected $totalTime; + + function setUp() { + + } + + abstract function test(); + + function go() { + + $this->setUp(); + $this->startTime = microtime(true); + $this->test(); + $this->totalTime = microtime(true) - $this->startTime; + return $this->totalTime; + + } + +} + +class OneCallBack extends BenchMark { + + protected $emitter; + protected $iterations = 100000; + + function setUp() { + + $this->emitter = new EventEmitter(); + $this->emitter->on('foo', function() { + // NOOP + }); + + } + + function test() { + + for ($i = 0;$i < $this->iterations;$i++) { + $this->emitter->emit('foo', []); + } + + } + +} + +class ManyCallBacks extends BenchMark { + + protected $emitter; + + function setUp() { + + $this->emitter = new EventEmitter(); + for ($i = 0;$i < 100;$i++) { + $this->emitter->on('foo', function() { + // NOOP + }); + } + + } + + function test() { + + for ($i = 0;$i < $this->iterations;$i++) { + $this->emitter->emit('foo', []); + } + + } + +} + +class ManyPrioritizedCallBacks extends BenchMark { + + protected $emitter; + + function setUp() { + + $this->emitter = new EventEmitter(); + for ($i = 0;$i < 100;$i++) { + $this->emitter->on('foo', function() { + }, 1000 - $i); + } + + } + + function test() { + + for ($i = 0;$i < $this->iterations;$i++) { + $this->emitter->emit('foo', []); + } + + } + +} + +$tests = [ + 'OneCallBack', + 'ManyCallBacks', + 'ManyPrioritizedCallBacks', +]; + +foreach ($tests as $test) { + + $testObj = new $test(); + $result = $testObj->go(); + echo $test . " " . $result . "\n"; + +} diff --git a/htdocs/includes/sabre/sabre/http/.gitignore b/htdocs/includes/sabre/sabre/http/.gitignore new file mode 100644 index 00000000000..8c97686fb57 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/.gitignore @@ -0,0 +1,15 @@ +# Composer +vendor/ +composer.lock + +# Tests +tests/cov/ + +# Composer binaries +bin/phpunit +bin/phpcs +bin/php-cs-fixer +bin/sabre-cs-fixer + +# Vim +.*.swp diff --git a/htdocs/includes/sabre/sabre/http/.travis.yml b/htdocs/includes/sabre/sabre/http/.travis.yml new file mode 100644 index 00000000000..8ae84d90f8e --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/.travis.yml @@ -0,0 +1,26 @@ +language: php +php: + - 5.4 + - 5.5 + - 5.6 + - 7 + - 7.1 + - hhvm + +matrix: + fast_finish: true + +env: + matrix: + - PREFER_LOWEST="" + - PREFER_LOWEST="--prefer-lowest" + + +before_script: + - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini +# - composer self-update + - composer update --prefer-source $PREFER_LOWEST + +script: + - ./bin/phpunit --configuration tests/phpunit.xml + - ./bin/sabre-cs-fixer fix . --dry-run --diff diff --git a/htdocs/includes/sabre/sabre/http/CHANGELOG.md b/htdocs/includes/sabre/sabre/http/CHANGELOG.md new file mode 100644 index 00000000000..63d85afe35c --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/CHANGELOG.md @@ -0,0 +1,256 @@ +ChangeLog +========= + +4.2.2 (2017-01-02) +------------------ + +* #72: Handling clients that send invalid `Content-Length` headers. + + +4.2.1 (2016-01-06) +------------------ + +* #56: `getBodyAsString` now returns at most as many bytes as the contents of + the `Content-Length` header. This allows users to pass much larger strings + without having to copy and truncate them. +* The client now sets a default `User-Agent` header identifying this library. + + +4.2.0 (2016-01-04) +------------------ + +* This package now supports sabre/event 3.0. + + +4.1.0 (2015-09-04) +------------------ + +* The async client wouldn't `wait()` for new http requests being started + after the (previous) last request in the queue was resolved. +* Added `Sabre\HTTP\Auth\Bearer`, to easily extract a OAuth2 bearer token. + + +4.0.0 (2015-05-20) +------------------ + +* Deprecated: All static functions from `Sabre\HTTP\URLUtil` and + `Sabre\HTTP\Util` moved to a separate `functions.php`, which is also + autoloaded. The old functions are still there, but will be removed in a + future version. (#49) + + +4.0.0-alpha3 (2015-05-19) +------------------------- + +* Added a parser for the HTTP `Prefer` header, as defined in [RFC7240][rfc7240]. +* Deprecated `Sabre\HTTP\Util::parseHTTPDate`, use `Sabre\HTTP\parseDate()`. +* Deprecated `Sabre\HTTP\Util::toHTTPDate` use `Sabre\HTTP\toDate()`. + + +4.0.0-alpha2 (2015-05-18) +------------------------- + +* #45: Don't send more data than what is promised in the HTTP content-length. + (@dratini0). +* #43: `getCredentials` returns null if incomplete. (@Hywan) +* #48: Now using php-cs-fixer to make our CS consistent (yay!) +* This includes fixes released in version 3.0.5. + + +4.0.0-alpha1 (2015-02-25) +------------------------- + +* #41: Fixing bugs related to comparing URLs in `Request::getPath()`. +* #41: This library now uses the `sabre/uri` package for uri handling. +* Added `421 Misdirected Request` from the HTTP/2.0 spec. + + +3.0.5 (2015-05-11) +------------------ + +* #47 #35: When re-using the client and doing any request after a `HEAD` + request, the client discards the body. + + +3.0.4 (2014-12-10) +------------------ + +* #38: The Authentication helpers no longer overwrite any existing + `WWW-Authenticate` headers, but instead append new headers. This ensures + that multiple authentication systems can exist in the same environment. + + +3.0.3 (2014-12-03) +------------------ + +* Hiding `Authorization` header value from `Request::__toString`. + + +3.0.2 (2014-10-09) +------------------ + +* When parsing `Accept:` headers, we're ignoring invalid parts. Before we + would throw a PHP E_NOTICE. + + +3.0.1 (2014-09-29) +------------------ + +* Minor change in unittests. + + +3.0.0 (2014-09-23) +------------------ + +* `getHeaders()` now returns header values as an array, just like psr/http. +* Added `hasHeader()`. + + +2.1.0-alpha1 (2014-09-15) +------------------------- + +* Changed: Copied most of the header-semantics for the PSR draft for + representing HTTP messages. [Reference here][psr-http]. +* This means that `setHeaders()` does not wipe out every existing header + anymore. +* We also support multiple headers with the same name. +* Use `Request::getHeaderAsArray()` and `Response::getHeaderAsArray()` to + get a hold off multiple headers with the same name. +* If you use `getHeader()`, and there's more than 1 header with that name, we + concatenate all these with a comma. +* `addHeader()` will now preserve an existing header with that name, and add a + second header with the same name. +* The message class should be a lot faster now for looking up headers. No more + array traversal, because we maintain a tiny index. +* Added: `URLUtil::resolve()` to make resolving relative urls super easy. +* Switched to PSR-4. +* #12: Circumventing CURL's FOLLOW_LOCATION and doing it in PHP instead. This + fixes compatibility issues with people that have open_basedir turned on. +* Added: Content negotiation now correctly support mime-type parameters such as + charset. +* Changed: `Util::negotiate()` is now deprecated. Use + `Util::negotiateContentType()` instead. +* #14: The client now only follows http and https urls. + + +2.0.4 (2014-07-14) +------------------ + +* Changed: No longer escaping @ in urls when it's not needed. +* Fixed: #7: Client now correctly deals with responses without a body. + + +2.0.3 (2014-04-17) +------------------ + +* Now works on hhvm! +* Fixed: Now throwing an error when a Request object is being created with + arguments that were valid for sabre/http 1.0. Hopefully this will aid with + debugging for upgraders. + + +2.0.2 (2014-02-09) +------------------ + +* Fixed: Potential security problem in the client. + + +2.0.1 (2014-01-09) +------------------ + +* Fixed: getBodyAsString on an empty body now works. +* Fixed: Version string + + +2.0.0 (2014-01-08) +------------------ + +* Removed: Request::createFromPHPRequest. This is now handled by + Sapi::getRequest. + + +2.0.0alpha6 (2014-01-03) +------------------------ + +* Added: Asynchronous HTTP client. See examples/asyncclient.php. +* Fixed: Issue #4: Don't escape colon (:) when it's not needed. +* Fixed: Fixed a bug in the content negotation script. +* Fixed: Fallback for when CURLOPT_POSTREDIR is not defined (mainly for hhvm). +* Added: The Request and Response object now have a `__toString()` method that + serializes the objects into a standard HTTP message. This is mainly for + debugging purposes. +* Changed: Added Response::getStatusText(). This method returns the + human-readable HTTP status message. This part has been removed from + Response::getStatus(), which now always returns just the status code as an + int. +* Changed: Response::send() is now Sapi::sendResponse($response). +* Changed: Request::createFromPHPRequest is now Sapi::getRequest(). +* Changed: Message::getBodyAsStream and Message::getBodyAsString were added. The + existing Message::getBody changed it's behavior, so be careful. + + +2.0.0alpha5 (2013-11-07) +------------------------ + +* Added: HTTP Status 451 Unavailable For Legal Reasons. Fight government + censorship! +* Added: Ability to catch and retry http requests in the client when a curl + error occurs. +* Changed: Request::getPath does not return the query part of the url, so + everything after the ? is stripped. +* Added: a reverse proxy example. + + +2.0.0alpha4 (2013-08-07) +------------------------ + +* Fixed: Doing a GET request with the client uses the last used HTTP method + instead. +* Added: HttpException +* Added: The Client class can now automatically emit exceptions when HTTP errors + occurred. + + +2.0.0alpha3 (2013-07-24) +------------------------ + +* Changed: Now depends on sabre/event package. +* Changed: setHeaders() now overwrites any existing http headers. +* Added: getQueryParameters to RequestInterface. +* Added: Util::negotiate. +* Added: RequestDecorator, ResponseDecorator. +* Added: A very simple HTTP client. +* Added: addHeaders() to append a list of new headers. +* Fixed: Not erroring on unknown HTTP status codes. +* Fixed: Throwing exceptions on invalid HTTP status codes (not 3 digits). +* Fixed: Much better README.md +* Changed: getBody() now uses a bitfield to specify what type to return. + + +2.0.0alpha2 (2013-07-02) +------------------------ + +* Added: Digest & AWS Authentication. +* Added: Message::getHttpVersion and Message::setHttpVersion. +* Added: Request::setRawServerArray, getRawServerValue. +* Added: Request::createFromPHPRequest +* Added: Response::send +* Added: Request::getQueryParameters +* Added: Utility for dealing with HTTP dates. +* Added: Request::setPostData and Request::getPostData. +* Added: Request::setAbsoluteUrl and Request::getAbsoluteUrl. +* Added: URLUtil, methods for calculation relative and base urls. +* Removed: Response::sendBody + + +2.0.0alpha1 (2012-10-07) +------------------------ + +* Fixed: Lots of small naming improvements +* Added: Introduction of Message, MessageInterface, Response, ResponseInterface. + +Before 2.0.0, this package was built-into SabreDAV, where it first appeared in +January 2009. + +[psr-http]: https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md +[rfc-7240]: http://tools.ietf.org/html/rfc7240 diff --git a/htdocs/includes/sabre/sabre/http/LICENSE b/htdocs/includes/sabre/sabre/http/LICENSE new file mode 100644 index 00000000000..864041b22ca --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2009-2017 fruux GmbH (https://fruux.com/) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name Sabre nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/htdocs/includes/sabre/sabre/http/README.md b/htdocs/includes/sabre/sabre/http/README.md new file mode 100644 index 00000000000..ae03a796e13 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/README.md @@ -0,0 +1,746 @@ +sabre/http +========== + +This library provides a toolkit to make working with the HTTP protocol easier. + +Most PHP scripts run within a HTTP request but accessing information about the +HTTP request is cumbersome at least. + +There's bad practices, inconsistencies and confusion. This library is +effectively a wrapper around the following PHP constructs: + +For Input: + +* `$_GET`, +* `$_POST`, +* `$_SERVER`, +* `php://input` or `$HTTP_RAW_POST_DATA`. + +For output: + +* `php://output` or `echo`, +* `header()`. + +What this library provides, is a `Request` object, and a `Response` object. + +The objects are extendable and easily mockable. + +Build status +------------ + +| branch | status | +| ------ | ------ | +| master | [![Build Status](https://travis-ci.org/fruux/sabre-http.svg?branch=master)](https://travis-ci.org/fruux/sabre-http) | +| 3.0 | [![Build Status](https://travis-ci.org/fruux/sabre-http.svg?branch=3.0)](https://travis-ci.org/fruux/sabre-http) | + +Installation +------------ + +Make sure you have [composer][1] installed. In your project directory, create, +or edit a `composer.json` file, and make sure it contains something like this: + +```json +{ + "require" : { + "sabre/http" : "~3.0.0" + } +} +``` + +After that, just hit `composer install` and you should be rolling. + +Quick history +------------- + +This library came to existence in 2009, as a part of the [`sabre/dav`][2] +project, which uses it heavily. + +It got split off into a separate library to make it easier to manage +releases and hopefully giving it use outside of the scope of just `sabre/dav`. + +Although completely independently developed, this library has a LOT of +overlap with [Symfony's `HttpFoundation`][3]. + +Said library does a lot more stuff and is significantly more popular, +so if you are looking for something to fulfill this particular requirement, +I'd recommend also considering [`HttpFoundation`][3]. + + +Getting started +--------------- + +First and foremost, this library wraps the superglobals. The easiest way to +instantiate a request object is as follows: + +```php +use Sabre\HTTP; + +include 'vendor/autoload.php'; + +$request = HTTP\Sapi::getRequest(); +``` + +This line should only happen once in your entire application. Everywhere else +you should pass this request object around using dependency injection. + +You should always typehint on it's interface: + +```php +function handleRequest(HTTP\RequestInterface $request) { + + // Do something with this request :) + +} +``` + +A response object you can just create as such: + +```php +use Sabre\HTTP; + +include 'vendor/autoload.php'; + +$response = new HTTP\Response(); +$response->setStatus(201); // created ! +$response->setHeader('X-Foo', 'bar'); +$response->setBody( + 'success!' +); + +``` + +After you fully constructed your response, you must call: + +```php +HTTP\Sapi::sendResponse($response); +``` + +This line should generally also appear once in your application (at the very +end). + +Decorators +---------- + +It may be useful to extend the `Request` and `Response` objects in your +application, if you for example would like them to carry a bit more +information about the current request. + +For instance, you may want to add an `isLoggedIn` method to the Request +object. + +Simply extending Request and Response may pose some problems: + +1. You may want to extend the objects with new behaviors differently, in + different subsystems of your application, +2. The `Sapi::getRequest` factory always returns a instance of + `Request` so you would have to override the factory method as well, +3. By controlling the instantation and depend on specific `Request` and + `Response` instances in your library or application, you make it harder to + work with other applications which also use `sabre/http`. + +In short: it would be bad design. Instead, it's recommended to use the +[decorator pattern][6] to add new behavior where you need it. `sabre/http` +provides helper classes to quickly do this. + +Example: + +```php +use Sabre\HTTP; + +class MyRequest extends HTTP\RequestDecorator { + + function isLoggedIn() { + + return true; + + } + +} +``` + +Our application assumes that the true `Request` object was instantiated +somewhere else, by some other subsystem. This could simply be a call like +`$request = Sapi::getRequest()` at the top of your application, +but could also be somewhere in a unittest. + +All we know in the current subsystem, is that we received a `$request` and +that it implements `Sabre\HTTP\RequestInterface`. To decorate this object, +all we need to do is: + +```php +$request = new MyRequest($request); +``` + +And that's it, we now have an `isLoggedIn` method, without having to mess +with the core instances. + + +Client +------ + +This package also contains a simple wrapper around [cURL][4], which will allow +you to write simple clients, using the `Request` and `Response` objects you're +already familiar with. + +It's by no means a replacement for something like [Guzzle][7], but it provides +a simple and lightweight API for making the occasional API call. + +### Usage + +```php +use Sabre\HTTP; + +$request = new HTTP\Request('GET', 'http://example.org/'); +$request->setHeader('X-Foo', 'Bar'); + +$client = new HTTP\Client(); +$response = $client->send($request); + +echo $response->getBodyAsString(); +``` + +The client emits 3 event using [`sabre/event`][5]. `beforeRequest`, +`afterRequest` and `error`. + +```php +$client = new HTTP\Client(); +$client->on('beforeRequest', function($request) { + + // You could use beforeRequest to for example inject a few extra headers. + // into the Request object. + +}); + +$client->on('afterRequest', function($request, $response) { + + // The afterRequest event could be a good time to do some logging, or + // do some rewriting in the response. + +}); + +$client->on('error', function($request, $response, &$retry, $retryCount) { + + // The error event is triggered for every response with a HTTP code higher + // than 399. + +}); + +$client->on('error:401', function($request, $response, &$retry, $retryCount) { + + // You can also listen for specific error codes. This example shows how + // to inject HTTP authentication headers if a 401 was returned. + + if ($retryCount > 1) { + // We're only going to retry exactly once. + } + + $request->setHeader('Authorization', 'Basic xxxxxxxxxx'); + $retry = true; + +}); +``` + +### Asynchronous requests + +The `Client` also supports doing asynchronous requests. This is especially handy +if you need to perform a number of requests, that are allowed to be executed +in parallel. + +The underlying system for this is simply [cURL's multi request handler][8], +but this provides a much nicer API to handle this. + +Sample usage: + +```php + +use Sabre\HTTP; + +$request = new Request('GET', 'http://localhost/'); +$client = new Client(); + +// Executing 1000 requests +for ($i = 0; $i < 1000; $i++) { + $client->sendAsync( + $request, + function(ResponseInterface $response) { + // Success handler + }, + function($error) { + // Error handler + } + ); +} + +// Wait for all requests to get a result. +$client->wait(); + +``` + +Check out `examples/asyncclient.php` for more information. + +Writing a reverse proxy +----------------------- + +With all these tools combined, it becomes very easy to write a simple reverse +http proxy. + +```php +use + Sabre\HTTP\Sapi, + Sabre\HTTP\Client; + +// The url we're proxying to. +$remoteUrl = 'http://example.org/'; + +// The url we're proxying from. Please note that this must be a relative url, +// and basically acts as the base url. +// +// If youre $remoteUrl doesn't end with a slash, this one probably shouldn't +// either. +$myBaseUrl = '/reverseproxy.php'; +// $myBaseUrl = '/~evert/sabre/http/examples/reverseproxy.php/'; + +$request = Sapi::getRequest(); +$request->setBaseUrl($myBaseUrl); + +$subRequest = clone $request; + +// Removing the Host header. +$subRequest->removeHeader('Host'); + +// Rewriting the url. +$subRequest->setUrl($remoteUrl . $request->getPath()); + +$client = new Client(); + +// Sends the HTTP request to the server +$response = $client->send($subRequest); + +// Sends the response back to the client that connected to the proxy. +Sapi::sendResponse($response); +``` + +The Request and Response API's +------------------------------ + +### Request + +```php + +/** + * Creates the request object + * + * @param string $method + * @param string $url + * @param array $headers + * @param resource $body + */ +public function __construct($method = null, $url = null, array $headers = null, $body = null); + +/** + * Returns the current HTTP method + * + * @return string + */ +function getMethod(); + +/** + * Sets the HTTP method + * + * @param string $method + * @return void + */ +function setMethod($method); + +/** + * Returns the request url. + * + * @return string + */ +function getUrl(); + +/** + * Sets the request url. + * + * @param string $url + * @return void + */ +function setUrl($url); + +/** + * Returns the absolute url. + * + * @return string + */ +function getAbsoluteUrl(); + +/** + * Sets the absolute url. + * + * @param string $url + * @return void + */ +function setAbsoluteUrl($url); + +/** + * Returns the current base url. + * + * @return string + */ +function getBaseUrl(); + +/** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * The base url should default to / + * + * @param string $url + * @return void + */ +function setBaseUrl($url); + +/** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ +function getPath(); + +/** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ +function getQueryParameters(); + +/** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ +function getPostData(); + +/** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ +function setPostData(array $postData); + +/** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ +function getRawServerValue($valueName); + +/** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ +function setRawServerData(array $data); + +/** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ +function getBodyAsStream(); + +/** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ +function getBodyAsString(); + +/** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ +function getBody(); + +/** + * Updates the body resource with a new stream. + * + * @param resource $body + * @return void + */ +function setBody($body); + +/** + * Returns all the HTTP headers as an array. + * + * @return array + */ +function getHeaders(); + +/** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * + * If the header does not exist, this method must return null. + * + * @param string $name + * @return string|null + */ +function getHeader($name); + +/** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * @param string $name + * @param string $value + * @return void + */ +function setHeader($name, $value); + +/** + * Resets HTTP headers + * + * This method overwrites all existing HTTP headers + * + * @param array $headers + * @return void + */ +function setHeaders(array $headers); + +/** + * Adds a new set of HTTP headers. + * + * Any header specified in the array that already exists will be + * overwritten, but any other existing headers will be retained. + * + * @param array $headers + * @return void + */ +function addHeaders(array $headers); + +/** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @return bool + */ +function removeHeader($name); + +/** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ +function setHttpVersion($version); + +/** + * Returns the HTTP version. + * + * @return string + */ +function getHttpVersion(); +``` + +### Response + +```php +/** + * Returns the current HTTP status. + * + * This is the status-code as well as the human readable string. + * + * @return string + */ +function getStatus(); + +/** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @throws \InvalidArgumentExeption + * @return void + */ +function setStatus($status); + +/** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ +function getBodyAsStream(); + +/** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ +function getBodyAsString(); + +/** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ +function getBody(); + + +/** + * Updates the body resource with a new stream. + * + * @param resource $body + * @return void + */ +function setBody($body); + +/** + * Returns all the HTTP headers as an array. + * + * @return array + */ +function getHeaders(); + +/** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * + * If the header does not exist, this method must return null. + * + * @param string $name + * @return string|null + */ +function getHeader($name); + +/** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * @param string $name + * @param string $value + * @return void + */ +function setHeader($name, $value); + +/** + * Resets HTTP headers + * + * This method overwrites all existing HTTP headers + * + * @param array $headers + * @return void + */ +function setHeaders(array $headers); + +/** + * Adds a new set of HTTP headers. + * + * Any header specified in the array that already exists will be + * overwritten, but any other existing headers will be retained. + * + * @param array $headers + * @return void + */ +function addHeaders(array $headers); + +/** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @return bool + */ +function removeHeader($name); + +/** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ +function setHttpVersion($version); + +/** + * Returns the HTTP version. + * + * @return string + */ +function getHttpVersion(); +``` + +Made at fruux +------------- + +This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. + +[1]: http://getcomposer.org/ +[2]: http://sabre.io/ +[3]: https://github.com/symfony/HttpFoundation +[4]: http://php.net/curl +[5]: https://github.com/fruux/sabre-event +[6]: http://en.wikipedia.org/wiki/Decorator_pattern +[7]: http://guzzlephp.org/ +[8]: http://php.net/curl_multi_init diff --git a/htdocs/includes/sabre/sabre/http/bin/.empty b/htdocs/includes/sabre/sabre/http/bin/.empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/includes/sabre/sabre/http/composer.json b/htdocs/includes/sabre/sabre/http/composer.json new file mode 100644 index 00000000000..507d5d28df0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/composer.json @@ -0,0 +1,44 @@ +{ + "name": "sabre/http", + "description" : "The sabre/http library provides utilities for dealing with http requests and responses. ", + "keywords" : [ "HTTP" ], + "homepage" : "https://github.com/fruux/sabre-http", + "license" : "BSD-3-Clause", + "require" : { + "php" : ">=5.4", + "ext-mbstring" : "*", + "ext-ctype" : "*", + "sabre/event" : ">=1.0.0,<4.0.0", + "sabre/uri" : "~1.0" + }, + "require-dev" : { + "phpunit/phpunit" : "~4.3", + "sabre/cs" : "~0.0.1" + }, + "suggest" : { + "ext-curl" : " to make http requests with the Client class" + }, + "authors" : [ + { + "name" : "Evert Pot", + "email" : "me@evertpot.com", + "homepage" : "http://evertpot.com/", + "role" : "Developer" + } + ], + "support" : { + "forum" : "https://groups.google.com/group/sabredav-discuss", + "source" : "https://github.com/fruux/sabre-http" + }, + "autoload" : { + "files" : [ + "lib/functions.php" + ], + "psr-4" : { + "Sabre\\HTTP\\" : "lib/" + } + }, + "config" : { + "bin-dir" : "bin/" + } +} diff --git a/htdocs/includes/sabre/sabre/http/examples/asyncclient.php b/htdocs/includes/sabre/sabre/http/examples/asyncclient.php new file mode 100644 index 00000000000..b399e1e4f7b --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/examples/asyncclient.php @@ -0,0 +1,65 @@ +<?php + +/** + * This example demonstrates the ability for clients to work asynchronously. + * + * By default up to 10 requests will be executed in paralel. HTTP connections + * are re-used and DNS is cached, all thanks to the power of curl. + * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +use Sabre\HTTP\Client; +use Sabre\HTTP\Request; + +// Find the autoloader +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', + __DIR__ . '/vendor/autoload.php', + +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +// This is the request we're repeating a 1000 times. +$request = new Request('GET', 'http://localhost/'); +$client = new Client(); + +for ($i = 0; $i < 1000; $i++) { + + echo "$i sending\n"; + $client->sendAsync( + $request, + + // This is the 'success' callback + function($response) use ($i) { + echo "$i -> " . $response->getStatus() . "\n"; + }, + + // This is the 'error' callback. It is called for general connection + // problems (such as not being able to connect to a host, dns errors, + // etc.) and also cases where a response was returned, but it had a + // status code of 400 or higher. + function($error) use ($i) { + + if ($error['status'] === Client::STATUS_CURLERROR) { + // Curl errors + echo "$i -> curl error: " . $error['curl_errmsg'] . "\n"; + } else { + // HTTP errors + echo "$i -> " . $error['response']->getStatus() . "\n"; + } + } + ); +} + +// After everything is done, we call 'wait'. This causes the client to wait for +// all outstanding http requests to complete. +$client->wait(); diff --git a/htdocs/includes/sabre/sabre/http/examples/basicauth.php b/htdocs/includes/sabre/sabre/http/examples/basicauth.php new file mode 100644 index 00000000000..58bb7899268 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/examples/basicauth.php @@ -0,0 +1,55 @@ +<?php + +/** + * This example shows how to do Basic authentication. + * * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +$userList = [ + "user1" => "password", + "user2" => "password", +]; + +use Sabre\HTTP\Auth; +use Sabre\HTTP\Response; +use Sabre\HTTP\Sapi; + +// Find the autoloader +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', + __DIR__ . '/vendor/autoload.php', + +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +$request = Sapi::getRequest(); +$response = new Response(); + +$basicAuth = new Auth\Basic("Locked down area", $request, $response); +if (!$userPass = $basicAuth->getCredentials()) { + + // No username or password given + $basicAuth->requireLogin(); + +} elseif (!isset($userList[$userPass[0]]) || $userList[$userPass[0]] !== $userPass[1]) { + + // Username or password are incorrect + $basicAuth->requireLogin(); +} else { + + // Success ! + $response->setBody('You are logged in!'); + +} + +// Sending the response +Sapi::sendResponse($response); diff --git a/htdocs/includes/sabre/sabre/http/examples/client.php b/htdocs/includes/sabre/sabre/http/examples/client.php new file mode 100644 index 00000000000..d16c1651b6c --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/examples/client.php @@ -0,0 +1,38 @@ +<?php + +/** + * This example shows how to make a HTTP request with the Request and Response + * objects. + * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +use Sabre\HTTP\Client; +use Sabre\HTTP\Request; + +// Find the autoloader +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', + __DIR__ . '/vendor/autoload.php', + +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +// Constructing the request. +$request = new Request('GET', 'http://localhost/'); + +$client = new Client(); +//$client->addCurlSetting(CURLOPT_PROXY,'localhost:8888'); +$response = $client->send($request); + +echo "Response:\n"; + +echo (string)$response; diff --git a/htdocs/includes/sabre/sabre/http/examples/digestauth.php b/htdocs/includes/sabre/sabre/http/examples/digestauth.php new file mode 100644 index 00000000000..30a5501eb56 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/examples/digestauth.php @@ -0,0 +1,56 @@ +<?php + +/** + * This example shows how to do Digest authentication. + * * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Markus Staab + * @license http://sabre.io/license/ Modified BSD License + */ +$userList = [ + "user1" => "password", + "user2" => "password", +]; + +use Sabre\HTTP\Auth; +use Sabre\HTTP\Response; +use Sabre\HTTP\Sapi; + +// Find the autoloader +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', + __DIR__ . '/vendor/autoload.php', + +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +$request = Sapi::getRequest(); +$response = new Response(); + +$digestAuth = new Auth\Digest("Locked down area", $request, $response); +$digestAuth->init(); +if (!$userName = $digestAuth->getUsername()) { + + // No username given + $digestAuth->requireLogin(); + +} elseif (!isset($userList[$userName]) || !$digestAuth->validatePassword($userList[$userName])) { + + // Username or password are incorrect + $digestAuth->requireLogin(); +} else { + + // Success ! + $response->setBody('You are logged in!'); + +} + +// Sending the response +Sapi::sendResponse($response); diff --git a/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php b/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php new file mode 100644 index 00000000000..289e2b55198 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php @@ -0,0 +1,50 @@ +<?php + +// The url we're proxying to. +$remoteUrl = 'http://example.org/'; + +// The url we're proxying from. Please note that this must be a relative url, +// and basically acts as the base url. +// +// If your $remoteUrl doesn't end with a slash, this one probably shouldn't +// either. +$myBaseUrl = '/reverseproxy.php'; +// $myBaseUrl = '/~evert/sabre/http/examples/reverseproxy.php/'; + +use Sabre\HTTP\Client; +use Sabre\HTTP\Sapi; + +// Find the autoloader +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', + __DIR__ . '/vendor/autoload.php', + +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + + +$request = Sapi::getRequest(); +$request->setBaseUrl($myBaseUrl); + +$subRequest = clone $request; + +// Removing the Host header. +$subRequest->removeHeader('Host'); + +// Rewriting the url. +$subRequest->setUrl($remoteUrl . $request->getPath()); + +$client = new Client(); + +// Sends the HTTP request to the server +$response = $client->send($subRequest); + +// Sends the response back to the client that connected to the proxy. +Sapi::sendResponse($response); diff --git a/htdocs/includes/sabre/sabre/http/examples/stringify.php b/htdocs/includes/sabre/sabre/http/examples/stringify.php new file mode 100644 index 00000000000..9f56201af9e --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/examples/stringify.php @@ -0,0 +1,51 @@ +<?php + +/** + * This simple example shows the capability of Request and Response objects to + * serialize themselves as strings. + * + * This is mainly useful for debugging purposes. + * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +// Find the autoloader +$paths = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php', + __DIR__ . '/vendor/autoload.php', + +]; +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +$request = new Request('POST', '/foo'); +$request->setHeaders([ + 'Host' => 'example.org', + 'Content-Type' => 'application/json' + ]); + +$request->setBody(json_encode(['foo' => 'bar'])); + +echo $request; +echo "\r\n\r\n"; + +$response = new Response(424); +$response->setHeaders([ + 'Content-Type' => 'text/plain', + 'Connection' => 'close', + ]); + +$response->setBody("ABORT! ABORT!"); + +echo $response; + +echo "\r\n"; diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php b/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php new file mode 100644 index 00000000000..5e176646aaf --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php @@ -0,0 +1,234 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\Util; + +/** + * HTTP AWS Authentication handler + * + * Use this class to leverage amazon's AWS authentication header + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class AWS extends AbstractAuth { + + /** + * The signature supplied by the HTTP client + * + * @var string + */ + private $signature = null; + + /** + * The accesskey supplied by the HTTP client + * + * @var string + */ + private $accessKey = null; + + /** + * An error code, if any + * + * This value will be filled with one of the ERR_* constants + * + * @var int + */ + public $errorCode = 0; + + const ERR_NOAWSHEADER = 1; + const ERR_MD5CHECKSUMWRONG = 2; + const ERR_INVALIDDATEFORMAT = 3; + const ERR_REQUESTTIMESKEWED = 4; + const ERR_INVALIDSIGNATURE = 5; + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return bool + */ + function init() { + + $authHeader = $this->request->getHeader('Authorization'); + $authHeader = explode(' ', $authHeader); + + if ($authHeader[0] != 'AWS' || !isset($authHeader[1])) { + $this->errorCode = self::ERR_NOAWSHEADER; + return false; + } + + list($this->accessKey, $this->signature) = explode(':', $authHeader[1]); + + return true; + + } + + /** + * Returns the username for the request + * + * @return string + */ + function getAccessKey() { + + return $this->accessKey; + + } + + /** + * Validates the signature based on the secretKey + * + * @param string $secretKey + * @return bool + */ + function validate($secretKey) { + + $contentMD5 = $this->request->getHeader('Content-MD5'); + + if ($contentMD5) { + // We need to validate the integrity of the request + $body = $this->request->getBody(); + $this->request->setBody($body); + + if ($contentMD5 != base64_encode(md5($body, true))) { + // content-md5 header did not match md5 signature of body + $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; + } + + } + + if (!$requestDate = $this->request->getHeader('x-amz-date')) + $requestDate = $this->request->getHeader('Date'); + + if (!$this->validateRFC2616Date($requestDate)) + return false; + + $amzHeaders = $this->getAmzHeaders(); + + $signature = base64_encode( + $this->hmacsha1($secretKey, + $this->request->getMethod() . "\n" . + $contentMD5 . "\n" . + $this->request->getHeader('Content-type') . "\n" . + $requestDate . "\n" . + $amzHeaders . + $this->request->getUrl() + ) + ); + + if ($this->signature != $signature) { + + $this->errorCode = self::ERR_INVALIDSIGNATURE; + return false; + + } + + return true; + + } + + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'AWS'); + $this->response->setStatus(401); + + } + + /** + * Makes sure the supplied value is a valid RFC2616 date. + * + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * user just supplied the word 'now' for the date header. + * + * This function also makes sure the Date header is within 15 minutes of the operating + * system date, to prevent replay attacks. + * + * @param string $dateHeader + * @return bool + */ + protected function validateRFC2616Date($dateHeader) { + + $date = Util::parseHTTPDate($dateHeader); + + // Unknown format + if (!$date) { + $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; + } + + $min = new \DateTime('-15 minutes'); + $max = new \DateTime('+15 minutes'); + + // We allow 15 minutes around the current date/time + if ($date > $max || $date < $min) { + $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; + } + + return $date; + + } + + /** + * Returns a list of AMZ headers + * + * @return string + */ + protected function getAmzHeaders() { + + $amzHeaders = []; + $headers = $this->request->getHeaders(); + foreach ($headers as $headerName => $headerValue) { + if (strpos(strtolower($headerName), 'x-amz-') === 0) { + $amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0]) . "\n"; + } + } + ksort($amzHeaders); + + $headerStr = ''; + foreach ($amzHeaders as $h => $v) { + $headerStr .= $h . ':' . $v; + } + + return $headerStr; + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + if (function_exists('hash_hmac')) { + return hash_hmac('sha1', $message, $key, true); + } + + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', sha1($key)); + } + $key = str_pad($key, $blocksize, chr(0x00)); + $ipad = str_repeat(chr(0x36), $blocksize); + $opad = str_repeat(chr(0x5c), $blocksize); + $hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $message)))); + return $hmac; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php b/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php new file mode 100644 index 00000000000..ae45b3ee22a --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php @@ -0,0 +1,73 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +/** + * HTTP Authentication base class. + * + * This class provides some common functionality for the various base classes. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class AbstractAuth { + + /** + * Authentication realm + * + * @var string + */ + protected $realm; + + /** + * Request object + * + * @var RequestInterface + */ + protected $request; + + /** + * Response object + * + * @var ResponseInterface + */ + protected $response; + + /** + * Creates the object + * + * @param string $realm + * @return void + */ + function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) { + + $this->realm = $realm; + $this->request = $request; + $this->response = $response; + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * the user to login. + * + * @return void + */ + abstract function requireLogin(); + + /** + * Returns the HTTP realm + * + * @return string + */ + function getRealm() { + + return $this->realm; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php b/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php new file mode 100644 index 00000000000..60633b95726 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php @@ -0,0 +1,63 @@ +<?php + +namespace Sabre\HTTP\Auth; + +/** + * HTTP Basic authentication utility. + * + * This class helps you setup basic auth. The process is fairly simple: + * + * 1. Instantiate the class. + * 2. Call getCredentials (this will return null or a user/pass pair) + * 3. If you didn't get valid credentials, call 'requireLogin' + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Basic extends AbstractAuth { + + /** + * This method returns a numeric array with a username and password as the + * only elements. + * + * If no credentials were found, this method returns null. + * + * @return null|array + */ + function getCredentials() { + + $auth = $this->request->getHeader('Authorization'); + + if (!$auth) { + return null; + } + + if (strtolower(substr($auth, 0, 6)) !== 'basic ') { + return null; + } + + $credentials = explode(':', base64_decode(substr($auth, 6)), 2); + + if (2 !== count($credentials)) { + return null; + } + + return $credentials; + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * the user to login. + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'Basic realm="' . $this->realm . '"'); + $this->response->setStatus(401); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php b/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php new file mode 100644 index 00000000000..eefdf11ee97 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php @@ -0,0 +1,56 @@ +<?php + +namespace Sabre\HTTP\Auth; + +/** + * HTTP Bearer authentication utility. + * + * This class helps you setup bearer auth. The process is fairly simple: + * + * 1. Instantiate the class. + * 2. Call getToken (this will return null or a token as string) + * 3. If you didn't get a valid token, call 'requireLogin' + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author François Kooman (fkooman@tuxed.net) + * @license http://sabre.io/license/ Modified BSD License + */ +class Bearer extends AbstractAuth { + + /** + * This method returns a string with an access token. + * + * If no token was found, this method returns null. + * + * @return null|string + */ + function getToken() { + + $auth = $this->request->getHeader('Authorization'); + + if (!$auth) { + return null; + } + + if (strtolower(substr($auth, 0, 7)) !== 'bearer ') { + return null; + } + + return substr($auth, 7); + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * authentication. + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'Bearer realm="' . $this->realm . '"'); + $this->response->setStatus(401); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php b/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php new file mode 100644 index 00000000000..4b3f0746fc6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php @@ -0,0 +1,231 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +/** + * HTTP Digest Authentication handler + * + * Use this class for easy http digest authentication. + * Instructions: + * + * 1. Create the object + * 2. Call the setRealm() method with the realm you plan to use + * 3. Call the init method function. + * 4. Call the getUserName() function. This function may return null if no + * authentication information was supplied. Based on the username you + * should check your internal database for either the associated password, + * or the so-called A1 hash of the digest. + * 5. Call either validatePassword() or validateA1(). This will return true + * or false. + * 6. To make sure an authentication prompt is displayed, call the + * requireLogin() method. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Digest extends AbstractAuth { + + /** + * These constants are used in setQOP(); + */ + const QOP_AUTH = 1; + const QOP_AUTHINT = 2; + + protected $nonce; + protected $opaque; + protected $digestParts; + protected $A1; + protected $qop = self::QOP_AUTH; + + /** + * Initializes the object + */ + function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) { + + $this->nonce = uniqid(); + $this->opaque = md5($realm); + parent::__construct($realm, $request, $response); + + } + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return void + */ + function init() { + + $digest = $this->getDigest(); + $this->digestParts = $this->parseDigest($digest); + + } + + /** + * Sets the quality of protection value. + * + * Possible values are: + * Sabre\HTTP\DigestAuth::QOP_AUTH + * Sabre\HTTP\DigestAuth::QOP_AUTHINT + * + * Multiple values can be specified using logical OR. + * + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * request body to be md5'ed, which can put strains on CPU and memory. + * + * @param int $qop + * @return void + */ + function setQOP($qop) { + + $this->qop = $qop; + + } + + /** + * Validates the user. + * + * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); + * + * @param string $A1 + * @return bool + */ + function validateA1($A1) { + + $this->A1 = $A1; + return $this->validate(); + + } + + /** + * Validates authentication through a password. The actual password must be provided here. + * It is strongly recommended not store the password in plain-text and use validateA1 instead. + * + * @param string $password + * @return bool + */ + function validatePassword($password) { + + $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); + return $this->validate(); + + } + + /** + * Returns the username for the request + * + * @return string + */ + function getUsername() { + + return $this->digestParts['username']; + + } + + /** + * Validates the digest challenge + * + * @return bool + */ + protected function validate() { + + $A2 = $this->request->getMethod() . ':' . $this->digestParts['uri']; + + if ($this->digestParts['qop'] == 'auth-int') { + // Making sure we support this qop value + if (!($this->qop & self::QOP_AUTHINT)) return false; + // We need to add an md5 of the entire request body to the A2 part of the hash + $body = $this->request->getBody($asString = true); + $this->request->setBody($body); + $A2 .= ':' . md5($body); + } else { + + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; + } + + $A2 = md5($A2); + + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + + return $this->digestParts['response'] == $validResponse; + + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + function requireLogin() { + + $qop = ''; + switch ($this->qop) { + case self::QOP_AUTH : + $qop = 'auth'; + break; + case self::QOP_AUTHINT : + $qop = 'auth-int'; + break; + case self::QOP_AUTH | self::QOP_AUTHINT : + $qop = 'auth,auth-int'; + break; + } + + $this->response->addHeader('WWW-Authenticate', 'Digest realm="' . $this->realm . '",qop="' . $qop . '",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->response->setStatus(401); + + } + + + /** + * This method returns the full digest string. + * + * It should be compatibile with mod_php format and other webservers. + * + * If the header could not be found, null will be returned + * + * @return mixed + */ + function getDigest() { + + return $this->request->getHeader('Authorization'); + + } + + + /** + * Parses the different pieces of the digest string into an array. + * + * This method returns false if an incomplete digest was supplied + * + * @param string $digest + * @return mixed + */ + protected function parseDigest($digest) { + + // protect against missing data + $needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1]; + $data = []; + + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Client.php b/htdocs/includes/sabre/sabre/http/lib/Client.php new file mode 100644 index 00000000000..0810c4a25b1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Client.php @@ -0,0 +1,601 @@ +<?php + +namespace Sabre\HTTP; + +use Sabre\Event\EventEmitter; +use Sabre\Uri; + +/** + * A rudimentary HTTP client. + * + * This object wraps PHP's curl extension and provides an easy way to send it a + * Request object, and return a Response object. + * + * This is by no means intended as the next best HTTP client, but it does the + * job and provides a simple integration with the rest of sabre/http. + * + * This client emits the following events: + * beforeRequest(RequestInterface $request) + * afterRequest(RequestInterface $request, ResponseInterface $response) + * error(RequestInterface $request, ResponseInterface $response, bool &$retry, int $retryCount) + * exception(RequestInterface $request, ClientException $e, bool &$retry, int $retryCount) + * + * The beforeRequest event allows you to do some last minute changes to the + * request before it's done, such as adding authentication headers. + * + * The afterRequest event will be emitted after the request is completed + * succesfully. + * + * If a HTTP error is returned (status code higher than 399) the error event is + * triggered. It's possible using this event to retry the request, by setting + * retry to true. + * + * The amount of times a request has retried is passed as $retryCount, which + * can be used to avoid retrying indefinitely. The first time the event is + * called, this will be 0. + * + * It's also possible to intercept specific http errors, by subscribing to for + * example 'error:401'. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Client extends EventEmitter { + + /** + * List of curl settings + * + * @var array + */ + protected $curlSettings = []; + + /** + * Wether or not exceptions should be thrown when a HTTP error is returned. + * + * @var bool + */ + protected $throwExceptions = false; + + /** + * The maximum number of times we'll follow a redirect. + * + * @var int + */ + protected $maxRedirects = 5; + + /** + * Initializes the client. + * + * @return void + */ + function __construct() { + + $this->curlSettings = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_NOBODY => false, + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + } + + /** + * Sends a request to a HTTP server, and returns a response. + * + * @param RequestInterface $request + * @return ResponseInterface + */ + function send(RequestInterface $request) { + + $this->emit('beforeRequest', [$request]); + + $retryCount = 0; + $redirects = 0; + + do { + + $doRedirect = false; + $retry = false; + + try { + + $response = $this->doRequest($request); + + $code = (int)$response->getStatus(); + + // We are doing in-PHP redirects, because curl's + // FOLLOW_LOCATION throws errors when PHP is configured with + // open_basedir. + // + // https://github.com/fruux/sabre-http/issues/12 + if (in_array($code, [301, 302, 307, 308]) && $redirects < $this->maxRedirects) { + + $oldLocation = $request->getUrl(); + + // Creating a new instance of the request object. + $request = clone $request; + + // Setting the new location + $request->setUrl(Uri\resolve( + $oldLocation, + $response->getHeader('Location') + )); + + $doRedirect = true; + $redirects++; + + } + + // This was a HTTP error + if ($code >= 400) { + + $this->emit('error', [$request, $response, &$retry, $retryCount]); + $this->emit('error:' . $code, [$request, $response, &$retry, $retryCount]); + + } + + } catch (ClientException $e) { + + $this->emit('exception', [$request, $e, &$retry, $retryCount]); + + // If retry was still set to false, it means no event handler + // dealt with the problem. In this case we just re-throw the + // exception. + if (!$retry) { + throw $e; + } + + } + + if ($retry) { + $retryCount++; + } + + } while ($retry || $doRedirect); + + $this->emit('afterRequest', [$request, $response]); + + if ($this->throwExceptions && $code >= 400) { + throw new ClientHttpException($response); + } + + return $response; + + } + + /** + * Sends a HTTP request asynchronously. + * + * Due to the nature of PHP, you must from time to time poll to see if any + * new responses came in. + * + * After calling sendAsync, you must therefore occasionally call the poll() + * method, or wait(). + * + * @param RequestInterface $request + * @param callable $success + * @param callable $error + * @return void + */ + function sendAsync(RequestInterface $request, callable $success = null, callable $error = null) { + + $this->emit('beforeRequest', [$request]); + $this->sendAsyncInternal($request, $success, $error); + $this->poll(); + + } + + + /** + * This method checks if any http requests have gotten results, and if so, + * call the appropriate success or error handlers. + * + * This method will return true if there are still requests waiting to + * return, and false if all the work is done. + * + * @return bool + */ + function poll() { + + // nothing to do? + if (!$this->curlMultiMap) { + return false; + } + + do { + $r = curl_multi_exec( + $this->curlMultiHandle, + $stillRunning + ); + } while ($r === CURLM_CALL_MULTI_PERFORM); + + do { + + messageQueue: + + $status = curl_multi_info_read( + $this->curlMultiHandle, + $messagesInQueue + ); + + if ($status && $status['msg'] === CURLMSG_DONE) { + + $resourceId = intval($status['handle']); + list( + $request, + $successCallback, + $errorCallback, + $retryCount, + ) = $this->curlMultiMap[$resourceId]; + unset($this->curlMultiMap[$resourceId]); + $curlResult = $this->parseCurlResult(curl_multi_getcontent($status['handle']), $status['handle']); + $retry = false; + + if ($curlResult['status'] === self::STATUS_CURLERROR) { + + $e = new ClientException($curlResult['curl_errmsg'], $curlResult['curl_errno']); + $this->emit('exception', [$request, $e, &$retry, $retryCount]); + + if ($retry) { + $retryCount++; + $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount); + goto messageQueue; + } + + $curlResult['request'] = $request; + + if ($errorCallback) { + $errorCallback($curlResult); + } + + } elseif ($curlResult['status'] === self::STATUS_HTTPERROR) { + + $this->emit('error', [$request, $curlResult['response'], &$retry, $retryCount]); + $this->emit('error:' . $curlResult['http_code'], [$request, $curlResult['response'], &$retry, $retryCount]); + + if ($retry) { + + $retryCount++; + $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount); + goto messageQueue; + + } + + $curlResult['request'] = $request; + + if ($errorCallback) { + $errorCallback($curlResult); + } + + } else { + + $this->emit('afterRequest', [$request, $curlResult['response']]); + + if ($successCallback) { + $successCallback($curlResult['response']); + } + + } + } + + } while ($messagesInQueue > 0); + + return count($this->curlMultiMap) > 0; + + } + + /** + * Processes every HTTP request in the queue, and waits till they are all + * completed. + * + * @return void + */ + function wait() { + + do { + curl_multi_select($this->curlMultiHandle); + $stillRunning = $this->poll(); + } while ($stillRunning); + + } + + /** + * If this is set to true, the Client will automatically throw exceptions + * upon HTTP errors. + * + * This means that if a response came back with a status code greater than + * or equal to 400, we will throw a ClientHttpException. + * + * This only works for the send() method. Throwing exceptions for + * sendAsync() is not supported. + * + * @param bool $throwExceptions + * @return void + */ + function setThrowExceptions($throwExceptions) { + + $this->throwExceptions = $throwExceptions; + + } + + /** + * Adds a CURL setting. + * + * These settings will be included in every HTTP request. + * + * @param int $name + * @param mixed $value + * @return void + */ + function addCurlSetting($name, $value) { + + $this->curlSettings[$name] = $value; + + } + + /** + * This method is responsible for performing a single request. + * + * @param RequestInterface $request + * @return ResponseInterface + */ + protected function doRequest(RequestInterface $request) { + + $settings = $this->createCurlSettingsArray($request); + + if (!$this->curlHandle) { + $this->curlHandle = curl_init(); + } + + curl_setopt_array($this->curlHandle, $settings); + $response = $this->curlExec($this->curlHandle); + $response = $this->parseCurlResult($response, $this->curlHandle); + + if ($response['status'] === self::STATUS_CURLERROR) { + throw new ClientException($response['curl_errmsg'], $response['curl_errno']); + } + + return $response['response']; + + } + + /** + * Cached curl handle. + * + * By keeping this resource around for the lifetime of this object, things + * like persistent connections are possible. + * + * @var resource + */ + private $curlHandle; + + /** + * Handler for curl_multi requests. + * + * The first time sendAsync is used, this will be created. + * + * @var resource + */ + private $curlMultiHandle; + + /** + * Has a list of curl handles, as well as their associated success and + * error callbacks. + * + * @var array + */ + private $curlMultiMap = []; + + /** + * Turns a RequestInterface object into an array with settings that can be + * fed to curl_setopt + * + * @param RequestInterface $request + * @return array + */ + protected function createCurlSettingsArray(RequestInterface $request) { + + $settings = $this->curlSettings; + + switch ($request->getMethod()) { + case 'HEAD' : + $settings[CURLOPT_NOBODY] = true; + $settings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; + $settings[CURLOPT_POSTFIELDS] = ''; + $settings[CURLOPT_PUT] = false; + break; + case 'GET' : + $settings[CURLOPT_CUSTOMREQUEST] = 'GET'; + $settings[CURLOPT_POSTFIELDS] = ''; + $settings[CURLOPT_PUT] = false; + break; + default : + $body = $request->getBody(); + if (is_resource($body)) { + // This needs to be set to PUT, regardless of the actual + // method used. Without it, INFILE will be ignored for some + // reason. + $settings[CURLOPT_PUT] = true; + $settings[CURLOPT_INFILE] = $request->getBody(); + } else { + // For security we cast this to a string. If somehow an array could + // be passed here, it would be possible for an attacker to use @ to + // post local files. + $settings[CURLOPT_POSTFIELDS] = (string)$body; + } + $settings[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); + break; + + } + + $nHeaders = []; + foreach ($request->getHeaders() as $key => $values) { + + foreach ($values as $value) { + $nHeaders[] = $key . ': ' . $value; + } + + } + $settings[CURLOPT_HTTPHEADER] = $nHeaders; + $settings[CURLOPT_URL] = $request->getUrl(); + // FIXME: CURLOPT_PROTOCOLS is currently unsupported by HHVM + if (defined('CURLOPT_PROTOCOLS')) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + // FIXME: CURLOPT_REDIR_PROTOCOLS is currently unsupported by HHVM + if (defined('CURLOPT_REDIR_PROTOCOLS')) { + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + return $settings; + + } + + const STATUS_SUCCESS = 0; + const STATUS_CURLERROR = 1; + const STATUS_HTTPERROR = 2; + + /** + * Parses the result of a curl call in a format that's a bit more + * convenient to work with. + * + * The method returns an array with the following elements: + * * status - one of the 3 STATUS constants. + * * curl_errno - A curl error number. Only set if status is + * STATUS_CURLERROR. + * * curl_errmsg - A current error message. Only set if status is + * STATUS_CURLERROR. + * * response - Response object. Only set if status is STATUS_SUCCESS, or + * STATUS_HTTPERROR. + * * http_code - HTTP status code, as an int. Only set if Only set if + * status is STATUS_SUCCESS, or STATUS_HTTPERROR + * + * @param string $response + * @param resource $curlHandle + * @return Response + */ + protected function parseCurlResult($response, $curlHandle) { + + list( + $curlInfo, + $curlErrNo, + $curlErrMsg + ) = $this->curlStuff($curlHandle); + + if ($curlErrNo) { + return [ + 'status' => self::STATUS_CURLERROR, + 'curl_errno' => $curlErrNo, + 'curl_errmsg' => $curlErrMsg, + ]; + } + + $headerBlob = substr($response, 0, $curlInfo['header_size']); + // In the case of 204 No Content, strlen($response) == $curlInfo['header_size]. + // This will cause substr($response, $curlInfo['header_size']) return FALSE instead of NULL + // An exception will be thrown when calling getBodyAsString then + $responseBody = substr($response, $curlInfo['header_size']) ?: null; + + unset($response); + + // In the case of 100 Continue, or redirects we'll have multiple lists + // of headers for each separate HTTP response. We can easily split this + // because they are separated by \r\n\r\n + $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n")); + + // We only care about the last set of headers + $headerBlob = $headerBlob[count($headerBlob) - 1]; + + // Splitting headers + $headerBlob = explode("\r\n", $headerBlob); + + $response = new Response(); + $response->setStatus($curlInfo['http_code']); + + foreach ($headerBlob as $header) { + $parts = explode(':', $header, 2); + if (count($parts) == 2) { + $response->addHeader(trim($parts[0]), trim($parts[1])); + } + } + + $response->setBody($responseBody); + + $httpCode = intval($response->getStatus()); + + return [ + 'status' => $httpCode >= 400 ? self::STATUS_HTTPERROR : self::STATUS_SUCCESS, + 'response' => $response, + 'http_code' => $httpCode, + ]; + + } + + /** + * Sends an asynchronous HTTP request. + * + * We keep this in a separate method, so we can call it without triggering + * the beforeRequest event and don't do the poll(). + * + * @param RequestInterface $request + * @param callable $success + * @param callable $error + * @param int $retryCount + */ + protected function sendAsyncInternal(RequestInterface $request, callable $success, callable $error, $retryCount = 0) { + + if (!$this->curlMultiHandle) { + $this->curlMultiHandle = curl_multi_init(); + } + $curl = curl_init(); + curl_setopt_array( + $curl, + $this->createCurlSettingsArray($request) + ); + curl_multi_add_handle($this->curlMultiHandle, $curl); + $this->curlMultiMap[intval($curl)] = [ + $request, + $success, + $error, + $retryCount + ]; + + } + + // @codeCoverageIgnoreStart + + /** + * Calls curl_exec + * + * This method exists so it can easily be overridden and mocked. + * + * @param resource $curlHandle + * @return string + */ + protected function curlExec($curlHandle) { + + return curl_exec($curlHandle); + + } + + /** + * Returns a bunch of information about a curl request. + * + * This method exists so it can easily be overridden and mocked. + * + * @param resource $curlHandle + * @return array + */ + protected function curlStuff($curlHandle) { + + return [ + curl_getinfo($curlHandle), + curl_errno($curlHandle), + curl_error($curlHandle), + ]; + + } + // @codeCoverageIgnoreEnd + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/ClientException.php b/htdocs/includes/sabre/sabre/http/lib/ClientException.php new file mode 100644 index 00000000000..69631f44eef --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/ClientException.php @@ -0,0 +1,15 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This exception may be emitted by the HTTP\Client class, in case there was a + * problem emitting the request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ClientException extends \Exception { + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/ClientHttpException.php b/htdocs/includes/sabre/sabre/http/lib/ClientHttpException.php new file mode 100644 index 00000000000..2923ef3b53a --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/ClientHttpException.php @@ -0,0 +1,58 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This exception represents a HTTP error coming from the Client. + * + * By default the Client will not emit these, this has to be explicitly enabled + * with the setThrowExceptions method. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ClientHttpException extends \Exception implements HttpException { + + /** + * Response object + * + * @var ResponseInterface + */ + protected $response; + + /** + * Constructor + * + * @param ResponseInterface $response + */ + function __construct(ResponseInterface $response) { + + $this->response = $response; + parent::__construct($response->getStatusText(), $response->getStatus()); + + } + + /** + * The http status code for the error. + * + * @return int + */ + function getHttpStatus() { + + return $this->response->getStatus(); + + } + + /** + * Returns the full response object. + * + * @return ResponseInterface + */ + function getResponse() { + + return $this->response; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/HttpException.php b/htdocs/includes/sabre/sabre/http/lib/HttpException.php new file mode 100644 index 00000000000..1303dec970e --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/HttpException.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\HTTP; + +/** + * An exception representing a HTTP error. + * + * This can be used as a generic exception in your application, if you'd like + * to map HTTP errors to exceptions. + * + * If you'd like to use this, create a new exception class, extending Exception + * and implementing this interface. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface HttpException { + + /** + * The http status code for the error. + * + * This may either be just the number, or a number and a human-readable + * message, separated by a space. + * + * @return string|null + */ + function getHttpStatus(); + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Message.php b/htdocs/includes/sabre/sabre/http/lib/Message.php new file mode 100644 index 00000000000..45bd183980d --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Message.php @@ -0,0 +1,314 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This is the abstract base class for both the Request and Response objects. + * + * This object contains a few simple methods that are shared by both. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class Message implements MessageInterface { + + /** + * Request body + * + * This should be a stream resource + * + * @var resource + */ + protected $body; + + /** + * Contains the list of HTTP headers + * + * @var array + */ + protected $headers = []; + + /** + * HTTP message version (1.0 or 1.1) + * + * @var string + */ + protected $httpVersion = '1.1'; + + /** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ + function getBodyAsStream() { + + $body = $this->getBody(); + if (is_string($body) || is_null($body)) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $body); + rewind($stream); + return $stream; + } + return $body; + + } + + /** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ + function getBodyAsString() { + + $body = $this->getBody(); + if (is_string($body)) { + return $body; + } + if (is_null($body)) { + return ''; + } + $contentLength = $this->getHeader('Content-Length'); + if (is_int($contentLength) || ctype_digit($contentLength)) { + return stream_get_contents($body, $contentLength); + } else { + return stream_get_contents($body); + } + } + + /** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ + function getBody() { + + return $this->body; + + } + + /** + * Replaces the body resource with a new stream or string. + * + * @param resource|string $body + */ + function setBody($body) { + + $this->body = $body; + + } + + /** + * Returns all the HTTP headers as an array. + * + * Every header is returned as an array, with one or more values. + * + * @return array + */ + function getHeaders() { + + $result = []; + foreach ($this->headers as $headerInfo) { + $result[$headerInfo[0]] = $headerInfo[1]; + } + return $result; + + } + + /** + * Will return true or false, depending on if a HTTP header exists. + * + * @param string $name + * @return bool + */ + function hasHeader($name) { + + return isset($this->headers[strtolower($name)]); + + } + + /** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * If the header does not exist, this method must return null. + * + * If a header appeared more than once in a HTTP request, this method will + * concatenate all the values with a comma. + * + * Note that this not make sense for all headers. Some, such as + * `Set-Cookie` cannot be logically combined with a comma. In those cases + * you *should* use getHeaderAsArray(). + * + * @param string $name + * @return string|null + */ + function getHeader($name) { + + $name = strtolower($name); + + if (isset($this->headers[$name])) { + return implode(',', $this->headers[$name][1]); + } + return null; + + } + + /** + * Returns a HTTP header as an array. + * + * For every time the HTTP header appeared in the request or response, an + * item will appear in the array. + * + * If the header did not exists, this method will return an empty array. + * + * @param string $name + * @return string[] + */ + function getHeaderAsArray($name) { + + $name = strtolower($name); + + if (isset($this->headers[$name])) { + return $this->headers[$name][1]; + } + + return []; + + } + + /** + * Updates a HTTP header. + * + * The case-sensitivity of the name value must be retained as-is. + * + * If the header already existed, it will be overwritten. + * + * @param string $name + * @param string|string[] $value + * @return void + */ + function setHeader($name, $value) { + + $this->headers[strtolower($name)] = [$name, (array)$value]; + + } + + /** + * Sets a new set of HTTP headers. + * + * The headers array should contain headernames for keys, and their value + * should be specified as either a string or an array. + * + * Any header that already existed will be overwritten. + * + * @param array $headers + * @return void + */ + function setHeaders(array $headers) { + + foreach ($headers as $name => $value) { + $this->setHeader($name, $value); + } + + } + + /** + * Adds a HTTP header. + * + * This method will not overwrite any existing HTTP header, but instead add + * another value. Individual values can be retrieved with + * getHeadersAsArray. + * + * @param string $name + * @param string $value + * @return void + */ + function addHeader($name, $value) { + + $lName = strtolower($name); + if (isset($this->headers[$lName])) { + $this->headers[$lName][1] = array_merge( + $this->headers[$lName][1], + (array)$value + ); + } else { + $this->headers[$lName] = [ + $name, + (array)$value + ]; + } + + } + + /** + * Adds a new set of HTTP headers. + * + * Any existing headers will not be overwritten. + * + * @param array $headers + * @return void + */ + function addHeaders(array $headers) { + + foreach ($headers as $name => $value) { + $this->addHeader($name, $value); + } + + } + + + /** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insensitive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @param string $name + * @return bool + */ + function removeHeader($name) { + + $name = strtolower($name); + if (!isset($this->headers[$name])) { + return false; + } + unset($this->headers[$name]); + return true; + + } + + /** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ + function setHttpVersion($version) { + + $this->httpVersion = $version; + + } + + /** + * Returns the HTTP version. + * + * @return string + */ + function getHttpVersion() { + + return $this->httpVersion; + + } +} diff --git a/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php b/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php new file mode 100644 index 00000000000..1cb32da2259 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php @@ -0,0 +1,251 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This trait contains a bunch of methods, shared by both the RequestDecorator + * and the ResponseDecorator. + * + * Didn't seem needed to create a full class for this, so we're just + * implementing it as a trait. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +trait MessageDecoratorTrait { + + /** + * The inner request object. + * + * All method calls will be forwarded here. + * + * @var MessageInterface + */ + protected $inner; + + /** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ + function getBodyAsStream() { + + return $this->inner->getBodyAsStream(); + + } + + /** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ + function getBodyAsString() { + + return $this->inner->getBodyAsString(); + + } + + /** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ + function getBody() { + + return $this->inner->getBody(); + + } + + /** + * Updates the body resource with a new stream. + * + * @param resource $body + * @return void + */ + function setBody($body) { + + $this->inner->setBody($body); + + } + + /** + * Returns all the HTTP headers as an array. + * + * Every header is returned as an array, with one or more values. + * + * @return array + */ + function getHeaders() { + + return $this->inner->getHeaders(); + + } + + /** + * Will return true or false, depending on if a HTTP header exists. + * + * @param string $name + * @return bool + */ + function hasHeader($name) { + + return $this->inner->hasHeader($name); + + } + + /** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * If the header does not exist, this method must return null. + * + * If a header appeared more than once in a HTTP request, this method will + * concatenate all the values with a comma. + * + * Note that this not make sense for all headers. Some, such as + * `Set-Cookie` cannot be logically combined with a comma. In those cases + * you *should* use getHeaderAsArray(). + * + * @param string $name + * @return string|null + */ + function getHeader($name) { + + return $this->inner->getHeader($name); + + } + + /** + * Returns a HTTP header as an array. + * + * For every time the HTTP header appeared in the request or response, an + * item will appear in the array. + * + * If the header did not exists, this method will return an empty array. + * + * @param string $name + * @return string[] + */ + function getHeaderAsArray($name) { + + return $this->inner->getHeaderAsArray($name); + + } + + /** + * Updates a HTTP header. + * + * The case-sensitivity of the name value must be retained as-is. + * + * If the header already existed, it will be overwritten. + * + * @param string $name + * @param string|string[] $value + * @return void + */ + function setHeader($name, $value) { + + $this->inner->setHeader($name, $value); + + } + + /** + * Sets a new set of HTTP headers. + * + * The headers array should contain headernames for keys, and their value + * should be specified as either a string or an array. + * + * Any header that already existed will be overwritten. + * + * @param array $headers + * @return void + */ + function setHeaders(array $headers) { + + $this->inner->setHeaders($headers); + + } + + /** + * Adds a HTTP header. + * + * This method will not overwrite any existing HTTP header, but instead add + * another value. Individual values can be retrieved with + * getHeadersAsArray. + * + * @param string $name + * @param string $value + * @return void + */ + function addHeader($name, $value) { + + $this->inner->addHeader($name, $value); + + } + + /** + * Adds a new set of HTTP headers. + * + * Any existing headers will not be overwritten. + * + * @param array $headers + * @return void + */ + function addHeaders(array $headers) { + + $this->inner->addHeaders($headers); + + } + + + /** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insensitive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @param string $name + * @return bool + */ + function removeHeader($name) { + + return $this->inner->removeHeader($name); + + } + + /** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ + function setHttpVersion($version) { + + $this->inner->setHttpVersion($version); + + } + + /** + * Returns the HTTP version. + * + * @return string + */ + function getHttpVersion() { + + return $this->inner->getHttpVersion(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php b/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php new file mode 100644 index 00000000000..df55beb2ffe --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php @@ -0,0 +1,178 @@ +<?php + +namespace Sabre\HTTP; + +/** + * The MessageInterface is the base interface that's used by both + * the RequestInterface and ResponseInterface. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface MessageInterface { + + /** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ + function getBodyAsStream(); + + /** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ + function getBodyAsString(); + + /** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ + function getBody(); + + /** + * Updates the body resource with a new stream. + * + * @param resource|string $body + * @return void + */ + function setBody($body); + + /** + * Returns all the HTTP headers as an array. + * + * Every header is returned as an array, with one or more values. + * + * @return array + */ + function getHeaders(); + + /** + * Will return true or false, depending on if a HTTP header exists. + * + * @param string $name + * @return bool + */ + function hasHeader($name); + + /** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * If the header does not exist, this method must return null. + * + * If a header appeared more than once in a HTTP request, this method will + * concatenate all the values with a comma. + * + * Note that this not make sense for all headers. Some, such as + * `Set-Cookie` cannot be logically combined with a comma. In those cases + * you *should* use getHeaderAsArray(). + * + * @param string $name + * @return string|null + */ + function getHeader($name); + + /** + * Returns a HTTP header as an array. + * + * For every time the HTTP header appeared in the request or response, an + * item will appear in the array. + * + * If the header did not exists, this method will return an empty array. + * + * @param string $name + * @return string[] + */ + function getHeaderAsArray($name); + + /** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * If the header already existed, it will be overwritten. + * + * @param string $name + * @param string|string[] $value + * @return void + */ + function setHeader($name, $value); + + /** + * Sets a new set of HTTP headers. + * + * The headers array should contain headernames for keys, and their value + * should be specified as either a string or an array. + * + * Any header that already existed will be overwritten. + * + * @param array $headers + * @return void + */ + function setHeaders(array $headers); + + /** + * Adds a HTTP header. + * + * This method will not overwrite any existing HTTP header, but instead add + * another value. Individual values can be retrieved with + * getHeadersAsArray. + * + * @param string $name + * @param string $value + * @return void + */ + function addHeader($name, $value); + + /** + * Adds a new set of HTTP headers. + * + * Any existing headers will not be overwritten. + * + * @param array $headers + * @return void + */ + function addHeaders(array $headers); + + /** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @param string $name + * @return bool + */ + function removeHeader($name); + + /** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ + function setHttpVersion($version); + + /** + * Returns the HTTP version. + * + * @return string + */ + function getHttpVersion(); + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Request.php b/htdocs/includes/sabre/sabre/http/lib/Request.php new file mode 100644 index 00000000000..dfa3d5b4857 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Request.php @@ -0,0 +1,316 @@ +<?php + +namespace Sabre\HTTP; + +use InvalidArgumentException; +use Sabre\Uri; + +/** + * The Request class represents a single HTTP request. + * + * You can either simply construct the object from scratch, or if you need + * access to the current HTTP request, use Sapi::getRequest. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Request extends Message implements RequestInterface { + + /** + * HTTP Method + * + * @var string + */ + protected $method; + + /** + * Request Url + * + * @var string + */ + protected $url; + + /** + * Creates the request object + * + * @param string $method + * @param string $url + * @param array $headers + * @param resource $body + */ + function __construct($method = null, $url = null, array $headers = null, $body = null) { + + if (is_array($method)) { + throw new InvalidArgumentException('The first argument for this constructor should be a string or null, not an array. Did you upgrade from sabre/http 1.0 to 2.0?'); + } + if (!is_null($method)) $this->setMethod($method); + if (!is_null($url)) $this->setUrl($url); + if (!is_null($headers)) $this->setHeaders($headers); + if (!is_null($body)) $this->setBody($body); + + } + + /** + * Returns the current HTTP method + * + * @return string + */ + function getMethod() { + + return $this->method; + + } + + /** + * Sets the HTTP method + * + * @param string $method + * @return void + */ + function setMethod($method) { + + $this->method = $method; + + } + + /** + * Returns the request url. + * + * @return string + */ + function getUrl() { + + return $this->url; + + } + + /** + * Sets the request url. + * + * @param string $url + * @return void + */ + function setUrl($url) { + + $this->url = $url; + + } + + /** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ + function getQueryParameters() { + + $url = $this->getUrl(); + if (($index = strpos($url, '?')) === false) { + return []; + } else { + parse_str(substr($url, $index + 1), $queryParams); + return $queryParams; + } + + } + + /** + * Sets the absolute url. + * + * @param string $url + * @return void + */ + function setAbsoluteUrl($url) { + + $this->absoluteUrl = $url; + + } + + /** + * Returns the absolute url. + * + * @return string + */ + function getAbsoluteUrl() { + + return $this->absoluteUrl; + + } + + /** + * Base url + * + * @var string + */ + protected $baseUrl = '/'; + + /** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * @param string $url + * @return void + */ + function setBaseUrl($url) { + + $this->baseUrl = $url; + + } + + /** + * Returns the current base url. + * + * @return string + */ + function getBaseUrl() { + + return $this->baseUrl; + + } + + /** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ + function getPath() { + + // Removing duplicated slashes. + $uri = str_replace('//', '/', $this->getUrl()); + + $uri = Uri\normalize($uri); + $baseUri = Uri\normalize($this->getBaseUrl()); + + if (strpos($uri, $baseUri) === 0) { + + // We're not interested in the query part (everything after the ?). + list($uri) = explode('?', $uri); + return trim(URLUtil::decodePath(substr($uri, strlen($baseUri))), '/'); + + } + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + elseif ($uri . '/' === $baseUri) { + + return ''; + + } + + throw new \LogicException('Requested uri (' . $this->getUrl() . ') is out of base uri (' . $this->getBaseUrl() . ')'); + } + + /** + * Equivalent of PHP's $_POST. + * + * @var array + */ + protected $postData = []; + + /** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ + function setPostData(array $postData) { + + $this->postData = $postData; + + } + + /** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ + function getPostData() { + + return $this->postData; + + } + + /** + * An array containing the raw _SERVER array. + * + * @var array + */ + protected $rawServerData; + + /** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ + function getRawServerValue($valueName) { + + if (isset($this->rawServerData[$valueName])) { + return $this->rawServerData[$valueName]; + } + + } + + /** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ + function setRawServerData(array $data) { + + $this->rawServerData = $data; + + } + + /** + * Serializes the request object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + $out = $this->getMethod() . ' ' . $this->getUrl() . ' HTTP/' . $this->getHTTPVersion() . "\r\n"; + + foreach ($this->getHeaders() as $key => $value) { + foreach ($value as $v) { + if ($key === 'Authorization') { + list($v) = explode(' ', $v, 2); + $v .= ' REDACTED'; + } + $out .= $key . ": " . $v . "\r\n"; + } + } + $out .= "\r\n"; + $out .= $this->getBodyAsString(); + + return $out; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php b/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php new file mode 100644 index 00000000000..7ee3f6fc85c --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php @@ -0,0 +1,231 @@ +<?php + +namespace Sabre\HTTP; + +/** + * Request Decorator + * + * This helper class allows you to easily create decorators for the Request + * object. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class RequestDecorator implements RequestInterface { + + use MessageDecoratorTrait; + + /** + * Constructor. + * + * @param RequestInterface $inner + */ + function __construct(RequestInterface $inner) { + + $this->inner = $inner; + + } + + /** + * Returns the current HTTP method + * + * @return string + */ + function getMethod() { + + return $this->inner->getMethod(); + + } + + /** + * Sets the HTTP method + * + * @param string $method + * @return void + */ + function setMethod($method) { + + $this->inner->setMethod($method); + + } + + /** + * Returns the request url. + * + * @return string + */ + function getUrl() { + + return $this->inner->getUrl(); + + } + + /** + * Sets the request url. + * + * @param string $url + * @return void + */ + function setUrl($url) { + + $this->inner->setUrl($url); + + } + + /** + * Returns the absolute url. + * + * @return string + */ + function getAbsoluteUrl() { + + return $this->inner->getAbsoluteUrl(); + + } + + /** + * Sets the absolute url. + * + * @param string $url + * @return void + */ + function setAbsoluteUrl($url) { + + $this->inner->setAbsoluteUrl($url); + + } + + /** + * Returns the current base url. + * + * @return string + */ + function getBaseUrl() { + + return $this->inner->getBaseUrl(); + + } + + /** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * The base url should default to / + * + * @param string $url + * @return void + */ + function setBaseUrl($url) { + + $this->inner->setBaseUrl($url); + + } + + /** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ + function getPath() { + + return $this->inner->getPath(); + + } + + /** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ + function getQueryParameters() { + + return $this->inner->getQueryParameters(); + + } + + /** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ + function getPostData() { + + return $this->inner->getPostData(); + + } + + /** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ + function setPostData(array $postData) { + + $this->inner->setPostData($postData); + + } + + + /** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ + function getRawServerValue($valueName) { + + return $this->inner->getRawServerValue($valueName); + + } + + /** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ + function setRawServerData(array $data) { + + $this->inner->setRawServerData($data); + + } + + /** + * Serializes the request object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + return $this->inner->__toString(); + + } +} diff --git a/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php b/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php new file mode 100644 index 00000000000..63d9cbb51a0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php @@ -0,0 +1,147 @@ +<?php + +namespace Sabre\HTTP; + +/** + * The RequestInterface represents a HTTP request. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface RequestInterface extends MessageInterface { + + /** + * Returns the current HTTP method + * + * @return string + */ + function getMethod(); + + /** + * Sets the HTTP method + * + * @param string $method + * @return void + */ + function setMethod($method); + + /** + * Returns the request url. + * + * @return string + */ + function getUrl(); + + /** + * Sets the request url. + * + * @param string $url + * @return void + */ + function setUrl($url); + + /** + * Returns the absolute url. + * + * @return string + */ + function getAbsoluteUrl(); + + /** + * Sets the absolute url. + * + * @param string $url + * @return void + */ + function setAbsoluteUrl($url); + + /** + * Returns the current base url. + * + * @return string + */ + function getBaseUrl(); + + /** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * The base url should default to / + * + * @param string $url + * @return void + */ + function setBaseUrl($url); + + /** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ + function getPath(); + + /** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ + function getQueryParameters(); + + /** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ + function getPostData(); + + /** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ + function setPostData(array $postData); + + /** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ + function getRawServerValue($valueName); + + /** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ + function setRawServerData(array $data); + + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Response.php b/htdocs/includes/sabre/sabre/http/lib/Response.php new file mode 100644 index 00000000000..01920d8d9fb --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Response.php @@ -0,0 +1,193 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This class represents a single HTTP response. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Response extends Message implements ResponseInterface { + + /** + * This is the list of currently registered HTTP status codes. + * + * @var array + */ + static $statusCodes = [ + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authorative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC 4918 + 208 => 'Already Reported', // RFC 5842 + 226 => 'IM Used', // RFC 3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC 2324 + 421 => 'Misdirected Request', // RFC7540 (HTTP/2) + 422 => 'Unprocessable Entity', // RFC 4918 + 423 => 'Locked', // RFC 4918 + 424 => 'Failed Dependency', // RFC 4918 + 426 => 'Upgrade Required', + 428 => 'Precondition Required', // RFC 6585 + 429 => 'Too Many Requests', // RFC 6585 + 431 => 'Request Header Fields Too Large', // RFC 6585 + 451 => 'Unavailable For Legal Reasons', // draft-tbray-http-legally-restricted-status + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', // RFC 4918 + 508 => 'Loop Detected', // RFC 5842 + 509 => 'Bandwidth Limit Exceeded', // non-standard + 510 => 'Not extended', + 511 => 'Network Authentication Required', // RFC 6585 + ]; + + /** + * HTTP status code + * + * @var int + */ + protected $status; + + /** + * HTTP status text + * + * @var string + */ + protected $statusText; + + /** + * Creates the response object + * + * @param string|int $status + * @param array $headers + * @param resource $body + */ + function __construct($status = null, array $headers = null, $body = null) { + + if (!is_null($status)) $this->setStatus($status); + if (!is_null($headers)) $this->setHeaders($headers); + if (!is_null($body)) $this->setBody($body); + + } + + + /** + * Returns the current HTTP status code. + * + * @return int + */ + function getStatus() { + + return $this->status; + + } + + /** + * Returns the human-readable status string. + * + * In the case of a 200, this may for example be 'OK'. + * + * @return string + */ + function getStatusText() { + + return $this->statusText; + + } + + /** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @throws \InvalidArgumentException + * @return void + */ + function setStatus($status) { + + if (ctype_digit($status) || is_int($status)) { + + $statusCode = $status; + $statusText = isset(self::$statusCodes[$status]) ? self::$statusCodes[$status] : 'Unknown'; + + } else { + list( + $statusCode, + $statusText + ) = explode(' ', $status, 2); + } + if ($statusCode < 100 || $statusCode > 999) { + throw new \InvalidArgumentException('The HTTP status code must be exactly 3 digits'); + } + + $this->status = $statusCode; + $this->statusText = $statusText; + + } + + /** + * Serializes the response object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + $str = 'HTTP/' . $this->httpVersion . ' ' . $this->getStatus() . ' ' . $this->getStatusText() . "\r\n"; + foreach ($this->getHeaders() as $key => $value) { + foreach ($value as $v) { + $str .= $key . ": " . $v . "\r\n"; + } + } + $str .= "\r\n"; + $str .= $this->getBodyAsString(); + return $str; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php b/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php new file mode 100644 index 00000000000..db3a6750718 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php @@ -0,0 +1,84 @@ +<?php + +namespace Sabre\HTTP; + +/** + * Response Decorator + * + * This helper class allows you to easily create decorators for the Response + * object. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ResponseDecorator implements ResponseInterface { + + use MessageDecoratorTrait; + + /** + * Constructor. + * + * @param ResponseInterface $inner + */ + function __construct(ResponseInterface $inner) { + + $this->inner = $inner; + + } + + /** + * Returns the current HTTP status code. + * + * @return int + */ + function getStatus() { + + return $this->inner->getStatus(); + + } + + + /** + * Returns the human-readable status string. + * + * In the case of a 200, this may for example be 'OK'. + * + * @return string + */ + function getStatusText() { + + return $this->inner->getStatusText(); + + } + /** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @return void + */ + function setStatus($status) { + + $this->inner->setStatus($status); + + } + + /** + * Serializes the request object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + return $this->inner->__toString(); + + } +} diff --git a/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php b/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php new file mode 100644 index 00000000000..411cdc06cbb --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php @@ -0,0 +1,45 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This interface represents a HTTP response. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface ResponseInterface extends MessageInterface { + + /** + * Returns the current HTTP status code. + * + * @return int + */ + function getStatus(); + + /** + * Returns the human-readable status string. + * + * In the case of a 200, this may for example be 'OK'. + * + * @return string + */ + function getStatusText(); + + /** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @throws \InvalidArgumentException + * @return void + */ + function setStatus($status); + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Sapi.php b/htdocs/includes/sabre/sabre/http/lib/Sapi.php new file mode 100644 index 00000000000..6c83c8719d5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Sapi.php @@ -0,0 +1,194 @@ +<?php + +namespace Sabre\HTTP; + +/** + * PHP SAPI + * + * This object is responsible for: + * 1. Constructing a Request object based on the current HTTP request sent to + * the PHP process. + * 2. Sending the Response object back to the client. + * + * It could be said that this class provides a mapping between the Request and + * Response objects, and php's: + * + * * $_SERVER + * * $_POST + * * $_FILES + * * php://input + * * echo() + * * header() + * * php://output + * + * You can choose to either call all these methods statically, but you can also + * instantiate this as an object to allow for polymorhpism. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Sapi { + + /** + * This static method will create a new Request object, based on the + * current PHP request. + * + * @return Request + */ + static function getRequest() { + + $r = self::createFromServerArray($_SERVER); + $r->setBody(fopen('php://input', 'r')); + $r->setPostData($_POST); + return $r; + + } + + /** + * Sends the HTTP response back to a HTTP client. + * + * This calls php's header() function and streams the body to php://output. + * + * @param ResponseInterface $response + * @return void + */ + static function sendResponse(ResponseInterface $response) { + + header('HTTP/' . $response->getHttpVersion() . ' ' . $response->getStatus() . ' ' . $response->getStatusText()); + foreach ($response->getHeaders() as $key => $value) { + + foreach ($value as $k => $v) { + if ($k === 0) { + header($key . ': ' . $v); + } else { + header($key . ': ' . $v, false); + } + } + + } + + $body = $response->getBody(); + if (is_null($body)) return; + + $contentLength = $response->getHeader('Content-Length'); + if ($contentLength !== null) { + $output = fopen('php://output', 'wb'); + if (is_resource($body) && get_resource_type($body) == 'stream') { + stream_copy_to_stream($body, $output, $contentLength); + } else { + fwrite($output, $body, $contentLength); + } + } else { + file_put_contents('php://output', $body); + } + + if (is_resource($body)) { + fclose($body); + } + + } + + /** + * This static method will create a new Request object, based on a PHP + * $_SERVER array. + * + * @param array $serverArray + * @return Request + */ + static function createFromServerArray(array $serverArray) { + + $headers = []; + $method = null; + $url = null; + $httpVersion = '1.1'; + + $protocol = 'http'; + $hostName = 'localhost'; + + foreach ($serverArray as $key => $value) { + + switch ($key) { + + case 'SERVER_PROTOCOL' : + if ($value === 'HTTP/1.0') { + $httpVersion = '1.0'; + } + break; + case 'REQUEST_METHOD' : + $method = $value; + break; + case 'REQUEST_URI' : + $url = $value; + break; + + // These sometimes show up without a HTTP_ prefix + case 'CONTENT_TYPE' : + $headers['Content-Type'] = $value; + break; + case 'CONTENT_LENGTH' : + $headers['Content-Length'] = $value; + break; + + // mod_php on apache will put credentials in these variables. + // (fast)cgi does not usually do this, however. + case 'PHP_AUTH_USER' : + if (isset($serverArray['PHP_AUTH_PW'])) { + $headers['Authorization'] = 'Basic ' . base64_encode($value . ':' . $serverArray['PHP_AUTH_PW']); + } + break; + + // Similarly, mod_php may also screw around with digest auth. + case 'PHP_AUTH_DIGEST' : + $headers['Authorization'] = 'Digest ' . $value; + break; + + // Apache may prefix the HTTP_AUTHORIZATION header with + // REDIRECT_, if mod_rewrite was used. + case 'REDIRECT_HTTP_AUTHORIZATION' : + $headers['Authorization'] = $value; + break; + + case 'HTTP_HOST' : + $hostName = $value; + $headers['Host'] = $value; + break; + + case 'HTTPS' : + if (!empty($value) && $value !== 'off') { + $protocol = 'https'; + } + break; + + default : + if (substr($key, 0, 5) === 'HTTP_') { + // It's a HTTP header + + // Normalizing it to be prettier + $header = strtolower(substr($key, 5)); + + // Transforming dashes into spaces, and uppercasing + // every first letter. + $header = ucwords(str_replace('_', ' ', $header)); + + // Turning spaces into dashes. + $header = str_replace(' ', '-', $header); + $headers[$header] = $value; + + } + break; + + + } + + } + + $r = new Request($method, $url, $headers); + $r->setHttpVersion($httpVersion); + $r->setRawServerData($serverArray); + $r->setAbsoluteUrl($protocol . '://' . $hostName . $url); + return $r; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/URLUtil.php b/htdocs/includes/sabre/sabre/http/lib/URLUtil.php new file mode 100644 index 00000000000..85c0e11504a --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/URLUtil.php @@ -0,0 +1,103 @@ +<?php + +namespace Sabre\HTTP; + +use Sabre\URI; + +/** + * URL utility class + * + * Note: this class is deprecated. All its functionality moved to functions.php + * or sabre\uri. + * + * @deprecated + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class URLUtil { + + /** + * Encodes the path of a url. + * + * slashes (/) are treated as path-separators. + * + * @deprecated use \Sabre\HTTP\encodePath() + * @param string $path + * @return string + */ + static function encodePath($path) { + + return encodePath($path); + + } + + /** + * Encodes a 1 segment of a path + * + * Slashes are considered part of the name, and are encoded as %2f + * + * @deprecated use \Sabre\HTTP\encodePathSegment() + * @param string $pathSegment + * @return string + */ + static function encodePathSegment($pathSegment) { + + return encodePathSegment($pathSegment); + + } + + /** + * Decodes a url-encoded path + * + * @deprecated use \Sabre\HTTP\decodePath + * @param string $path + * @return string + */ + static function decodePath($path) { + + return decodePath($path); + + } + + /** + * Decodes a url-encoded path segment + * + * @deprecated use \Sabre\HTTP\decodePathSegment() + * @param string $path + * @return string + */ + static function decodePathSegment($path) { + + return decodePathSegment($path); + + } + + /** + * Returns the 'dirname' and 'basename' for a path. + * + * @deprecated Use Sabre\Uri\split(). + * @param string $path + * @return array + */ + static function splitPath($path) { + + return Uri\split($path); + + } + + /** + * Resolves relative urls, like a browser would. + * + * @deprecated Use Sabre\Uri\resolve(). + * @param string $basePath + * @param string $newPath + * @return string + */ + static function resolve($basePath, $newPath) { + + return Uri\resolve($basePath, $newPath); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Util.php b/htdocs/includes/sabre/sabre/http/lib/Util.php new file mode 100644 index 00000000000..e3f13a645b9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Util.php @@ -0,0 +1,74 @@ +<?php + +namespace Sabre\HTTP; + +/** + * HTTP utility methods + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @author Paul Voegler + * @deprecated All these functions moved to functions.php + * @license http://sabre.io/license/ Modified BSD License + */ +class Util { + + /** + * Content negotiation + * + * @deprecated Use \Sabre\HTTP\negotiateContentType + * @param string|null $acceptHeaderValue + * @param array $availableOptions + * @return string|null + */ + static function negotiateContentType($acceptHeaderValue, array $availableOptions) { + + return negotiateContentType($acceptHeaderValue, $availableOptions); + + } + + /** + * Deprecated! Use negotiateContentType. + * + * @deprecated Use \Sabre\HTTP\NegotiateContentType + * @param string|null $acceptHeaderValue + * @param array $availableOptions + * @return string|null + */ + static function negotiate($acceptHeaderValue, array $availableOptions) { + + return negotiateContentType($acceptHeaderValue, $availableOptions); + + } + + /** + * Parses a RFC2616-compatible date string + * + * This method returns false if the date is invalid + * + * @deprecated Use parseDate + * @param string $dateHeader + * @return bool|DateTime + */ + static function parseHTTPDate($dateHeader) { + + return parseDate($dateHeader); + + } + + /** + * Transforms a DateTime object to HTTP's most common date format. + * + * We're serializing it as the RFC 1123 date, which, for HTTP must be + * specified as GMT. + * + * @deprecated Use toDate + * @param \DateTime $dateTime + * @return string + */ + static function toHTTPDate(\DateTime $dateTime) { + + return toDate($dateTime); + + } +} diff --git a/htdocs/includes/sabre/sabre/http/lib/Version.php b/htdocs/includes/sabre/sabre/http/lib/Version.php new file mode 100644 index 00000000000..a5a42740512 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/Version.php @@ -0,0 +1,19 @@ +<?php + +namespace Sabre\HTTP; + +/** + * This class contains the version number for the HTTP package + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Version { + + /** + * Full version number + */ + const VERSION = '4.2.2'; + +} diff --git a/htdocs/includes/sabre/sabre/http/lib/functions.php b/htdocs/includes/sabre/sabre/http/lib/functions.php new file mode 100644 index 00000000000..d9411962399 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/lib/functions.php @@ -0,0 +1,445 @@ +<?php + +namespace Sabre\HTTP; + +use DateTime; + +/** + * A collection of useful helpers for parsing or generating various HTTP + * headers. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ + +/** + * Parses a HTTP date-string. + * + * This method returns false if the date is invalid. + * + * The following formats are supported: + * Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate + * Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format + * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + * + * See: + * http://tools.ietf.org/html/rfc7231#section-7.1.1.1 + * + * @param string $dateString + * @return bool|DateTime + */ +function parseDate($dateString) { + + // Only the format is checked, valid ranges are checked by strtotime below + $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'; + $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)'; + $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)'; + $time = '([0-1]\d|2[0-3])(\:[0-5]\d){2}'; + $date3 = $month . ' ([12]\d|3[01]| [1-9])'; + $date2 = '(0[1-9]|[12]\d|3[01])\-' . $month . '\-\d{2}'; + // 4-digit year cannot begin with 0 - unix timestamp begins in 1970 + $date1 = '(0[1-9]|[12]\d|3[01]) ' . $month . ' [1-9]\d{3}'; + + // ANSI C's asctime() format + // 4-digit year cannot begin with 0 - unix timestamp begins in 1970 + $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}'; + // RFC 850, obsoleted by RFC 1036 + $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT'; + // RFC 822, updated by RFC 1123 + $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT'; + // allowed date formats by RFC 2616 + $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)"; + + // allow for space around the string and strip it + $dateString = trim($dateString, ' '); + if (!preg_match('/^' . $HTTP_date . '$/', $dateString)) + return false; + + // append implicit GMT timezone to ANSI C time format + if (strpos($dateString, ' GMT') === false) + $dateString .= ' GMT'; + + try { + return new DateTime($dateString, new \DateTimeZone('UTC')); + } catch (\Exception $e) { + return false; + } + +} + +/** + * Transforms a DateTime object to a valid HTTP/1.1 Date header value + * + * @param DateTime $dateTime + * @return string + */ +function toDate(DateTime $dateTime) { + + // We need to clone it, as we don't want to affect the existing + // DateTime. + $dateTime = clone $dateTime; + $dateTime->setTimezone(new \DateTimeZone('GMT')); + return $dateTime->format('D, d M Y H:i:s \G\M\T'); + +} + +/** + * This function can be used to aid with content negotiation. + * + * It takes 2 arguments, the $acceptHeaderValue, which usually comes from + * an Accept header, and $availableOptions, which contains an array of + * items that the server can support. + * + * The result of this function will be the 'best possible option'. If no + * best possible option could be found, null is returned. + * + * When it's null you can according to the spec either return a default, or + * you can choose to emit 406 Not Acceptable. + * + * The method also accepts sending 'null' for the $acceptHeaderValue, + * implying that no accept header was sent. + * + * @param string|null $acceptHeaderValue + * @param array $availableOptions + * @return string|null + */ +function negotiateContentType($acceptHeaderValue, array $availableOptions) { + + if (!$acceptHeaderValue) { + // Grabbing the first in the list. + return reset($availableOptions); + } + + $proposals = array_map( + 'Sabre\HTTP\parseMimeType', + explode(',', $acceptHeaderValue) + ); + + // Ensuring array keys are reset. + $availableOptions = array_values($availableOptions); + + $options = array_map( + 'Sabre\HTTP\parseMimeType', + $availableOptions + ); + + $lastQuality = 0; + $lastSpecificity = 0; + $lastOptionIndex = 0; + $lastChoice = null; + + foreach ($proposals as $proposal) { + + // Ignoring broken values. + if (is_null($proposal)) continue; + + // If the quality is lower we don't have to bother comparing. + if ($proposal['quality'] < $lastQuality) { + continue; + } + + foreach ($options as $optionIndex => $option) { + + if ($proposal['type'] !== '*' && $proposal['type'] !== $option['type']) { + // no match on type. + continue; + } + if ($proposal['subType'] !== '*' && $proposal['subType'] !== $option['subType']) { + // no match on subtype. + continue; + } + + // Any parameters appearing on the options must appear on + // proposals. + foreach ($option['parameters'] as $paramName => $paramValue) { + if (!array_key_exists($paramName, $proposal['parameters'])) { + continue 2; + } + if ($paramValue !== $proposal['parameters'][$paramName]) { + continue 2; + } + } + + // If we got here, we have a match on parameters, type and + // subtype. We need to calculate a score for how specific the + // match was. + $specificity = + ($proposal['type'] !== '*' ? 20 : 0) + + ($proposal['subType'] !== '*' ? 10 : 0) + + count($option['parameters']); + + + // Does this entry win? + if ( + ($proposal['quality'] > $lastQuality) || + ($proposal['quality'] === $lastQuality && $specificity > $lastSpecificity) || + ($proposal['quality'] === $lastQuality && $specificity === $lastSpecificity && $optionIndex < $lastOptionIndex) + ) { + + $lastQuality = $proposal['quality']; + $lastSpecificity = $specificity; + $lastOptionIndex = $optionIndex; + $lastChoice = $availableOptions[$optionIndex]; + + } + + } + + } + + return $lastChoice; + +} + +/** + * Parses the Prefer header, as defined in RFC7240. + * + * Input can be given as a single header value (string) or multiple headers + * (array of string). + * + * This method will return a key->value array with the various Prefer + * parameters. + * + * Prefer: return=minimal will result in: + * + * [ 'return' => 'minimal' ] + * + * Prefer: foo, wait=10 will result in: + * + * [ 'foo' => true, 'wait' => '10'] + * + * This method also supports the formats from older drafts of RFC7240, and + * it will automatically map them to the new values, as the older values + * are still pretty common. + * + * Parameters are currently discarded. There's no known prefer value that + * uses them. + * + * @param string|string[] $input + * @return array + */ +function parsePrefer($input) { + + $token = '[!#$%&\'*+\-.^_`~A-Za-z0-9]+'; + + // Work in progress + $word = '(?: [a-zA-Z0-9]+ | "[a-zA-Z0-9]*" )'; + + $regex = <<<REGEX +/ +^ +(?<name> $token) # Prefer property name +\s* # Optional space +(?: = \s* # Prefer property value + (?<value> $word) +)? +(?: \s* ; (?: .*))? # Prefer parameters (ignored) +$ +/x +REGEX; + + $output = []; + foreach (getHeaderValues($input) as $value) { + + if (!preg_match($regex, $value, $matches)) { + // Ignore + continue; + } + + // Mapping old values to their new counterparts + switch ($matches['name']) { + case 'return-asynch' : + $output['respond-async'] = true; + break; + case 'return-representation' : + $output['return'] = 'representation'; + break; + case 'return-minimal' : + $output['return'] = 'minimal'; + break; + case 'strict' : + $output['handling'] = 'strict'; + break; + case 'lenient' : + $output['handling'] = 'lenient'; + break; + default : + if (isset($matches['value'])) { + $value = trim($matches['value'], '"'); + } else { + $value = true; + } + $output[strtolower($matches['name'])] = empty($value) ? true : $value; + break; + } + + } + + return $output; + +} + +/** + * This method splits up headers into all their individual values. + * + * A HTTP header may have more than one header, such as this: + * Cache-Control: private, no-store + * + * Header values are always split with a comma. + * + * You can pass either a string, or an array. The resulting value is always + * an array with each spliced value. + * + * If the second headers argument is set, this value will simply be merged + * in. This makes it quicker to merge an old list of values with a new set. + * + * @param string|string[] $values + * @param string|string[] $values2 + * @return string[] + */ +function getHeaderValues($values, $values2 = null) { + + $values = (array)$values; + if ($values2) { + $values = array_merge($values, (array)$values2); + } + foreach ($values as $l1) { + foreach (explode(',', $l1) as $l2) { + $result[] = trim($l2); + } + } + return $result; + +} + +/** + * Parses a mime-type and splits it into: + * + * 1. type + * 2. subtype + * 3. quality + * 4. parameters + * + * @param string $str + * @return array + */ +function parseMimeType($str) { + + $parameters = []; + // If no q= parameter appears, then quality = 1. + $quality = 1; + + $parts = explode(';', $str); + + // The first part is the mime-type. + $mimeType = array_shift($parts); + + $mimeType = explode('/', trim($mimeType)); + if (count($mimeType) !== 2) { + // Illegal value + return null; + } + list($type, $subType) = $mimeType; + + foreach ($parts as $part) { + + $part = trim($part); + if (strpos($part, '=')) { + list($partName, $partValue) = + explode('=', $part, 2); + } else { + $partName = $part; + $partValue = null; + } + + // The quality parameter, if it appears, also marks the end of + // the parameter list. Anything after the q= counts as an + // 'accept extension' and could introduce new semantics in + // content-negotation. + if ($partName !== 'q') { + $parameters[$partName] = $part; + } else { + $quality = (float)$partValue; + break; // Stop parsing parts + } + + } + + return [ + 'type' => $type, + 'subType' => $subType, + 'quality' => $quality, + 'parameters' => $parameters, + ]; + +} + +/** + * Encodes the path of a url. + * + * slashes (/) are treated as path-separators. + * + * @param string $path + * @return string + */ +function encodePath($path) { + + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:@])/', function($match) { + + return '%' . sprintf('%02x', ord($match[0])); + + }, $path); + +} + +/** + * Encodes a 1 segment of a path + * + * Slashes are considered part of the name, and are encoded as %2f + * + * @param string $pathSegment + * @return string + */ +function encodePathSegment($pathSegment) { + + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\):@])/', function($match) { + + return '%' . sprintf('%02x', ord($match[0])); + + }, $pathSegment); +} + +/** + * Decodes a url-encoded path + * + * @param string $path + * @return string + */ +function decodePath($path) { + + return decodePathSegment($path); + +} + +/** + * Decodes a url-encoded path segment + * + * @param string $path + * @return string + */ +function decodePathSegment($path) { + + $path = rawurldecode($path); + $encoding = mb_detect_encoding($path, ['UTF-8', 'ISO-8859-1']); + + switch ($encoding) { + + case 'ISO-8859-1' : + $path = utf8_encode($path); + + } + + return $path; + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/AWSTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/AWSTest.php new file mode 100644 index 00000000000..650761acae3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/AWSTest.php @@ -0,0 +1,235 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +class AWSTest extends \PHPUnit_Framework_TestCase { + + /** + * @var Sabre\HTTP\Response + */ + private $response; + + /** + * @var Sabre\HTTP\Request + */ + private $request; + + /** + * @var Sabre\HTTP\Auth\AWS + */ + private $auth; + + const REALM = 'SabreDAV unittest'; + + function setUp() { + + $this->response = new Response(); + $this->request = new Request(); + $this->auth = new AWS(self::REALM, $this->request, $this->response); + + } + + function testNoHeader() { + + $this->request->setMethod('GET'); + $result = $this->auth->init(); + + $this->assertFalse($result, 'No AWS Authorization header was supplied, so we should have gotten false'); + $this->assertEquals(AWS::ERR_NOAWSHEADER, $this->auth->errorCode); + + } + + function testIncorrectContentMD5() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + + $this->request->setMethod('GET'); + $this->request->setHeaders([ + 'Authorization' => "AWS $accessKey:sig", + 'Content-MD5' => 'garbage', + ]); + $this->request->setUrl('/'); + + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(AWS::ERR_MD5CHECKSUMWRONG, $this->auth->errorCode); + + } + + function testNoDate() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content, true)); + + $this->request->setMethod('POST'); + $this->request->setHeaders([ + 'Authorization' => "AWS $accessKey:sig", + 'Content-MD5' => $contentMD5, + ]); + $this->request->setUrl('/'); + $this->request->setBody($content); + + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(AWS::ERR_INVALIDDATEFORMAT, $this->auth->errorCode); + + } + + function testFutureDate() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content, true)); + + $date = new \DateTime('@' . (time() + (60 * 20))); + $date->setTimeZone(new \DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + $this->request->setMethod('POST'); + $this->request->setHeaders([ + 'Authorization' => "AWS $accessKey:sig", + 'Content-MD5' => $contentMD5, + 'Date' => $date, + ]); + + $this->request->setBody($content); + + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(AWS::ERR_REQUESTTIMESKEWED, $this->auth->errorCode); + + } + + function testPastDate() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content, true)); + + $date = new \DateTime('@' . (time() - (60 * 20))); + $date->setTimeZone(new \DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + $this->request->setMethod('POST'); + $this->request->setHeaders([ + 'Authorization' => "AWS $accessKey:sig", + 'Content-MD5' => $contentMD5, + 'Date' => $date, + ]); + + $this->request->setBody($content); + + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(AWS::ERR_REQUESTTIMESKEWED, $this->auth->errorCode); + + } + + function testIncorrectSignature() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + + $contentMD5 = base64_encode(md5($content, true)); + + $date = new \DateTime('now'); + $date->setTimeZone(new \DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + $this->request->setUrl('/'); + $this->request->setMethod('POST'); + $this->request->setHeaders([ + 'Authorization' => "AWS $accessKey:sig", + 'Content-MD5' => $contentMD5, + 'X-amz-date' => $date, + ]); + $this->request->setBody($content); + + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertFalse($result); + $this->assertEquals(AWS::ERR_INVALIDSIGNATURE, $this->auth->errorCode); + + } + + function testValidRequest() { + + $accessKey = 'accessKey'; + $secretKey = 'secretKey'; + $content = 'thisisthebody'; + $contentMD5 = base64_encode(md5($content, true)); + + $date = new \DateTime('now'); + $date->setTimeZone(new \DateTimeZone('GMT')); + $date = $date->format('D, d M Y H:i:s \\G\\M\\T'); + + + $sig = base64_encode($this->hmacsha1($secretKey, + "POST\n$contentMD5\n\n$date\nx-amz-date:$date\n/evert" + )); + + $this->request->setUrl('/evert'); + $this->request->setMethod('POST'); + $this->request->setHeaders([ + 'Authorization' => "AWS $accessKey:$sig", + 'Content-MD5' => $contentMD5, + 'X-amz-date' => $date, + ]); + + $this->request->setBody($content); + + $this->auth->init(); + $result = $this->auth->validate($secretKey); + + $this->assertTrue($result, 'Signature did not validate, got errorcode ' . $this->auth->errorCode); + $this->assertEquals($accessKey, $this->auth->getAccessKey()); + + } + + function test401() { + + $this->auth->requireLogin(); + $test = preg_match('/^AWS$/', $this->response->getHeader('WWW-Authenticate'), $matches); + $this->assertTrue($test == true, 'The WWW-Authenticate response didn\'t match our pattern'); + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + $blocksize = 64; + if (strlen($key) > $blocksize) + $key = pack('H*', sha1($key)); + $key = str_pad($key, $blocksize, chr(0x00)); + $ipad = str_repeat(chr(0x36), $blocksize); + $opad = str_repeat(chr(0x5c), $blocksize); + $hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $message)))); + return $hmac; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/BasicTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/BasicTest.php new file mode 100644 index 00000000000..7c25e59de1b --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/BasicTest.php @@ -0,0 +1,69 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +class BasicTest extends \PHPUnit_Framework_TestCase { + + function testGetCredentials() { + + $request = new Request('GET', '/', [ + 'Authorization' => 'Basic ' . base64_encode('user:pass:bla') + ]); + + $basic = new Basic('Dagger', $request, new Response()); + + $this->assertEquals([ + 'user', + 'pass:bla', + ], $basic->getCredentials()); + + } + + function testGetInvalidCredentialsColonMissing() { + + $request = new Request('GET', '/', [ + 'Authorization' => 'Basic ' . base64_encode('userpass') + ]); + + $basic = new Basic('Dagger', $request, new Response()); + + $this->assertNull($basic->getCredentials()); + + } + + function testGetCredentialsNoheader() { + + $request = new Request('GET', '/', []); + $basic = new Basic('Dagger', $request, new Response()); + + $this->assertNull($basic->getCredentials()); + + } + + function testGetCredentialsNotBasic() { + + $request = new Request('GET', '/', [ + 'Authorization' => 'QBasic ' . base64_encode('user:pass:bla') + ]); + $basic = new Basic('Dagger', $request, new Response()); + + $this->assertNull($basic->getCredentials()); + + } + + function testRequireLogin() { + + $response = new Response(); + $basic = new Basic('Dagger', new Request(), $response); + + $basic->requireLogin(); + + $this->assertEquals('Basic realm="Dagger"', $response->getHeader('WWW-Authenticate')); + $this->assertEquals(401, $response->getStatus()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/BearerTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/BearerTest.php new file mode 100644 index 00000000000..ee2e9e0bd72 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/BearerTest.php @@ -0,0 +1,57 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +class BearerTest extends \PHPUnit_Framework_TestCase { + + function testGetToken() { + + $request = new Request('GET', '/', [ + 'Authorization' => 'Bearer 12345' + ]); + + $bearer = new Bearer('Dagger', $request, new Response()); + + $this->assertEquals( + '12345', + $bearer->getToken() + ); + + } + + function testGetCredentialsNoheader() { + + $request = new Request('GET', '/', []); + $bearer = new Bearer('Dagger', $request, new Response()); + + $this->assertNull($bearer->getToken()); + + } + + function testGetCredentialsNotBearer() { + + $request = new Request('GET', '/', [ + 'Authorization' => 'QBearer 12345' + ]); + $bearer = new Bearer('Dagger', $request, new Response()); + + $this->assertNull($bearer->getToken()); + + } + + function testRequireLogin() { + + $response = new Response(); + $bearer = new Bearer('Dagger', new Request(), $response); + + $bearer->requireLogin(); + + $this->assertEquals('Bearer realm="Dagger"', $response->getHeader('WWW-Authenticate')); + $this->assertEquals(401, $response->getStatus()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/DigestTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/DigestTest.php new file mode 100644 index 00000000000..ffb69c76d6d --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/Auth/DigestTest.php @@ -0,0 +1,191 @@ +<?php + +namespace Sabre\HTTP\Auth; + +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; + +class DigestTest extends \PHPUnit_Framework_TestCase { + + /** + * @var Sabre\HTTP\Response + */ + private $response; + + /** + * request + * + * @var Sabre\HTTP\Request + */ + private $request; + + /** + * @var Sabre\HTTP\Auth\Digest + */ + private $auth; + + const REALM = 'SabreDAV unittest'; + + function setUp() { + + $this->response = new Response(); + $this->request = new Request(); + $this->auth = new Digest(self::REALM, $this->request, $this->response); + + + } + + function testDigest() { + + list($nonce, $opaque) = $this->getServerTokens(); + + $username = 'admin'; + $password = 12345; + $nc = '00002'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth:' . + md5('GET' . ':' . '/') + ); + + $this->request->setMethod('GET'); + $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc=' . $nc . ',cnonce="' . $cnonce . '"'); + + $this->auth->init(); + + $this->assertEquals($username, $this->auth->getUsername()); + $this->assertEquals(self::REALM, $this->auth->getRealm()); + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)), 'Authentication is deemed invalid through validateA1'); + $this->assertTrue($this->auth->validatePassword($password), 'Authentication is deemed invalid through validatePassword'); + + } + + function testInvalidDigest() { + + list($nonce, $opaque) = $this->getServerTokens(); + + $username = 'admin'; + $password = 12345; + $nc = '00002'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth:' . + md5('GET' . ':' . '/') + ); + + $this->request->setMethod('GET'); + $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth,nc=' . $nc . ',cnonce="' . $cnonce . '"'); + + $this->auth->init(); + + $this->assertFalse($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . ($password . 'randomness'))), 'Authentication is deemed invalid through validateA1'); + + } + + function testInvalidDigest2() { + + $this->request->setMethod('GET'); + $this->request->setHeader('Authorization', 'basic blablabla'); + + $this->auth->init(); + $this->assertFalse($this->auth->validateA1(md5('user:realm:password'))); + + } + + + function testDigestAuthInt() { + + $this->auth->setQOP(Digest::QOP_AUTHINT); + list($nonce, $opaque) = $this->getServerTokens(Digest::QOP_AUTHINT); + + $username = 'admin'; + $password = 12345; + $nc = '00003'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth-int:' . + md5('POST' . ':' . '/' . ':' . md5('body')) + ); + + $this->request->setMethod('POST'); + $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth-int,nc=' . $nc . ',cnonce="' . $cnonce . '"'); + $this->request->setBody('body'); + + $this->auth->init(); + + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)), 'Authentication is deemed invalid through validateA1'); + + } + + function testDigestAuthBoth() { + + $this->auth->setQOP(Digest::QOP_AUTHINT | Digest::QOP_AUTH); + list($nonce, $opaque) = $this->getServerTokens(Digest::QOP_AUTHINT | Digest::QOP_AUTH); + + $username = 'admin'; + $password = 12345; + $nc = '00003'; + $cnonce = uniqid(); + + $digestHash = md5( + md5($username . ':' . self::REALM . ':' . $password) . ':' . + $nonce . ':' . + $nc . ':' . + $cnonce . ':' . + 'auth-int:' . + md5('POST' . ':' . '/' . ':' . md5('body')) + ); + + $this->request->setMethod('POST'); + $this->request->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . self::REALM . '", nonce="' . $nonce . '", uri="/", response="' . $digestHash . '", opaque="' . $opaque . '", qop=auth-int,nc=' . $nc . ',cnonce="' . $cnonce . '"'); + $this->request->setBody('body'); + + $this->auth->init(); + + $this->assertTrue($this->auth->validateA1(md5($username . ':' . self::REALM . ':' . $password)), 'Authentication is deemed invalid through validateA1'); + + } + + + private function getServerTokens($qop = Digest::QOP_AUTH) { + + $this->auth->requireLogin(); + + switch ($qop) { + case Digest::QOP_AUTH : $qopstr = 'auth'; break; + case Digest::QOP_AUTHINT : $qopstr = 'auth-int'; break; + default : $qopstr = 'auth,auth-int'; break; + } + + $test = preg_match('/Digest realm="' . self::REALM . '",qop="' . $qopstr . '",nonce="([0-9a-f]*)",opaque="([0-9a-f]*)"/', + $this->response->getHeader('WWW-Authenticate'), $matches); + + $this->assertTrue($test == true, 'The WWW-Authenticate response didn\'t match our pattern. We received: ' . $this->response->getHeader('WWW-Authenticate')); + + $nonce = $matches[1]; + $opaque = $matches[2]; + + // Reset our environment + $this->setUp(); + $this->auth->setQOP($qop); + + return [$nonce,$opaque]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/ClientTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/ClientTest.php new file mode 100644 index 00000000000..ea25907df2d --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/ClientTest.php @@ -0,0 +1,474 @@ +<?php + +namespace Sabre\HTTP; + +class ClientTest extends \PHPUnit_Framework_TestCase { + + function testCreateCurlSettingsArrayGET() { + + $client = new ClientMock(); + $client->addCurlSetting(CURLOPT_POSTREDIR, 0); + + $request = new Request('GET', 'http://example.org/', ['X-Foo' => 'bar']); + + $settings = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_POSTREDIR => 0, + CURLOPT_HTTPHEADER => ['X-Foo: bar'], + CURLOPT_NOBODY => false, + CURLOPT_URL => 'http://example.org/', + CURLOPT_CUSTOMREQUEST => 'GET', + CURLOPT_POSTFIELDS => '', + CURLOPT_PUT => false, + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + // FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM + // at least if this unit test fails in the future we know it is :) + if (defined('HHVM_VERSION') === false) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + + $this->assertEquals($settings, $client->createCurlSettingsArray($request)); + + } + + function testCreateCurlSettingsArrayHEAD() { + + $client = new ClientMock(); + $request = new Request('HEAD', 'http://example.org/', ['X-Foo' => 'bar']); + + + $settings = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_NOBODY => true, + CURLOPT_CUSTOMREQUEST => 'HEAD', + CURLOPT_HTTPHEADER => ['X-Foo: bar'], + CURLOPT_URL => 'http://example.org/', + CURLOPT_POSTFIELDS => '', + CURLOPT_PUT => false, + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + // FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM + // at least if this unit test fails in the future we know it is :) + if (defined('HHVM_VERSION') === false) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $this->assertEquals($settings, $client->createCurlSettingsArray($request)); + + } + + function testCreateCurlSettingsArrayGETAfterHEAD() { + + $client = new ClientMock(); + $request = new Request('HEAD', 'http://example.org/', ['X-Foo' => 'bar']); + + // Parsing the settings for this method, and discarding the result. + // This will cause the client to automatically persist previous + // settings and will help us detect problems. + $client->createCurlSettingsArray($request); + + // This is the real request. + $request = new Request('GET', 'http://example.org/', ['X-Foo' => 'bar']); + + $settings = [ + CURLOPT_CUSTOMREQUEST => 'GET', + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => ['X-Foo: bar'], + CURLOPT_NOBODY => false, + CURLOPT_URL => 'http://example.org/', + CURLOPT_POSTFIELDS => '', + CURLOPT_PUT => false, + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + // FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM + // at least if this unit test fails in the future we know it is :) + if (defined('HHVM_VERSION') === false) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $this->assertEquals($settings, $client->createCurlSettingsArray($request)); + + } + + function testCreateCurlSettingsArrayPUTStream() { + + $client = new ClientMock(); + + $h = fopen('php://memory', 'r+'); + fwrite($h, 'booh'); + $request = new Request('PUT', 'http://example.org/', ['X-Foo' => 'bar'], $h); + + $settings = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_PUT => true, + CURLOPT_INFILE => $h, + CURLOPT_NOBODY => false, + CURLOPT_CUSTOMREQUEST => 'PUT', + CURLOPT_HTTPHEADER => ['X-Foo: bar'], + CURLOPT_URL => 'http://example.org/', + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + // FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM + // at least if this unit test fails in the future we know it is :) + if (defined('HHVM_VERSION') === false) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $this->assertEquals($settings, $client->createCurlSettingsArray($request)); + + } + + function testCreateCurlSettingsArrayPUTString() { + + $client = new ClientMock(); + $request = new Request('PUT', 'http://example.org/', ['X-Foo' => 'bar'], 'boo'); + + $settings = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_NOBODY => false, + CURLOPT_POSTFIELDS => 'boo', + CURLOPT_CUSTOMREQUEST => 'PUT', + CURLOPT_HTTPHEADER => ['X-Foo: bar'], + CURLOPT_URL => 'http://example.org/', + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + // FIXME: CURLOPT_PROTOCOLS and CURLOPT_REDIR_PROTOCOLS are currently unsupported by HHVM + // at least if this unit test fails in the future we know it is :) + if (defined('HHVM_VERSION') === false) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + $this->assertEquals($settings, $client->createCurlSettingsArray($request)); + + } + + function testSend() { + + $client = new ClientMock(); + $request = new Request('GET', 'http://example.org/'); + + $client->on('doRequest', function($request, &$response) { + $response = new Response(200); + }); + + $response = $client->send($request); + + $this->assertEquals(200, $response->getStatus()); + + } + + function testSendClientError() { + + $client = new ClientMock(); + $request = new Request('GET', 'http://example.org/'); + + $client->on('doRequest', function($request, &$response) { + throw new ClientException('aaah', 1); + }); + $called = false; + $client->on('exception', function() use (&$called) { + $called = true; + }); + + try { + $client->send($request); + $this->fail('send() should have thrown an exception'); + } catch (ClientException $e) { + + } + $this->assertTrue($called); + + } + + function testSendHttpError() { + + $client = new ClientMock(); + $request = new Request('GET', 'http://example.org/'); + + $client->on('doRequest', function($request, &$response) { + $response = new Response(404); + }); + $called = 0; + $client->on('error', function() use (&$called) { + $called++; + }); + $client->on('error:404', function() use (&$called) { + $called++; + }); + + $client->send($request); + $this->assertEquals(2, $called); + + } + + function testSendRetry() { + + $client = new ClientMock(); + $request = new Request('GET', 'http://example.org/'); + + $called = 0; + $client->on('doRequest', function($request, &$response) use (&$called) { + $called++; + if ($called < 3) { + $response = new Response(404); + } else { + $response = new Response(200); + } + }); + + $errorCalled = 0; + $client->on('error', function($request, $response, &$retry, $retryCount) use (&$errorCalled) { + + $errorCalled++; + $retry = true; + + }); + + $response = $client->send($request); + $this->assertEquals(3, $called); + $this->assertEquals(2, $errorCalled); + $this->assertEquals(200, $response->getStatus()); + + } + + function testHttpErrorException() { + + $client = new ClientMock(); + $client->setThrowExceptions(true); + $request = new Request('GET', 'http://example.org/'); + + $client->on('doRequest', function($request, &$response) { + $response = new Response(404); + }); + + try { + $client->send($request); + $this->fail('An exception should have been thrown'); + } catch (ClientHttpException $e) { + $this->assertEquals(404, $e->getHttpStatus()); + $this->assertInstanceOf('Sabre\HTTP\Response', $e->getResponse()); + } + + } + + function testParseCurlResult() { + + $client = new ClientMock(); + $client->on('curlStuff', function(&$return) { + + $return = [ + [ + 'header_size' => 33, + 'http_code' => 200, + ], + 0, + '', + ]; + + }); + + $body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo"; + $result = $client->parseCurlResult($body, 'foobar'); + + $this->assertEquals(Client::STATUS_SUCCESS, $result['status']); + $this->assertEquals(200, $result['http_code']); + $this->assertEquals(200, $result['response']->getStatus()); + $this->assertEquals(['Header1' => ['Val1']], $result['response']->getHeaders()); + $this->assertEquals('Foo', $result['response']->getBodyAsString()); + + } + + function testParseCurlError() { + + $client = new ClientMock(); + $client->on('curlStuff', function(&$return) { + + $return = [ + [], + 1, + 'Curl error', + ]; + + }); + + $body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo"; + $result = $client->parseCurlResult($body, 'foobar'); + + $this->assertEquals(Client::STATUS_CURLERROR, $result['status']); + $this->assertEquals(1, $result['curl_errno']); + $this->assertEquals('Curl error', $result['curl_errmsg']); + + } + + function testDoRequest() { + + $client = new ClientMock(); + $request = new Request('GET', 'http://example.org/'); + $client->on('curlExec', function(&$return) { + + $return = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo"; + + }); + $client->on('curlStuff', function(&$return) { + + $return = [ + [ + 'header_size' => 33, + 'http_code' => 200, + ], + 0, + '', + ]; + + }); + $response = $client->doRequest($request); + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals(['Header1' => ['Val1']], $response->getHeaders()); + $this->assertEquals('Foo', $response->getBodyAsString()); + + } + + function testDoRequestCurlError() { + + $client = new ClientMock(); + $request = new Request('GET', 'http://example.org/'); + $client->on('curlExec', function(&$return) { + + $return = ""; + + }); + $client->on('curlStuff', function(&$return) { + + $return = [ + [], + 1, + 'Curl error', + ]; + + }); + + try { + $response = $client->doRequest($request); + $this->fail('This should have thrown an exception'); + } catch (ClientException $e) { + $this->assertEquals(1, $e->getCode()); + $this->assertEquals('Curl error', $e->getMessage()); + } + + } + +} + +class ClientMock extends Client { + + protected $persistedSettings = []; + + /** + * Making this method public. + * + * We are also going to persist all settings this method generates. While + * the underlying object doesn't behave exactly the same, it helps us + * simulate what curl does internally, and helps us identify problems with + * settings that are set by _some_ methods and not correctly reset by other + * methods after subsequent use. + * forces + */ + function createCurlSettingsArray(RequestInterface $request) { + + $settings = parent::createCurlSettingsArray($request); + $settings = $settings + $this->persistedSettings; + $this->persistedSettings = $settings; + return $settings; + + } + /** + * Making this method public. + */ + function parseCurlResult($response, $curlHandle) { + + return parent::parseCurlResult($response, $curlHandle); + + } + + /** + * This method is responsible for performing a single request. + * + * @param RequestInterface $request + * @return ResponseInterface + */ + function doRequest(RequestInterface $request) { + + $response = null; + $this->emit('doRequest', [$request, &$response]); + + // If nothing modified $response, we're using the default behavior. + if (is_null($response)) { + return parent::doRequest($request); + } else { + return $response; + } + + } + + /** + * Returns a bunch of information about a curl request. + * + * This method exists so it can easily be overridden and mocked. + * + * @param resource $curlHandle + * @return array + */ + protected function curlStuff($curlHandle) { + + $return = null; + $this->emit('curlStuff', [&$return]); + + // If nothing modified $return, we're using the default behavior. + if (is_null($return)) { + return parent::curlStuff($curlHandle); + } else { + return $return; + } + + } + + /** + * Calls curl_exec + * + * This method exists so it can easily be overridden and mocked. + * + * @param resource $curlHandle + * @return string + */ + protected function curlExec($curlHandle) { + + $return = null; + $this->emit('curlExec', [&$return]); + + // If nothing modified $return, we're using the default behavior. + if (is_null($return)) { + return parent::curlExec($curlHandle); + } else { + return $return; + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/FunctionsTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/FunctionsTest.php new file mode 100644 index 00000000000..a107d1f007a --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/FunctionsTest.php @@ -0,0 +1,121 @@ +<?php + +namespace Sabre\HTTP; + +class FunctionsTest extends \PHPUnit_Framework_TestCase { + + /** + * @dataProvider getHeaderValuesData + */ + function testGetHeaderValues($input, $output) { + + $this->assertEquals( + $output, + getHeaderValues($input) + ); + + } + + function getHeaderValuesData() { + + return [ + [ + "a", + ["a"] + ], + [ + "a,b", + ["a", "b"] + ], + [ + "a, b", + ["a", "b"] + ], + [ + ["a, b"], + ["a", "b"] + ], + [ + ["a, b", "c", "d,e"], + ["a", "b", "c", "d", "e"] + ], + ]; + + } + + /** + * @dataProvider preferData + */ + function testPrefer($input, $output) { + + $this->assertEquals( + $output, + parsePrefer($input) + ); + + } + + function preferData() { + + return [ + [ + 'foo; bar', + ['foo' => true] + ], + [ + 'foo; bar=""', + ['foo' => true] + ], + [ + 'foo=""; bar', + ['foo' => true] + ], + [ + 'FOO', + ['foo' => true] + ], + [ + 'respond-async', + ['respond-async' => true] + ], + [ + + ['respond-async, wait=100', 'handling=lenient'], + ['respond-async' => true, 'wait' => 100, 'handling' => 'lenient'] + ], + [ + + ['respond-async, wait=100, handling=lenient'], + ['respond-async' => true, 'wait' => 100, 'handling' => 'lenient'] + ], + // Old values + [ + + 'return-asynch, return-representation', + ['respond-async' => true, 'return' => 'representation'], + ], + [ + + 'return-minimal', + ['return' => 'minimal'], + ], + [ + + 'strict', + ['handling' => 'strict'], + ], + [ + + 'lenient', + ['handling' => 'lenient'], + ], + // Invalid token + [ + ['foo=%bar%'], + [], + ] + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/MessageDecoratorTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/MessageDecoratorTest.php new file mode 100644 index 00000000000..a4052c60c0c --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/MessageDecoratorTest.php @@ -0,0 +1,93 @@ +<?php + +namespace Sabre\HTTP; + +class MessageDecoratorTest extends \PHPUnit_Framework_TestCase { + + protected $inner; + protected $outer; + + function setUp() { + + $this->inner = new Request(); + $this->outer = new RequestDecorator($this->inner); + + } + + function testBody() { + + $this->outer->setBody('foo'); + $this->assertEquals('foo', stream_get_contents($this->inner->getBodyAsStream())); + $this->assertEquals('foo', stream_get_contents($this->outer->getBodyAsStream())); + $this->assertEquals('foo', $this->inner->getBodyAsString()); + $this->assertEquals('foo', $this->outer->getBodyAsString()); + $this->assertEquals('foo', $this->inner->getBody()); + $this->assertEquals('foo', $this->outer->getBody()); + + } + + function testHeaders() { + + $this->outer->setHeaders([ + 'a' => 'b', + ]); + + $this->assertEquals(['a' => ['b']], $this->inner->getHeaders()); + $this->assertEquals(['a' => ['b']], $this->outer->getHeaders()); + + $this->outer->setHeaders([ + 'c' => 'd', + ]); + + $this->assertEquals(['a' => ['b'], 'c' => ['d']], $this->inner->getHeaders()); + $this->assertEquals(['a' => ['b'], 'c' => ['d']], $this->outer->getHeaders()); + + $this->outer->addHeaders([ + 'e' => 'f', + ]); + + $this->assertEquals(['a' => ['b'], 'c' => ['d'], 'e' => ['f']], $this->inner->getHeaders()); + $this->assertEquals(['a' => ['b'], 'c' => ['d'], 'e' => ['f']], $this->outer->getHeaders()); + } + + function testHeader() { + + $this->assertFalse($this->outer->hasHeader('a')); + $this->assertFalse($this->inner->hasHeader('a')); + $this->outer->setHeader('a', 'c'); + $this->assertTrue($this->outer->hasHeader('a')); + $this->assertTrue($this->inner->hasHeader('a')); + + $this->assertEquals('c', $this->inner->getHeader('A')); + $this->assertEquals('c', $this->outer->getHeader('A')); + + $this->outer->addHeader('A', 'd'); + + $this->assertEquals( + ['c', 'd'], + $this->inner->getHeaderAsArray('A') + ); + $this->assertEquals( + ['c', 'd'], + $this->outer->getHeaderAsArray('A') + ); + + $success = $this->outer->removeHeader('a'); + + $this->assertTrue($success); + $this->assertNull($this->inner->getHeader('A')); + $this->assertNull($this->outer->getHeader('A')); + + $this->assertFalse($this->outer->removeHeader('i-dont-exist')); + } + + function testHttpVersion() { + + $this->outer->setHttpVersion('1.0'); + + $this->assertEquals('1.0', $this->inner->getHttpVersion()); + $this->assertEquals('1.0', $this->outer->getHttpVersion()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/MessageTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/MessageTest.php new file mode 100644 index 00000000000..cb5aadc416c --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/MessageTest.php @@ -0,0 +1,246 @@ +<?php + +namespace Sabre\HTTP; + +class MessageTest extends \PHPUnit_Framework_TestCase { + + function testConstruct() { + + $message = new MessageMock(); + $this->assertInstanceOf('Sabre\HTTP\Message', $message); + + } + + function testStreamBody() { + + $body = 'foo'; + $h = fopen('php://memory', 'r+'); + fwrite($h, $body); + rewind($h); + + $message = new MessageMock(); + $message->setBody($h); + + $this->assertEquals($body, $message->getBodyAsString()); + rewind($h); + $this->assertEquals($body, stream_get_contents($message->getBodyAsStream())); + rewind($h); + $this->assertEquals($body, stream_get_contents($message->getBody())); + + } + + function testStringBody() { + + $body = 'foo'; + + $message = new MessageMock(); + $message->setBody($body); + + $this->assertEquals($body, $message->getBodyAsString()); + $this->assertEquals($body, stream_get_contents($message->getBodyAsStream())); + $this->assertEquals($body, $message->getBody()); + + } + + /** + * It's possible that streams contains more data than the Content-Length. + * + * The request object should make sure to never emit more than + * Content-Length, if Content-Length is set. + * + * This is in particular useful when respoding to range requests with + * streams that represent files on the filesystem, as it's possible to just + * seek the stream to a certain point, set the content-length and let the + * request object do the rest. + */ + function testLongStreamToStringBody() { + + $body = fopen('php://memory', 'r+'); + fwrite($body, 'abcdefg'); + fseek($body, 2); + + $message = new MessageMock(); + $message->setBody($body); + $message->setHeader('Content-Length', '4'); + + $this->assertEquals( + 'cdef', + $message->getBodyAsString() + ); + + } + + /** + * Some clients include a content-length header, but the header is empty. + * This is definitely broken behavior, but we should support it. + */ + function testEmptyContentLengthHeader() { + + $body = fopen('php://memory', 'r+'); + fwrite($body, 'abcdefg'); + fseek($body, 2); + + $message = new MessageMock(); + $message->setBody($body); + $message->setHeader('Content-Length', ''); + + $this->assertEquals( + 'cdefg', + $message->getBodyAsString() + ); + + } + + + function testGetEmptyBodyStream() { + + $message = new MessageMock(); + $body = $message->getBodyAsStream(); + + $this->assertEquals('', stream_get_contents($body)); + + } + + function testGetEmptyBodyString() { + + $message = new MessageMock(); + $body = $message->getBodyAsString(); + + $this->assertEquals('', $body); + + } + + function testHeaders() { + + $message = new MessageMock(); + $message->setHeader('X-Foo', 'bar'); + + // Testing caselessness + $this->assertEquals('bar', $message->getHeader('X-Foo')); + $this->assertEquals('bar', $message->getHeader('x-fOO')); + + $this->assertTrue( + $message->removeHeader('X-FOO') + ); + $this->assertNull($message->getHeader('X-Foo')); + $this->assertFalse( + $message->removeHeader('X-FOO') + ); + + } + + function testSetHeaders() { + + $message = new MessageMock(); + + $headers = [ + 'X-Foo' => ['1'], + 'X-Bar' => ['2'], + ]; + + $message->setHeaders($headers); + $this->assertEquals($headers, $message->getHeaders()); + + $message->setHeaders([ + 'X-Foo' => ['3', '4'], + 'X-Bar' => '5', + ]); + + $expected = [ + 'X-Foo' => ['3','4'], + 'X-Bar' => ['5'], + ]; + + $this->assertEquals($expected, $message->getHeaders()); + + } + + function testAddHeaders() { + + $message = new MessageMock(); + + $headers = [ + 'X-Foo' => ['1'], + 'X-Bar' => ['2'], + ]; + + $message->addHeaders($headers); + $this->assertEquals($headers, $message->getHeaders()); + + $message->addHeaders([ + 'X-Foo' => ['3', '4'], + 'X-Bar' => '5', + ]); + + $expected = [ + 'X-Foo' => ['1','3','4'], + 'X-Bar' => ['2','5'], + ]; + + $this->assertEquals($expected, $message->getHeaders()); + + } + + function testSendBody() { + + $message = new MessageMock(); + + // String + $message->setBody('foo'); + + // Stream + $h = fopen('php://memory', 'r+'); + fwrite($h, 'bar'); + rewind($h); + $message->setBody($h); + + $body = $message->getBody(); + rewind($body); + + $this->assertEquals('bar', stream_get_contents($body)); + + } + + function testMultipleHeaders() { + + $message = new MessageMock(); + $message->setHeader('a', '1'); + $message->addHeader('A', '2'); + + $this->assertEquals( + "1,2", + $message->getHeader('A') + ); + $this->assertEquals( + "1,2", + $message->getHeader('a') + ); + + $this->assertEquals( + ['1', '2'], + $message->getHeaderAsArray('a') + ); + $this->assertEquals( + ['1', '2'], + $message->getHeaderAsArray('A') + ); + $this->assertEquals( + [], + $message->getHeaderAsArray('B') + ); + + } + + function testHasHeaders() { + + $message = new MessageMock(); + + $this->assertFalse($message->hasHeader('X-Foo')); + $message->setHeader('X-Foo', 'Bar'); + $this->assertTrue($message->hasHeader('X-Foo')); + + } + +} + +class MessageMock extends Message { } diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/RequestDecoratorTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/RequestDecoratorTest.php new file mode 100644 index 00000000000..08af48749bd --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/RequestDecoratorTest.php @@ -0,0 +1,112 @@ +<?php + +namespace Sabre\HTTP; + +class RequestDecoratorTest extends \PHPUnit_Framework_TestCase { + + protected $inner; + protected $outer; + + function setUp() { + + $this->inner = new Request(); + $this->outer = new RequestDecorator($this->inner); + + } + + function testMethod() { + + $this->outer->setMethod('FOO'); + $this->assertEquals('FOO', $this->inner->getMethod()); + $this->assertEquals('FOO', $this->outer->getMethod()); + + } + + function testUrl() { + + $this->outer->setUrl('/foo'); + $this->assertEquals('/foo', $this->inner->getUrl()); + $this->assertEquals('/foo', $this->outer->getUrl()); + + } + + function testAbsoluteUrl() { + + $this->outer->setAbsoluteUrl('http://example.org/foo'); + $this->assertEquals('http://example.org/foo', $this->inner->getAbsoluteUrl()); + $this->assertEquals('http://example.org/foo', $this->outer->getAbsoluteUrl()); + + } + + function testBaseUrl() { + + $this->outer->setBaseUrl('/foo'); + $this->assertEquals('/foo', $this->inner->getBaseUrl()); + $this->assertEquals('/foo', $this->outer->getBaseUrl()); + + } + + function testPath() { + + $this->outer->setBaseUrl('/foo'); + $this->outer->setUrl('/foo/bar'); + $this->assertEquals('bar', $this->inner->getPath()); + $this->assertEquals('bar', $this->outer->getPath()); + + } + + function testQueryParams() { + + $this->outer->setUrl('/foo?a=b&c=d&e'); + $expected = [ + 'a' => 'b', + 'c' => 'd', + 'e' => null, + ]; + + $this->assertEquals($expected, $this->inner->getQueryParameters()); + $this->assertEquals($expected, $this->outer->getQueryParameters()); + + } + + function testPostData() { + + $postData = [ + 'a' => 'b', + 'c' => 'd', + 'e' => null, + ]; + + $this->outer->setPostData($postData); + $this->assertEquals($postData, $this->inner->getPostData()); + $this->assertEquals($postData, $this->outer->getPostData()); + + } + + + function testServerData() { + + $serverData = [ + 'HTTPS' => 'On', + ]; + + $this->outer->setRawServerData($serverData); + $this->assertEquals('On', $this->inner->getRawServerValue('HTTPS')); + $this->assertEquals('On', $this->outer->getRawServerValue('HTTPS')); + + $this->assertNull($this->inner->getRawServerValue('FOO')); + $this->assertNull($this->outer->getRawServerValue('FOO')); + } + + function testToString() { + + $this->inner->setMethod('POST'); + $this->inner->setUrl('/foo/bar/'); + $this->inner->setBody('foo'); + $this->inner->setHeader('foo', 'bar'); + + $this->assertEquals((string)$this->inner, (string)$this->outer); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/RequestTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/RequestTest.php new file mode 100644 index 00000000000..e3daab4d354 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/RequestTest.php @@ -0,0 +1,167 @@ +<?php + +namespace Sabre\HTTP; + +class RequestTest extends \PHPUnit_Framework_TestCase { + + function testConstruct() { + + $request = new Request('GET', '/foo', [ + 'User-Agent' => 'Evert', + ]); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'User-Agent' => ['Evert'], + ], $request->getHeaders()); + + } + + function testGetQueryParameters() { + + $request = new Request('GET', '/foo?a=b&c&d=e'); + $this->assertEquals([ + 'a' => 'b', + 'c' => null, + 'd' => 'e', + ], $request->getQueryParameters()); + + } + + function testGetQueryParametersNoData() { + + $request = new Request('GET', '/foo'); + $this->assertEquals([], $request->getQueryParameters()); + + } + + /** + * @backupGlobals + */ + function testCreateFromPHPRequest() { + + $_SERVER['REQUEST_METHOD'] = 'PUT'; + + $request = Sapi::getRequest(); + $this->assertEquals('PUT', $request->getMethod()); + + } + + function testGetAbsoluteUrl() { + + $s = [ + 'HTTP_HOST' => 'sabredav.org', + 'REQUEST_URI' => '/foo' + ]; + + $r = Sapi::createFromServerArray($s); + + $this->assertEquals('http://sabredav.org/foo', $r->getAbsoluteUrl()); + + $s = [ + 'HTTP_HOST' => 'sabredav.org', + 'REQUEST_URI' => '/foo', + 'HTTPS' => 'on', + ]; + + $r = Sapi::createFromServerArray($s); + + $this->assertEquals('https://sabredav.org/foo', $r->getAbsoluteUrl()); + + } + + function testGetPostData() { + + $post = [ + 'bla' => 'foo', + ]; + $r = new Request(); + $r->setPostData($post); + $this->assertEquals($post, $r->getPostData()); + + } + + function testGetPath() { + + $request = new Request(); + $request->setBaseUrl('/foo'); + $request->setUrl('/foo/bar/'); + + $this->assertEquals('bar', $request->getPath()); + + } + + function testGetPathStrippedQuery() { + + $request = new Request(); + $request->setBaseUrl('/foo'); + $request->setUrl('/foo/bar/?a=b'); + + $this->assertEquals('bar', $request->getPath()); + + } + + function testGetPathMissingSlash() { + + $request = new Request(); + $request->setBaseUrl('/foo/'); + $request->setUrl('/foo'); + + $this->assertEquals('', $request->getPath()); + + } + + /** + * @expectedException \LogicException + */ + function testGetPathOutsideBaseUrl() { + + $request = new Request(); + $request->setBaseUrl('/foo/'); + $request->setUrl('/bar/'); + + $request->getPath(); + + } + + function testToString() { + + $request = new Request('PUT', '/foo/bar', ['Content-Type' => 'text/xml']); + $request->setBody('foo'); + + $expected = <<<HI +PUT /foo/bar HTTP/1.1\r +Content-Type: text/xml\r +\r +foo +HI; + $this->assertEquals($expected, (string)$request); + + } + + function testToStringAuthorization() { + + $request = new Request('PUT', '/foo/bar', ['Content-Type' => 'text/xml', 'Authorization' => 'Basic foobar']); + $request->setBody('foo'); + + $expected = <<<HI +PUT /foo/bar HTTP/1.1\r +Content-Type: text/xml\r +Authorization: Basic REDACTED\r +\r +foo +HI; + $this->assertEquals($expected, (string)$request); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testConstructorWithArray() { + + $request = new Request([]); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/ResponseDecoratorTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/ResponseDecoratorTest.php new file mode 100644 index 00000000000..838953b3144 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/ResponseDecoratorTest.php @@ -0,0 +1,37 @@ +<?php + +namespace Sabre\HTTP; + +class ResponseDecoratorTest extends \PHPUnit_Framework_TestCase { + + protected $inner; + protected $outer; + + function setUp() { + + $this->inner = new Response(); + $this->outer = new ResponseDecorator($this->inner); + + } + + function testStatus() { + + $this->outer->setStatus(201); + $this->assertEquals(201, $this->inner->getStatus()); + $this->assertEquals(201, $this->outer->getStatus()); + $this->assertEquals('Created', $this->inner->getStatusText()); + $this->assertEquals('Created', $this->outer->getStatusText()); + + } + + function testToString() { + + $this->inner->setStatus(201); + $this->inner->setBody('foo'); + $this->inner->setHeader('foo', 'bar'); + + $this->assertEquals((string)$this->inner, (string)$this->outer); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/ResponseTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/ResponseTest.php new file mode 100644 index 00000000000..117551bb969 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/ResponseTest.php @@ -0,0 +1,48 @@ +<?php + +namespace Sabre\HTTP; + +class ResponseTest extends \PHPUnit_Framework_TestCase { + + function testConstruct() { + + $response = new Response(200, ['Content-Type' => 'text/xml']); + $this->assertEquals(200, $response->getStatus()); + $this->assertEquals('OK', $response->getStatusText()); + + } + + function testSetStatus() { + + $response = new Response(); + $response->setStatus('402 Where\'s my money?'); + $this->assertEquals(402, $response->getStatus()); + $this->assertEquals('Where\'s my money?', $response->getStatusText()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidStatus() { + + $response = new Response(1000); + + } + + function testToString() { + + $response = new Response(200, ['Content-Type' => 'text/xml']); + $response->setBody('foo'); + + $expected = <<<HI +HTTP/1.1 200 OK\r +Content-Type: text/xml\r +\r +foo +HI; + $this->assertEquals($expected, (string)$response); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/SapiTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/SapiTest.php new file mode 100644 index 00000000000..158ce2171b9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/SapiTest.php @@ -0,0 +1,167 @@ +<?php + +namespace Sabre\HTTP; + +class SapiTest extends \PHPUnit_Framework_TestCase { + + function testConstructFromServerArray() { + + $request = Sapi::createFromServerArray([ + 'REQUEST_URI' => '/foo', + 'REQUEST_METHOD' => 'GET', + 'HTTP_USER_AGENT' => 'Evert', + 'CONTENT_TYPE' => 'text/xml', + 'CONTENT_LENGTH' => '400', + 'SERVER_PROTOCOL' => 'HTTP/1.0', + ]); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'User-Agent' => ['Evert'], + 'Content-Type' => ['text/xml'], + 'Content-Length' => ['400'], + ], $request->getHeaders()); + + $this->assertEquals('1.0', $request->getHttpVersion()); + + $this->assertEquals('400', $request->getRawServerValue('CONTENT_LENGTH')); + $this->assertNull($request->getRawServerValue('FOO')); + + } + + function testConstructPHPAuth() { + + $request = Sapi::createFromServerArray([ + 'REQUEST_URI' => '/foo', + 'REQUEST_METHOD' => 'GET', + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'pass', + ]); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'Authorization' => ['Basic ' . base64_encode('user:pass')], + ], $request->getHeaders()); + + } + + function testConstructPHPAuthDigest() { + + $request = Sapi::createFromServerArray([ + 'REQUEST_URI' => '/foo', + 'REQUEST_METHOD' => 'GET', + 'PHP_AUTH_DIGEST' => 'blabla', + ]); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'Authorization' => ['Digest blabla'], + ], $request->getHeaders()); + + } + + function testConstructRedirectAuth() { + + $request = Sapi::createFromServerArray([ + 'REQUEST_URI' => '/foo', + 'REQUEST_METHOD' => 'GET', + 'REDIRECT_HTTP_AUTHORIZATION' => 'Basic bla', + ]); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('/foo', $request->getUrl()); + $this->assertEquals([ + 'Authorization' => ['Basic bla'], + ], $request->getHeaders()); + + } + + /** + * @runInSeparateProcess + * + * Unfortunately we have no way of testing if the HTTP response code got + * changed. + */ + function testSend() { + + if (!function_exists('xdebug_get_headers')) { + $this->markTestSkipped('XDebug needs to be installed for this test to run'); + } + + $response = new Response(204, ['Content-Type' => 'text/xml;charset=UTF-8']); + + // Second Content-Type header. Normally this doesn't make sense. + $response->addHeader('Content-Type', 'application/xml'); + $response->setBody('foo'); + + ob_start(); + + Sapi::sendResponse($response); + $headers = xdebug_get_headers(); + + $result = ob_get_clean(); + header_remove(); + + $this->assertEquals( + [ + "Content-Type: text/xml;charset=UTF-8", + "Content-Type: application/xml", + ], + $headers + ); + + $this->assertEquals('foo', $result); + + } + + /** + * @runInSeparateProcess + * @depends testSend + */ + function testSendLimitedByContentLengthString() { + + $response = new Response(200); + + $response->addHeader('Content-Length', 19); + $response->setBody('Send this sentence. Ignore this one.'); + + ob_start(); + + Sapi::sendResponse($response); + + $result = ob_get_clean(); + header_remove(); + + $this->assertEquals('Send this sentence.', $result); + + } + + /** + * @runInSeparateProcess + * @depends testSend + */ + function testSendLimitedByContentLengthStream() { + + $response = new Response(200, ['Content-Length' => 19]); + + $body = fopen('php://memory', 'w'); + fwrite($body, 'Ignore this. Send this sentence. Ignore this too.'); + rewind($body); + fread($body, 13); + $response->setBody($body); + + ob_start(); + + Sapi::sendResponse($response); + + $result = ob_get_clean(); + header_remove(); + + $this->assertEquals('Send this sentence.', $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/URLUtilTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/URLUtilTest.php new file mode 100644 index 00000000000..a2d65a5e36d --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/URLUtilTest.php @@ -0,0 +1,187 @@ +<?php + +namespace Sabre\HTTP; + +class URLUtilTest extends \PHPUnit_Framework_TestCase{ + + function testEncodePath() { + + $str = ''; + for ($i = 0;$i < 128;$i++) $str .= chr($i); + + $newStr = URLUtil::encodePath($str); + + $this->assertEquals( + '%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f' . + '%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f' . + '%20%21%22%23%24%25%26%27()%2a%2b%2c-./' . + '0123456789:%3b%3c%3d%3e%3f' . + '@ABCDEFGHIJKLMNO' . + 'PQRSTUVWXYZ%5b%5c%5d%5e_' . + '%60abcdefghijklmno' . + 'pqrstuvwxyz%7b%7c%7d~%7f', + $newStr); + + $this->assertEquals($str, URLUtil::decodePath($newStr)); + + } + + function testEncodePathSegment() { + + $str = ''; + for ($i = 0;$i < 128;$i++) $str .= chr($i); + + $newStr = URLUtil::encodePathSegment($str); + + // Note: almost exactly the same as the last test, with the + // exception of the encoding of / (ascii code 2f) + $this->assertEquals( + '%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f' . + '%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f' . + '%20%21%22%23%24%25%26%27()%2a%2b%2c-.%2f' . + '0123456789:%3b%3c%3d%3e%3f' . + '@ABCDEFGHIJKLMNO' . + 'PQRSTUVWXYZ%5b%5c%5d%5e_' . + '%60abcdefghijklmno' . + 'pqrstuvwxyz%7b%7c%7d~%7f', + $newStr); + + $this->assertEquals($str, URLUtil::decodePathSegment($newStr)); + + } + + function testDecode() { + + $str = 'Hello%20Test+Test2.txt'; + $newStr = URLUtil::decodePath($str); + $this->assertEquals('Hello Test+Test2.txt', $newStr); + + } + + /** + * @depends testDecode + */ + function testDecodeUmlaut() { + + $str = 'Hello%C3%BC.txt'; + $newStr = URLUtil::decodePath($str); + $this->assertEquals("Hello\xC3\xBC.txt", $newStr); + + } + + /** + * @depends testDecodeUmlaut + */ + function testDecodeUmlautLatin1() { + + $str = 'Hello%FC.txt'; + $newStr = URLUtil::decodePath($str); + $this->assertEquals("Hello\xC3\xBC.txt", $newStr); + + } + + /** + * This testcase was sent by a bug reporter + * + * @depends testDecode + */ + function testDecodeAccentsWindows7() { + + $str = '/webdav/%C3%A0fo%C3%B3'; + $newStr = URLUtil::decodePath($str); + $this->assertEquals(strtolower($str), URLUtil::encodePath($newStr)); + + } + + function testSplitPath() { + + $strings = [ + + // input // expected result + '/foo/bar' => ['/foo','bar'], + '/foo/bar/' => ['/foo','bar'], + 'foo/bar/' => ['foo','bar'], + 'foo/bar' => ['foo','bar'], + 'foo/bar/baz' => ['foo/bar','baz'], + 'foo/bar/baz/' => ['foo/bar','baz'], + 'foo' => ['','foo'], + 'foo/' => ['','foo'], + '/foo/' => ['','foo'], + '/foo' => ['','foo'], + '' => [null,null], + + // UTF-8 + "/\xC3\xA0fo\xC3\xB3/bar" => ["/\xC3\xA0fo\xC3\xB3",'bar'], + "/\xC3\xA0foo/b\xC3\xBCr/" => ["/\xC3\xA0foo","b\xC3\xBCr"], + "foo/\xC3\xA0\xC3\xBCr" => ["foo","\xC3\xA0\xC3\xBCr"], + + ]; + + foreach ($strings as $input => $expected) { + + $output = URLUtil::splitPath($input); + $this->assertEquals($expected, $output, 'The expected output for \'' . $input . '\' was incorrect'); + + + } + + } + + /** + * @dataProvider resolveData + */ + function testResolve($base, $update, $expected) { + + $this->assertEquals( + $expected, + URLUtil::resolve($base, $update) + ); + + } + + function resolveData() { + + return [ + [ + 'http://example.org/foo/baz', + '/bar', + 'http://example.org/bar', + ], + [ + 'https://example.org/foo', + '//example.net/', + 'https://example.net/', + ], + [ + 'https://example.org/foo', + '?a=b', + 'https://example.org/foo?a=b', + ], + [ + '//example.org/foo', + '?a=b', + '//example.org/foo?a=b', + ], + // Ports and fragments + [ + 'https://example.org:81/foo#hey', + '?a=b#c=d', + 'https://example.org:81/foo?a=b#c=d', + ], + // Relative.. in-directory paths + [ + 'http://example.org/foo/bar', + 'bar2', + 'http://example.org/foo/bar2', + ], + // Now the base path ended with a slash + [ + 'http://example.org/foo/bar/', + 'bar2/bar3', + 'http://example.org/foo/bar/bar2/bar3', + ], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/http/tests/HTTP/UtilTest.php b/htdocs/includes/sabre/sabre/http/tests/HTTP/UtilTest.php new file mode 100644 index 00000000000..5659bdd2e82 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/HTTP/UtilTest.php @@ -0,0 +1,206 @@ +<?php + +namespace Sabre\HTTP; + +class UtilTest extends \PHPUnit_Framework_TestCase { + + function testParseHTTPDate() { + + $times = [ + 'Wed, 13 Oct 2010 10:26:00 GMT', + 'Wednesday, 13-Oct-10 10:26:00 GMT', + 'Wed Oct 13 10:26:00 2010', + ]; + + $expected = 1286965560; + + foreach ($times as $time) { + $result = Util::parseHTTPDate($time); + $this->assertEquals($expected, $result->format('U')); + } + + $result = Util::parseHTTPDate('Wed Oct 6 10:26:00 2010'); + $this->assertEquals(1286360760, $result->format('U')); + + } + + function testParseHTTPDateFail() { + + $times = [ + //random string + 'NOW', + // not-GMT timezone + 'Wednesday, 13-Oct-10 10:26:00 UTC', + // No space before the 6 + 'Wed Oct 6 10:26:00 2010', + // Invalid day + 'Wed Oct 0 10:26:00 2010', + 'Wed Oct 32 10:26:00 2010', + 'Wed, 0 Oct 2010 10:26:00 GMT', + 'Wed, 32 Oct 2010 10:26:00 GMT', + 'Wednesday, 32-Oct-10 10:26:00 GMT', + // Invalid hour + 'Wed, 13 Oct 2010 24:26:00 GMT', + 'Wednesday, 13-Oct-10 24:26:00 GMT', + 'Wed Oct 13 24:26:00 2010', + ]; + + foreach ($times as $time) { + $this->assertFalse(Util::parseHTTPDate($time), 'We used the string: ' . $time); + } + + } + + function testTimezones() { + + $default = date_default_timezone_get(); + date_default_timezone_set('Europe/Amsterdam'); + + $this->testParseHTTPDate(); + + date_default_timezone_set($default); + + } + + function testToHTTPDate() { + + $dt = new \DateTime('2011-12-10 12:00:00 +0200'); + + $this->assertEquals( + 'Sat, 10 Dec 2011 10:00:00 GMT', + Util::toHTTPDate($dt) + ); + + } + + /** + * @dataProvider negotiateData + */ + function testNegotiate($acceptHeader, $available, $expected) { + + $this->assertEquals( + $expected, + Util::negotiate($acceptHeader, $available) + ); + + } + + function negotiateData() { + + return [ + [ // simple + 'application/xml', + ['application/xml'], + 'application/xml', + ], + [ // no header + null, + ['application/xml'], + 'application/xml', + ], + [ // 2 options + 'application/json', + ['application/xml', 'application/json'], + 'application/json', + ], + [ // 2 choices + 'application/json, application/xml', + ['application/xml'], + 'application/xml', + ], + [ // quality + 'application/xml;q=0.2, application/json', + ['application/xml', 'application/json'], + 'application/json', + ], + [ // wildcard + 'image/jpeg, image/png, */*', + ['application/xml', 'application/json'], + 'application/xml', + ], + [ // wildcard + quality + 'image/jpeg, image/png; q=0.5, */*', + ['application/xml', 'application/json', 'image/png'], + 'application/xml', + ], + [ // no match + 'image/jpeg', + ['application/xml'], + null, + ], + [ // This is used in sabre/dav + 'text/vcard; version=4.0', + [ + // Most often used mime-type. Version 3 + 'text/x-vcard', + // The correct standard mime-type. Defaults to version 3 as + // well. + 'text/vcard', + // vCard 4 + 'text/vcard; version=4.0', + // vCard 3 + 'text/vcard; version=3.0', + // jCard + 'application/vcard+json', + ], + 'text/vcard; version=4.0', + + ], + [ // rfc7231 example 1 + 'audio/*; q=0.2, audio/basic', + [ + 'audio/pcm', + 'audio/basic', + ], + 'audio/basic', + ], + [ // Lower quality after + 'audio/pcm; q=0.2, audio/basic; q=0.1', + [ + 'audio/pcm', + 'audio/basic', + ], + 'audio/pcm', + ], + [ // Random parameter, should be ignored + 'audio/pcm; hello; q=0.2, audio/basic; q=0.1', + [ + 'audio/pcm', + 'audio/basic', + ], + 'audio/pcm', + ], + [ // No whitepace after type, should pick the one that is the most specific. + 'text/vcard;version=3.0, text/vcard', + [ + 'text/vcard', + 'text/vcard; version=3.0' + ], + 'text/vcard; version=3.0', + ], + [ // Same as last one, but order is different + 'text/vcard, text/vcard;version=3.0', + [ + 'text/vcard; version=3.0', + 'text/vcard', + ], + 'text/vcard; version=3.0', + ], + [ // Charset should be ignored here. + 'text/vcard; charset=utf-8; version=3.0, text/vcard', + [ + 'text/vcard', + 'text/vcard; version=3.0' + ], + 'text/vcard; version=3.0', + ], + [ // Undefined offset issue. + 'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2', + ['application/xml', 'application/json', 'image/png'], + 'application/xml', + ], + + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/http/tests/bootstrap.php b/htdocs/includes/sabre/sabre/http/tests/bootstrap.php new file mode 100644 index 00000000000..74931b6f118 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/bootstrap.php @@ -0,0 +1,8 @@ +<?php + +date_default_timezone_set('UTC'); + +ini_set('error_reporting', E_ALL | E_STRICT | E_DEPRECATED); + +// Composer autoloader +include __DIR__ . '/../vendor/autoload.php'; diff --git a/htdocs/includes/sabre/sabre/http/tests/phpcs/ruleset.xml b/htdocs/includes/sabre/sabre/http/tests/phpcs/ruleset.xml new file mode 100644 index 00000000000..ec2c4c84b1d --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/phpcs/ruleset.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<ruleset name="sabre.php"> + <description>sabre.io codesniffer ruleset</description> + + <!-- Include the whole PSR-1 standard --> + <rule ref="PSR1" /> + + <!-- All PHP files MUST use the Unix LF (linefeed) line ending. --> + <rule ref="Generic.Files.LineEndings"> + <properties> + <property name="eolChar" value="\n"/> + </properties> + </rule> + + <!-- The closing ?> tag MUST be omitted from files containing only PHP. --> + <rule ref="Zend.Files.ClosingTag"/> + + <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. --> + <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"> + <properties> + <property name="ignoreBlankLines" value="true"/> + </properties> + </rule> + + <!-- There MUST NOT be more than one statement per line. --> + <rule ref="Generic.Formatting.DisallowMultipleStatements"/> + + <rule ref="Generic.WhiteSpace.ScopeIndent"> + <properties> + <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT"/> + </properties> + </rule> + <rule ref="Generic.WhiteSpace.DisallowTabIndent"/> + + <!-- PHP keywords MUST be in lower case. --> + <rule ref="Generic.PHP.LowerCaseKeyword"/> + + <!-- The PHP constants true, false, and null MUST be in lower case. --> + <rule ref="Generic.PHP.LowerCaseConstant"/> + + <!-- <rule ref="Squiz.Scope.MethodScope"/> --> + <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/> + + <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. --> + <!-- + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing"> + <properties> + <property name="equalsSpacing" value="1"/> + </properties> + </rule> + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint"> + <severity>0</severity> + </rule> + --> + <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/> + +</ruleset> diff --git a/htdocs/includes/sabre/sabre/http/tests/phpunit.xml b/htdocs/includes/sabre/sabre/http/tests/phpunit.xml new file mode 100644 index 00000000000..32d701a37f7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/tests/phpunit.xml @@ -0,0 +1,18 @@ +<phpunit + colors="true" + bootstrap="bootstrap.php" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + strict="true" + > + <testsuite name="Sabre_HTTP"> + <directory>HTTP/</directory> + </testsuite> + + <filter> + <whitelist addUncoveredFilesFromWhitelist="true"> + <directory suffix=".php">../lib/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/htdocs/includes/sabre/sabre/uri/.gitignore b/htdocs/includes/sabre/sabre/uri/.gitignore new file mode 100644 index 00000000000..19d1affd4a7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/.gitignore @@ -0,0 +1,13 @@ +# Composer +vendor/ +composer.lock + +# Tests +tests/cov/ + +# Composer binaries +bin/phpunit +bin/phpcs + +# Vim +.*.swp diff --git a/htdocs/includes/sabre/sabre/uri/.travis.yml b/htdocs/includes/sabre/sabre/uri/.travis.yml new file mode 100644 index 00000000000..75c8270df21 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/.travis.yml @@ -0,0 +1,14 @@ +language: php +php: + - 5.4 + - 5.5 + - 5.6 + - 7 + - 7.1 + +script: + - ./bin/phpunit --configuration tests/phpunit.xml.dist + - ./bin/sabre-cs-fixer fix lib/ --dry-run --diff + +before_script: composer install --dev + diff --git a/htdocs/includes/sabre/sabre/uri/CHANGELOG.md b/htdocs/includes/sabre/sabre/uri/CHANGELOG.md new file mode 100644 index 00000000000..a30e45139ae --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/CHANGELOG.md @@ -0,0 +1,51 @@ +ChangeLog +========= + +1.2.0 (2016-12-06) +------------------ + +* Now throwing `InvalidUriException` if a uri passed to the `parse` function + is invalid or could not be parsed. +* #11: Fix support for URIs that start with a triple slash. PHP's `parse_uri()` + doesn't support them, so we now have a pure-php fallback in case it fails. +* #9: Fix support for relative URI's that have a non-uri encoded colon `:` in + them. + + +1.1.1 (2016-10-27) +------------------ + +* #10: Correctly support file:// URIs in the build() method. (@yuloh) + + +1.1.0 (2016-03-07) +------------------ + +* #6: PHP's `parse_url()` corrupts strings if they contain certain + non ascii-characters such as Chinese or Hebrew. sabre/uri's `parse()` + function now percent-encodes these characters beforehand. + + +1.0.1 (2015-04-28) +------------------ + +* #4: Using php-cs-fixer to automatically enforce conding standards. +* #5: Resolving to and building `mailto:` urls were not correctly handled. + + +1.0.0 (2015-01-27) +------------------ + +* Added a `normalize` function. +* Added a `buildUri` function. +* Fixed a bug in the `resolve` when only a new fragment is specified. + +San José, CalConnect XXXII release! + +0.0.1 (2014-11-17) +------------------ + +* First version! +* Source was lifted from sabre/http package. +* Provides a `resolve` and a `split` function. +* Requires PHP 5.4.8 and up. diff --git a/htdocs/includes/sabre/sabre/uri/LICENSE b/htdocs/includes/sabre/sabre/uri/LICENSE new file mode 100644 index 00000000000..9a3a9194600 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2014-2016 fruux GmbH (https://fruux.com/) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name Sabre nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/htdocs/includes/sabre/sabre/uri/README.md b/htdocs/includes/sabre/sabre/uri/README.md new file mode 100644 index 00000000000..76f55d8e454 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/README.md @@ -0,0 +1,55 @@ +sabre/uri +========= + +sabre/uri is a lightweight library that provides several functions for working +with URIs, staying true to the rules of [RFC3986][2]. + +Partially inspired by [Node.js URL library][3], and created to solve real +problems in PHP applications. 100% unitested and many tests are based on +examples from RFC3986. + +The library provides the following functions: + +1. `resolve` to resolve relative urls. +2. `normalize` to aid in comparing urls. +3. `parse`, which works like PHP's [parse_url][6]. +4. `build` to do the exact opposite of `parse`. +5. `split` to easily get the 'dirname' and 'basename' of a URL without all the + problems those two functions have. + + +Further reading +--------------- + +* [Installation][7] +* [Usage][8] + + +Build status +------------ + +| branch | status | +| ------ | ------ | +| master | [![Build Status](https://travis-ci.org/fruux/sabre-uri.svg?branch=master)](https://travis-ci.org/fruux/sabre-uri) | + + +Questions? +---------- + +Head over to the [sabre/dav mailinglist][4], or you can also just open a ticket +on [GitHub][5]. + + +Made at fruux +------------- + +This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. + +[1]: http://sabre.io/uri/ +[2]: https://tools.ietf.org/html/rfc3986/ +[3]: http://nodejs.org/api/url.html +[4]: http://groups.google.com/group/sabredav-discuss +[5]: https://github.com/fruux/sabre-uri/issues/ +[6]: http://php.net/manual/en/function.parse-url.php +[7]: http://sabre.io/uri/install/ +[8]: http://sabre.io/uri/usage/ diff --git a/htdocs/includes/sabre/sabre/uri/composer.json b/htdocs/includes/sabre/sabre/uri/composer.json new file mode 100644 index 00000000000..efa5a693004 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/composer.json @@ -0,0 +1,41 @@ +{ + "name": "sabre/uri", + "description": "Functions for making sense out of URIs.", + "keywords": [ + "URI", + "URL", + "rfc3986" + ], + "homepage": "http://sabre.io/uri/", + "license": "BSD-3-Clause", + "require": { + "php": ">=5.4.7" + }, + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "support": { + "forum": "https://groups.google.com/group/sabredav-discuss", + "source": "https://github.com/fruux/sabre-uri" + }, + "autoload": { + "files" : [ + "lib/functions.php" + ], + "psr-4" : { + "Sabre\\Uri\\" : "lib/" + } + }, + "require-dev": { + "sabre/cs": "~1.0.0", + "phpunit/phpunit" : "*" + }, + "config" : { + "bin-dir" : "bin/" + } +} diff --git a/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php b/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php new file mode 100644 index 00000000000..0385fd4628d --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php @@ -0,0 +1,17 @@ +<?php + +namespace Sabre\Uri; + +/** + * Invalid Uri + * + * This is thrown when an attempt was made to use Sabre\Uri parse a uri that + * it could not. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (https://evertpot.com/) + * @license http://sabre.io/license/ + */ +class InvalidUriException extends \Exception { + +} diff --git a/htdocs/includes/sabre/sabre/uri/lib/Version.php b/htdocs/includes/sabre/sabre/uri/lib/Version.php new file mode 100644 index 00000000000..4633a209861 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/lib/Version.php @@ -0,0 +1,19 @@ +<?php + +namespace Sabre\Uri; + +/** + * This class contains the version number for this package. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ + */ +class Version { + + /** + * Full version number + */ + const VERSION = '1.2.0'; + +} diff --git a/htdocs/includes/sabre/sabre/uri/lib/functions.php b/htdocs/includes/sabre/sabre/uri/lib/functions.php new file mode 100644 index 00000000000..df74b98b324 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/lib/functions.php @@ -0,0 +1,373 @@ +<?php + +namespace Sabre\Uri; + +/** + * This file contains all the uri handling functions. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ + */ + +/** + * Resolves relative urls, like a browser would. + * + * This function takes a basePath, which itself _may_ also be relative, and + * then applies the relative path on top of it. + * + * @param string $basePath + * @param string $newPath + * @return string + */ +function resolve($basePath, $newPath) { + + $base = parse($basePath); + $delta = parse($newPath); + + $pick = function($part) use ($base, $delta) { + + if ($delta[$part]) { + return $delta[$part]; + } elseif ($base[$part]) { + return $base[$part]; + } + return null; + + }; + + // If the new path defines a scheme, it's absolute and we can just return + // that. + if ($delta['scheme']) { + return build($delta); + } + + $newParts = []; + + $newParts['scheme'] = $pick('scheme'); + $newParts['host'] = $pick('host'); + $newParts['port'] = $pick('port'); + + $path = ''; + if ($delta['path']) { + // If the path starts with a slash + if ($delta['path'][0] === '/') { + $path = $delta['path']; + } else { + // Removing last component from base path. + $path = $base['path']; + if (strpos($path, '/') !== false) { + $path = substr($path, 0, strrpos($path, '/')); + } + $path .= '/' . $delta['path']; + } + } else { + $path = $base['path'] ?: '/'; + } + // Removing .. and . + $pathParts = explode('/', $path); + $newPathParts = []; + foreach ($pathParts as $pathPart) { + + switch ($pathPart) { + //case '' : + case '.' : + break; + case '..' : + array_pop($newPathParts); + break; + default : + $newPathParts[] = $pathPart; + break; + } + } + + $path = implode('/', $newPathParts); + + // If the source url ended with a /, we want to preserve that. + $newParts['path'] = $path; + if ($delta['query']) { + $newParts['query'] = $delta['query']; + } elseif (!empty($base['query']) && empty($delta['host']) && empty($delta['path'])) { + // Keep the old query if host and path didn't change + $newParts['query'] = $base['query']; + } + if ($delta['fragment']) { + $newParts['fragment'] = $delta['fragment']; + } + return build($newParts); + +} + +/** + * Takes a URI or partial URI as its argument, and normalizes it. + * + * After normalizing a URI, you can safely compare it to other URIs. + * This function will for instance convert a %7E into a tilde, according to + * rfc3986. + * + * It will also change a %3a into a %3A. + * + * @param string $uri + * @return string + */ +function normalize($uri) { + + $parts = parse($uri); + + if (!empty($parts['path'])) { + $pathParts = explode('/', ltrim($parts['path'], '/')); + $newPathParts = []; + foreach ($pathParts as $pathPart) { + switch ($pathPart) { + case '.': + // skip + break; + case '..' : + // One level up in the hierarchy + array_pop($newPathParts); + break; + default : + // Ensuring that everything is correctly percent-encoded. + $newPathParts[] = rawurlencode(rawurldecode($pathPart)); + break; + } + } + $parts['path'] = '/' . implode('/', $newPathParts); + } + + if ($parts['scheme']) { + $parts['scheme'] = strtolower($parts['scheme']); + $defaultPorts = [ + 'http' => '80', + 'https' => '443', + ]; + + if (!empty($parts['port']) && isset($defaultPorts[$parts['scheme']]) && $defaultPorts[$parts['scheme']] == $parts['port']) { + // Removing default ports. + unset($parts['port']); + } + // A few HTTP specific rules. + switch ($parts['scheme']) { + case 'http' : + case 'https' : + if (empty($parts['path'])) { + // An empty path is equivalent to / in http. + $parts['path'] = '/'; + } + break; + } + } + + if ($parts['host']) $parts['host'] = strtolower($parts['host']); + + return build($parts); + +} + +/** + * Parses a URI and returns its individual components. + * + * This method largely behaves the same as PHP's parse_url, except that it will + * return an array with all the array keys, including the ones that are not + * set by parse_url, which makes it a bit easier to work with. + * + * Unlike PHP's parse_url, it will also convert any non-ascii characters to + * percent-encoded strings. PHP's parse_url corrupts these characters on OS X. + * + * @param string $uri + * @return array + */ +function parse($uri) { + + // Normally a URI must be ASCII, however. However, often it's not and + // parse_url might corrupt these strings. + // + // For that reason we take any non-ascii characters from the uri and + // uriencode them first. + $uri = preg_replace_callback( + '/[^[:ascii:]]/u', + function($matches) { + return rawurlencode($matches[0]); + }, + $uri + ); + + $result = parse_url($uri); + if (!$result) { + $result = _parse_fallback($uri); + } + + return + $result + [ + 'scheme' => null, + 'host' => null, + 'path' => null, + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null, + ]; + +} + +/** + * This function takes the components returned from PHP's parse_url, and uses + * it to generate a new uri. + * + * @param array $parts + * @return string + */ +function build(array $parts) { + + $uri = ''; + + $authority = ''; + if (!empty($parts['host'])) { + $authority = $parts['host']; + if (!empty($parts['user'])) { + $authority = $parts['user'] . '@' . $authority; + } + if (!empty($parts['port'])) { + $authority = $authority . ':' . $parts['port']; + } + } + + if (!empty($parts['scheme'])) { + // If there's a scheme, there's also a host. + $uri = $parts['scheme'] . ':'; + + } + if ($authority || (!empty($parts['scheme']) && $parts['scheme'] === 'file')) { + // No scheme, but there is a host. + $uri .= '//' . $authority; + + } + + if (!empty($parts['path'])) { + $uri .= $parts['path']; + } + if (!empty($parts['query'])) { + $uri .= '?' . $parts['query']; + } + if (!empty($parts['fragment'])) { + $uri .= '#' . $parts['fragment']; + } + + return $uri; + +} + +/** + * Returns the 'dirname' and 'basename' for a path. + * + * The reason there is a custom function for this purpose, is because + * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale + * is used) and we need a method that just operates on UTF-8 characters. + * + * In addition basename and dirname are platform aware, and will treat + * backslash (\) as a directory separator on windows. + * + * This method returns the 2 components as an array. + * + * If there is no dirname, it will return an empty string. Any / appearing at + * the end of the string is stripped off. + * + * @param string $path + * @return array + */ +function split($path) { + + $matches = []; + if (preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u', $path, $matches)) { + return [$matches[1], $matches[2]]; + } + return [null,null]; + +} + +/** + * This function is another implementation of parse_url, except this one is + * fully written in PHP. + * + * The reason is that the PHP bug team is not willing to admit that there are + * bugs in the parse_url implementation. + * + * This function is only called if the main parse method fails. It's pretty + * crude and probably slow, so the original parse_url is usually preferred. + * + * @param string $uri + * @return array + */ +function _parse_fallback($uri) { + + // Normally a URI must be ASCII, however. However, often it's not and + // parse_url might corrupt these strings. + // + // For that reason we take any non-ascii characters from the uri and + // uriencode them first. + $uri = preg_replace_callback( + '/[^[:ascii:]]/u', + function($matches) { + return rawurlencode($matches[0]); + }, + $uri + ); + + $result = [ + 'scheme' => null, + 'host' => null, + 'port' => null, + 'user' => null, + 'path' => null, + 'fragment' => null, + 'query' => null, + ]; + + if (preg_match('% ^([A-Za-z][A-Za-z0-9+-\.]+): %x', $uri, $matches)) { + + $result['scheme'] = $matches[1]; + // Take what's left. + $uri = substr($uri, strlen($result['scheme']) + 1); + + } + + // Taking off a fragment part + if (strpos($uri, '#')) { + list($uri, $result['fragment']) = explode('#', $uri, 2); + } + // Taking off the query part + if (strpos($uri, '?')) { + list($uri, $result['query']) = explode('?', $uri, 2); + } + + if (substr($uri, 0, 3) === '///') { + // The triple slash uris are a bit unusual, but we have special handling + // for them. + $result['path'] = substr($uri, 2); + $result['host'] = ''; + } elseif (substr($uri, 0, 2) === '//') { + // Uris that have an authority part. + $regex = ' + %^ + // + (?: (?<user> [^:@]+) (: (?<pass> [^@]+)) @)? + (?<host> ( [^:/]* | \[ [^\]]+ \] )) + (?: : (?<port> [0-9]+))? + (?<path> / .*)? + $%x + '; + if (!preg_match($regex, $uri, $matches)) { + throw new InvalidUriException('Invalid, or could not parse URI'); + } + if ($matches['host']) $result['host'] = $matches['host']; + if ($matches['port']) $result['port'] = (int)$matches['port']; + if (isset($matches['path'])) $result['path'] = $matches['path']; + if ($matches['user']) $result['user'] = $matches['user']; + if ($matches['pass']) $result['pass'] = $matches['pass']; + } else { + $result['path'] = $uri; + } + + return $result; +} diff --git a/htdocs/includes/sabre/sabre/uri/tests/BuildTest.php b/htdocs/includes/sabre/sabre/uri/tests/BuildTest.php new file mode 100644 index 00000000000..ae4b4ba2719 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/BuildTest.php @@ -0,0 +1,41 @@ +<?php + +namespace Sabre\Uri; + +class BuildTest extends \PHPUnit_Framework_TestCase{ + + /** + * @dataProvider buildUriData + */ + function testBuild($value) { + + $this->assertEquals( + $value, + build(parse_url($value)) + ); + + } + + function buildUriData() { + + return [ + ['http://example.org/'], + ['http://example.org/foo/bar'], + ['//example.org/foo/bar'], + ['/foo/bar'], + ['http://example.org:81/'], + ['http://user@example.org:81/'], + ['http://example.org:81/hi?a=b'], + ['http://example.org:81/hi?a=b#c=d'], + // [ '//example.org:81/hi?a=b#c=d'], // Currently fails due to a + // PHP bug. + ['/hi?a=b#c=d'], + ['?a=b#c=d'], + ['#c=d'], + ['file:///etc/hosts'], + ['file://localhost/etc/hosts'], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/uri/tests/NormalizeTest.php b/htdocs/includes/sabre/sabre/uri/tests/NormalizeTest.php new file mode 100644 index 00000000000..4dbe943205e --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/NormalizeTest.php @@ -0,0 +1,42 @@ +<?php + +namespace Sabre\Uri; + +class NormalizeTest extends \PHPUnit_Framework_TestCase{ + + /** + * @dataProvider normalizeData + */ + function testNormalize($in, $out) { + + $this->assertEquals( + $out, + normalize($in) + ); + + } + + function normalizeData() { + + return [ + ['http://example.org/', 'http://example.org/'], + ['HTTP://www.EXAMPLE.com/', 'http://www.example.com/'], + ['http://example.org/%7Eevert', 'http://example.org/~evert'], + ['http://example.org/./evert', 'http://example.org/evert'], + ['http://example.org/../evert', 'http://example.org/evert'], + ['http://example.org/foo/../evert', 'http://example.org/evert'], + ['/%41', '/A'], + ['/%3F', '/%3F'], + ['/%3f', '/%3F'], + ['http://example.org', 'http://example.org/'], + ['http://example.org:/', 'http://example.org/'], + ['http://example.org:80/', 'http://example.org/'], + // See issue #6. parse_url corrupts strings like this, but only on + // macs. + //[ 'http://example.org/有词法别名.zh','http://example.org/%E6%9C%89%E8%AF%8D%E6%B3%95%E5%88%AB%E5%90%8D.zh'], + + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/uri/tests/ParseTest.php b/htdocs/includes/sabre/sabre/uri/tests/ParseTest.php new file mode 100644 index 00000000000..ab0ead2cc96 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/ParseTest.php @@ -0,0 +1,179 @@ +<?php + +namespace Sabre\Uri; + +class ParseTest extends \PHPUnit_Framework_TestCase{ + + /** + * @dataProvider parseData + */ + function testParse($in, $out) { + + $this->assertEquals( + $out, + parse($in) + ); + + } + + /** + * @dataProvider parseData + */ + function testParseFallback($in, $out) { + + $result = _parse_fallback($in); + $result = $result + [ + 'scheme' => null, + 'host' => null, + 'path' => null, + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null, + ]; + + $this->assertEquals( + $out, + $result + ); + + } + + function parseData() { + + return [ + [ + 'http://example.org/hello?foo=bar#test', + [ + 'scheme' => 'http', + 'host' => 'example.org', + 'path' => '/hello', + 'port' => null, + 'user' => null, + 'query' => 'foo=bar', + 'fragment' => 'test' + ] + ], + // See issue #6. parse_url corrupts strings like this, but only on + // macs. + [ + 'http://example.org/有词法别名.zh', + [ + 'scheme' => 'http', + 'host' => 'example.org', + 'path' => '/%E6%9C%89%E8%AF%8D%E6%B3%95%E5%88%AB%E5%90%8D.zh', + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null + ] + ], + [ + 'ftp://user:password@ftp.example.org/', + [ + 'scheme' => 'ftp', + 'host' => 'ftp.example.org', + 'path' => '/', + 'port' => null, + 'user' => 'user', + 'pass' => 'password', + 'query' => null, + 'fragment' => null, + ] + ], + // See issue #9, parse_url doesn't like colons followed by numbers even + // though they are allowed since RFC 3986 + [ + 'http://example.org/hello:12?foo=bar#test', + [ + 'scheme' => 'http', + 'host' => 'example.org', + 'path' => '/hello:12', + 'port' => null, + 'user' => null, + 'query' => 'foo=bar', + 'fragment' => 'test' + ] + ], + [ + '/path/to/colon:34', + [ + 'scheme' => null, + 'host' => null, + 'path' => '/path/to/colon:34', + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null, + ] + ], + // File scheme + [ + 'file:///foo/bar', + [ + 'scheme' => 'file', + 'host' => '', + 'path' => '/foo/bar', + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null, + ] + ], + // Weird scheme with triple-slash. See Issue #11. + [ + 'vfs:///somefile', + [ + 'scheme' => 'vfs', + 'host' => '', + 'path' => '/somefile', + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null, + ] + ], + // Examples from RFC3986 + [ + 'ldap://[2001:db8::7]/c=GB?objectClass?one', + [ + 'scheme' => 'ldap', + 'host' => '[2001:db8::7]', + 'path' => '/c=GB', + 'port' => null, + 'user' => null, + 'query' => 'objectClass?one', + 'fragment' => null, + ] + ], + [ + 'news:comp.infosystems.www.servers.unix', + [ + 'scheme' => 'news', + 'host' => null, + 'path' => 'comp.infosystems.www.servers.unix', + 'port' => null, + 'user' => null, + 'query' => null, + 'fragment' => null, + ] + ], + // Port + [ + 'http://example.org:8080/', + [ + 'scheme' => 'http', + 'host' => 'example.org', + 'path' => '/', + 'port' => 8080, + 'user' => null, + 'query' => null, + 'fragment' => null, + ] + ], + + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/uri/tests/ResolveTest.php b/htdocs/includes/sabre/sabre/uri/tests/ResolveTest.php new file mode 100644 index 00000000000..73e4dea69d8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/ResolveTest.php @@ -0,0 +1,83 @@ +<?php + +namespace Sabre\Uri; + +class ResolveTest extends \PHPUnit_Framework_TestCase{ + + /** + * @dataProvider resolveData + */ + function testResolve($base, $update, $expected) { + + $this->assertEquals( + $expected, + resolve($base, $update) + ); + + } + + function resolveData() { + + return [ + [ + 'http://example.org/foo/baz', + '/bar', + 'http://example.org/bar', + ], + [ + 'https://example.org/foo', + '//example.net/', + 'https://example.net/', + ], + [ + 'https://example.org/foo', + '?a=b', + 'https://example.org/foo?a=b', + ], + [ + '//example.org/foo', + '?a=b', + '//example.org/foo?a=b', + ], + // Ports and fragments + [ + 'https://example.org:81/foo#hey', + '?a=b#c=d', + 'https://example.org:81/foo?a=b#c=d', + ], + // Relative.. in-directory paths + [ + 'http://example.org/foo/bar', + 'bar2', + 'http://example.org/foo/bar2', + ], + // Now the base path ended with a slash + [ + 'http://example.org/foo/bar/', + 'bar2/bar3', + 'http://example.org/foo/bar/bar2/bar3', + ], + // .. and . + [ + 'http://example.org/foo/bar/', + '../bar2/.././/bar3/', + 'http://example.org/foo//bar3/', + ], + // Only updating the fragment + [ + 'https://example.org/foo?a=b', + '#comments', + 'https://example.org/foo?a=b#comments', + ], + // Switching to mailto! + [ + 'https://example.org/foo?a=b', + 'mailto:foo@example.org', + 'mailto:foo@example.org', + ], + + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/uri/tests/SplitTest.php b/htdocs/includes/sabre/sabre/uri/tests/SplitTest.php new file mode 100644 index 00000000000..2d73c9b255d --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/SplitTest.php @@ -0,0 +1,41 @@ +<?php + +namespace Sabre\Uri; + +class SplitTest extends \PHPUnit_Framework_TestCase{ + + function testSplit() { + + $strings = [ + + // input // expected result + '/foo/bar' => ['/foo','bar'], + '/foo/bar/' => ['/foo','bar'], + 'foo/bar/' => ['foo','bar'], + 'foo/bar' => ['foo','bar'], + 'foo/bar/baz' => ['foo/bar','baz'], + 'foo/bar/baz/' => ['foo/bar','baz'], + 'foo' => ['','foo'], + 'foo/' => ['','foo'], + '/foo/' => ['','foo'], + '/foo' => ['','foo'], + '' => [null,null], + + // UTF-8 + "/\xC3\xA0fo\xC3\xB3/bar" => ["/\xC3\xA0fo\xC3\xB3",'bar'], + "/\xC3\xA0foo/b\xC3\xBCr/" => ["/\xC3\xA0foo","b\xC3\xBCr"], + "foo/\xC3\xA0\xC3\xBCr" => ["foo","\xC3\xA0\xC3\xBCr"], + + ]; + + foreach ($strings as $input => $expected) { + + $output = split($input); + $this->assertEquals($expected, $output, 'The expected output for \'' . $input . '\' was incorrect'); + + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/uri/tests/phpcs/ruleset.xml b/htdocs/includes/sabre/sabre/uri/tests/phpcs/ruleset.xml new file mode 100644 index 00000000000..ec2c4c84b1d --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/phpcs/ruleset.xml @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<ruleset name="sabre.php"> + <description>sabre.io codesniffer ruleset</description> + + <!-- Include the whole PSR-1 standard --> + <rule ref="PSR1" /> + + <!-- All PHP files MUST use the Unix LF (linefeed) line ending. --> + <rule ref="Generic.Files.LineEndings"> + <properties> + <property name="eolChar" value="\n"/> + </properties> + </rule> + + <!-- The closing ?> tag MUST be omitted from files containing only PHP. --> + <rule ref="Zend.Files.ClosingTag"/> + + <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. --> + <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"> + <properties> + <property name="ignoreBlankLines" value="true"/> + </properties> + </rule> + + <!-- There MUST NOT be more than one statement per line. --> + <rule ref="Generic.Formatting.DisallowMultipleStatements"/> + + <rule ref="Generic.WhiteSpace.ScopeIndent"> + <properties> + <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT"/> + </properties> + </rule> + <rule ref="Generic.WhiteSpace.DisallowTabIndent"/> + + <!-- PHP keywords MUST be in lower case. --> + <rule ref="Generic.PHP.LowerCaseKeyword"/> + + <!-- The PHP constants true, false, and null MUST be in lower case. --> + <rule ref="Generic.PHP.LowerCaseConstant"/> + + <!-- <rule ref="Squiz.Scope.MethodScope"/> --> + <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/> + + <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. --> + <!-- + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing"> + <properties> + <property name="equalsSpacing" value="1"/> + </properties> + </rule> + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint"> + <severity>0</severity> + </rule> + --> + <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/> + +</ruleset> diff --git a/htdocs/includes/sabre/sabre/uri/tests/phpunit.xml.dist b/htdocs/includes/sabre/sabre/uri/tests/phpunit.xml.dist new file mode 100644 index 00000000000..338d24d3c06 --- /dev/null +++ b/htdocs/includes/sabre/sabre/uri/tests/phpunit.xml.dist @@ -0,0 +1,18 @@ +<phpunit + colors="true" + bootstrap="../vendor/autoload.php" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + strict="true" + > + <testsuite name="sabre-uri"> + <directory>.</directory> + </testsuite> + + <filter> + <whitelist addUncoveredFilesFromWhitelist="true"> + <directory suffix=".php">../lib/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/htdocs/includes/sabre/sabre/vobject/.gitignore b/htdocs/includes/sabre/sabre/vobject/.gitignore new file mode 100644 index 00000000000..95935f7988e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/.gitignore @@ -0,0 +1,21 @@ +# Composer stuff +vendor/ +composer.lock +tests/cov/ +tests/temp + +#vim +.*.swp + +#binaries +bin/phpunit +bin/phpcs +bin/php-cs-fixer +bin/sabre-cs-fixer +bin/hoa + +# Development stuff +testdata/ + +# OS X +.DS_Store diff --git a/htdocs/includes/sabre/sabre/vobject/.travis.yml b/htdocs/includes/sabre/sabre/vobject/.travis.yml new file mode 100644 index 00000000000..3c5b321571a --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/.travis.yml @@ -0,0 +1,20 @@ +language: php +php: + - 5.5 + - 5.6 + - 7.0 + - 7.1 + +sudo: false + +script: + - phpunit --configuration tests/phpunit.xml + - ./bin/sabre-cs-fixer fix . --dry-run --diff + +before_script: + - phpenv config-rm xdebug.ini; true + - composer install + +cache: + directories: + - $HOME/.composer/cache diff --git a/htdocs/includes/sabre/sabre/vobject/CHANGELOG.md b/htdocs/includes/sabre/sabre/vobject/CHANGELOG.md new file mode 100644 index 00000000000..c8f4cb4beb9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/CHANGELOG.md @@ -0,0 +1,782 @@ +ChangeLog +========= + +4.1.2 (2016-12-15) +------------------ + +* #340: Support for `BYYEARDAY` recurrence when `FREQ=YEARLY`. (@PHPGangsta) +* #341: Support for `BYWEEKNO` recurrence when `FREQ=YEARLY`. (@PHPGangsta) +* Updated to the latest windows timezone data mappings. +* #344: Auto-detecting more Outlook 365-generated timezone identifiers. + (@jpirkey) +* #348: `FreeBusyGenerator` can now accept streams. +* Support sabre/xml 1.5 and 2.0. +* #355: Support `DateTimeInterface` in more places where only `DateTime` was + supported. (@gharlan). +* #351: Fixing an inclusive/exclusive problem with `isInTimeRange` and + `fastForward` with all-day events. (@strokyl, thanks you are brilliant). + + +4.1.1 (2016-07-15) +------------------ + +* #327: Throwing `InvalidDataException` in more cases where invalid iCalendar + dates and times were provided. (@rsto) +* #331: Fix dealing with multiple overridden instances falling on the same + date/time (@afedyk-sugarcrm). +* #333: Fix endless loop on invalid `BYMONTH` values in recurrence. + (@PHPGangsta) +* #339: Fixed a few `validate()` results when repair is off. (@PHPGangsta) +* #338: Stripping invalid `BYMONTH=` rules during `validate()` (@PHPGangsta) +* #336: Fix incorrect `BYSECOND=` validation. (@PHPGangsta) + + +4.1.0 (2016-04-06) +------------------ + +* #309: When expanding recurring events, the first event should also have a + `RECURRENCE-ID` property. +* #306: iTip REPLYs to the first instance of a recurring event was not handled + correctly. +* Slightly better error message during validation of `N` and `ADR` properties. +* #312: Correctly extracing timezone in the iTip broker, even when we don't + have a master event. (@vkomrakov-sugar). +* When validating a component's property that must appear once and which could + automatically be repaired, make sure we report the change as 'repaired'. +* Added a PHPUnitAssertions trait. This trait makes it easy to compare two + vcards or iCalendar objects semantically. +* Better error message when parsing objects with an invalid `VALUE` parameter. + + +4.0.3 (2016-03-12) +------------------ + +* #300: Added `VCard::getByType()` to quickly get a property with a specific + `TYPE` parameter. (@kbond) +* #302: `UNTIL` was not encoded correctly when converting to jCal. + (@GrahamLinagora) +* #303: `COUNT` is now encoded as an int in jCal instead of a string. (@strokyl) +* #295: `RRULE` now has more validation and repair rules. + + +4.0.2 (2016-01-11) +------------------ + +* #288: Only decode `CHARSET` if we're reading vCard 2.1. If it appears + in any other document, we must ignore it. + + +4.0.1 (2016-01-04) +------------------ + +* #284: When generating `CANCEL` iTip messages, we now include `DTEND`. + (@kewisch) + + +4.0.0 (2015-12-11) +------------------ + +* #274: When creating new vCards, the default vCard version is now 4.0. +* #275: `VEVENT`, `VTODO` and `VCARD` now automatically get a `UID` and + `DTSTAMP` property if this was not already specified. +* `ParseException` now extends `\Exception`. +* `Sabre\VObject\Reader::read` now has a `$charset` argument. +* #272: `Sabre\VObject\Recur\EventIterator::$maxInstances` is now + `Sabre\VObject\Settings::$maxRecurrences` and is also honored by the + FreeBusyGenerator. +* #278: `expand()` did not work correctly on events with sub-components. + + +4.0.0-beta1 (2015-12-02) +------------------------ + +* #258: Support for expanding events that use `RDATE`. (@jabdoa2) +* #258: Correctly support TZID for events that use `RDATE`. (@jabdoa2) +* #240: `Component\VCalendar::expand()` now returns a new expanded `VCalendar` + object, instead of editing the existing `VCalendar` in-place. This is a BC + break. +* #265: Using the new `InvalidDataException` in place of + `InvalidArgumentException` and `LogicException` in all places where we fail + because there was something wrong with input data. +* #227: Always add `VALUE=URI` to `PHOTO` properties. +* #235: Always add `VALUE=URI` to `URL` properties. +* It's now possible to override which class is used instead of + `Component\VCalendar` or `Component\VCard` during parsing. +* #263: Lots of small cleanups. (@jakobsack) +* #220: Automatically stop recurring after 3500 recurrences. +* #41: Allow user to set different encoding than UTF-8 when decoding vCards. +* #41: Support the `ENCODING` parameter from vCard 2.1. + Both ISO-8859-1 and Windows-1252 are currently supported. +* #185: Fix encoding/decoding of `TIME` values in jCal/jCard. + + +4.0.0-alpha2 (2015-09-04) +------------------------- + +* Updated windows timezone file to support new mexican timezone. +* #239: Added a `BirthdayCalendarGenerator`. (@DominikTo) +* #250: `isInTimeRange()` now considers the timezone for floating dates and + times. (@armin-hackmann) +* Added a duplicate vcard merging tool for the command line. +* #253: `isInTimeRange()` now correctly handles events that throw the + `NoInstancesException` exception. (@migrax, @DominikTo) +* #254: The parser threw an `E_NOTICE` for certain invalid objects. It now + correctly throws a `ParseException`. + + +4.0.0-alpha1 (2015-07-17) +------------------------- + +* sabre/vobject now requires PHP 5.5. +* #244: PHP7 support. +* Lots of speedups and reduced memory usage! +* #160: Support for xCal a.k.a. RFC6321! (@Hywan) +* #192: Support for xCard a.k.a. RFC6351! (@Hywan) +* #139: We now accept `DateTimeInterface` wherever it accepted `DateTime` + before in arguments. This means that either `DateTime` or + `DateTimeImmutable` may be used everywhere. +* #242: Full support for the `VAVAILABILITY` component, and calculating + `VFREEBUSY` based on `VAVAILABILITY` data. +* #186: Fixing conversion of `UTC-OFFSET` properties when going back and + forward between jCal and iCalendar. +* Properties, Components and Parameters now implement PHP's `JsonSerializable` + interface. +* #139: We now _always_ return `DateTimeImmutable` from any method. This could + potentially have big implications if you manipulate Date objects anywhere. +* #161: Simplified `ElementList` by extending `ArrayIterator`. +* Removed `RecurrenceIterator` (use Recur\EventIterator instead). +* Now using php-cs-fixer to automatically enforce and correct CS. +* #233: The `+00:00` timezone is now recognized as UTC. (@c960657) +* #237: Added a `destroy()` method to all documents. This method breaks any + circular references, allowing PHP to free up memory. +* #197: Made accessing properties and objects by their name a lot faster. This + especially helps objects that have a lot of sub-components or properties, + such as large iCalendar objects. +* #197: The `$children` property on components has been changed from `public` + to `protected`. Use the `children()` method instead to get a flat list of + objects. +* #244: The `Float` and `Integer` classes have been renamed to `FloatValue` + and `IntegerValue` to allow PHP 7 compatibility. + + +3.5.3 (2016-10-06) +------------------ + +* #331: Fix dealing with multiple overridden instances falling on the same + date/time (@afedyk-sugarcrm). + + +3.5.2 (2016-04-24) +----------------- + +* #312: Backported a fix related to iTip processing of events with timezones, + without a master event. + + +3.5.1 (2016-04-06) +------------------ + +* #309: When expanding recurring events, the first event should also have a + `RECURRENCE-ID` property. +* #306: iTip REPLYs to the first instance of a recurring event was not handled + correctly. + + +3.5.0 (2016-01-11) +------------------ + +* This release supports PHP 7, contrary to 3.4.x versions. +* BC Break: `Sabre\VObject\Property\Float` has been renamed to + `Sabre\VObject\Property\FloatValue`. +* BC Break: `Sabre\VObject\Property\Integer` has been renamed to + `Sabre\VObject\Property\IntegerValue`. + + +3.4.9 (2016-01-11) +------------------ + +* This package now specifies in composer.json that it does not support PHP 7. + For PHP 7, use version 3.5.x or 4.x. + + +3.4.8 (2016-01-04) +------------------ + +* #284: When generating `CANCEL` iTip messages, we now include `DTEND`. + (@kewisch). + + +3.4.7 (2015-09-05) +------------------ + +* #253: Handle `isInTimeRange` for recurring events that have 0 valid + instances. (@DominikTo, @migrax). + + +3.4.6 (2015-08-06) +------------------ + +* #250: Recurring all-day events are incorrectly included in time range + requests when not using UTC in the time range. (@armin-hackmann) + + +3.4.5 (2015-06-02) +------------------ + +* #229: Converting vcards from 3.0 to 4.0 that contained a `LANG` property + would throw an error. + + +3.4.4 (2015-05-27) +------------------ + +* #228: Fixed a 'party crasher' bug in the iTip broker. This would break + scheduling in some cases. + + +3.4.3 (2015-05-19) +------------------ + +* #219: Corrected validation of `EXDATE` properties with more than one value. +* #212: `BYSETPOS` with values below `-1` was broken and could cause infinite + loops. +* #211: Fix `BYDAY=-5TH` in recurrence iterator. (@lindquist) +* #216: `ENCODING` parameter is now validated for all document types. +* #217: Initializing vCard `DATE` objects with a PHP DateTime object will now + work correctly. (@thomascube) + + +3.4.2 (2015-02-25) +------------------ + +* #210: iTip: Replying to an event without a master event was broken. + + +3.4.1 (2015-02-24) +------------------ + +* A minor change to ensure that unittests work correctly in the sabre/dav + test-suite. + + +3.4.0 (2015-02-23) +------------------ + +* #196: Made parsing recurrence rules a lot faster on big calendars. +* Updated windows timezone mappings to latest unicode version. +* #202: Support for parsing and validating `VAVAILABILITY` components. (@Hywan) +* #195: PHP 5.3 compatibility in 'generatevcards' script. (@rickdenhaan) +* #205: Improving handling of multiple `EXDATE` when processing iTip changes. + (@armin-hackmann) +* #187: Fixed validator rules for `LAST-MODIFIED` properties. +* #188: Retain floating times when generating instances using + `Recur\EventIterator`. +* #203: Skip tests for timezones that are not supported on older PHP versions, + instead of a hard fail. +* #204: Dealing a bit better with vCard date-time values that contained + milliseconds. (which is normally invalid). (@armin-hackmann) + + +3.3.5 (2015-01-09) +------------------ + +* #168: Expanding calendars now removes objects with recurrence rules that + don't have a valid recurrence instance. +* #177: SCHEDULE-STATUS should not contain a reason phrase, only a status + code. +* #175: Parser can now read and skip the UTF-8 BOM. +* #179: Added `isFloating` to `DATE-TIME` properties. +* #179: Fixed jCal serialization of floating `DATE-TIME` properties. +* #173: vCard converter failed for `X-ABDATE` properties that had no + `X-ABLABEL`. +* #180: Added `PROFILE_CALDAV` and `PROFILE_CARDDAV` to enable validation rules + specific for CalDAV/CardDAV servers. +* #176: A missing `UID` is no longer an error, but a warning for the vCard + validator, unless `PROFILE_CARDDAV` is specified. + + +3.3.4 (2014-11-19) +------------------ + +* #154: Converting `ANNIVERSARY` to `X-ANNIVERSARY` and `X-ABDATE` and + vice-versa when converting to/from vCard 4. +* #154: It's now possible to easily select all vCard properties belonging to + a single group with `$vcard->{'ITEM1.'}` syntax. (@armin-hackmann) +* #156: Simpler way to check if a string is UTF-8. (@Hywan) +* Unittest improvements. +* #159: The recurrence iterator, freebusy generator and iCalendar DATE and + DATE-TIME properties can now all accept a reference timezone when working + floating times or all-day events. +* #159: Master events will no longer get a `RECURRENCE-ID` when expanding. +* #159: `RECURRENCE-ID` for all-day events will now be correct when expanding. +* #163: Added a `getTimeZone()` method to `VTIMEZONE` components. + + +3.3.3 (2014-10-09) +------------------ + +* #142: `CANCEL` and `REPLY` messages now include the `DTSTART` from the + original event. +* #143: `SCHEDULE-AGENT` on the `ORGANIZER` property is respected. +* #144: `PARTSTAT=NEEDS-ACTION` is now set for new invites, if no `PARTSTAT` is + set to support the inbox feature of iOS. +* #147: Bugs related to scheduling all-day events. +* #148: Ignore events that have attendees but no organizer. +* #149: Avoiding logging errors during timezone detection. This is a workaround + for a PHP bug. +* Support for "Line Islands Standard Time" windows timezone. +* #154: Correctly work around vCard parameters that have a value but no name. + + +3.3.2 (2014-09-19) +------------------ + +* Changed: iTip broker now sets RSVP status to false when replies are received. +* #118: iTip Message now has a `getScheduleStatus()` method. +* #119: Support for detecting 'significant changes'. +* #120: Support for `SCHEDULE-FORCE-SEND`. +* #121: iCal demands parameters containing the + sign to be quoted. +* #122: Don't generate REPLY messages for events that have been cancelled. +* #123: Added `SUMMARY` to iTip messages. +* #130: Incorrect validation rules for `RELATED` (should be `RELATED-TO`). +* #128: `ATTACH` in iCalendar is `URI` by default, not `BINARY`. +* #131: RRULE that doesn't provide a single valid instance now throws an + exception. +* #136: Validator rejects *all* control characters. We were missing a few. +* #133: Splitter objects will throw exceptions when receiving incompatible + objects. +* #127: Attendees who delete recurring event instances events they had already + declined earlier will no longer generate another reply. +* #125: Send CANCEL messages when ORGANIZER property gets deleted. + + +3.3.1 (2014-08-18) +------------------ + +* Changed: It's now possible to pass DateTime objects when using the magic + setters on properties. (`$event->DTSTART = new DateTime('now')`). +* #111: iTip Broker does not process attendee adding events to EXDATE. +* #112: EventIterator now sets TZID on RECURRENCE-ID. +* #113: Timezone support during creation of iTip REPLY messages. +* #114: VTIMEZONE is retained when generating new REQUEST objects. +* #114: Support for 'MAILTO:' style email addresses (in uppercase) in the iTip + broker. This improves evolution support. +* #115: Using REQUEST-STATUS from REPLY messages and now propegating that into + SCHEDULE-STATUS. + + +3.3.0 (2014-08-07) +------------------ + +* We now use PSR-4 for the directory structure. This means that everything + that was used to be in the `lib/Sabre/VObject` directory is now moved to + `lib/`. If you use composer to load this library, you shouldn't have to do + anything about that though. +* VEVENT now get populated with a DTSTAMP and UID property by default. +* BC Break: Removed the 'includes.php' file. Use composer instead. +* #103: Added support for processing [iTip][iTip] messages. This allows a user + to parse incoming iTip messages and apply the result on existing calendars, + or automatically generate invites/replies/cancellations based on changes that + a user made on objects. +* #75, #58, #18: Fixes related to overriding the first event in recurrences. +* Added: VCalendar::getBaseComponent to find the 'master' component in a + calendar. +* #51: Support for iterating RDATE properties. +* Fixed: Issue #101: RecurrenceIterator::nextMonthly() shows events that are + excluded events with wrong time + + +3.2.4 (2014-07-14) +------------------ + +* Added: Issue #98. The VCardConverter now takes `X-APPLE-OMIT-YEAR` into + consideration when converting between vCard 3 and 4. +* Fixed: Issue #96. Some support for Yahoo's broken vcards. +* Fixed: PHP 5.3 support was broken in the cli tool. + + +3.2.3 (2014-06-12) +------------------ + +* Validator now checks if DUE and DTSTART are of the same type in VTODO, and + ensures that DUE is always after DTSTART. +* Removed documentation from source repository, to http://sabre.io/vobject/ +* Expanded the vobject cli tool validation output to make it easier to find + issues. +* Fixed: vobject repair. It was not working for iCalendar objects. + + +3.2.2 (2014-05-07) +------------------ + +* Minor tweak in unittests to make it run on PHP 5.5.12. Json-prettifying + slightly changed which caused the test to fail. + + +3.2.1 (2014-05-03) +------------------ + +* Minor tweak to make the unittests run with the latest hhvm on travis. +* Updated timezone definitions. +* Updated copyright links to point to http://sabre.io/ + + +3.2.0 (2014-04-02) +------------------ + +* Now hhvm compatible! +* The validator can now detect a _lot_ more problems. Many rules for both + iCalendar and vCard were added. +* Added: bin/generate_vcards, a utility to generate random vcards for testing + purposes. Patches are welcome to add more data. +* Updated: Windows timezone mapping to latest version from unicode.org +* Changed: The timezone maps are now loaded in from external files, in + lib/Sabre/VObject/timezonedata. +* Added: Fixing badly encoded URL's from google contacts vcards. +* Fixed: Issue #68. Couldn't decode properties ending in a colon. +* Fixed: Issue #72. RecurrenceIterator should respect timezone in the UNTIL + clause. +* Fixed: Issue #67. BYMONTH limit on DAILY recurrences. +* Fixed: Issue #26. Return a more descriptive error when coming across broken + BYDAY rules. +* Fixed: Issue #28. Incorrect timezone detection for some timezones. +* Fixed: Issue #70. Casting a parameter with a null value to string would fail. +* Added: Support for rfc6715 and rfc6474. +* Added: Support for DateTime objects in the VCard DATE-AND-OR-TIME property. +* Added: UUIDUtil, for easily creating unique identifiers. +* Fixed: Issue #83. Creating new VALUE=DATE objects using php's DateTime. +* Fixed: Issue #86. Don't go into an infinite loop when php errors are + disabled and an invalid file is read. + + +3.1.4 (2014-03-30) +------------------ + +* Fixed: Issue #87: Several compatibility fixes related to timezone handling + changes in PHP 5.5.10. + + +3.1.3 (2013-10-02) +------------------ + +* Fixed: Support from properties from draft-daboo-valarm-extensions-04. Issue + #56. +* Fixed: Issue #54. Parsing a stream of multiple vcards separated by more than + one newline. Thanks @Vedmak for the patch. +* Fixed: Serializing vcard 2.1 parameters with no name caused a literal '1' to + be inserted. +* Added: VCardConverter removed properties that are no longer supported in vCard + 4.0. +* Added: vCards with a minimum number of values (such as N), but don't have that + many, are now automatically padded with empty components. +* Added: The vCard validator now also checks for a minimum number of components, + and has the ability to repair these. +* Added: Some support for vCard 2.1 in the VCard converter, to upgrade to vCard + 3.0 or 4.0. +* Fixed: Issue 60 Use Document::$componentMap when instantiating the top-level + VCalendar and VCard components. +* Fixed: Issue 62: Parsing iCalendar parameters with no value. +* Added: --forgiving option to vobject utility. +* Fixed: Compound properties such as ADR were not correctly split up in vCard + 2.1 quoted printable-encoded properties. +* Fixed: Issue 64: Encoding of binary properties of converted vCards. Thanks + @DominikTo for the patch. + + +3.1.2 (2013-08-13) +------------------ + +* Fixed: Setting correct property group on VCard conversion + + +3.1.1 (2013-08-02) +------------------ + +* Fixed: Issue #53. A regression in RecurrenceIterator. + + +3.1.0 (2013-07-27) +------------------ + +* Added: bad-ass new cli debugging utility (in bin/vobject). +* Added: jCal and jCard parser. +* Fixed: URI properties should not escape ; and ,. +* Fixed: VCard 4 documents now correctly use URI as a default value-type for + PHOTO and others. BINARY no longer exists in vCard 4. +* Added: Utility to convert between 2.1, 3.0 and 4.0 vCards. +* Added: You can now add() multiple parameters to a property in one call. +* Added: Parameter::has() for easily checking if a parameter value exists. +* Added: VCard::preferred() to find a preferred email, phone number, etc for a + contact. +* Changed: All $duration properties are now public. +* Added: A few validators for iCalendar documents. +* Fixed: Issue #50. RecurrenceIterator gives incorrect result when exception + events are out of order in the iCalendar file. +* Fixed: Issue #48. Overridden events in the recurrence iterator that were past + the UNTIL date were ignored. +* Added: getDuration for DURATION values such as TRIGGER. Thanks to + @SimonSimCity. +* Fixed: Issue #52. vCard 2.1 parameters with no name may lose values if there's + more than 1. Thanks to @Vedmak. + + +3.0.0 (2013-06-21) +------------------ + +* Fixed: includes.php file was still broken. Our tool to generate it had some + bugs. + + +3.0.0-beta4 (2013-06-21) +------------------------ + +* Fixed: includes.php was no longer up to date. + + +3.0.0-beta3 (2013-06-17) +------------------------ + +* Added: OPTION_FORGIVING now also allows slashes in property names. +* Fixed: DateTimeParser no longer fails on dates with years < 1000 & > 4999 +* Fixed: Issue 36: Workaround for the recurrenceiterator and caldav events with + a missing base event. +* Fixed: jCard encoding of TIME properties. +* Fixed: jCal encoding of REQUEST-STATUS, GEO and PERIOD values. + + +3.0.0-beta2 (2013-06-10) +------------------------ + +* Fixed: Corrected includes.php file. +* Fixed: vCard date-time parser supported extended-format dates as well. +* Changed: Properties have been moved to an ICalendar or VCard directory. +* Fixed: Couldn't parse vCard 3 extended format dates and times. +* Fixed: Couldn't export jCard DATE values correctly. +* Fixed: Recursive loop in ICalendar\DateTime property. + + +3.0.0-beta1 (2013-06-07) +------------------------ + +* Added: jsonSerialize() for creating jCal and jCard documents. +* Added: helper method to parse vCard dates and times. +* Added: Specialized classes for FLOAT, LANGUAGE-TAG, TIME, TIMESTAMP, + DATE-AND-OR-TIME, CAL-ADDRESS, UNKNOWN and UTC-OFFSET properties. +* Removed: CommaSeparatedText property. Now included into Text. +* Fixed: Multiple parameters with the same name are now correctly encoded. +* Fixed: Parameter values containing a comma are now enclosed in double-quotes. +* Fixed: Iterating parameter values should now fully work as expected. +* Fixed: Support for vCard 2.1 nameless parameters. +* Changed: $valueMap, $componentMap and $propertyMap now all use fully-qualified + class names, so they are actually overridable. +* Fixed: Updating DATE-TIME to DATE values now behaves like expected. + + +3.0.0-alpha4 (2013-05-31) +------------------------- + +* Added: It's now possible to send parser options to the splitter classes. +* Added: A few tweaks to improve component and property creation. + + +3.0.0-alpha3 (2013-05-13) +------------------------- + +* Changed: propertyMap, valueMap and componentMap are now static properties. +* Changed: Component::remove() will throw an exception when trying to a node + that's not a child of said component. +* Added: Splitter objects are now faster, line numbers are accurately reported + and use less memory. +* Added: MimeDir parser can now continue parsing with the same stream buffer. +* Fixed: vobjectvalidate.php is operational again. +* Fixed: \r is properly stripped in text values. +* Fixed: QUOTED-PRINTABLE is now correctly encoded as well as encoded, for + vCards 2.1. +* Fixed: Parser assumes vCard 2.1, if no version was supplied. + + +3.0.0-alpha2 (2013-05-22) +------------------------- + +* Fixed: vCard URL properties were referencing a non-existant class. + + +3.0.0-alpha1 (2013-05-21) +------------------------- + +* Fixed: Now correctly dealing with escaping of properties. This solves the + problem with double-backslashes where they don't belong. +* Added: Easy support for properties with more than one value, using setParts + and getParts. +* Added: Support for broken 2.1 vCards produced by microsoft. +* Added: Automatically decoding quoted-printable values. +* Added: Automatically decoding base64 values. +* Added: Decoding RFC6868 parameter values (uses ^ as an escape character). +* Added: Fancy new MimeDir parser that can also parse streams. +* Added: Automatically mapping many, many properties to a property-class with + specialized API's. +* Added: remove() method for easily removing properties and sub-components + components. +* Changed: Components, Properties and Parameters can no longer be created with + Component::create, Property::create and Parameter::create. They must instead + be created through the root component. (A VCalendar or VCard object). +* Changed: API for DateTime properties has slightly changed. +* Changed: the ->value property is now protected everywhere. Use getParts() and + getValue() instead. +* BC Break: No support for mac newlines (\r). Never came across these anyway. +* Added: add() method to the Property class. +* Added: It's now possible to easy set multi-value properties as arrays. +* Added: When setting date-time properties you can just pass PHP's DateTime + object. +* Added: New components automatically get a bunch of default properties, such as + VERSION and CALSCALE. +* Added: You can add new sub-components much quicker with the magic setters, and + add() method. + + +2.1.7 (2015-01-21) +------------------ + +* Fixed: Issue #94, a workaround for bad escaping of ; and , in compound + properties. It's not a full solution, but it's an improvement for those + stuck in the 2.1 versions. + + +2.1.6 (2014-12-10) +------------------ + +* Fixed: Minor change to make sure that unittests succeed on every PHP version. + + +2.1.5 (2014-06-03) +------------------ + +* Fixed: #94: Better parameter escaping. +* Changed: Documentation cleanups. + + +2.1.4 (2014-03-30) +------------------ + +* Fixed: Issue #87: Several compatibility fixes related to timezone handling + changes in PHP 5.5.10. + + +2.1.3 (2013-10-02) +------------------ + +* Fixed: Issue #55. \r must be stripped from property values. +* Fixed: Issue #65. Putting quotes around parameter values that contain a colon. + + +2.1.2 (2013-08-02) +------------------ + +* Fixed: Issue #53. A regression in RecurrenceIterator. + + +2.1.1 (2013-07-27) +------------------ + +* Fixed: Issue #50. RecurrenceIterator gives incorrect result when exception + events are out of order in the iCalendar file. +* Fixed: Issue #48. Overridden events in the recurrence iterator that were past + the UNTIL date were ignored. + + +2.1.0 (2013-06-17) +------------------ + +* This version is fully backwards compatible with 2.0.\*. However, it contains a + few new API's that mimic the VObject 3 API. This allows it to be used a + 'bridge' version. Specifically, this new version exists so SabreDAV 1.7 and + 1.8 can run with both the 2 and 3 versions of this library. +* Added: Property\DateTime::hasTime(). +* Added: Property\MultiDateTime::hasTime(). +* Added: Property::getValue(). +* Added: Document class. +* Added: Document::createComponent and Document::createProperty. +* Added: Parameter::getValue(). + + +2.0.7 (2013-03-05) +------------------ + +* Fixed: Microsoft re-uses their magic numbers for different timezones, + specifically id 2 for both Sarajevo and Lisbon). A workaround was added to + deal with this. + + +2.0.6 (2013-02-17) +------------------ + +* Fixed: The reader now properly parses parameters without a value. + + +2.0.5 (2012-11-05) +------------------ + +* Fixed: The FreeBusyGenerator is now properly using the factory methods for + creation of components and properties. + + +2.0.4 (2012-11-02) +------------------ + +* Added: Known Lotus Notes / Domino timezone id's. + + +2.0.3 (2012-10-29) +------------------ + +* Added: Support for 'GMT+????' format in TZID's. +* Added: Support for formats like SystemV/EST5EDT in TZID's. +* Fixed: RecurrenceIterator now repairs recurrence rules where UNTIL < DTSTART. +* Added: Support for BYHOUR in FREQ=DAILY (@hollodk). +* Added: Support for BYHOUR and BYDAY in FREQ=WEEKLY. + + +2.0.2 (2012-10-06) +------------------ + +* Added: includes.php file, to load the entire library in one go. +* Fixed: A problem with determining alarm triggers for TODO's. + + +2.0.1 (2012-09-22) +------------------ + +* Removed: Element class. It wasn't used. +* Added: Basic validation and repair methods for broken input data. +* Fixed: RecurrenceIterator could infinitely loop when an INTERVAL of 0 was + specified. +* Added: A cli script that can validate and automatically repair vcards and + iCalendar objects. +* Added: A new 'Compound' property, that can automatically split up parts for + properties such as N, ADR, ORG and CATEGORIES. +* Added: Splitter classes, that can split up large objects (such as exports) + into individual objects (thanks @DominikTo and @armin-hackmann). +* Added: VFREEBUSY component, which allows easily checking wether timeslots are + available. +* Added: The Reader class now has a 'FORGIVING' option, which allows it to parse + properties with incorrect characters in the name (at this time, it just allows + underscores). +* Added: Also added the 'IGNORE_INVALID_LINES' option, to completely disregard + any invalid lines. +* Fixed: A bug in Windows timezone-id mappings for times created in Greenlands + timezone (sorry Greenlanders! I do care!). +* Fixed: DTEND was not generated correctly for VFREEBUSY reports. +* Fixed: Parser is at least 25% faster with real-world data. + + +2.0.0 (2012-08-08) +------------------ + +* VObject is now a separate project from SabreDAV. See the SabreDAV changelog + for version information before 2.0. +* New: VObject library now uses PHP 5.3 namespaces. +* New: It's possible to specify lists of parameters when constructing + properties. +* New: made it easier to construct the FreeBusyGenerator. + +[iTip]: http://tools.ietf.org/html/rfc5546 diff --git a/htdocs/includes/sabre/sabre/vobject/LICENSE b/htdocs/includes/sabre/sabre/vobject/LICENSE new file mode 100644 index 00000000000..a99c8da1988 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2011-2016 fruux GmbH (https://fruux.com/) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name Sabre nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/htdocs/includes/sabre/sabre/vobject/README.md b/htdocs/includes/sabre/sabre/vobject/README.md new file mode 100644 index 00000000000..0e37f13880d --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/README.md @@ -0,0 +1,55 @@ +sabre/vobject +============= + +The VObject library allows you to easily parse and manipulate [iCalendar](https://tools.ietf.org/html/rfc5545) +and [vCard](https://tools.ietf.org/html/rfc6350) objects using PHP. + +The goal of the VObject library is to create a very complete library, with an easy to use API. + + +Installation +------------ + +Make sure you have [Composer][1] installed, and then run: + + composer require sabre/vobject "^4.0" + +This package requires PHP 5.5. If you need the PHP 5.3/5.4 version of this package instead, use: + + + composer require sabre/vobject "^3.4" + + +Usage +----- + +* [Working with vCards](http://sabre.io/vobject/vcard/) +* [Working with iCalendar](http://sabre.io/vobject/icalendar/) + + + +Build status +------------ + +| branch | status | +| ------ | ------ | +| master | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=master)](https://travis-ci.org/fruux/sabre-vobject) | +| 3.5 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=3.5)](https://travis-ci.org/fruux/sabre-vobject) | +| 3.4 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=3.4)](https://travis-ci.org/fruux/sabre-vobject) | +| 3.1 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=3.1)](https://travis-ci.org/fruux/sabre-vobject) | +| 2.1 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=2.1)](https://travis-ci.org/fruux/sabre-vobject) | +| 2.0 | [![Build Status](https://travis-ci.org/fruux/sabre-vobject.svg?branch=2.0)](https://travis-ci.org/fruux/sabre-vobject) | + + + +Support +------- + +Head over to the [SabreDAV mailing list](http://groups.google.com/group/sabredav-discuss) for any questions. + +Made at fruux +------------- + +This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. + +[1]: https://getcomposer.org/ diff --git a/htdocs/includes/sabre/sabre/vobject/bin/bench.php b/htdocs/includes/sabre/sabre/vobject/bin/bench.php new file mode 100644 index 00000000000..807b40777c6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/bench.php @@ -0,0 +1,12 @@ +#!/usr/bin/env php +<?php + +include __DIR__ . '/../vendor/autoload.php'; + +$data = stream_get_contents(STDIN); + +$start = microtime(true); + +$lol = Sabre\VObject\Reader::read($data); + +echo "time: " . (microtime(true) - $start) . "\n"; diff --git a/htdocs/includes/sabre/sabre/vobject/bin/bench_freebusygenerator.php b/htdocs/includes/sabre/sabre/vobject/bin/bench_freebusygenerator.php new file mode 100644 index 00000000000..2c51b2a32b0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/bench_freebusygenerator.php @@ -0,0 +1,62 @@ +<?php + +include __DIR__ . '/../vendor/autoload.php'; + +if ($argc < 2) { + echo "sabre/vobject ", Sabre\VObject\Version::VERSION, " freebusy benchmark\n"; + echo "\n"; + echo "This script can be used to measure the speed of generating a\n"; + echo "free-busy report based on a calendar.\n"; + echo "\n"; + echo "The process will be repeated 100 times to get accurate stats\n"; + echo "\n"; + echo "Usage: " . $argv[0] . " inputfile.ics\n"; + die(); +} + +list(, $inputFile) = $argv; + +$bench = new Hoa\Bench\Bench(); +$bench->parse->start(); + +$vcal = Sabre\VObject\Reader::read(fopen($inputFile, 'r')); + +$bench->parse->stop(); + +$repeat = 100; +$start = new \DateTime('2000-01-01'); +$end = new \DateTime('2020-01-01'); +$timeZone = new \DateTimeZone('America/Toronto'); + +$bench->fb->start(); + +for ($i = 0; $i < $repeat; $i++) { + + $fb = new Sabre\VObject\FreeBusyGenerator($start, $end, $vcal, $timeZone); + $results = $fb->getResult(); + +} +$bench->fb->stop(); + + + +echo $bench,"\n"; + +function formatMemory($input) { + + if (strlen($input) > 6) { + + return round($input / (1024 * 1024)) . 'M'; + + } elseif (strlen($input) > 3) { + + return round($input / 1024) . 'K'; + + } + +} + +unset($input, $splitter); + +echo "peak memory usage: " . formatMemory(memory_get_peak_usage()), "\n"; +echo "current memory usage: " . formatMemory(memory_get_usage()), "\n"; diff --git a/htdocs/includes/sabre/sabre/vobject/bin/bench_manipulatevcard.php b/htdocs/includes/sabre/sabre/vobject/bin/bench_manipulatevcard.php new file mode 100644 index 00000000000..adc198e9bc6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/bench_manipulatevcard.php @@ -0,0 +1,69 @@ +<?php + +include __DIR__ . '/../vendor/autoload.php'; + +if ($argc < 2) { + echo "sabre/vobject ", Sabre\VObject\Version::VERSION, " manipulation benchmark\n"; + echo "\n"; + echo "This script can be used to measure the speed of opening a large amount of\n"; + echo "vcards, making a few alterations and serializing them again.\n"; + echo "system."; + echo "\n"; + echo "Usage: " . $argv[0] . " inputfile.vcf\n"; + die(); +} + +list(, $inputFile) = $argv; + +$input = file_get_contents($inputFile); + +$splitter = new Sabre\VObject\Splitter\VCard($input); + +$bench = new Hoa\Bench\Bench(); + +while (true) { + + $bench->parse->start(); + $vcard = $splitter->getNext(); + $bench->parse->pause(); + + if (!$vcard) break; + + $bench->manipulate->start(); + $vcard->{'X-FOO'} = 'Random new value!'; + $emails = []; + if (isset($vcard->EMAIL)) foreach ($vcard->EMAIL as $email) { + $emails[] = (string)$email; + } + $bench->manipulate->pause(); + + $bench->serialize->start(); + $vcard2 = $vcard->serialize(); + $bench->serialize->pause(); + + $vcard->destroy(); + +} + + + +echo $bench,"\n"; + +function formatMemory($input) { + + if (strlen($input) > 6) { + + return round($input / (1024 * 1024)) . 'M'; + + } elseif (strlen($input) > 3) { + + return round($input / 1024) . 'K'; + + } + +} + +unset($input, $splitter); + +echo "peak memory usage: " . formatMemory(memory_get_peak_usage()), "\n"; +echo "current memory usage: " . formatMemory(memory_get_usage()), "\n"; diff --git a/htdocs/includes/sabre/sabre/vobject/bin/fetch_windows_zones.php b/htdocs/includes/sabre/sabre/vobject/bin/fetch_windows_zones.php new file mode 100644 index 00000000000..3f2a00f7ae0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/fetch_windows_zones.php @@ -0,0 +1,51 @@ +#!/usr/bin/env php +<?php + +$windowsZonesUrl = 'http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml'; +$outputFile = __DIR__ . '/../lib/timezonedata/windowszones.php'; + +echo "Fetching timezone map from: " . $windowsZonesUrl, "\n"; + +$data = file_get_contents($windowsZonesUrl); + +$xml = simplexml_load_string($data); + +$map = []; + +foreach ($xml->xpath('//mapZone') as $mapZone) { + + $from = (string)$mapZone['other']; + $to = (string)$mapZone['type']; + + list($to) = explode(' ', $to, 2); + + if (!isset($map[$from])) { + $map[$from] = $to; + } + +} + +ksort($map); +echo "Writing to: $outputFile\n"; + +$f = fopen($outputFile, 'w'); +fwrite($f, "<?php\n\n"); +fwrite($f, "/**\n"); +fwrite($f, " * Automatically generated timezone file\n"); +fwrite($f, " *\n"); +fwrite($f, " * Last update: " . date(DATE_W3C) . "\n"); +fwrite($f, " * Source: " . $windowsZonesUrl . "\n"); +fwrite($f, " *\n"); +fwrite($f, " * @copyright Copyright (C) fruux GmbH (https://fruux.com/).\n"); +fwrite($f, " * @license http://sabre.io/license/ Modified BSD License\n"); +fwrite($f, " */\n"); +fwrite($f, "\n"); +fwrite($f, "return "); +fwrite($f, var_export($map, true) . ';'); +fclose($f); + +echo "Formatting\n"; + +exec(__DIR__ . '/sabre-cs-fixer fix ' . escapeshellarg($outputFile)); + +echo "Done\n"; diff --git a/htdocs/includes/sabre/sabre/vobject/bin/generate_vcards b/htdocs/includes/sabre/sabre/vobject/bin/generate_vcards new file mode 100644 index 00000000000..4663c3c16d5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/generate_vcards @@ -0,0 +1,241 @@ +#!/usr/bin/env php +<?php + +namespace Sabre\VObject; + +// This sucks.. we have to try to find the composer autoloader. But chances +// are, we can't find it this way. So we'll do our bestest +$paths = [ + __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly + __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency. +]; + +foreach($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +if (!class_exists('Sabre\\VObject\\Version')) { + fwrite(STDERR, "Composer autoloader could not be properly loaded.\n"); + die(1); +} + +if ($argc < 2) { + + $version = Version::VERSION; + + $help = <<<HI +sabre/vobject $version +Usage: + generate_vcards [count] + +Options: + count The number of random vcards to generate + +Examples: + generate_vcards 1000 > testdata.vcf + +HI; + + fwrite(STDERR, $help); + exit(2); +} + +$count = (int)$argv[1]; +if ($count < 1) { + fwrite(STDERR, "Count must be at least 1\n"); + exit(2); +} + +fwrite(STDERR, "sabre/vobject " . Version::VERSION . "\n"); +fwrite(STDERR, "Generating " . $count . " vcards in vCard 4.0 format\n"); + +/** + * The following list is just some random data we compiled from various + * sources online. + * + * Very little thought went into compiling this list, and certainly nothing + * political or ethical. + * + * We would _love_ more additions to this to add more variation to this list. + * + * Send us PR's and don't be shy adding your own first and last name for fun. + */ + +$sets = array( + "nl" => array( + "country" => "Netherlands", + "boys" => array( + "Anno", + "Bram", + "Daan", + "Evert", + "Finn", + "Jayden", + "Jens", + "Jesse", + "Levi", + "Lucas", + "Luuk", + "Milan", + "René", + "Sem", + "Sibrand", + "Willem", + ), + "girls" => array( + "Celia", + "Emma", + "Fenna", + "Geke", + "Inge", + "Julia", + "Lisa", + "Lotte", + "Mila", + "Sara", + "Sophie", + "Tess", + "Zoë", + ), + "last" => array( + "Bakker", + "Bos", + "De Boer", + "De Groot", + "De Jong", + "De Vries", + "Jansen", + "Janssen", + "Meyer", + "Mulder", + "Peters", + "Smit", + "Van Dijk", + "Van den Berg", + "Visser", + "Vos", + ), + ), + "us" => array( + "country" => "United States", + "boys" => array( + "Aiden", + "Alexander", + "Charles", + "David", + "Ethan", + "Jacob", + "James", + "Jayden", + "John", + "Joseph", + "Liam", + "Mason", + "Michael", + "Noah", + "Richard", + "Robert", + "Thomas", + "William", + ), + "girls" => array( + "Ava", + "Barbara", + "Chloe", + "Dorothy", + "Elizabeth", + "Emily", + "Emma", + "Isabella", + "Jennifer", + "Lily", + "Linda", + "Margaret", + "Maria", + "Mary", + "Mia", + "Olivia", + "Patricia", + "Roxy", + "Sophia", + "Susan", + "Zoe", + ), + "last" => array( + "Smith", + "Johnson", + "Williams", + "Jones", + "Brown", + "Davis", + "Miller", + "Wilson", + "Moore", + "Taylor", + "Anderson", + "Thomas", + "Jackson", + "White", + "Harris", + "Martin", + "Thompson", + "Garcia", + "Martinez", + "Robinson", + ), + ), +); + +$current = 0; + +$r = function($arr) { + + return $arr[mt_rand(0,count($arr)-1)]; + +}; + +$bdayStart = strtotime('-85 years'); +$bdayEnd = strtotime('-20 years'); + +while($current < $count) { + + $current++; + fwrite(STDERR, "\033[100D$current/$count"); + + $country = array_rand($sets); + $gender = mt_rand(0,1)?'girls':'boys'; + + $vcard = new Component\VCard(array( + 'VERSION' => '4.0', + 'FN' => $r($sets[$country][$gender]) . ' ' . $r($sets[$country]['last']), + 'UID' => UUIDUtil::getUUID(), + )); + + $bdayRatio = mt_rand(0,9); + + if($bdayRatio < 2) { + // 20% has a birthday property with a full date + $dt = new \DateTime('@' . mt_rand($bdayStart, $bdayEnd)); + $vcard->add('BDAY', $dt->format('Ymd')); + + } elseif ($bdayRatio < 3) { + // 10% we only know the month and date of + $dt = new \DateTime('@' . mt_rand($bdayStart, $bdayEnd)); + $vcard->add('BDAY', '--' . $dt->format('md')); + } + if ($result = $vcard->validate()) { + ob_start(); + echo "\nWe produced an invalid vcard somehow!\n"; + foreach($result as $message) { + echo " " . $message['message'] . "\n"; + } + fwrite(STDERR, ob_get_clean()); + } + echo $vcard->serialize(); + +} + +fwrite(STDERR,"\nDone.\n"); diff --git a/htdocs/includes/sabre/sabre/vobject/bin/generateicalendardata.php b/htdocs/includes/sabre/sabre/vobject/bin/generateicalendardata.php new file mode 100644 index 00000000000..a2df3c63a0c --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/generateicalendardata.php @@ -0,0 +1,88 @@ +#!/usr/bin/env php +<?php + +use Sabre\VObject; + +if ($argc < 2) { + $cmd = $argv[0]; + fwrite(STDERR, <<<HI +Fruux test data generator + +This script generates a lot of test data. This is used for profiling and stuff. +Currently it just generates events in a single calendar. + +The iCalendar output goes to stdout. Other messages to stderr. + +{$cmd} [events] + + +HI + ); + die(); +} + +$events = 100; + +if (isset($argv[1])) $events = (int)$argv[1]; + +include __DIR__ . '/../vendor/autoload.php'; + +fwrite(STDERR, "Generating " . $events . " events\n"); + +$currentDate = new DateTime('-' . round($events / 2) . ' days'); + +$calendar = new VObject\Component\VCalendar(); + +$ii = 0; + +while ($ii < $events) { + + $ii++; + + $event = $calendar->add('VEVENT'); + $event->DTSTART = 'bla'; + $event->SUMMARY = 'Event #' . $ii; + $event->UID = md5(microtime(true)); + + $doctorRandom = mt_rand(1, 1000); + + switch ($doctorRandom) { + // All-day event + case 1 : + $event->DTEND = 'bla'; + $dtStart = clone $currentDate; + $dtEnd = clone $currentDate; + $dtEnd->modify('+' . mt_rand(1, 3) . ' days'); + $event->DTSTART->setDateTime($dtStart); + $event->DTSTART['VALUE'] = 'DATE'; + $event->DTEND->setDateTime($dtEnd); + break; + case 2 : + $event->RRULE = 'FREQ=DAILY;COUNT=' . mt_rand(1, 10); + // No break intentional + default : + $dtStart = clone $currentDate; + $dtStart->setTime(mt_rand(1, 23), mt_rand(0, 59), mt_rand(0, 59)); + $event->DTSTART->setDateTime($dtStart); + $event->DURATION = 'PT' . mt_rand(1, 3) . 'H'; + break; + + } + + $currentDate->modify('+ ' . mt_rand(0, 3) . ' days'); + +} +fwrite(STDERR, "Validating\n"); + +$result = $calendar->validate(); +if ($result) { + fwrite(STDERR, "Errors!\n"); + fwrite(STDERR, print_r($result, true)); + die(-1); +} + +fwrite(STDERR, "Serializing this beast\n"); + +echo $calendar->serialize(); + +fwrite(STDERR, "done.\n"); diff --git a/htdocs/includes/sabre/sabre/vobject/bin/mergeduplicates.php b/htdocs/includes/sabre/sabre/vobject/bin/mergeduplicates.php new file mode 100644 index 00000000000..076524d36a6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/mergeduplicates.php @@ -0,0 +1,184 @@ +#!/usr/bin/env php +<?php + +namespace Sabre\VObject; + +// This sucks.. we have to try to find the composer autoloader. But chances +// are, we can't find it this way. So we'll do our bestest +$paths = [ + __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly + __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency. +]; + +foreach ($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +if (!class_exists('Sabre\\VObject\\Version')) { + fwrite(STDERR, "Composer autoloader could not be loaded.\n"); + die(1); +} + +echo "sabre/vobject ", Version::VERSION, " duplicate contact merge tool\n"; + +if ($argc < 3) { + + echo "\n"; + echo "Usage: ", $argv[0], " input.vcf output.vcf [debug.log]\n"; + die(1); + +} + +$input = fopen($argv[1], 'r'); +$output = fopen($argv[2], 'w'); +$debug = isset($argv[3]) ? fopen($argv[3], 'w') : null; + +$splitter = new Splitter\VCard($input); + +// The following properties are ignored. If they appear in some vcards +// but not in others, we don't consider them for the sake of finding +// differences. +$ignoredProperties = [ + "PRODID", + "VERSION", + "REV", + "UID", + "X-ABLABEL", +]; + + +$collectedNames = []; + +$stats = [ + "Total vcards" => 0, + "No FN property" => 0, + "Ignored duplicates" => 0, + "Merged values" => 0, + "Error" => 0, + "Unique cards" => 0, + "Total written" => 0, +]; + +function writeStats() { + + global $stats; + foreach ($stats as $name => $value) { + echo str_pad($name, 23, " ", STR_PAD_RIGHT), str_pad($value, 6, " ", STR_PAD_LEFT), "\n"; + } + // Moving cursor back a few lines. + echo "\033[" . count($stats) . "A"; + +} + +function write($vcard) { + + global $stats, $output; + + $stats["Total written"]++; + fwrite($output, $vcard->serialize() . "\n"); + +} + +while ($vcard = $splitter->getNext()) { + + $stats["Total vcards"]++; + writeStats(); + + $fn = isset($vcard->FN) ? (string)$vcard->FN : null; + + if (empty($fn)) { + + // Immediately write this vcard, we don't compare it. + $stats["No FN property"]++; + $stats['Unique cards']++; + write($vcard); + $vcard->destroy(); + continue; + + } + + if (!isset($collectedNames[$fn])) { + + $collectedNames[$fn] = $vcard; + $stats['Unique cards']++; + continue; + + } else { + + // Starting comparison for all properties. We only check if properties + // in the current vcard exactly appear in the earlier vcard as well. + foreach ($vcard->children() as $newProp) { + + if (in_array($newProp->name, $ignoredProperties)) { + // We don't care about properties such as UID and REV. + continue; + } + $ok = false; + foreach ($collectedNames[$fn]->select($newProp->name) as $compareProp) { + + if ($compareProp->serialize() === $newProp->serialize()) { + $ok = true; + break; + } + } + + if (!$ok) { + + if ($newProp->name === 'EMAIL' || $newProp->name === 'TEL') { + + // We're going to make another attempt to find this + // property, this time just by value. If we find it, we + // consider it a success. + foreach ($collectedNames[$fn]->select($newProp->name) as $compareProp) { + + if ($compareProp->getValue() === $newProp->getValue()) { + $ok = true; + break; + } + } + + if (!$ok) { + + // Merging the new value in the old vcard. + $collectedNames[$fn]->add(clone $newProp); + $ok = true; + $stats['Merged values']++; + + } + + } + + } + + if (!$ok) { + + // echo $newProp->serialize() . " does not appear in earlier vcard!\n"; + $stats['Error']++; + if ($debug) fwrite($debug, "Missing '" . $newProp->name . "' property in duplicate. Earlier vcard:\n" . $collectedNames[$fn]->serialize() . "\n\nLater:\n" . $vcard->serialize() . "\n\n"); + + $vcard->destroy(); + continue 2; + } + + } + + } + + $vcard->destroy(); + $stats['Ignored duplicates']++; + +} + +foreach ($collectedNames as $vcard) { + + // Overwriting any old PRODID + $vcard->PRODID = '-//Sabre//Sabre VObject ' . Version::VERSION . '//EN'; + write($vcard); + writeStats(); + +} + +echo str_repeat("\n", count($stats)), "\nDone.\n"; diff --git a/htdocs/includes/sabre/sabre/vobject/bin/rrulebench.php b/htdocs/includes/sabre/sabre/vobject/bin/rrulebench.php new file mode 100644 index 00000000000..af26b4765ca --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/rrulebench.php @@ -0,0 +1,32 @@ +<?php + +include __DIR__ . '/../vendor/autoload.php'; + +if ($argc < 4) { + echo "sabre/vobject ", Sabre\VObject\Version::VERSION, " RRULE benchmark\n"; + echo "\n"; + echo "This script can be used to measure the speed of the 'recurrence expansion'\n"; + echo "system."; + echo "\n"; + echo "Usage: " . $argv[0] . " inputfile.ics startdate enddate\n"; + die(); +} + +list(, $inputFile, $startDate, $endDate) = $argv; + +$bench = new Hoa\Bench\Bench(); +$bench->parse->start(); + +echo "Parsing.\n"; +$vobj = Sabre\VObject\Reader::read(fopen($inputFile, 'r')); + +$bench->parse->stop(); + +echo "Expanding.\n"; +$bench->expand->start(); + +$vobj->expand(new DateTime($startDate), new DateTime($endDate)); + +$bench->expand->stop(); + +echo $bench,"\n"; diff --git a/htdocs/includes/sabre/sabre/vobject/bin/vobject b/htdocs/includes/sabre/sabre/vobject/bin/vobject new file mode 100644 index 00000000000..2aca7e72965 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/bin/vobject @@ -0,0 +1,27 @@ +#!/usr/bin/env php +<?php + +namespace Sabre\VObject; + +// This sucks.. we have to try to find the composer autoloader. But chances +// are, we can't find it this way. So we'll do our bestest +$paths = [ + __DIR__ . '/../vendor/autoload.php', // In case vobject is cloned directly + __DIR__ . '/../../../autoload.php', // In case vobject is a composer dependency. +]; + +foreach($paths as $path) { + if (file_exists($path)) { + include $path; + break; + } +} + +if (!class_exists('Sabre\\VObject\\Version')) { + fwrite(STDERR, "Composer autoloader could not be loaded.\n"); + die(1); +} + +$cli = new Cli(); +exit($cli->main($argv)); + diff --git a/htdocs/includes/sabre/sabre/vobject/composer.json b/htdocs/includes/sabre/sabre/vobject/composer.json new file mode 100644 index 00000000000..cfa4a712d37 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/composer.json @@ -0,0 +1,88 @@ +{ + "name": "sabre/vobject", + "description" : "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects", + "keywords" : [ + "iCalendar", + "iCal", + "vCalendar", + "vCard", + "jCard", + "jCal", + "ics", + "vcf", + "xCard", + "xCal", + "freebusy", + "recurrence", + "availability", + "rfc2425", + "rfc2426", + "rfc2739", + "rfc4770", + "rfc5545", + "rfc5546", + "rfc6321", + "rfc6350", + "rfc6351", + "rfc6474", + "rfc6638", + "rfc6715", + "rfc6868" + ], + "homepage" : "http://sabre.io/vobject/", + "license" : "BSD-3-Clause", + "require" : { + "php" : ">=5.5", + "ext-mbstring" : "*", + "sabre/xml" : ">=1.5 <3.0" + }, + "require-dev" : { + "phpunit/phpunit" : "*", + "sabre/cs" : "^1.0.0" + + }, + "suggest" : { + "hoa/bench" : "If you would like to run the benchmark scripts" + }, + "authors" : [ + { + "name" : "Evert Pot", + "email" : "me@evertpot.com", + "homepage" : "http://evertpot.com/", + "role" : "Developer" + }, + { + "name" : "Dominik Tobschall", + "email" : "dominik@fruux.com", + "homepage" : "http://tobschall.de/", + "role" : "Developer" + }, + { + "name" : "Ivan Enderlin", + "email" : "ivan.enderlin@hoa-project.net", + "homepage" : "http://mnt.io/", + "role" : "Developer" + } + ], + "support" : { + "forum" : "https://groups.google.com/group/sabredav-discuss", + "source" : "https://github.com/fruux/sabre-vobject" + }, + "autoload" : { + "psr-4" : { + "Sabre\\VObject\\" : "lib/" + } + }, + "bin" : [ + "bin/vobject", + "bin/generate_vcards" + ], + "extra" : { + "branch-alias" : { + "dev-master" : "4.0.x-dev" + } + }, + "config" : { + "bin-dir" : "bin" + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/BirthdayCalendarGenerator.php b/htdocs/includes/sabre/sabre/vobject/lib/BirthdayCalendarGenerator.php new file mode 100644 index 00000000000..55391224997 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/BirthdayCalendarGenerator.php @@ -0,0 +1,191 @@ +<?php + +namespace Sabre\VObject; + +use Sabre\VObject\Component\VCalendar; + +/** + * This class generates birthday calendars. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Dominik Tobschall (http://tobschall.de/) + * @license http://sabre.io/license/ Modified BSD License + */ +class BirthdayCalendarGenerator { + + /** + * Input objects. + * + * @var array + */ + protected $objects = []; + + /** + * Default year. + * Used for dates without a year. + */ + const DEFAULT_YEAR = 2000; + + /** + * Output format for the SUMMARY. + * + * @var string + */ + protected $format = '%1$s\'s Birthday'; + + /** + * Creates the generator. + * + * Check the setTimeRange and setObjects methods for details about the + * arguments. + * + * @param mixed $objects + */ + function __construct($objects = null) { + + if ($objects) { + $this->setObjects($objects); + } + + } + + /** + * Sets the input objects. + * + * You must either supply a vCard as a string or as a Component/VCard object. + * It's also possible to supply an array of strings or objects. + * + * @param mixed $objects + * + * @return void + */ + function setObjects($objects) { + + if (!is_array($objects)) { + $objects = [$objects]; + } + + $this->objects = []; + foreach ($objects as $object) { + + if (is_string($object)) { + + $vObj = Reader::read($object); + if (!$vObj instanceof Component\VCard) { + throw new \InvalidArgumentException('String could not be parsed as \\Sabre\\VObject\\Component\\VCard by setObjects'); + } + + $this->objects[] = $vObj; + + } elseif ($object instanceof Component\VCard) { + + $this->objects[] = $object; + + } else { + + throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component\\VCard arguments to setObjects'); + + } + + } + + } + + /** + * Sets the output format for the SUMMARY + * + * @param string $format + * + * @return void + */ + function setFormat($format) { + + $this->format = $format; + + } + + /** + * Parses the input data and returns a VCALENDAR. + * + * @return Component/VCalendar + */ + function getResult() { + + $calendar = new VCalendar(); + + foreach ($this->objects as $object) { + + // Skip if there is no BDAY property. + if (!$object->select('BDAY')) { + continue; + } + + // We've seen clients (ez-vcard) putting "BDAY:" properties + // without a value into vCards. If we come across those, we'll + // skip them. + if (empty($object->BDAY->getValue())) { + continue; + } + + // We're always converting to vCard 4.0 so we can rely on the + // VCardConverter handling the X-APPLE-OMIT-YEAR property for us. + $object = $object->convert(Document::VCARD40); + + // Skip if the card has no FN property. + if (!isset($object->FN)) { + continue; + } + + // Skip if the BDAY property is not of the right type. + if (!$object->BDAY instanceof Property\VCard\DateAndOrTime) { + continue; + } + + // Skip if we can't parse the BDAY value. + try { + $dateParts = DateTimeParser::parseVCardDateTime($object->BDAY->getValue()); + } catch (InvalidDataException $e) { + continue; + } + + // Set a year if it's not set. + $unknownYear = false; + + if (!$dateParts['year']) { + $object->BDAY = self::DEFAULT_YEAR . '-' . $dateParts['month'] . '-' . $dateParts['date']; + + $unknownYear = true; + } + + // Create event. + $event = $calendar->add('VEVENT', [ + 'SUMMARY' => sprintf($this->format, $object->FN->getValue()), + 'DTSTART' => new \DateTime($object->BDAY->getValue()), + 'RRULE' => 'FREQ=YEARLY', + 'TRANSP' => 'TRANSPARENT', + ]); + + // add VALUE=date + $event->DTSTART['VALUE'] = 'DATE'; + + // Add X-SABRE-BDAY property. + if ($unknownYear) { + $event->add('X-SABRE-BDAY', 'BDAY', [ + 'X-SABRE-VCARD-UID' => $object->UID->getValue(), + 'X-SABRE-VCARD-FN' => $object->FN->getValue(), + 'X-SABRE-OMIT-YEAR' => self::DEFAULT_YEAR, + ]); + } else { + $event->add('X-SABRE-BDAY', 'BDAY', [ + 'X-SABRE-VCARD-UID' => $object->UID->getValue(), + 'X-SABRE-VCARD-FN' => $object->FN->getValue(), + ]); + } + + } + + return $calendar; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Cli.php b/htdocs/includes/sabre/sabre/vobject/lib/Cli.php new file mode 100644 index 00000000000..df7ac22f3cc --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Cli.php @@ -0,0 +1,771 @@ +<?php + +namespace Sabre\VObject; + +use + InvalidArgumentException; + +/** + * This is the CLI interface for sabre-vobject. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Cli { + + /** + * No output. + * + * @var bool + */ + protected $quiet = false; + + /** + * Help display. + * + * @var bool + */ + protected $showHelp = false; + + /** + * Wether to spit out 'mimedir' or 'json' format. + * + * @var string + */ + protected $format; + + /** + * JSON pretty print. + * + * @var bool + */ + protected $pretty; + + /** + * Source file. + * + * @var string + */ + protected $inputPath; + + /** + * Destination file. + * + * @var string + */ + protected $outputPath; + + /** + * output stream. + * + * @var resource + */ + protected $stdout; + + /** + * stdin. + * + * @var resource + */ + protected $stdin; + + /** + * stderr. + * + * @var resource + */ + protected $stderr; + + /** + * Input format (one of json or mimedir). + * + * @var string + */ + protected $inputFormat; + + /** + * Makes the parser less strict. + * + * @var bool + */ + protected $forgiving = false; + + /** + * Main function. + * + * @return int + */ + function main(array $argv) { + + // @codeCoverageIgnoreStart + // We cannot easily test this, so we'll skip it. Pretty basic anyway. + + if (!$this->stderr) { + $this->stderr = fopen('php://stderr', 'w'); + } + if (!$this->stdout) { + $this->stdout = fopen('php://stdout', 'w'); + } + if (!$this->stdin) { + $this->stdin = fopen('php://stdin', 'r'); + } + + // @codeCoverageIgnoreEnd + + + try { + + list($options, $positional) = $this->parseArguments($argv); + + if (isset($options['q'])) { + $this->quiet = true; + } + $this->log($this->colorize('green', "sabre/vobject ") . $this->colorize('yellow', Version::VERSION)); + + foreach ($options as $name => $value) { + + switch ($name) { + + case 'q' : + // Already handled earlier. + break; + case 'h' : + case 'help' : + $this->showHelp(); + return 0; + break; + case 'format' : + switch ($value) { + + // jcard/jcal documents + case 'jcard' : + case 'jcal' : + + // specific document versions + case 'vcard21' : + case 'vcard30' : + case 'vcard40' : + case 'icalendar20' : + + // specific formats + case 'json' : + case 'mimedir' : + + // icalendar/vcad + case 'icalendar' : + case 'vcard' : + $this->format = $value; + break; + + default : + throw new InvalidArgumentException('Unknown format: ' . $value); + + } + break; + case 'pretty' : + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + $this->pretty = true; + } + break; + case 'forgiving' : + $this->forgiving = true; + break; + case 'inputformat' : + switch ($value) { + // json formats + case 'jcard' : + case 'jcal' : + case 'json' : + $this->inputFormat = 'json'; + break; + + // mimedir formats + case 'mimedir' : + case 'icalendar' : + case 'vcard' : + case 'vcard21' : + case 'vcard30' : + case 'vcard40' : + case 'icalendar20' : + + $this->inputFormat = 'mimedir'; + break; + + default : + throw new InvalidArgumentException('Unknown format: ' . $value); + + } + break; + default : + throw new InvalidArgumentException('Unknown option: ' . $name); + + } + + } + + if (count($positional) === 0) { + $this->showHelp(); + return 1; + } + + if (count($positional) === 1) { + throw new InvalidArgumentException('Inputfile is a required argument'); + } + + if (count($positional) > 3) { + throw new InvalidArgumentException('Too many arguments'); + } + + if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) { + throw new InvalidArgumentException('Uknown command: ' . $positional[0]); + } + + } catch (InvalidArgumentException $e) { + $this->showHelp(); + $this->log('Error: ' . $e->getMessage(), 'red'); + return 1; + } + + $command = $positional[0]; + + $this->inputPath = $positional[1]; + $this->outputPath = isset($positional[2]) ? $positional[2] : '-'; + + if ($this->outputPath !== '-') { + $this->stdout = fopen($this->outputPath, 'w'); + } + + if (!$this->inputFormat) { + if (substr($this->inputPath, -5) === '.json') { + $this->inputFormat = 'json'; + } else { + $this->inputFormat = 'mimedir'; + } + } + if (!$this->format) { + if (substr($this->outputPath, -5) === '.json') { + $this->format = 'json'; + } else { + $this->format = 'mimedir'; + } + } + + + $realCode = 0; + + try { + + while ($input = $this->readInput()) { + + $returnCode = $this->$command($input); + if ($returnCode !== 0) $realCode = $returnCode; + + } + + } catch (EofException $e) { + // end of file + } catch (\Exception $e) { + $this->log('Error: ' . $e->getMessage(), 'red'); + return 2; + } + + return $realCode; + + } + + /** + * Shows the help message. + * + * @return void + */ + protected function showHelp() { + + $this->log('Usage:', 'yellow'); + $this->log(" vobject [options] command [arguments]"); + $this->log(''); + $this->log('Options:', 'yellow'); + $this->log($this->colorize('green', ' -q ') . "Don't output anything."); + $this->log($this->colorize('green', ' -help -h ') . "Display this help message."); + $this->log($this->colorize('green', ' --format ') . "Convert to a specific format. Must be one of: vcard, vcard21,"); + $this->log($this->colorize('green', ' --forgiving ') . "Makes the parser less strict."); + $this->log(" vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir."); + $this->log($this->colorize('green', ' --inputformat ') . "If the input format cannot be guessed from the extension, it"); + $this->log(" must be specified here."); + // Only PHP 5.4 and up + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + $this->log($this->colorize('green', ' --pretty ') . "json pretty-print."); + } + $this->log(''); + $this->log('Commands:', 'yellow'); + $this->log($this->colorize('green', ' validate') . ' source_file Validates a file for correctness.'); + $this->log($this->colorize('green', ' repair') . ' source_file [output_file] Repairs a file.'); + $this->log($this->colorize('green', ' convert') . ' source_file [output_file] Converts a file.'); + $this->log($this->colorize('green', ' color') . ' source_file Colorize a file, useful for debbugging.'); + $this->log( + <<<HELP + +If source_file is set as '-', STDIN will be used. +If output_file is omitted, STDOUT will be used. +All other output is sent to STDERR. + +HELP + ); + + $this->log('Examples:', 'yellow'); + $this->log(' vobject convert contact.vcf contact.json'); + $this->log(' vobject convert --format=vcard40 old.vcf new.vcf'); + $this->log(' vobject convert --inputformat=json --format=mimedir - -'); + $this->log(' vobject color calendar.ics'); + $this->log(''); + $this->log('https://github.com/fruux/sabre-vobject', 'purple'); + + } + + /** + * Validates a VObject file. + * + * @param Component $vObj + * + * @return int + */ + protected function validate(Component $vObj) { + + $returnCode = 0; + + switch ($vObj->name) { + case 'VCALENDAR' : + $this->log("iCalendar: " . (string)$vObj->VERSION); + break; + case 'VCARD' : + $this->log("vCard: " . (string)$vObj->VERSION); + break; + } + + $warnings = $vObj->validate(); + if (!count($warnings)) { + $this->log(" No warnings!"); + } else { + + $levels = [ + 1 => 'REPAIRED', + 2 => 'WARNING', + 3 => 'ERROR', + ]; + $returnCode = 2; + foreach ($warnings as $warn) { + + $extra = ''; + if ($warn['node'] instanceof Property) { + $extra = ' (property: "' . $warn['node']->name . '")'; + } + $this->log(" [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra); + + } + + } + + return $returnCode; + + } + + /** + * Repairs a VObject file. + * + * @param Component $vObj + * + * @return int + */ + protected function repair(Component $vObj) { + + $returnCode = 0; + + switch ($vObj->name) { + case 'VCALENDAR' : + $this->log("iCalendar: " . (string)$vObj->VERSION); + break; + case 'VCARD' : + $this->log("vCard: " . (string)$vObj->VERSION); + break; + } + + $warnings = $vObj->validate(Node::REPAIR); + if (!count($warnings)) { + $this->log(" No warnings!"); + } else { + + $levels = [ + 1 => 'REPAIRED', + 2 => 'WARNING', + 3 => 'ERROR', + ]; + $returnCode = 2; + foreach ($warnings as $warn) { + + $extra = ''; + if ($warn['node'] instanceof Property) { + $extra = ' (property: "' . $warn['node']->name . '")'; + } + $this->log(" [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra); + + } + + } + fwrite($this->stdout, $vObj->serialize()); + + return $returnCode; + + } + + /** + * Converts a vObject file to a new format. + * + * @param Component $vObj + * + * @return int + */ + protected function convert($vObj) { + + $json = false; + $convertVersion = null; + $forceInput = null; + + switch ($this->format) { + case 'json' : + $json = true; + if ($vObj->name === 'VCARD') { + $convertVersion = Document::VCARD40; + } + break; + case 'jcard' : + $json = true; + $forceInput = 'VCARD'; + $convertVersion = Document::VCARD40; + break; + case 'jcal' : + $json = true; + $forceInput = 'VCALENDAR'; + break; + case 'mimedir' : + case 'icalendar' : + case 'icalendar20' : + case 'vcard' : + break; + case 'vcard21' : + $convertVersion = Document::VCARD21; + break; + case 'vcard30' : + $convertVersion = Document::VCARD30; + break; + case 'vcard40' : + $convertVersion = Document::VCARD40; + break; + + } + + if ($forceInput && $vObj->name !== $forceInput) { + throw new \Exception('You cannot convert a ' . strtolower($vObj->name) . ' to ' . $this->format); + } + if ($convertVersion) { + $vObj = $vObj->convert($convertVersion); + } + if ($json) { + $jsonOptions = 0; + if ($this->pretty) { + $jsonOptions = JSON_PRETTY_PRINT; + } + fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions)); + } else { + fwrite($this->stdout, $vObj->serialize()); + } + + return 0; + + } + + /** + * Colorizes a file. + * + * @param Component $vObj + * + * @return int + */ + protected function color($vObj) { + + fwrite($this->stdout, $this->serializeComponent($vObj)); + + } + + /** + * Returns an ansi color string for a color name. + * + * @param string $color + * + * @return string + */ + protected function colorize($color, $str, $resetTo = 'default') { + + $colors = [ + 'cyan' => '1;36', + 'red' => '1;31', + 'yellow' => '1;33', + 'blue' => '0;34', + 'green' => '0;32', + 'default' => '0', + 'purple' => '0;35', + ]; + return "\033[" . $colors[$color] . 'm' . $str . "\033[" . $colors[$resetTo] . "m"; + + } + + /** + * Writes out a string in specific color. + * + * @param string $color + * @param string $str + * + * @return void + */ + protected function cWrite($color, $str) { + + fwrite($this->stdout, $this->colorize($color, $str)); + + } + + protected function serializeComponent(Component $vObj) { + + $this->cWrite('cyan', 'BEGIN'); + $this->cWrite('red', ':'); + $this->cWrite('yellow', $vObj->name . "\n"); + + /** + * Gives a component a 'score' for sorting purposes. + * + * This is solely used by the childrenSort method. + * + * A higher score means the item will be lower in the list. + * To avoid score collisions, each "score category" has a reasonable + * space to accomodate elements. The $key is added to the $score to + * preserve the original relative order of elements. + * + * @param int $key + * @param array $array + * + * @return int + */ + $sortScore = function($key, $array) { + + if ($array[$key] instanceof Component) { + + // We want to encode VTIMEZONE first, this is a personal + // preference. + if ($array[$key]->name === 'VTIMEZONE') { + $score = 300000000; + return $score + $key; + } else { + $score = 400000000; + return $score + $key; + } + } else { + // Properties get encoded first + // VCARD version 4.0 wants the VERSION property to appear first + if ($array[$key] instanceof Property) { + if ($array[$key]->name === 'VERSION') { + $score = 100000000; + return $score + $key; + } else { + // All other properties + $score = 200000000; + return $score + $key; + } + } + } + + }; + + $children = $vObj->children(); + $tmp = $children; + uksort( + $children, + function($a, $b) use ($sortScore, $tmp) { + + $sA = $sortScore($a, $tmp); + $sB = $sortScore($b, $tmp); + + return $sA - $sB; + + } + ); + + foreach ($children as $child) { + if ($child instanceof Component) { + $this->serializeComponent($child); + } else { + $this->serializeProperty($child); + } + } + + $this->cWrite('cyan', 'END'); + $this->cWrite('red', ':'); + $this->cWrite('yellow', $vObj->name . "\n"); + + } + + /** + * Colorizes a property. + * + * @param Property $property + * + * @return void + */ + protected function serializeProperty(Property $property) { + + if ($property->group) { + $this->cWrite('default', $property->group); + $this->cWrite('red', '.'); + } + + $this->cWrite('yellow', $property->name); + + foreach ($property->parameters as $param) { + + $this->cWrite('red', ';'); + $this->cWrite('blue', $param->serialize()); + + } + $this->cWrite('red', ':'); + + if ($property instanceof Property\Binary) { + + $this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)'); + + } else { + + $parts = $property->getParts(); + $first1 = true; + // Looping through property values + foreach ($parts as $part) { + if ($first1) { + $first1 = false; + } else { + $this->cWrite('red', $property->delimiter); + } + $first2 = true; + // Looping through property sub-values + foreach ((array)$part as $subPart) { + if ($first2) { + $first2 = false; + } else { + // The sub-value delimiter is always comma + $this->cWrite('red', ','); + } + + $subPart = strtr( + $subPart, + [ + '\\' => $this->colorize('purple', '\\\\', 'green'), + ';' => $this->colorize('purple', '\;', 'green'), + ',' => $this->colorize('purple', '\,', 'green'), + "\n" => $this->colorize('purple', "\\n\n\t", 'green'), + "\r" => "", + ] + ); + + $this->cWrite('green', $subPart); + } + } + + } + $this->cWrite("default", "\n"); + + } + + /** + * Parses the list of arguments. + * + * @param array $argv + * + * @return void + */ + protected function parseArguments(array $argv) { + + $positional = []; + $options = []; + + for ($ii = 0; $ii < count($argv); $ii++) { + + // Skipping the first argument. + if ($ii === 0) continue; + + $v = $argv[$ii]; + + if (substr($v, 0, 2) === '--') { + // This is a long-form option. + $optionName = substr($v, 2); + $optionValue = true; + if (strpos($optionName, '=')) { + list($optionName, $optionValue) = explode('=', $optionName); + } + $options[$optionName] = $optionValue; + } elseif (substr($v, 0, 1) === '-' && strlen($v) > 1) { + // This is a short-form option. + foreach (str_split(substr($v, 1)) as $option) { + $options[$option] = true; + } + + } else { + + $positional[] = $v; + + } + + } + + return [$options, $positional]; + + } + + protected $parser; + + /** + * Reads the input file. + * + * @return Component + */ + protected function readInput() { + + if (!$this->parser) { + if ($this->inputPath !== '-') { + $this->stdin = fopen($this->inputPath, 'r'); + } + + if ($this->inputFormat === 'mimedir') { + $this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0)); + } else { + $this->parser = new Parser\Json($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0)); + } + } + + return $this->parser->parse(); + + } + + /** + * Sends a message to STDERR. + * + * @param string $msg + * + * @return void + */ + protected function log($msg, $color = 'default') { + + if (!$this->quiet) { + if ($color !== 'default') { + $msg = $this->colorize($color, $msg); + } + fwrite($this->stderr, $msg . "\n"); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component.php b/htdocs/includes/sabre/sabre/vobject/lib/Component.php new file mode 100644 index 00000000000..9a10ed3f8c7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component.php @@ -0,0 +1,700 @@ +<?php + +namespace Sabre\VObject; + +use Sabre\Xml; + +/** + * Component. + * + * A component represents a group of properties, such as VCALENDAR, VEVENT, or + * VCARD. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Component extends Node { + + /** + * Component name. + * + * This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD. + * + * @var string + */ + public $name; + + /** + * A list of properties and/or sub-components. + * + * @var array + */ + protected $children = []; + + /** + * Creates a new component. + * + * You can specify the children either in key=>value syntax, in which case + * properties will automatically be created, or you can just pass a list of + * Component and Property object. + * + * By default, a set of sensible values will be added to the component. For + * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To + * ensure that this does not happen, set $defaults to false. + * + * @param Document $root + * @param string $name such as VCALENDAR, VEVENT. + * @param array $children + * @param bool $defaults + * + * @return void + */ + function __construct(Document $root, $name, array $children = [], $defaults = true) { + + $this->name = strtoupper($name); + $this->root = $root; + + if ($defaults) { + // This is a terribly convoluted way to do this, but this ensures + // that the order of properties as they are specified in both + // defaults and the childrens list, are inserted in the object in a + // natural way. + $list = $this->getDefaults(); + $nodes = []; + foreach ($children as $key => $value) { + if ($value instanceof Node) { + if (isset($list[$value->name])) { + unset($list[$value->name]); + } + $nodes[] = $value; + } else { + $list[$key] = $value; + } + } + foreach ($list as $key => $value) { + $this->add($key, $value); + } + foreach ($nodes as $node) { + $this->add($node); + } + } else { + foreach ($children as $k => $child) { + if ($child instanceof Node) { + // Component or Property + $this->add($child); + } else { + + // Property key=>value + $this->add($k, $child); + } + } + } + + } + + /** + * Adds a new property or component, and returns the new item. + * + * This method has 3 possible signatures: + * + * add(Component $comp) // Adds a new component + * add(Property $prop) // Adds a new property + * add($name, $value, array $parameters = []) // Adds a new property + * add($name, array $children = []) // Adds a new component + * by name. + * + * @return Node + */ + function add() { + + $arguments = func_get_args(); + + if ($arguments[0] instanceof Node) { + if (isset($arguments[1])) { + throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node'); + } + $arguments[0]->parent = $this; + $newNode = $arguments[0]; + + } elseif (is_string($arguments[0])) { + + $newNode = call_user_func_array([$this->root, 'create'], $arguments); + + } else { + + throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string'); + + } + + $name = $newNode->name; + if (isset($this->children[$name])) { + $this->children[$name][] = $newNode; + } else { + $this->children[$name] = [$newNode]; + } + return $newNode; + + } + + /** + * This method removes a component or property from this component. + * + * You can either specify the item by name (like DTSTART), in which case + * all properties/components with that name will be removed, or you can + * pass an instance of a property or component, in which case only that + * exact item will be removed. + * + * @param string|Property|Component $item + * @return void + */ + function remove($item) { + + if (is_string($item)) { + // If there's no dot in the name, it's an exact property name and + // we can just wipe out all those properties. + // + if (strpos($item, '.') === false) { + unset($this->children[strtoupper($item)]); + return; + } + // If there was a dot, we need to ask select() to help us out and + // then we just call remove recursively. + foreach ($this->select($item) as $child) { + + $this->remove($child); + + } + } else { + foreach ($this->select($item->name) as $k => $child) { + if ($child === $item) { + unset($this->children[$item->name][$k]); + return; + } + } + } + + throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component'); + + } + + /** + * Returns a flat list of all the properties and components in this + * component. + * + * @return array + */ + function children() { + + $result = []; + foreach ($this->children as $childGroup) { + $result = array_merge($result, $childGroup); + } + return $result; + + } + + /** + * This method only returns a list of sub-components. Properties are + * ignored. + * + * @return array + */ + function getComponents() { + + $result = []; + + foreach ($this->children as $childGroup) { + foreach ($childGroup as $child) { + if ($child instanceof self) { + $result[] = $child; + } + } + } + return $result; + + } + + /** + * Returns an array with elements that match the specified name. + * + * This function is also aware of MIME-Directory groups (as they appear in + * vcards). This means that if a property is grouped as "HOME.EMAIL", it + * will also be returned when searching for just "EMAIL". If you want to + * search for a property in a specific group, you can select on the entire + * string ("HOME.EMAIL"). If you want to search on a specific property that + * has not been assigned a group, specify ".EMAIL". + * + * @param string $name + * @return array + */ + function select($name) { + + $group = null; + $name = strtoupper($name); + if (strpos($name, '.') !== false) { + list($group, $name) = explode('.', $name, 2); + } + if ($name === '') $name = null; + + if (!is_null($name)) { + + $result = isset($this->children[$name]) ? $this->children[$name] : []; + + if (is_null($group)) { + return $result; + } else { + // If we have a group filter as well, we need to narrow it down + // more. + return array_filter( + $result, + function($child) use ($group) { + + return $child instanceof Property && strtoupper($child->group) === $group; + + } + ); + } + + } + + // If we got to this point, it means there was no 'name' specified for + // searching, implying that this is a group-only search. + $result = []; + foreach ($this->children as $childGroup) { + + foreach ($childGroup as $child) { + + if ($child instanceof Property && strtoupper($child->group) === $group) { + $result[] = $child; + } + + } + + } + return $result; + + } + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + function serialize() { + + $str = "BEGIN:" . $this->name . "\r\n"; + + /** + * Gives a component a 'score' for sorting purposes. + * + * This is solely used by the childrenSort method. + * + * A higher score means the item will be lower in the list. + * To avoid score collisions, each "score category" has a reasonable + * space to accomodate elements. The $key is added to the $score to + * preserve the original relative order of elements. + * + * @param int $key + * @param array $array + * + * @return int + */ + $sortScore = function($key, $array) { + + if ($array[$key] instanceof Component) { + + // We want to encode VTIMEZONE first, this is a personal + // preference. + if ($array[$key]->name === 'VTIMEZONE') { + $score = 300000000; + return $score + $key; + } else { + $score = 400000000; + return $score + $key; + } + } else { + // Properties get encoded first + // VCARD version 4.0 wants the VERSION property to appear first + if ($array[$key] instanceof Property) { + if ($array[$key]->name === 'VERSION') { + $score = 100000000; + return $score + $key; + } else { + // All other properties + $score = 200000000; + return $score + $key; + } + } + } + + }; + + $children = $this->children(); + $tmp = $children; + uksort( + $children, + function($a, $b) use ($sortScore, $tmp) { + + $sA = $sortScore($a, $tmp); + $sB = $sortScore($b, $tmp); + + return $sA - $sB; + + } + ); + + foreach ($children as $child) $str .= $child->serialize(); + $str .= "END:" . $this->name . "\r\n"; + + return $str; + + } + + /** + * This method returns an array, with the representation as it should be + * encoded in JSON. This is used to create jCard or jCal documents. + * + * @return array + */ + function jsonSerialize() { + + $components = []; + $properties = []; + + foreach ($this->children as $childGroup) { + foreach ($childGroup as $child) { + if ($child instanceof self) { + $components[] = $child->jsonSerialize(); + } else { + $properties[] = $child->jsonSerialize(); + } + } + } + + return [ + strtolower($this->name), + $properties, + $components + ]; + + } + + /** + * This method serializes the data into XML. This is used to create xCard or + * xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $components = []; + $properties = []; + + foreach ($this->children as $childGroup) { + foreach ($childGroup as $child) { + if ($child instanceof self) { + $components[] = $child; + } else { + $properties[] = $child; + } + } + } + + $writer->startElement(strtolower($this->name)); + + if (!empty($properties)) { + + $writer->startElement('properties'); + + foreach ($properties as $property) { + $property->xmlSerialize($writer); + } + + $writer->endElement(); + + } + + if (!empty($components)) { + + $writer->startElement('components'); + + foreach ($components as $component) { + $component->xmlSerialize($writer); + } + + $writer->endElement(); + } + + $writer->endElement(); + + } + + /** + * This method should return a list of default property values. + * + * @return array + */ + protected function getDefaults() { + + return []; + + } + + /* Magic property accessors {{{ */ + + /** + * Using 'get' you will either get a property or component. + * + * If there were no child-elements found with the specified name, + * null is returned. + * + * To use this, this may look something like this: + * + * $event = $calendar->VEVENT; + * + * @param string $name + * + * @return Property + */ + function __get($name) { + + if ($name === 'children') { + + throw new \RuntimeException('Starting sabre/vobject 4.0 the children property is now protected. You should use the children() method instead'); + + } + + $matches = $this->select($name); + if (count($matches) === 0) { + return; + } else { + $firstMatch = current($matches); + /** @var $firstMatch Property */ + $firstMatch->setIterator(new ElementList(array_values($matches))); + return $firstMatch; + } + + } + + /** + * This method checks if a sub-element with the specified name exists. + * + * @param string $name + * + * @return bool + */ + function __isset($name) { + + $matches = $this->select($name); + return count($matches) > 0; + + } + + /** + * Using the setter method you can add properties or subcomponents. + * + * You can either pass a Component, Property + * object, or a string to automatically create a Property. + * + * If the item already exists, it will be removed. If you want to add + * a new item with the same name, always use the add() method. + * + * @param string $name + * @param mixed $value + * + * @return void + */ + function __set($name, $value) { + + $name = strtoupper($name); + $this->remove($name); + if ($value instanceof self || $value instanceof Property) { + $this->add($value); + } else { + $this->add($name, $value); + } + } + + /** + * Removes all properties and components within this component with the + * specified name. + * + * @param string $name + * + * @return void + */ + function __unset($name) { + + $this->remove($name); + + } + + /* }}} */ + + /** + * This method is automatically called when the object is cloned. + * Specifically, this will ensure all child elements are also cloned. + * + * @return void + */ + function __clone() { + + foreach ($this->children as $childName => $childGroup) { + foreach ($childGroup as $key => $child) { + $clonedChild = clone $child; + $clonedChild->parent = $this; + $clonedChild->root = $this->root; + $this->children[$childName][$key] = $clonedChild; + } + } + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * It is also possible to specify defaults and severity levels for + * violating the rule. + * + * See the VEVENT implementation for getValidationRules for a more complex + * example. + * + * @var array + */ + function getValidationRules() { + + return []; + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes. + * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on). + * 2 - A warning. + * 3 - An error. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $rules = $this->getValidationRules(); + $defaults = $this->getDefaults(); + + $propertyCounters = []; + + $messages = []; + + foreach ($this->children() as $child) { + $name = strtoupper($child->name); + if (!isset($propertyCounters[$name])) { + $propertyCounters[$name] = 1; + } else { + $propertyCounters[$name]++; + } + $messages = array_merge($messages, $child->validate($options)); + } + + foreach ($rules as $propName => $rule) { + + switch ($rule) { + case '0' : + if (isset($propertyCounters[$propName])) { + $messages[] = [ + 'level' => 3, + 'message' => $propName . ' MUST NOT appear in a ' . $this->name . ' component', + 'node' => $this, + ]; + } + break; + case '1' : + if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] !== 1) { + $repaired = false; + if ($options & self::REPAIR && isset($defaults[$propName])) { + $this->add($propName, $defaults[$propName]); + $repaired = true; + } + $messages[] = [ + 'level' => $repaired ? 1 : 3, + 'message' => $propName . ' MUST appear exactly once in a ' . $this->name . ' component', + 'node' => $this, + ]; + } + break; + case '+' : + if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) { + $messages[] = [ + 'level' => 3, + 'message' => $propName . ' MUST appear at least once in a ' . $this->name . ' component', + 'node' => $this, + ]; + } + break; + case '*' : + break; + case '?' : + if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) { + $messages[] = [ + 'level' => 3, + 'message' => $propName . ' MUST NOT appear more than once in a ' . $this->name . ' component', + 'node' => $this, + ]; + } + break; + + } + + } + return $messages; + + } + + /** + * Call this method on a document if you're done using it. + * + * It's intended to remove all circular references, so PHP can easily clean + * it up. + * + * @return void + */ + function destroy() { + + parent::destroy(); + foreach ($this->children as $childGroup) { + foreach ($childGroup as $child) { + $child->destroy(); + } + } + $this->children = []; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/Available.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/Available.php new file mode 100644 index 00000000000..b3aaf08afad --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/Available.php @@ -0,0 +1,126 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; + +/** + * The Available sub-component. + * + * This component adds functionality to a component, specific for AVAILABLE + * components. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Ivan Enderlin + * @license http://sabre.io/license/ Modified BSD License + */ +class Available extends VObject\Component { + + /** + * Returns the 'effective start' and 'effective end' of this VAVAILABILITY + * component. + * + * We use the DTSTART and DTEND or DURATION to determine this. + * + * The returned value is an array containing DateTimeImmutable instances. + * If either the start or end is 'unbounded' its value will be null + * instead. + * + * @return array + */ + function getEffectiveStartEnd() { + + $effectiveStart = $this->DTSTART->getDateTime(); + if (isset($this->DTEND)) { + $effectiveEnd = $this->DTEND->getDateTime(); + } else { + $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION)); + } + + return [$effectiveStart, $effectiveEnd]; + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'UID' => 1, + 'DTSTART' => 1, + 'DTSTAMP' => 1, + + 'DTEND' => '?', + 'DURATION' => '?', + + 'CREATED' => '?', + 'DESCRIPTION' => '?', + 'LAST-MODIFIED' => '?', + 'RECURRENCE-ID' => '?', + 'RRULE' => '?', + 'SUMMARY' => '?', + + 'CATEGORIES' => '*', + 'COMMENT' => '*', + 'CONTACT' => '*', + 'EXDATE' => '*', + 'RDATE' => '*', + + 'AVAILABLE' => '*', + ]; + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes. + * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on). + * 2 - A warning. + * 3 - An error. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $result = parent::validate($options); + + if (isset($this->DTEND) && isset($this->DURATION)) { + $result[] = [ + 'level' => 3, + 'message' => 'DTEND and DURATION cannot both be present', + 'node' => $this + ]; + } + + return $result; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VAlarm.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VAlarm.php new file mode 100644 index 00000000000..faa8a5e74be --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VAlarm.php @@ -0,0 +1,142 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeImmutable; +use DateTimeInterface; +use Sabre\VObject; +use Sabre\VObject\InvalidDataException; + +/** + * VAlarm component. + * + * This component contains some additional functionality specific for VALARMs. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VAlarm extends VObject\Component { + + /** + * Returns a DateTime object when this alarm is going to trigger. + * + * This ignores repeated alarm, only the first trigger is returned. + * + * @return DateTimeImmutable + */ + function getEffectiveTriggerTime() { + + $trigger = $this->TRIGGER; + if (!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') { + $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER); + $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START'; + + $parentComponent = $this->parent; + if ($related === 'START') { + + if ($parentComponent->name === 'VTODO') { + $propName = 'DUE'; + } else { + $propName = 'DTSTART'; + } + + $effectiveTrigger = $parentComponent->$propName->getDateTime(); + $effectiveTrigger = $effectiveTrigger->add($triggerDuration); + } else { + if ($parentComponent->name === 'VTODO') { + $endProp = 'DUE'; + } elseif ($parentComponent->name === 'VEVENT') { + $endProp = 'DTEND'; + } else { + throw new InvalidDataException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); + } + + if (isset($parentComponent->$endProp)) { + $effectiveTrigger = $parentComponent->$endProp->getDateTime(); + $effectiveTrigger = $effectiveTrigger->add($triggerDuration); + } elseif (isset($parentComponent->DURATION)) { + $effectiveTrigger = $parentComponent->DTSTART->getDateTime(); + $duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION); + $effectiveTrigger = $effectiveTrigger->add($duration); + $effectiveTrigger = $effectiveTrigger->add($triggerDuration); + } else { + $effectiveTrigger = $parentComponent->DTSTART->getDateTime(); + $effectiveTrigger = $effectiveTrigger->add($triggerDuration); + } + } + } else { + $effectiveTrigger = $trigger->getDateTime(); + } + return $effectiveTrigger; + + } + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on the CalDAV specification. + * + * @param DateTime $start + * @param DateTime $end + * + * @return bool + */ + function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { + + $effectiveTrigger = $this->getEffectiveTriggerTime(); + + if (isset($this->DURATION)) { + $duration = VObject\DateTimeParser::parseDuration($this->DURATION); + $repeat = (string)$this->REPEAT; + if (!$repeat) { + $repeat = 1; + } + + $period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat); + + foreach ($period as $occurrence) { + + if ($start <= $occurrence && $end > $occurrence) { + return true; + } + } + return false; + } else { + return ($start <= $effectiveTrigger && $end > $effectiveTrigger); + } + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'ACTION' => 1, + 'TRIGGER' => 1, + + 'DURATION' => '?', + 'REPEAT' => '?', + + 'ATTACH' => '?', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VAvailability.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VAvailability.php new file mode 100644 index 00000000000..66b3310c5e8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VAvailability.php @@ -0,0 +1,156 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeInterface; +use Sabre\VObject; + +/** + * The VAvailability component. + * + * This component adds functionality to a component, specific for VAVAILABILITY + * components. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Ivan Enderlin + * @license http://sabre.io/license/ Modified BSD License + */ +class VAvailability extends VObject\Component { + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on: + * + * https://tools.ietf.org/html/draft-daboo-calendar-availability-05#section-3.1 + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * + * @return bool + */ + function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { + + list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd(); + return ( + (is_null($effectiveStart) || $start < $effectiveEnd) && + (is_null($effectiveEnd) || $end > $effectiveStart) + ); + + } + + /** + * Returns the 'effective start' and 'effective end' of this VAVAILABILITY + * component. + * + * We use the DTSTART and DTEND or DURATION to determine this. + * + * The returned value is an array containing DateTimeImmutable instances. + * If either the start or end is 'unbounded' its value will be null + * instead. + * + * @return array + */ + function getEffectiveStartEnd() { + + $effectiveStart = null; + $effectiveEnd = null; + + if (isset($this->DTSTART)) { + $effectiveStart = $this->DTSTART->getDateTime(); + } + if (isset($this->DTEND)) { + $effectiveEnd = $this->DTEND->getDateTime(); + } elseif ($effectiveStart && isset($this->DURATION)) { + $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION)); + } + + return [$effectiveStart, $effectiveEnd]; + + } + + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'UID' => 1, + 'DTSTAMP' => 1, + + 'BUSYTYPE' => '?', + 'CLASS' => '?', + 'CREATED' => '?', + 'DESCRIPTION' => '?', + 'DTSTART' => '?', + 'LAST-MODIFIED' => '?', + 'ORGANIZER' => '?', + 'PRIORITY' => '?', + 'SEQUENCE' => '?', + 'SUMMARY' => '?', + 'URL' => '?', + 'DTEND' => '?', + 'DURATION' => '?', + + 'CATEGORIES' => '*', + 'COMMENT' => '*', + 'CONTACT' => '*', + ]; + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes. + * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on). + * 2 - A warning. + * 3 - An error. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $result = parent::validate($options); + + if (isset($this->DTEND) && isset($this->DURATION)) { + $result[] = [ + 'level' => 3, + 'message' => 'DTEND and DURATION cannot both be present', + 'node' => $this + ]; + } + + return $result; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VCalendar.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VCalendar.php new file mode 100644 index 00000000000..1b3137d38eb --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VCalendar.php @@ -0,0 +1,561 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeInterface; +use DateTimeZone; +use Sabre\VObject; +use Sabre\VObject\Component; +use Sabre\VObject\InvalidDataException; +use Sabre\VObject\Property; +use Sabre\VObject\Recur\EventIterator; +use Sabre\VObject\Recur\NoInstancesException; + +/** + * The VCalendar component. + * + * This component adds functionality to a component, specific for a VCALENDAR. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VCalendar extends VObject\Document { + + /** + * The default name for this component. + * + * This should be 'VCALENDAR' or 'VCARD'. + * + * @var string + */ + static $defaultName = 'VCALENDAR'; + + /** + * This is a list of components, and which classes they should map to. + * + * @var array + */ + static $componentMap = [ + 'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar', + 'VALARM' => 'Sabre\\VObject\\Component\\VAlarm', + 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent', + 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy', + 'VAVAILABILITY' => 'Sabre\\VObject\\Component\\VAvailability', + 'AVAILABLE' => 'Sabre\\VObject\\Component\\Available', + 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal', + 'VTIMEZONE' => 'Sabre\\VObject\\Component\\VTimeZone', + 'VTODO' => 'Sabre\\VObject\\Component\\VTodo', + ]; + + /** + * List of value-types, and which classes they map to. + * + * @var array + */ + static $valueMap = [ + 'BINARY' => 'Sabre\\VObject\\Property\\Binary', + 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean', + 'CAL-ADDRESS' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress', + 'DATE' => 'Sabre\\VObject\\Property\\ICalendar\\Date', + 'DATE-TIME' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration', + 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue', + 'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue', + 'PERIOD' => 'Sabre\\VObject\\Property\\ICalendar\\Period', + 'RECUR' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', + 'TEXT' => 'Sabre\\VObject\\Property\\Text', + 'TIME' => 'Sabre\\VObject\\Property\\Time', + 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only. + 'URI' => 'Sabre\\VObject\\Property\\Uri', + 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset', + ]; + + /** + * List of properties, and which classes they map to. + * + * @var array + */ + static $propertyMap = [ + // Calendar properties + 'CALSCALE' => 'Sabre\\VObject\\Property\\FlatText', + 'METHOD' => 'Sabre\\VObject\\Property\\FlatText', + 'PRODID' => 'Sabre\\VObject\\Property\\FlatText', + 'VERSION' => 'Sabre\\VObject\\Property\\FlatText', + + // Component properties + 'ATTACH' => 'Sabre\\VObject\\Property\\Uri', + 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text', + 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', + 'COMMENT' => 'Sabre\\VObject\\Property\\FlatText', + 'DESCRIPTION' => 'Sabre\\VObject\\Property\\FlatText', + 'GEO' => 'Sabre\\VObject\\Property\\FloatValue', + 'LOCATION' => 'Sabre\\VObject\\Property\\FlatText', + 'PERCENT-COMPLETE' => 'Sabre\\VObject\\Property\\IntegerValue', + 'PRIORITY' => 'Sabre\\VObject\\Property\\IntegerValue', + 'RESOURCES' => 'Sabre\\VObject\\Property\\Text', + 'STATUS' => 'Sabre\\VObject\\Property\\FlatText', + 'SUMMARY' => 'Sabre\\VObject\\Property\\FlatText', + + // Date and Time Component Properties + 'COMPLETED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DTEND' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DUE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DTSTART' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration', + 'FREEBUSY' => 'Sabre\\VObject\\Property\\ICalendar\\Period', + 'TRANSP' => 'Sabre\\VObject\\Property\\FlatText', + + // Time Zone Component Properties + 'TZID' => 'Sabre\\VObject\\Property\\FlatText', + 'TZNAME' => 'Sabre\\VObject\\Property\\FlatText', + 'TZOFFSETFROM' => 'Sabre\\VObject\\Property\\UtcOffset', + 'TZOFFSETTO' => 'Sabre\\VObject\\Property\\UtcOffset', + 'TZURL' => 'Sabre\\VObject\\Property\\Uri', + + // Relationship Component Properties + 'ATTENDEE' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress', + 'CONTACT' => 'Sabre\\VObject\\Property\\FlatText', + 'ORGANIZER' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress', + 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'RELATED-TO' => 'Sabre\\VObject\\Property\\FlatText', + 'URL' => 'Sabre\\VObject\\Property\\Uri', + 'UID' => 'Sabre\\VObject\\Property\\FlatText', + + // Recurrence Component Properties + 'EXDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'RDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'RRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', + 'EXRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545 + + // Alarm Component Properties + 'ACTION' => 'Sabre\\VObject\\Property\\FlatText', + 'REPEAT' => 'Sabre\\VObject\\Property\\IntegerValue', + 'TRIGGER' => 'Sabre\\VObject\\Property\\ICalendar\\Duration', + + // Change Management Component Properties + 'CREATED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'DTSTAMP' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'SEQUENCE' => 'Sabre\\VObject\\Property\\IntegerValue', + + // Request Status + 'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text', + + // Additions from draft-daboo-valarm-extensions-04 + 'ALARM-AGENT' => 'Sabre\\VObject\\Property\\Text', + 'ACKNOWLEDGED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime', + 'PROXIMITY' => 'Sabre\\VObject\\Property\\Text', + 'DEFAULT-ALARM' => 'Sabre\\VObject\\Property\\Boolean', + + // Additions from draft-daboo-calendar-availability-05 + 'BUSYTYPE' => 'Sabre\\VObject\\Property\\Text', + + ]; + + /** + * Returns the current document type. + * + * @return int + */ + function getDocumentType() { + + return self::ICALENDAR20; + + } + + /** + * Returns a list of all 'base components'. For instance, if an Event has + * a recurrence rule, and one instance is overridden, the overridden event + * will have the same UID, but will be excluded from this list. + * + * VTIMEZONE components will always be excluded. + * + * @param string $componentName filter by component name + * + * @return VObject\Component[] + */ + function getBaseComponents($componentName = null) { + + $isBaseComponent = function($component) { + + if (!$component instanceof VObject\Component) { + return false; + } + if ($component->name === 'VTIMEZONE') { + return false; + } + if (isset($component->{'RECURRENCE-ID'})) { + return false; + } + return true; + + }; + + if ($componentName) { + // Early exit + return array_filter( + $this->select($componentName), + $isBaseComponent + ); + } + + $components = []; + foreach ($this->children as $childGroup) { + + foreach ($childGroup as $child) { + + if (!$child instanceof Component) { + // If one child is not a component, they all are so we skip + // the entire group. + continue 2; + } + if ($isBaseComponent($child)) { + $components[] = $child; + } + + } + + } + return $components; + + } + + /** + * Returns the first component that is not a VTIMEZONE, and does not have + * an RECURRENCE-ID. + * + * If there is no such component, null will be returned. + * + * @param string $componentName filter by component name + * + * @return VObject\Component|null + */ + function getBaseComponent($componentName = null) { + + $isBaseComponent = function($component) { + + if (!$component instanceof VObject\Component) { + return false; + } + if ($component->name === 'VTIMEZONE') { + return false; + } + if (isset($component->{'RECURRENCE-ID'})) { + return false; + } + return true; + + }; + + if ($componentName) { + foreach ($this->select($componentName) as $child) { + if ($isBaseComponent($child)) { + return $child; + } + } + return null; + } + + // Searching all components + foreach ($this->children as $childGroup) { + foreach ($childGroup as $child) { + if ($isBaseComponent($child)) { + return $child; + } + } + + } + return null; + + } + + /** + * Expand all events in this VCalendar object and return a new VCalendar + * with the expanded events. + * + * If this calendar object, has events with recurrence rules, this method + * can be used to expand the event into multiple sub-events. + * + * Each event will be stripped from it's recurrence information, and only + * the instances of the event in the specified timerange will be left + * alone. + * + * In addition, this method will cause timezone information to be stripped, + * and normalized to UTC. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * @param DateTimeZone $timeZone reference timezone for floating dates and + * times. + * @return VCalendar + */ + function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null) { + + $newChildren = []; + $recurringEvents = []; + + if (!$timeZone) { + $timeZone = new DateTimeZone('UTC'); + } + + $stripTimezones = function(Component $component) use ($timeZone, &$stripTimezones) { + + foreach ($component->children() as $componentChild) { + if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) { + + $dt = $componentChild->getDateTimes($timeZone); + // We only need to update the first timezone, because + // setDateTimes will match all other timezones to the + // first. + $dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC')); + $componentChild->setDateTimes($dt); + } elseif ($componentChild instanceof Component) { + $stripTimezones($componentChild); + } + + } + return $component; + + }; + + foreach ($this->children() as $child) { + + if ($child instanceof Property && $child->name !== 'PRODID') { + // We explictly want to ignore PRODID, because we want to + // overwrite it with our own. + $newChildren[] = clone $child; + } elseif ($child instanceof Component && $child->name !== 'VTIMEZONE') { + + // We're also stripping all VTIMEZONE objects because we're + // converting everything to UTC. + if ($child->name === 'VEVENT' && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) { + // Handle these a bit later. + $uid = (string)$child->UID; + if (!$uid) { + throw new InvalidDataException('Every VEVENT object must have a UID property'); + } + if (isset($recurringEvents[$uid])) { + $recurringEvents[$uid][] = clone $child; + } else { + $recurringEvents[$uid] = [clone $child]; + } + } elseif ($child->name === 'VEVENT' && $child->isInTimeRange($start, $end)) { + $newChildren[] = $stripTimezones(clone $child); + } + + } + + } + + foreach ($recurringEvents as $events) { + + try { + $it = new EventIterator($events, $timeZone); + + } catch (NoInstancesException $e) { + // This event is recurring, but it doesn't have a single + // instance. We are skipping this event from the output + // entirely. + continue; + } + $it->fastForward($start); + + while ($it->valid() && $it->getDTStart() < $end) { + + if ($it->getDTEnd() > $start) { + + $newChildren[] = $stripTimezones($it->getEventObject()); + + } + $it->next(); + + } + + } + + return new self($newChildren); + + } + + /** + * This method should return a list of default property values. + * + * @return array + */ + protected function getDefaults() { + + return [ + 'VERSION' => '2.0', + 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN', + 'CALSCALE' => 'GREGORIAN', + ]; + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'PRODID' => 1, + 'VERSION' => 1, + + 'CALSCALE' => '?', + 'METHOD' => '?', + ]; + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes. + * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on). + * 2 - A warning. + * 3 - An error. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $warnings = parent::validate($options); + + if ($ver = $this->VERSION) { + if ((string)$ver !== '2.0') { + $warnings[] = [ + 'level' => 3, + 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.', + 'node' => $this, + ]; + } + + } + + $uidList = []; + $componentsFound = 0; + $componentTypes = []; + + foreach ($this->children() as $child) { + if ($child instanceof Component) { + $componentsFound++; + + if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) { + continue; + } + $componentTypes[] = $child->name; + + $uid = (string)$child->UID; + $isMaster = isset($child->{'RECURRENCE-ID'}) ? 0 : 1; + if (isset($uidList[$uid])) { + $uidList[$uid]['count']++; + if ($isMaster && $uidList[$uid]['hasMaster']) { + $warnings[] = [ + 'level' => 3, + 'message' => 'More than one master object was found for the object with UID ' . $uid, + 'node' => $this, + ]; + } + $uidList[$uid]['hasMaster'] += $isMaster; + } else { + $uidList[$uid] = [ + 'count' => 1, + 'hasMaster' => $isMaster, + ]; + } + + } + } + + if ($componentsFound === 0) { + $warnings[] = [ + 'level' => 3, + 'message' => 'An iCalendar object must have at least 1 component.', + 'node' => $this, + ]; + } + + if ($options & self::PROFILE_CALDAV) { + if (count($uidList) > 1) { + $warnings[] = [ + 'level' => 3, + 'message' => 'A calendar object on a CalDAV server may only have components with the same UID.', + 'node' => $this, + ]; + } + if (count($componentTypes) === 0) { + $warnings[] = [ + 'level' => 3, + 'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).', + 'node' => $this, + ]; + } + if (count(array_unique($componentTypes)) > 1) { + $warnings[] = [ + 'level' => 3, + 'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).', + 'node' => $this, + ]; + } + + if (isset($this->METHOD)) { + $warnings[] = [ + 'level' => 3, + 'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.', + 'node' => $this, + ]; + } + } + + return $warnings; + + } + + /** + * Returns all components with a specific UID value. + * + * @return array + */ + function getByUID($uid) { + + return array_filter($this->getComponents(), function($item) use ($uid) { + + if (!$itemUid = $item->select('UID')) { + return false; + } + $itemUid = current($itemUid)->getValue(); + return $uid === $itemUid; + + }); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VCard.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VCard.php new file mode 100644 index 00000000000..4f620de1070 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VCard.php @@ -0,0 +1,553 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; +use Sabre\Xml; + +/** + * The VCard component. + * + * This component represents the BEGIN:VCARD and END:VCARD found in every + * vcard. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VCard extends VObject\Document { + + /** + * The default name for this component. + * + * This should be 'VCALENDAR' or 'VCARD'. + * + * @var string + */ + static $defaultName = 'VCARD'; + + /** + * Caching the version number. + * + * @var int + */ + private $version = null; + + /** + * This is a list of components, and which classes they should map to. + * + * @var array + */ + static $componentMap = [ + 'VCARD' => 'Sabre\\VObject\\Component\\VCard', + ]; + + /** + * List of value-types, and which classes they map to. + * + * @var array + */ + static $valueMap = [ + 'BINARY' => 'Sabre\\VObject\\Property\\Binary', + 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean', + 'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only + 'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date', + 'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime', + 'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only + 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue', + 'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue', + 'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag', + 'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp', + 'TEXT' => 'Sabre\\VObject\\Property\\Text', + 'TIME' => 'Sabre\\VObject\\Property\\Time', + 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only. + 'URI' => 'Sabre\\VObject\\Property\\Uri', + 'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only + 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset', + ]; + + /** + * List of properties, and which classes they map to. + * + * @var array + */ + static $propertyMap = [ + + // vCard 2.1 properties and up + 'N' => 'Sabre\\VObject\\Property\\Text', + 'FN' => 'Sabre\\VObject\\Property\\FlatText', + 'PHOTO' => 'Sabre\\VObject\\Property\\Binary', + 'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', + 'ADR' => 'Sabre\\VObject\\Property\\Text', + 'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 + 'TEL' => 'Sabre\\VObject\\Property\\FlatText', + 'EMAIL' => 'Sabre\\VObject\\Property\\FlatText', + 'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 + 'GEO' => 'Sabre\\VObject\\Property\\FlatText', + 'TITLE' => 'Sabre\\VObject\\Property\\FlatText', + 'ROLE' => 'Sabre\\VObject\\Property\\FlatText', + 'LOGO' => 'Sabre\\VObject\\Property\\Binary', + // 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so + // not supported at the moment + 'ORG' => 'Sabre\\VObject\\Property\\Text', + 'NOTE' => 'Sabre\\VObject\\Property\\FlatText', + 'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp', + 'SOUND' => 'Sabre\\VObject\\Property\\FlatText', + 'URL' => 'Sabre\\VObject\\Property\\Uri', + 'UID' => 'Sabre\\VObject\\Property\\FlatText', + 'VERSION' => 'Sabre\\VObject\\Property\\FlatText', + 'KEY' => 'Sabre\\VObject\\Property\\FlatText', + 'TZ' => 'Sabre\\VObject\\Property\\Text', + + // vCard 3.0 properties + 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text', + 'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText', + 'PRODID' => 'Sabre\\VObject\\Property\\FlatText', + 'NICKNAME' => 'Sabre\\VObject\\Property\\Text', + 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 + + // rfc2739 properties + 'FBURL' => 'Sabre\\VObject\\Property\\Uri', + 'CAPURI' => 'Sabre\\VObject\\Property\\Uri', + 'CALURI' => 'Sabre\\VObject\\Property\\Uri', + 'CALADRURI' => 'Sabre\\VObject\\Property\\Uri', + + // rfc4770 properties + 'IMPP' => 'Sabre\\VObject\\Property\\Uri', + + // vCard 4.0 properties + 'SOURCE' => 'Sabre\\VObject\\Property\\Uri', + 'XML' => 'Sabre\\VObject\\Property\\FlatText', + 'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', + 'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text', + 'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag', + 'GENDER' => 'Sabre\\VObject\\Property\\Text', + 'KIND' => 'Sabre\\VObject\\Property\\FlatText', + 'MEMBER' => 'Sabre\\VObject\\Property\\Uri', + 'RELATED' => 'Sabre\\VObject\\Property\\Uri', + + // rfc6474 properties + 'BIRTHPLACE' => 'Sabre\\VObject\\Property\\FlatText', + 'DEATHPLACE' => 'Sabre\\VObject\\Property\\FlatText', + 'DEATHDATE' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', + + // rfc6715 properties + 'EXPERTISE' => 'Sabre\\VObject\\Property\\FlatText', + 'HOBBY' => 'Sabre\\VObject\\Property\\FlatText', + 'INTEREST' => 'Sabre\\VObject\\Property\\FlatText', + 'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText', + + ]; + + /** + * Returns the current document type. + * + * @return int + */ + function getDocumentType() { + + if (!$this->version) { + + $version = (string)$this->VERSION; + + switch ($version) { + case '2.1' : + $this->version = self::VCARD21; + break; + case '3.0' : + $this->version = self::VCARD30; + break; + case '4.0' : + $this->version = self::VCARD40; + break; + default : + // We don't want to cache the version if it's unknown, + // because we might get a version property in a bit. + return self::UNKNOWN; + } + } + + return $this->version; + + } + + /** + * Converts the document to a different vcard version. + * + * Use one of the VCARD constants for the target. This method will return + * a copy of the vcard in the new version. + * + * At the moment the only supported conversion is from 3.0 to 4.0. + * + * If input and output version are identical, a clone is returned. + * + * @param int $target + * + * @return VCard + */ + function convert($target) { + + $converter = new VObject\VCardConverter(); + return $converter->convert($this, $target); + + } + + /** + * VCards with version 2.1, 3.0 and 4.0 are found. + * + * If the VCARD doesn't know its version, 2.1 is assumed. + */ + const DEFAULT_VERSION = self::VCARD21; + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on) + * 2 - An inconsequential issue + * 3 - A severe issue. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $warnings = []; + + $versionMap = [ + self::VCARD21 => '2.1', + self::VCARD30 => '3.0', + self::VCARD40 => '4.0', + ]; + + $version = $this->select('VERSION'); + if (count($version) === 1) { + $version = (string)$this->VERSION; + if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') { + $warnings[] = [ + 'level' => 3, + 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', + 'node' => $this, + ]; + if ($options & self::REPAIR) { + $this->VERSION = $versionMap[self::DEFAULT_VERSION]; + } + } + if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) { + $warnings[] = [ + 'level' => 3, + 'message' => 'CardDAV servers are not allowed to accept vCard 2.1.', + 'node' => $this, + ]; + } + + } + $uid = $this->select('UID'); + if (count($uid) === 0) { + if ($options & self::PROFILE_CARDDAV) { + // Required for CardDAV + $warningLevel = 3; + $message = 'vCards on CardDAV servers MUST have a UID property.'; + } else { + // Not required for regular vcards + $warningLevel = 2; + $message = 'Adding a UID to a vCard property is recommended.'; + } + if ($options & self::REPAIR) { + $this->UID = VObject\UUIDUtil::getUUID(); + $warningLevel = 1; + } + $warnings[] = [ + 'level' => $warningLevel, + 'message' => $message, + 'node' => $this, + ]; + } + + $fn = $this->select('FN'); + if (count($fn) !== 1) { + + $repaired = false; + if (($options & self::REPAIR) && count($fn) === 0) { + // We're going to try to see if we can use the contents of the + // N property. + if (isset($this->N)) { + $value = explode(';', (string)$this->N); + if (isset($value[1]) && $value[1]) { + $this->FN = $value[1] . ' ' . $value[0]; + } else { + $this->FN = $value[0]; + } + $repaired = true; + + // Otherwise, the ORG property may work + } elseif (isset($this->ORG)) { + $this->FN = (string)$this->ORG; + $repaired = true; + } + + } + $warnings[] = [ + 'level' => $repaired ? 1 : 3, + 'message' => 'The FN property must appear in the VCARD component exactly 1 time', + 'node' => $this, + ]; + } + + return array_merge( + parent::validate($options), + $warnings + ); + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'ADR' => '*', + 'ANNIVERSARY' => '?', + 'BDAY' => '?', + 'CALADRURI' => '*', + 'CALURI' => '*', + 'CATEGORIES' => '*', + 'CLIENTPIDMAP' => '*', + 'EMAIL' => '*', + 'FBURL' => '*', + 'IMPP' => '*', + 'GENDER' => '?', + 'GEO' => '*', + 'KEY' => '*', + 'KIND' => '?', + 'LANG' => '*', + 'LOGO' => '*', + 'MEMBER' => '*', + 'N' => '?', + 'NICKNAME' => '*', + 'NOTE' => '*', + 'ORG' => '*', + 'PHOTO' => '*', + 'PRODID' => '?', + 'RELATED' => '*', + 'REV' => '?', + 'ROLE' => '*', + 'SOUND' => '*', + 'SOURCE' => '*', + 'TEL' => '*', + 'TITLE' => '*', + 'TZ' => '*', + 'URL' => '*', + 'VERSION' => '1', + 'XML' => '*', + + // FN is commented out, because it's already handled by the + // validate function, which may also try to repair it. + // 'FN' => '+', + 'UID' => '?', + ]; + + } + + /** + * Returns a preferred field. + * + * VCards can indicate wether a field such as ADR, TEL or EMAIL is + * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x + * being a number between 1 and 100). + * + * If neither of those parameters are specified, the first is returned, if + * a field with that name does not exist, null is returned. + * + * @param string $fieldName + * + * @return VObject\Property|null + */ + function preferred($propertyName) { + + $preferred = null; + $lastPref = 101; + foreach ($this->select($propertyName) as $field) { + + $pref = 101; + if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) { + $pref = 1; + } elseif (isset($field['PREF'])) { + $pref = $field['PREF']->getValue(); + } + + if ($pref < $lastPref || is_null($preferred)) { + $preferred = $field; + $lastPref = $pref; + } + + } + return $preferred; + + } + + /** + * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL). + * + * This function will return null if the property does not exist. If there are + * multiple properties with the same TYPE value, only one will be returned. + * + * @param string $propertyName + * @param string $type + * + * @return VObject\Property|null + */ + function getByType($propertyName, $type) { + foreach ($this->select($propertyName) as $field) { + if (isset($field['TYPE']) && $field['TYPE']->has($type)) { + return $field; + } + } + } + + /** + * This method should return a list of default property values. + * + * @return array + */ + protected function getDefaults() { + + return [ + 'VERSION' => '4.0', + 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN', + 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(), + ]; + + } + + /** + * This method returns an array, with the representation as it should be + * encoded in json. This is used to create jCard or jCal documents. + * + * @return array + */ + function jsonSerialize() { + + // A vcard does not have sub-components, so we're overriding this + // method to remove that array element. + $properties = []; + + foreach ($this->children() as $child) { + $properties[] = $child->jsonSerialize(); + } + + return [ + strtolower($this->name), + $properties, + ]; + + } + + /** + * This method serializes the data into XML. This is used to create xCard or + * xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $propertiesByGroup = []; + + foreach ($this->children() as $property) { + + $group = $property->group; + + if (!isset($propertiesByGroup[$group])) { + $propertiesByGroup[$group] = []; + } + + $propertiesByGroup[$group][] = $property; + + } + + $writer->startElement(strtolower($this->name)); + + foreach ($propertiesByGroup as $group => $properties) { + + if (!empty($group)) { + + $writer->startElement('group'); + $writer->writeAttribute('name', strtolower($group)); + + } + + foreach ($properties as $property) { + switch ($property->name) { + + case 'VERSION': + continue; + + case 'XML': + $value = $property->getParts(); + $fragment = new Xml\Element\XmlFragment($value[0]); + $writer->write($fragment); + break; + + default: + $property->xmlSerialize($writer); + break; + + } + } + + if (!empty($group)) { + $writer->endElement(); + } + + } + + $writer->endElement(); + + } + + /** + * Returns the default class for a property name. + * + * @param string $propertyName + * + * @return string + */ + function getClassNameForPropertyName($propertyName) { + + $className = parent::getClassNameForPropertyName($propertyName); + + // In vCard 4, BINARY no longer exists, and we need URI instead. + if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType() === self::VCARD40) { + return 'Sabre\\VObject\\Property\\Uri'; + } + return $className; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VEvent.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VEvent.php new file mode 100644 index 00000000000..7f686119004 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VEvent.php @@ -0,0 +1,153 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeInterface; +use Sabre\VObject; +use Sabre\VObject\Recur\EventIterator; +use Sabre\VObject\Recur\NoInstancesException; + +/** + * VEvent component. + * + * This component contains some additional functionality specific for VEVENT's. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VEvent extends VObject\Component { + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on the CalDAV specification. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * + * @return bool + */ + function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { + + if ($this->RRULE) { + + try { + + $it = new EventIterator($this, null, $start->getTimezone()); + + } catch (NoInstancesException $e) { + + // If we've catched this exception, there are no instances + // for the event that fall into the specified time-range. + return false; + + } + + $it->fastForward($start); + + // We fast-forwarded to a spot where the end-time of the + // recurrence instance exceeded the start of the requested + // time-range. + // + // If the starttime of the recurrence did not exceed the + // end of the time range as well, we have a match. + return ($it->getDTStart() < $end && $it->getDTEnd() > $start); + + } + + $effectiveStart = $this->DTSTART->getDateTime($start->getTimezone()); + if (isset($this->DTEND)) { + + // The DTEND property is considered non inclusive. So for a 3 day + // event in july, dtstart and dtend would have to be July 1st and + // July 4th respectively. + // + // See: + // http://tools.ietf.org/html/rfc5545#page-54 + $effectiveEnd = $this->DTEND->getDateTime($end->getTimezone()); + + } elseif (isset($this->DURATION)) { + $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION)); + } elseif (!$this->DTSTART->hasTime()) { + $effectiveEnd = $effectiveStart->modify('+1 day'); + } else { + $effectiveEnd = $effectiveStart; + } + return ( + ($start < $effectiveEnd) && ($end > $effectiveStart) + ); + + } + + /** + * This method should return a list of default property values. + * + * @return array + */ + protected function getDefaults() { + + return [ + 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(), + 'DTSTAMP' => date('Ymd\\THis\\Z'), + ]; + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + $hasMethod = isset($this->parent->METHOD); + return [ + 'UID' => 1, + 'DTSTAMP' => 1, + 'DTSTART' => $hasMethod ? '?' : '1', + 'CLASS' => '?', + 'CREATED' => '?', + 'DESCRIPTION' => '?', + 'GEO' => '?', + 'LAST-MODIFIED' => '?', + 'LOCATION' => '?', + 'ORGANIZER' => '?', + 'PRIORITY' => '?', + 'SEQUENCE' => '?', + 'STATUS' => '?', + 'SUMMARY' => '?', + 'TRANSP' => '?', + 'URL' => '?', + 'RECURRENCE-ID' => '?', + 'RRULE' => '?', + 'DTEND' => '?', + 'DURATION' => '?', + + 'ATTACH' => '*', + 'ATTENDEE' => '*', + 'CATEGORIES' => '*', + 'COMMENT' => '*', + 'CONTACT' => '*', + 'EXDATE' => '*', + 'REQUEST-STATUS' => '*', + 'RELATED-TO' => '*', + 'RESOURCES' => '*', + 'RDATE' => '*', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VFreeBusy.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VFreeBusy.php new file mode 100644 index 00000000000..72294cc9f3b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VFreeBusy.php @@ -0,0 +1,102 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeInterface; +use Sabre\VObject; + +/** + * The VFreeBusy component. + * + * This component adds functionality to a component, specific for VFREEBUSY + * components. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VFreeBusy extends VObject\Component { + + /** + * Checks based on the contained FREEBUSY information, if a timeslot is + * available. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * + * @return bool + */ + function isFree(DateTimeInterface $start, DatetimeInterface $end) { + + foreach ($this->select('FREEBUSY') as $freebusy) { + + // We are only interested in FBTYPE=BUSY (the default), + // FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE. + if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'], 0, 4)) !== 'BUSY') { + continue; + } + + // The freebusy component can hold more than 1 value, separated by + // commas. + $periods = explode(',', (string)$freebusy); + + foreach ($periods as $period) { + // Every period is formatted as [start]/[end]. The start is an + // absolute UTC time, the end may be an absolute UTC time, or + // duration (relative) value. + list($busyStart, $busyEnd) = explode('/', $period); + + $busyStart = VObject\DateTimeParser::parse($busyStart); + $busyEnd = VObject\DateTimeParser::parse($busyEnd); + if ($busyEnd instanceof \DateInterval) { + $busyEnd = $busyStart->add($busyEnd); + } + + if ($start < $busyEnd && $end > $busyStart) { + return false; + } + + } + + } + + return true; + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'UID' => 1, + 'DTSTAMP' => 1, + + 'CONTACT' => '?', + 'DTSTART' => '?', + 'DTEND' => '?', + 'ORGANIZER' => '?', + 'URL' => '?', + + 'ATTENDEE' => '*', + 'COMMENT' => '*', + 'FREEBUSY' => '*', + 'REQUEST-STATUS' => '*', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VJournal.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VJournal.php new file mode 100644 index 00000000000..a1b1a863d2d --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VJournal.php @@ -0,0 +1,107 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeInterface; +use Sabre\VObject; + +/** + * VJournal component. + * + * This component contains some additional functionality specific for VJOURNALs. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VJournal extends VObject\Component { + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on the CalDAV specification. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * + * @return bool + */ + function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { + + $dtstart = isset($this->DTSTART) ? $this->DTSTART->getDateTime() : null; + if ($dtstart) { + $effectiveEnd = $dtstart; + if (!$this->DTSTART->hasTime()) { + $effectiveEnd = $effectiveEnd->modify('+1 day'); + } + + return ($start <= $effectiveEnd && $end > $dtstart); + + } + return false; + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'UID' => 1, + 'DTSTAMP' => 1, + + 'CLASS' => '?', + 'CREATED' => '?', + 'DTSTART' => '?', + 'LAST-MODIFIED' => '?', + 'ORGANIZER' => '?', + 'RECURRENCE-ID' => '?', + 'SEQUENCE' => '?', + 'STATUS' => '?', + 'SUMMARY' => '?', + 'URL' => '?', + + 'RRULE' => '?', + + 'ATTACH' => '*', + 'ATTENDEE' => '*', + 'CATEGORIES' => '*', + 'COMMENT' => '*', + 'CONTACT' => '*', + 'DESCRIPTION' => '*', + 'EXDATE' => '*', + 'RELATED-TO' => '*', + 'RDATE' => '*', + ]; + + } + + /** + * This method should return a list of default property values. + * + * @return array + */ + protected function getDefaults() { + + return [ + 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(), + 'DTSTAMP' => date('Ymd\\THis\\Z'), + ]; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VTimeZone.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VTimeZone.php new file mode 100644 index 00000000000..f6eb6cba18a --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VTimeZone.php @@ -0,0 +1,66 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; + +/** + * The VTimeZone component. + * + * This component adds functionality to a component, specific for VTIMEZONE + * components. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VTimeZone extends VObject\Component { + + /** + * Returns the PHP DateTimeZone for this VTIMEZONE component. + * + * If we can't accurately determine the timezone, this method will return + * UTC. + * + * @return \DateTimeZone + */ + function getTimeZone() { + + return VObject\TimeZoneUtil::getTimeZone((string)$this->TZID, $this->root); + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'TZID' => 1, + + 'LAST-MODIFIED' => '?', + 'TZURL' => '?', + + // At least 1 STANDARD or DAYLIGHT must appear. + // + // The validator is not specific yet to pick this up, so these + // rules are too loose. + 'STANDARD' => '*', + 'DAYLIGHT' => '*', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Component/VTodo.php b/htdocs/includes/sabre/sabre/vobject/lib/Component/VTodo.php new file mode 100644 index 00000000000..144ced694ac --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Component/VTodo.php @@ -0,0 +1,193 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeInterface; +use Sabre\VObject; + +/** + * VTodo component. + * + * This component contains some additional functionality specific for VTODOs. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VTodo extends VObject\Component { + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on the CalDAV specification. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * + * @return bool + */ + function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { + + $dtstart = isset($this->DTSTART) ? $this->DTSTART->getDateTime() : null; + $duration = isset($this->DURATION) ? VObject\DateTimeParser::parseDuration($this->DURATION) : null; + $due = isset($this->DUE) ? $this->DUE->getDateTime() : null; + $completed = isset($this->COMPLETED) ? $this->COMPLETED->getDateTime() : null; + $created = isset($this->CREATED) ? $this->CREATED->getDateTime() : null; + + if ($dtstart) { + if ($duration) { + $effectiveEnd = $dtstart->add($duration); + return $start <= $effectiveEnd && $end > $dtstart; + } elseif ($due) { + return + ($start < $due || $start <= $dtstart) && + ($end > $dtstart || $end >= $due); + } else { + return $start <= $dtstart && $end > $dtstart; + } + } + if ($due) { + return ($start < $due && $end >= $due); + } + if ($completed && $created) { + return + ($start <= $created || $start <= $completed) && + ($end >= $created || $end >= $completed); + } + if ($completed) { + return ($start <= $completed && $end >= $completed); + } + if ($created) { + return ($end > $created); + } + return true; + + } + + /** + * A simple list of validation rules. + * + * This is simply a list of properties, and how many times they either + * must or must not appear. + * + * Possible values per property: + * * 0 - Must not appear. + * * 1 - Must appear exactly once. + * * + - Must appear at least once. + * * * - Can appear any number of times. + * * ? - May appear, but not more than once. + * + * @var array + */ + function getValidationRules() { + + return [ + 'UID' => 1, + 'DTSTAMP' => 1, + + 'CLASS' => '?', + 'COMPLETED' => '?', + 'CREATED' => '?', + 'DESCRIPTION' => '?', + 'DTSTART' => '?', + 'GEO' => '?', + 'LAST-MODIFIED' => '?', + 'LOCATION' => '?', + 'ORGANIZER' => '?', + 'PERCENT' => '?', + 'PRIORITY' => '?', + 'RECURRENCE-ID' => '?', + 'SEQUENCE' => '?', + 'STATUS' => '?', + 'SUMMARY' => '?', + 'URL' => '?', + + 'RRULE' => '?', + 'DUE' => '?', + 'DURATION' => '?', + + 'ATTACH' => '*', + 'ATTENDEE' => '*', + 'CATEGORIES' => '*', + 'COMMENT' => '*', + 'CONTACT' => '*', + 'EXDATE' => '*', + 'REQUEST-STATUS' => '*', + 'RELATED-TO' => '*', + 'RESOURCES' => '*', + 'RDATE' => '*', + ]; + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on) + * 2 - An inconsequential issue + * 3 - A severe issue. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $result = parent::validate($options); + if (isset($this->DUE) && isset($this->DTSTART)) { + + $due = $this->DUE; + $dtStart = $this->DTSTART; + + if ($due->getValueType() !== $dtStart->getValueType()) { + + $result[] = [ + 'level' => 3, + 'message' => 'The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART', + 'node' => $due, + ]; + + } elseif ($due->getDateTime() < $dtStart->getDateTime()) { + + $result[] = [ + 'level' => 3, + 'message' => 'DUE must occur after DTSTART', + 'node' => $due, + ]; + + } + + } + + return $result; + + } + + /** + * This method should return a list of default property values. + * + * @return array + */ + protected function getDefaults() { + + return [ + 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(), + 'DTSTAMP' => date('Ymd\\THis\\Z'), + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/DateTimeParser.php b/htdocs/includes/sabre/sabre/vobject/lib/DateTimeParser.php new file mode 100644 index 00000000000..f9a802d2533 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/DateTimeParser.php @@ -0,0 +1,580 @@ +<?php + +namespace Sabre\VObject; + +use DateInterval; +use DateTimeImmutable; +use DateTimeZone; + +/** + * DateTimeParser. + * + * This class is responsible for parsing the several different date and time + * formats iCalendar and vCards have. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class DateTimeParser { + + /** + * Parses an iCalendar (rfc5545) formatted datetime and returns a + * DateTimeImmutable object. + * + * Specifying a reference timezone is optional. It will only be used + * if the non-UTC format is used. The argument is used as a reference, the + * returned DateTimeImmutable object will still be in the UTC timezone. + * + * @param string $dt + * @param DateTimeZone $tz + * + * @return DateTimeImmutable + */ + static function parseDateTime($dt, DateTimeZone $tz = null) { + + // Format is YYYYMMDD + "T" + hhmmss + $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/', $dt, $matches); + + if (!$result) { + throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: ' . $dt); + } + + if ($matches[7] === 'Z' || is_null($tz)) { + $tz = new DateTimeZone('UTC'); + } + + try { + $date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] . ':' . $matches[6], $tz); + } catch (\Exception $e) { + throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: ' . $dt); + } + + return $date; + + } + + /** + * Parses an iCalendar (rfc5545) formatted date and returns a DateTimeImmutable object. + * + * @param string $date + * @param DateTimeZone $tz + * + * @return DateTimeImmutable + */ + static function parseDate($date, DateTimeZone $tz = null) { + + // Format is YYYYMMDD + $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/', $date, $matches); + + if (!$result) { + throw new InvalidDataException('The supplied iCalendar date value is incorrect: ' . $date); + } + + if (is_null($tz)) { + $tz = new DateTimeZone('UTC'); + } + + try { + $date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3], $tz); + } catch (\Exception $e) { + throw new InvalidDataException('The supplied iCalendar date value is incorrect: ' . $date); + } + + return $date; + + } + + /** + * Parses an iCalendar (RFC5545) formatted duration value. + * + * This method will either return a DateTimeInterval object, or a string + * suitable for strtotime or DateTime::modify. + * + * @param string $duration + * @param bool $asString + * + * @return DateInterval|string + */ + static function parseDuration($duration, $asString = false) { + + $result = preg_match('/^(?<plusminus>\+|-)?P((?<week>\d+)W)?((?<day>\d+)D)?(T((?<hour>\d+)H)?((?<minute>\d+)M)?((?<second>\d+)S)?)?$/', $duration, $matches); + if (!$result) { + throw new InvalidDataException('The supplied iCalendar duration value is incorrect: ' . $duration); + } + + if (!$asString) { + + $invert = false; + + if ($matches['plusminus'] === '-') { + $invert = true; + } + + $parts = [ + 'week', + 'day', + 'hour', + 'minute', + 'second', + ]; + + foreach ($parts as $part) { + $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int)$matches[$part] : 0; + } + + // We need to re-construct the $duration string, because weeks and + // days are not supported by DateInterval in the same string. + $duration = 'P'; + $days = $matches['day']; + + if ($matches['week']) { + $days += $matches['week'] * 7; + } + + if ($days) { + $duration .= $days . 'D'; + } + + if ($matches['minute'] || $matches['second'] || $matches['hour']) { + + $duration .= 'T'; + + if ($matches['hour']) { + $duration .= $matches['hour'] . 'H'; + } + + if ($matches['minute']) { + $duration .= $matches['minute'] . 'M'; + } + + if ($matches['second']) { + $duration .= $matches['second'] . 'S'; + } + + } + + if ($duration === 'P') { + $duration = 'PT0S'; + } + + $iv = new DateInterval($duration); + + if ($invert) { + $iv->invert = true; + } + + return $iv; + + } + + $parts = [ + 'week', + 'day', + 'hour', + 'minute', + 'second', + ]; + + $newDur = ''; + + foreach ($parts as $part) { + if (isset($matches[$part]) && $matches[$part]) { + $newDur .= ' ' . $matches[$part] . ' ' . $part . 's'; + } + } + + $newDur = ($matches['plusminus'] === '-' ? '-' : '+') . trim($newDur); + + if ($newDur === '+') { + $newDur = '+0 seconds'; + }; + + return $newDur; + + } + + /** + * Parses either a Date or DateTime, or Duration value. + * + * @param string $date + * @param DateTimeZone|string $referenceTz + * + * @return DateTimeImmutable|DateInterval + */ + static function parse($date, $referenceTz = null) { + + if ($date[0] === 'P' || ($date[0] === '-' && $date[1] === 'P')) { + return self::parseDuration($date); + } elseif (strlen($date) === 8) { + return self::parseDate($date, $referenceTz); + } else { + return self::parseDateTime($date, $referenceTz); + } + + } + + /** + * This method parses a vCard date and or time value. + * + * This can be used for the DATE, DATE-TIME, TIMESTAMP and + * DATE-AND-OR-TIME value. + * + * This method returns an array, not a DateTime value. + * + * The elements in the array are in the following order: + * year, month, date, hour, minute, second, timezone + * + * Almost any part of the string may be omitted. It's for example legal to + * just specify seconds, leave out the year, etc. + * + * Timezone is either returned as 'Z' or as '+0800' + * + * For any non-specified values null is returned. + * + * List of date formats that are supported: + * YYYY + * YYYY-MM + * YYYYMMDD + * --MMDD + * ---DD + * + * YYYY-MM-DD + * --MM-DD + * ---DD + * + * List of supported time formats: + * + * HH + * HHMM + * HHMMSS + * -MMSS + * --SS + * + * HH + * HH:MM + * HH:MM:SS + * -MM:SS + * --SS + * + * A full basic-format date-time string looks like : + * 20130603T133901 + * + * A full extended-format date-time string looks like : + * 2013-06-03T13:39:01 + * + * Times may be postfixed by a timezone offset. This can be either 'Z' for + * UTC, or a string like -0500 or +1100. + * + * @param string $date + * + * @return array + */ + static function parseVCardDateTime($date) { + + $regex = '/^ + (?: # date part + (?: + (?: (?<year> [0-9]{4}) (?: -)?| --) + (?<month> [0-9]{2})? + |---) + (?<date> [0-9]{2})? + )? + (?:T # time part + (?<hour> [0-9]{2} | -) + (?<minute> [0-9]{2} | -)? + (?<second> [0-9]{2})? + + (?: \.[0-9]{3})? # milliseconds + (?P<timezone> # timezone offset + + Z | (?: \+|-)(?: [0-9]{4}) + + )? + + )? + $/x'; + + if (!preg_match($regex, $date, $matches)) { + + // Attempting to parse the extended format. + $regex = '/^ + (?: # date part + (?: (?<year> [0-9]{4}) - | -- ) + (?<month> [0-9]{2}) - + (?<date> [0-9]{2}) + )? + (?:T # time part + + (?: (?<hour> [0-9]{2}) : | -) + (?: (?<minute> [0-9]{2}) : | -)? + (?<second> [0-9]{2})? + + (?: \.[0-9]{3})? # milliseconds + (?P<timezone> # timezone offset + + Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2}) + + )? + + )? + $/x'; + + if (!preg_match($regex, $date, $matches)) { + throw new InvalidDataException('Invalid vCard date-time string: ' . $date); + } + + } + $parts = [ + 'year', + 'month', + 'date', + 'hour', + 'minute', + 'second', + 'timezone' + ]; + + $result = []; + foreach ($parts as $part) { + + if (empty($matches[$part])) { + $result[$part] = null; + } elseif ($matches[$part] === '-' || $matches[$part] === '--') { + $result[$part] = null; + } else { + $result[$part] = $matches[$part]; + } + + } + + return $result; + + } + + /** + * This method parses a vCard TIME value. + * + * This method returns an array, not a DateTime value. + * + * The elements in the array are in the following order: + * hour, minute, second, timezone + * + * Almost any part of the string may be omitted. It's for example legal to + * just specify seconds, leave out the hour etc. + * + * Timezone is either returned as 'Z' or as '+08:00' + * + * For any non-specified values null is returned. + * + * List of supported time formats: + * + * HH + * HHMM + * HHMMSS + * -MMSS + * --SS + * + * HH + * HH:MM + * HH:MM:SS + * -MM:SS + * --SS + * + * A full basic-format time string looks like : + * 133901 + * + * A full extended-format time string looks like : + * 13:39:01 + * + * Times may be postfixed by a timezone offset. This can be either 'Z' for + * UTC, or a string like -0500 or +11:00. + * + * @param string $date + * + * @return array + */ + static function parseVCardTime($date) { + + $regex = '/^ + (?<hour> [0-9]{2} | -) + (?<minute> [0-9]{2} | -)? + (?<second> [0-9]{2})? + + (?: \.[0-9]{3})? # milliseconds + (?P<timezone> # timezone offset + + Z | (?: \+|-)(?: [0-9]{4}) + + )? + $/x'; + + + if (!preg_match($regex, $date, $matches)) { + + // Attempting to parse the extended format. + $regex = '/^ + (?: (?<hour> [0-9]{2}) : | -) + (?: (?<minute> [0-9]{2}) : | -)? + (?<second> [0-9]{2})? + + (?: \.[0-9]{3})? # milliseconds + (?P<timezone> # timezone offset + + Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2}) + + )? + $/x'; + + if (!preg_match($regex, $date, $matches)) { + throw new InvalidDataException('Invalid vCard time string: ' . $date); + } + + } + $parts = [ + 'hour', + 'minute', + 'second', + 'timezone' + ]; + + $result = []; + foreach ($parts as $part) { + + if (empty($matches[$part])) { + $result[$part] = null; + } elseif ($matches[$part] === '-') { + $result[$part] = null; + } else { + $result[$part] = $matches[$part]; + } + + } + + return $result; + + } + + /** + * This method parses a vCard date and or time value. + * + * This can be used for the DATE, DATE-TIME and + * DATE-AND-OR-TIME value. + * + * This method returns an array, not a DateTime value. + * The elements in the array are in the following order: + * year, month, date, hour, minute, second, timezone + * Almost any part of the string may be omitted. It's for example legal to + * just specify seconds, leave out the year, etc. + * + * Timezone is either returned as 'Z' or as '+0800' + * + * For any non-specified values null is returned. + * + * List of date formats that are supported: + * 20150128 + * 2015-01 + * --01 + * --0128 + * ---28 + * + * List of supported time formats: + * 13 + * 1353 + * 135301 + * -53 + * -5301 + * --01 (unreachable, see the tests) + * --01Z + * --01+1234 + * + * List of supported date-time formats: + * 20150128T13 + * --0128T13 + * ---28T13 + * ---28T1353 + * ---28T135301 + * ---28T13Z + * ---28T13+1234 + * + * See the regular expressions for all the possible patterns. + * + * Times may be postfixed by a timezone offset. This can be either 'Z' for + * UTC, or a string like -0500 or +1100. + * + * @param string $date + * + * @return array + */ + static function parseVCardDateAndOrTime($date) { + + // \d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d + $valueDate = '/^(?J)(?:' . + '(?<year>\d{4})(?<month>\d\d)(?<date>\d\d)' . + '|(?<year>\d{4})-(?<month>\d\d)' . + '|--(?<month>\d\d)(?<date>\d\d)?' . + '|---(?<date>\d\d)' . + ')$/'; + + // (\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)(Z|[+\-]\d\d(\d\d)?)? + $valueTime = '/^(?J)(?:' . + '((?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' . + '|-(?<minute>\d\d)(?<second>\d\d)?' . + '|--(?<second>\d\d))' . + '(?<timezone>(Z|[+\-]\d\d(\d\d)?))?' . + ')$/'; + + // (\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?(Z|[+\-]\d\d(\d\d?)? + $valueDateTime = '/^(?:' . + '((?<year0>\d{4})(?<month0>\d\d)(?<date0>\d\d)' . + '|--(?<month1>\d\d)(?<date1>\d\d)' . + '|---(?<date2>\d\d))' . + 'T' . + '(?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' . + '(?<timezone>(Z|[+\-]\d\d(\d\d?)))?' . + ')$/'; + + // date-and-or-time is date | date-time | time + // in this strict order. + + if (0 === preg_match($valueDate, $date, $matches) + && 0 === preg_match($valueDateTime, $date, $matches) + && 0 === preg_match($valueTime, $date, $matches)) { + throw new InvalidDataException('Invalid vCard date-time string: ' . $date); + } + + $parts = [ + 'year' => null, + 'month' => null, + 'date' => null, + 'hour' => null, + 'minute' => null, + 'second' => null, + 'timezone' => null + ]; + + // The $valueDateTime expression has a bug with (?J) so we simulate it. + $parts['date0'] = &$parts['date']; + $parts['date1'] = &$parts['date']; + $parts['date2'] = &$parts['date']; + $parts['month0'] = &$parts['month']; + $parts['month1'] = &$parts['month']; + $parts['year0'] = &$parts['year']; + + foreach ($parts as $part => &$value) { + if (!empty($matches[$part])) { + $value = $matches[$part]; + } + } + + unset($parts['date0']); + unset($parts['date1']); + unset($parts['date2']); + unset($parts['month0']); + unset($parts['month1']); + unset($parts['year0']); + + return $parts; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Document.php b/htdocs/includes/sabre/sabre/vobject/lib/Document.php new file mode 100644 index 00000000000..03252ab06a2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Document.php @@ -0,0 +1,270 @@ +<?php + +namespace Sabre\VObject; + +/** + * Document. + * + * A document is just like a component, except that it's also the top level + * element. + * + * Both a VCALENDAR and a VCARD are considered documents. + * + * This class also provides a registry for document types. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class Document extends Component { + + /** + * Unknown document type. + */ + const UNKNOWN = 1; + + /** + * vCalendar 1.0. + */ + const VCALENDAR10 = 2; + + /** + * iCalendar 2.0. + */ + const ICALENDAR20 = 3; + + /** + * vCard 2.1. + */ + const VCARD21 = 4; + + /** + * vCard 3.0. + */ + const VCARD30 = 5; + + /** + * vCard 4.0. + */ + const VCARD40 = 6; + + /** + * The default name for this component. + * + * This should be 'VCALENDAR' or 'VCARD'. + * + * @var string + */ + static $defaultName; + + /** + * List of properties, and which classes they map to. + * + * @var array + */ + static $propertyMap = []; + + /** + * List of components, along with which classes they map to. + * + * @var array + */ + static $componentMap = []; + + /** + * List of value-types, and which classes they map to. + * + * @var array + */ + static $valueMap = []; + + /** + * Creates a new document. + * + * We're changing the default behavior slightly here. First, we don't want + * to have to specify a name (we already know it), and we want to allow + * children to be specified in the first argument. + * + * But, the default behavior also works. + * + * So the two sigs: + * + * new Document(array $children = [], $defaults = true); + * new Document(string $name, array $children = [], $defaults = true) + * + * @return void + */ + function __construct() { + + $args = func_get_args(); + if (count($args) === 0 || is_array($args[0])) { + array_unshift($args, $this, static::$defaultName); + call_user_func_array(['parent', '__construct'], $args); + } else { + array_unshift($args, $this); + call_user_func_array(['parent', '__construct'], $args); + } + + } + + /** + * Returns the current document type. + * + * @return int + */ + function getDocumentType() { + + return self::UNKNOWN; + + } + + /** + * Creates a new component or property. + * + * If it's a known component, we will automatically call createComponent. + * otherwise, we'll assume it's a property and call createProperty instead. + * + * @param string $name + * @param string $arg1,... Unlimited number of args + * + * @return mixed + */ + function create($name) { + + if (isset(static::$componentMap[strtoupper($name)])) { + + return call_user_func_array([$this, 'createComponent'], func_get_args()); + + } else { + + return call_user_func_array([$this, 'createProperty'], func_get_args()); + + } + + } + + /** + * Creates a new component. + * + * This method automatically searches for the correct component class, based + * on its name. + * + * You can specify the children either in key=>value syntax, in which case + * properties will automatically be created, or you can just pass a list of + * Component and Property object. + * + * By default, a set of sensible values will be added to the component. For + * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To + * ensure that this does not happen, set $defaults to false. + * + * @param string $name + * @param array $children + * @param bool $defaults + * + * @return Component + */ + function createComponent($name, array $children = null, $defaults = true) { + + $name = strtoupper($name); + $class = 'Sabre\\VObject\\Component'; + + if (isset(static::$componentMap[$name])) { + $class = static::$componentMap[$name]; + } + if (is_null($children)) $children = []; + return new $class($this, $name, $children, $defaults); + + } + + /** + * Factory method for creating new properties. + * + * This method automatically searches for the correct property class, based + * on its name. + * + * You can specify the parameters either in key=>value syntax, in which case + * parameters will automatically be created, or you can just pass a list of + * Parameter objects. + * + * @param string $name + * @param mixed $value + * @param array $parameters + * @param string $valueType Force a specific valuetype, such as URI or TEXT + * + * @return Property + */ + function createProperty($name, $value = null, array $parameters = null, $valueType = null) { + + // If there's a . in the name, it means it's prefixed by a groupname. + if (($i = strpos($name, '.')) !== false) { + $group = substr($name, 0, $i); + $name = strtoupper(substr($name, $i + 1)); + } else { + $name = strtoupper($name); + $group = null; + } + + $class = null; + + if ($valueType) { + // The valueType argument comes first to figure out the correct + // class. + $class = $this->getClassNameForPropertyValue($valueType); + } + + if (is_null($class)) { + // If a VALUE parameter is supplied, we should use that. + if (isset($parameters['VALUE'])) { + $class = $this->getClassNameForPropertyValue($parameters['VALUE']); + if (is_null($class)) { + throw new InvalidDataException('Unsupported VALUE parameter for ' . $name . ' property. You supplied "' . $parameters['VALUE'] . '"'); + } + } + else { + $class = $this->getClassNameForPropertyName($name); + } + } + if (is_null($parameters)) $parameters = []; + + return new $class($this, $name, $value, $parameters, $group); + + } + + /** + * This method returns a full class-name for a value parameter. + * + * For instance, DTSTART may have VALUE=DATE. In that case we will look in + * our valueMap table and return the appropriate class name. + * + * This method returns null if we don't have a specialized class. + * + * @param string $valueParam + * @return string|null + */ + function getClassNameForPropertyValue($valueParam) { + + $valueParam = strtoupper($valueParam); + if (isset(static::$valueMap[$valueParam])) { + return static::$valueMap[$valueParam]; + } + + } + + /** + * Returns the default class for a property name. + * + * @param string $propertyName + * + * @return string + */ + function getClassNameForPropertyName($propertyName) { + + if (isset(static::$propertyMap[$propertyName])) { + return static::$propertyMap[$propertyName]; + } else { + return 'Sabre\\VObject\\Property\\Unknown'; + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/ElementList.php b/htdocs/includes/sabre/sabre/vobject/lib/ElementList.php new file mode 100644 index 00000000000..95924924702 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/ElementList.php @@ -0,0 +1,54 @@ +<?php + +namespace Sabre\VObject; + +use ArrayIterator; +use LogicException; + +/** + * VObject ElementList. + * + * This class represents a list of elements. Lists are the result of queries, + * such as doing $vcalendar->vevent where there's multiple VEVENT objects. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ElementList extends ArrayIterator { + + + /* {{{ ArrayAccess Interface */ + + /** + * Sets an item through ArrayAccess. + * + * @param int $offset + * @param mixed $value + * + * @return void + */ + function offsetSet($offset, $value) { + + throw new LogicException('You can not add new objects to an ElementList'); + + } + + /** + * Sets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * + * @return void + */ + function offsetUnset($offset) { + + throw new LogicException('You can not remove objects from an ElementList'); + + } + + /* }}} */ + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/EofException.php b/htdocs/includes/sabre/sabre/vobject/lib/EofException.php new file mode 100644 index 00000000000..e9bd5587835 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/EofException.php @@ -0,0 +1,15 @@ +<?php + +namespace Sabre\VObject; + +/** + * Exception thrown by parser when the end of the stream has been reached, + * before this was expected. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class EofException extends ParseException { + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/FreeBusyData.php b/htdocs/includes/sabre/sabre/vobject/lib/FreeBusyData.php new file mode 100644 index 00000000000..0a6c72bb230 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/FreeBusyData.php @@ -0,0 +1,193 @@ +<?php + +namespace Sabre\VObject; + +/** + * FreeBusyData is a helper class that manages freebusy information. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class FreeBusyData { + + /** + * Start timestamp + * + * @var int + */ + protected $start; + + /** + * End timestamp + * + * @var int + */ + protected $end; + + /** + * A list of free-busy times. + * + * @var array + */ + protected $data; + + function __construct($start, $end) { + + $this->start = $start; + $this->end = $end; + $this->data = []; + + $this->data[] = [ + 'start' => $this->start, + 'end' => $this->end, + 'type' => 'FREE', + ]; + + } + + /** + * Adds free or busytime to the data. + * + * @param int $start + * @param int $end + * @param string $type FREE, BUSY, BUSY-UNAVAILABLE or BUSY-TENTATIVE + * @return void + */ + function add($start, $end, $type) { + + if ($start > $this->end || $end < $this->start) { + + // This new data is outside our timerange. + return; + + } + + if ($start < $this->start) { + // The item starts before our requested time range + $start = $this->start; + } + if ($end > $this->end) { + // The item ends after our requested time range + $end = $this->end; + } + + // Finding out where we need to insert the new item. + $currentIndex = 0; + while ($start > $this->data[$currentIndex]['end']) { + $currentIndex++; + } + + // The standard insertion point will be one _after_ the first + // overlapping item. + $insertStartIndex = $currentIndex + 1; + + $newItem = [ + 'start' => $start, + 'end' => $end, + 'type' => $type, + ]; + + $preceedingItem = $this->data[$insertStartIndex - 1]; + if ($this->data[$insertStartIndex - 1]['start'] === $start) { + // The old item starts at the exact same point as the new item. + $insertStartIndex--; + } + + // Now we know where to insert the item, we need to know where it + // starts overlapping with items on the tail end. We need to start + // looking one item before the insertStartIndex, because it's possible + // that the new item 'sits inside' the previous old item. + if ($insertStartIndex > 0) { + $currentIndex = $insertStartIndex - 1; + } else { + $currentIndex = 0; + } + + while ($end > $this->data[$currentIndex]['end']) { + + $currentIndex++; + + } + + // What we are about to insert into the array + $newItems = [ + $newItem + ]; + + // This is the amount of items that are completely overwritten by the + // new item. + $itemsToDelete = $currentIndex - $insertStartIndex; + if ($this->data[$currentIndex]['end'] <= $end) $itemsToDelete++; + + // If itemsToDelete was -1, it means that the newly inserted item is + // actually sitting inside an existing one. This means we need to split + // the item at the current position in two and insert the new item in + // between. + if ($itemsToDelete === -1) { + $itemsToDelete = 0; + if ($newItem['end'] < $preceedingItem['end']) { + $newItems[] = [ + 'start' => $newItem['end'] + 1, + 'end' => $preceedingItem['end'], + 'type' => $preceedingItem['type'] + ]; + } + } + + array_splice( + $this->data, + $insertStartIndex, + $itemsToDelete, + $newItems + ); + + $doMerge = false; + $mergeOffset = $insertStartIndex; + $mergeItem = $newItem; + $mergeDelete = 1; + + if (isset($this->data[$insertStartIndex - 1])) { + // Updating the start time of the previous item. + $this->data[$insertStartIndex - 1]['end'] = $start; + + // If the previous and the current are of the same type, we can + // merge them into one item. + if ($this->data[$insertStartIndex - 1]['type'] === $this->data[$insertStartIndex]['type']) { + $doMerge = true; + $mergeOffset--; + $mergeDelete++; + $mergeItem['start'] = $this->data[$insertStartIndex - 1]['start']; + } + } + if (isset($this->data[$insertStartIndex + 1])) { + // Updating the start time of the next item. + $this->data[$insertStartIndex + 1]['start'] = $end; + + // If the next and the current are of the same type, we can + // merge them into one item. + if ($this->data[$insertStartIndex + 1]['type'] === $this->data[$insertStartIndex]['type']) { + $doMerge = true; + $mergeDelete++; + $mergeItem['end'] = $this->data[$insertStartIndex + 1]['end']; + } + + } + if ($doMerge) { + array_splice( + $this->data, + $mergeOffset, + $mergeDelete, + [$mergeItem] + ); + } + + } + + function getData() { + + return $this->data; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/FreeBusyGenerator.php b/htdocs/includes/sabre/sabre/vobject/lib/FreeBusyGenerator.php new file mode 100644 index 00000000000..e30b136c43c --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/FreeBusyGenerator.php @@ -0,0 +1,604 @@ +<?php + +namespace Sabre\VObject; + +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Recur\EventIterator; +use Sabre\VObject\Recur\NoInstancesException; + +/** + * This class helps with generating FREEBUSY reports based on existing sets of + * objects. + * + * It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and + * generates a single VFREEBUSY object. + * + * VFREEBUSY components are described in RFC5545, The rules for what should + * go in a single freebusy report is taken from RFC4791, section 7.10. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class FreeBusyGenerator { + + /** + * Input objects. + * + * @var array + */ + protected $objects = []; + + /** + * Start of range. + * + * @var DateTimeInterface|null + */ + protected $start; + + /** + * End of range. + * + * @var DateTimeInterface|null + */ + protected $end; + + /** + * VCALENDAR object. + * + * @var Document + */ + protected $baseObject; + + /** + * Reference timezone. + * + * When we are calculating busy times, and we come across so-called + * floating times (times without a timezone), we use the reference timezone + * instead. + * + * This is also used for all-day events. + * + * This defaults to UTC. + * + * @var DateTimeZone + */ + protected $timeZone; + + /** + * A VAVAILABILITY document. + * + * If this is set, it's information will be included when calculating + * freebusy time. + * + * @var Document + */ + protected $vavailability; + + /** + * Creates the generator. + * + * Check the setTimeRange and setObjects methods for details about the + * arguments. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * @param mixed $objects + * @param DateTimeZone $timeZone + */ + function __construct(DateTimeInterface $start = null, DateTimeInterface $end = null, $objects = null, DateTimeZone $timeZone = null) { + + $this->setTimeRange($start, $end); + + if ($objects) { + $this->setObjects($objects); + } + if (is_null($timeZone)) { + $timeZone = new DateTimeZone('UTC'); + } + $this->setTimeZone($timeZone); + + } + + /** + * Sets the VCALENDAR object. + * + * If this is set, it will not be generated for you. You are responsible + * for setting things like the METHOD, CALSCALE, VERSION, etc.. + * + * The VFREEBUSY object will be automatically added though. + * + * @param Document $vcalendar + * @return void + */ + function setBaseObject(Document $vcalendar) { + + $this->baseObject = $vcalendar; + + } + + /** + * Sets a VAVAILABILITY document. + * + * @param Document $vcalendar + * @return void + */ + function setVAvailability(Document $vcalendar) { + + $this->vavailability = $vcalendar; + + } + + /** + * Sets the input objects. + * + * You must either specify a valendar object as a string, or as the parse + * Component. + * It's also possible to specify multiple objects as an array. + * + * @param mixed $objects + * + * @return void + */ + function setObjects($objects) { + + if (!is_array($objects)) { + $objects = [$objects]; + } + + $this->objects = []; + foreach ($objects as $object) { + + if (is_string($object) || is_resource($object)) { + $this->objects[] = Reader::read($object); + } elseif ($object instanceof Component) { + $this->objects[] = $object; + } else { + throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects'); + } + + } + + } + + /** + * Sets the time range. + * + * Any freebusy object falling outside of this time range will be ignored. + * + * @param DateTimeInterface $start + * @param DateTimeInterface $end + * + * @return void + */ + function setTimeRange(DateTimeInterface $start = null, DateTimeInterface $end = null) { + + if (!$start) { + $start = new DateTimeImmutable(Settings::$minDate); + } + if (!$end) { + $end = new DateTimeImmutable(Settings::$maxDate); + } + $this->start = $start; + $this->end = $end; + + } + + /** + * Sets the reference timezone for floating times. + * + * @param DateTimeZone $timeZone + * + * @return void + */ + function setTimeZone(DateTimeZone $timeZone) { + + $this->timeZone = $timeZone; + + } + + /** + * Parses the input data and returns a correct VFREEBUSY object, wrapped in + * a VCALENDAR. + * + * @return Component + */ + function getResult() { + + $fbData = new FreeBusyData( + $this->start->getTimeStamp(), + $this->end->getTimeStamp() + ); + if ($this->vavailability) { + + $this->calculateAvailability($fbData, $this->vavailability); + + } + + $this->calculateBusy($fbData, $this->objects); + + return $this->generateFreeBusyCalendar($fbData); + + + } + + /** + * This method takes a VAVAILABILITY component and figures out all the + * available times. + * + * @param FreeBusyData $fbData + * @param VCalendar $vavailability + * @return void + */ + protected function calculateAvailability(FreeBusyData $fbData, VCalendar $vavailability) { + + $vavailComps = iterator_to_array($vavailability->VAVAILABILITY); + usort( + $vavailComps, + function($a, $b) { + + // We need to order the components by priority. Priority 1 + // comes first, up until priority 9. Priority 0 comes after + // priority 9. No priority implies priority 0. + // + // Yes, I'm serious. + $priorityA = isset($a->PRIORITY) ? (int)$a->PRIORITY->getValue() : 0; + $priorityB = isset($b->PRIORITY) ? (int)$b->PRIORITY->getValue() : 0; + + if ($priorityA === 0) $priorityA = 10; + if ($priorityB === 0) $priorityB = 10; + + return $priorityA - $priorityB; + + } + ); + + // Now we go over all the VAVAILABILITY components and figure if + // there's any we don't need to consider. + // + // This is can be because of one of two reasons: either the + // VAVAILABILITY component falls outside the time we are interested in, + // or a different VAVAILABILITY component with a higher priority has + // already completely covered the time-range. + $old = $vavailComps; + $new = []; + + foreach ($old as $vavail) { + + list($compStart, $compEnd) = $vavail->getEffectiveStartEnd(); + + // We don't care about datetimes that are earlier or later than the + // start and end of the freebusy report, so this gets normalized + // first. + if (is_null($compStart) || $compStart < $this->start) { + $compStart = $this->start; + } + if (is_null($compEnd) || $compEnd > $this->end) { + $compEnd = $this->end; + } + + // If the item fell out of the timerange, we can just skip it. + if ($compStart > $this->end || $compEnd < $this->start) { + continue; + } + + // Going through our existing list of components to see if there's + // a higher priority component that already fully covers this one. + foreach ($new as $higherVavail) { + + list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd(); + if ( + (is_null($higherStart) || $higherStart < $compStart) && + (is_null($higherEnd) || $higherEnd > $compEnd) + ) { + + // Component is fully covered by a higher priority + // component. We can skip this component. + continue 2; + + } + + } + + // We're keeping it! + $new[] = $vavail; + + } + + // Lastly, we need to traverse the remaining components and fill in the + // freebusydata slots. + // + // We traverse the components in reverse, because we want the higher + // priority components to override the lower ones. + foreach (array_reverse($new) as $vavail) { + + $busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE'; + list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd(); + + // Making the component size no larger than the requested free-busy + // report range. + if (!$vavailStart || $vavailStart < $this->start) { + $vavailStart = $this->start; + } + if (!$vavailEnd || $vavailEnd > $this->end) { + $vavailEnd = $this->end; + } + + // Marking the entire time range of the VAVAILABILITY component as + // busy. + $fbData->add( + $vavailStart->getTimeStamp(), + $vavailEnd->getTimeStamp(), + $busyType + ); + + // Looping over the AVAILABLE components. + if (isset($vavail->AVAILABLE)) foreach ($vavail->AVAILABLE as $available) { + + list($availStart, $availEnd) = $available->getEffectiveStartEnd(); + $fbData->add( + $availStart->getTimeStamp(), + $availEnd->getTimeStamp(), + 'FREE' + ); + + if ($available->RRULE) { + // Our favourite thing: recurrence!! + + $rruleIterator = new Recur\RRuleIterator( + $available->RRULE->getValue(), + $availStart + ); + $rruleIterator->fastForward($vavailStart); + + $startEndDiff = $availStart->diff($availEnd); + + while ($rruleIterator->valid()) { + + $recurStart = $rruleIterator->current(); + $recurEnd = $recurStart->add($startEndDiff); + + if ($recurStart > $vavailEnd) { + // We're beyond the legal timerange. + break; + } + + if ($recurEnd > $vavailEnd) { + // Truncating the end if it exceeds the + // VAVAILABILITY end. + $recurEnd = $vavailEnd; + } + + $fbData->add( + $recurStart->getTimeStamp(), + $recurEnd->getTimeStamp(), + 'FREE' + ); + + $rruleIterator->next(); + + } + } + + } + + } + + } + + /** + * This method takes an array of iCalendar objects and applies its busy + * times on fbData. + * + * @param FreeBusyData $fbData + * @param VCalendar[] $objects + */ + protected function calculateBusy(FreeBusyData $fbData, array $objects) { + + foreach ($objects as $key => $object) { + + foreach ($object->getBaseComponents() as $component) { + + switch ($component->name) { + + case 'VEVENT' : + + $FBTYPE = 'BUSY'; + if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) { + break; + } + if (isset($component->STATUS)) { + $status = strtoupper($component->STATUS); + if ($status === 'CANCELLED') { + break; + } + if ($status === 'TENTATIVE') { + $FBTYPE = 'BUSY-TENTATIVE'; + } + } + + $times = []; + + if ($component->RRULE) { + try { + $iterator = new EventIterator($object, (string)$component->UID, $this->timeZone); + } catch (NoInstancesException $e) { + // This event is recurring, but it doesn't have a single + // instance. We are skipping this event from the output + // entirely. + unset($this->objects[$key]); + continue; + } + + if ($this->start) { + $iterator->fastForward($this->start); + } + + $maxRecurrences = Settings::$maxRecurrences; + + while ($iterator->valid() && --$maxRecurrences) { + + $startTime = $iterator->getDTStart(); + if ($this->end && $startTime > $this->end) { + break; + } + $times[] = [ + $iterator->getDTStart(), + $iterator->getDTEnd(), + ]; + + $iterator->next(); + + } + + } else { + + $startTime = $component->DTSTART->getDateTime($this->timeZone); + if ($this->end && $startTime > $this->end) { + break; + } + $endTime = null; + if (isset($component->DTEND)) { + $endTime = $component->DTEND->getDateTime($this->timeZone); + } elseif (isset($component->DURATION)) { + $duration = DateTimeParser::parseDuration((string)$component->DURATION); + $endTime = clone $startTime; + $endTime = $endTime->add($duration); + } elseif (!$component->DTSTART->hasTime()) { + $endTime = clone $startTime; + $endTime = $endTime->modify('+1 day'); + } else { + // The event had no duration (0 seconds) + break; + } + + $times[] = [$startTime, $endTime]; + + } + + foreach ($times as $time) { + + if ($this->end && $time[0] > $this->end) break; + if ($this->start && $time[1] < $this->start) break; + + $fbData->add( + $time[0]->getTimeStamp(), + $time[1]->getTimeStamp(), + $FBTYPE + ); + } + break; + + case 'VFREEBUSY' : + foreach ($component->FREEBUSY as $freebusy) { + + $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY'; + + // Skipping intervals marked as 'free' + if ($fbType === 'FREE') + continue; + + $values = explode(',', $freebusy); + foreach ($values as $value) { + list($startTime, $endTime) = explode('/', $value); + $startTime = DateTimeParser::parseDateTime($startTime); + + if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') { + $duration = DateTimeParser::parseDuration($endTime); + $endTime = clone $startTime; + $endTime = $endTime->add($duration); + } else { + $endTime = DateTimeParser::parseDateTime($endTime); + } + + if ($this->start && $this->start > $endTime) continue; + if ($this->end && $this->end < $startTime) continue; + $fbData->add( + $startTime->getTimeStamp(), + $endTime->getTimeStamp(), + $fbType + ); + + } + + + } + break; + + } + + + } + + } + + } + + /** + * This method takes a FreeBusyData object and generates the VCALENDAR + * object associated with it. + * + * @return VCalendar + */ + protected function generateFreeBusyCalendar(FreeBusyData $fbData) { + + if ($this->baseObject) { + $calendar = $this->baseObject; + } else { + $calendar = new VCalendar(); + } + + $vfreebusy = $calendar->createComponent('VFREEBUSY'); + $calendar->add($vfreebusy); + + if ($this->start) { + $dtstart = $calendar->createProperty('DTSTART'); + $dtstart->setDateTime($this->start); + $vfreebusy->add($dtstart); + } + if ($this->end) { + $dtend = $calendar->createProperty('DTEND'); + $dtend->setDateTime($this->end); + $vfreebusy->add($dtend); + } + + $tz = new \DateTimeZone('UTC'); + $dtstamp = $calendar->createProperty('DTSTAMP'); + $dtstamp->setDateTime(new DateTimeImmutable('now', $tz)); + $vfreebusy->add($dtstamp); + + foreach ($fbData->getData() as $busyTime) { + + $busyType = strtoupper($busyTime['type']); + + // Ignoring all the FREE parts, because those are already assumed. + if ($busyType === 'FREE') { + continue; + } + + $busyTime[0] = new \DateTimeImmutable('@' . $busyTime['start'], $tz); + $busyTime[1] = new \DateTimeImmutable('@' . $busyTime['end'], $tz); + + $prop = $calendar->createProperty( + 'FREEBUSY', + $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z') + ); + + // Only setting FBTYPE if it's not BUSY, because BUSY is the + // default anyway. + if ($busyType !== 'BUSY') { + $prop['FBTYPE'] = $busyType; + } + $vfreebusy->add($prop); + + } + + return $calendar; + + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/ITip/Broker.php b/htdocs/includes/sabre/sabre/vobject/lib/ITip/Broker.php new file mode 100644 index 00000000000..effa7431793 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/ITip/Broker.php @@ -0,0 +1,989 @@ +<?php + +namespace Sabre\VObject\ITip; + +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\Reader; +use Sabre\VObject\Recur\EventIterator; + +/** + * The ITip\Broker class is a utility class that helps with processing + * so-called iTip messages. + * + * iTip is defined in rfc5546, stands for iCalendar Transport-Independent + * Interoperability Protocol, and describes the underlying mechanism for + * using iCalendar for scheduling for for example through email (also known as + * IMip) and CalDAV Scheduling. + * + * This class helps by: + * + * 1. Creating individual invites based on an iCalendar event for each + * attendee. + * 2. Generating invite updates based on an iCalendar update. This may result + * in new invites, updates and cancellations for attendees, if that list + * changed. + * 3. On the receiving end, it can create a local iCalendar event based on + * a received invite. + * 4. It can also process an invite update on a local event, ensuring that any + * overridden properties from attendees are retained. + * 5. It can create a accepted or declined iTip reply based on an invite. + * 6. It can process a reply from an invite and update an events attendee + * status based on a reply. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Broker { + + /** + * This setting determines whether the rules for the SCHEDULE-AGENT + * parameter should be followed. + * + * This is a parameter defined on ATTENDEE properties, introduced by RFC + * 6638. This parameter allows a caldav client to tell the server 'Don't do + * any scheduling operations'. + * + * If this setting is turned on, any attendees with SCHEDULE-AGENT set to + * CLIENT will be ignored. This is the desired behavior for a CalDAV + * server, but if you're writing an iTip application that doesn't deal with + * CalDAV, you may want to ignore this parameter. + * + * @var bool + */ + public $scheduleAgentServerRules = true; + + /** + * The broker will try during 'parseEvent' figure out whether the change + * was significant. + * + * It uses a few different ways to do this. One of these ways is seeing if + * certain properties changed values. This list of specified here. + * + * This list is taken from: + * * http://tools.ietf.org/html/rfc5546#section-2.1.4 + * + * @var string[] + */ + public $significantChangeProperties = [ + 'DTSTART', + 'DTEND', + 'DURATION', + 'DUE', + 'RRULE', + 'RDATE', + 'EXDATE', + 'STATUS', + ]; + + /** + * This method is used to process an incoming itip message. + * + * Examples: + * + * 1. A user is an attendee to an event. The organizer sends an updated + * meeting using a new iTip message with METHOD:REQUEST. This function + * will process the message and update the attendee's event accordingly. + * + * 2. The organizer cancelled the event using METHOD:CANCEL. We will update + * the users event to state STATUS:CANCELLED. + * + * 3. An attendee sent a reply to an invite using METHOD:REPLY. We can + * update the organizers event to update the ATTENDEE with its correct + * PARTSTAT. + * + * The $existingObject is updated in-place. If there is no existing object + * (because it's a new invite for example) a new object will be created. + * + * If an existing object does not exist, and the method was CANCEL or + * REPLY, the message effectively gets ignored, and no 'existingObject' + * will be created. + * + * The updated $existingObject is also returned from this function. + * + * If the iTip message was not supported, we will always return false. + * + * @param Message $itipMessage + * @param VCalendar $existingObject + * + * @return VCalendar|null + */ + function processMessage(Message $itipMessage, VCalendar $existingObject = null) { + + // We only support events at the moment. + if ($itipMessage->component !== 'VEVENT') { + return false; + } + + switch ($itipMessage->method) { + + case 'REQUEST' : + return $this->processMessageRequest($itipMessage, $existingObject); + + case 'CANCEL' : + return $this->processMessageCancel($itipMessage, $existingObject); + + case 'REPLY' : + return $this->processMessageReply($itipMessage, $existingObject); + + default : + // Unsupported iTip message + return; + + } + + return $existingObject; + + } + + /** + * This function parses a VCALENDAR object and figure out if any messages + * need to be sent. + * + * A VCALENDAR object will be created from the perspective of either an + * attendee, or an organizer. You must pass a string identifying the + * current user, so we can figure out who in the list of attendees or the + * organizer we are sending this message on behalf of. + * + * It's possible to specify the current user as an array, in case the user + * has more than one identifying href (such as multiple emails). + * + * It $oldCalendar is specified, it is assumed that the operation is + * updating an existing event, which means that we need to look at the + * differences between events, and potentially send old attendees + * cancellations, and current attendees updates. + * + * If $calendar is null, but $oldCalendar is specified, we treat the + * operation as if the user has deleted an event. If the user was an + * organizer, this means that we need to send cancellation notices to + * people. If the user was an attendee, we need to make sure that the + * organizer gets the 'declined' message. + * + * @param VCalendar|string $calendar + * @param string|array $userHref + * @param VCalendar|string $oldCalendar + * + * @return array + */ + function parseEvent($calendar = null, $userHref, $oldCalendar = null) { + + if ($oldCalendar) { + if (is_string($oldCalendar)) { + $oldCalendar = Reader::read($oldCalendar); + } + if (!isset($oldCalendar->VEVENT)) { + // We only support events at the moment + return []; + } + + $oldEventInfo = $this->parseEventInfo($oldCalendar); + } else { + $oldEventInfo = [ + 'organizer' => null, + 'significantChangeHash' => '', + 'attendees' => [], + ]; + } + + $userHref = (array)$userHref; + + if (!is_null($calendar)) { + + if (is_string($calendar)) { + $calendar = Reader::read($calendar); + } + if (!isset($calendar->VEVENT)) { + // We only support events at the moment + return []; + } + $eventInfo = $this->parseEventInfo($calendar); + if (!$eventInfo['attendees'] && !$oldEventInfo['attendees']) { + // If there were no attendees on either side of the equation, + // we don't need to do anything. + return []; + } + if (!$eventInfo['organizer'] && !$oldEventInfo['organizer']) { + // There was no organizer before or after the change. + return []; + } + + $baseCalendar = $calendar; + + // If the new object didn't have an organizer, the organizer + // changed the object from a scheduling object to a non-scheduling + // object. We just copy the info from the old object. + if (!$eventInfo['organizer'] && $oldEventInfo['organizer']) { + $eventInfo['organizer'] = $oldEventInfo['organizer']; + $eventInfo['organizerName'] = $oldEventInfo['organizerName']; + } + + } else { + // The calendar object got deleted, we need to process this as a + // cancellation / decline. + if (!$oldCalendar) { + // No old and no new calendar, there's no thing to do. + return []; + } + + $eventInfo = $oldEventInfo; + + if (in_array($eventInfo['organizer'], $userHref)) { + // This is an organizer deleting the event. + $eventInfo['attendees'] = []; + // Increasing the sequence, but only if the organizer deleted + // the event. + $eventInfo['sequence']++; + } else { + // This is an attendee deleting the event. + foreach ($eventInfo['attendees'] as $key => $attendee) { + if (in_array($attendee['href'], $userHref)) { + $eventInfo['attendees'][$key]['instances'] = ['master' => + ['id' => 'master', 'partstat' => 'DECLINED'] + ]; + } + } + } + $baseCalendar = $oldCalendar; + + } + + if (in_array($eventInfo['organizer'], $userHref)) { + return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo); + } elseif ($oldCalendar) { + // We need to figure out if the user is an attendee, but we're only + // doing so if there's an oldCalendar, because we only want to + // process updates, not creation of new events. + foreach ($eventInfo['attendees'] as $attendee) { + if (in_array($attendee['href'], $userHref)) { + return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']); + } + } + } + return []; + + } + + /** + * Processes incoming REQUEST messages. + * + * This is message from an organizer, and is either a new event + * invite, or an update to an existing one. + * + * + * @param Message $itipMessage + * @param VCalendar $existingObject + * + * @return VCalendar|null + */ + protected function processMessageRequest(Message $itipMessage, VCalendar $existingObject = null) { + + if (!$existingObject) { + // This is a new invite, and we're just going to copy over + // all the components from the invite. + $existingObject = new VCalendar(); + foreach ($itipMessage->message->getComponents() as $component) { + $existingObject->add(clone $component); + } + } else { + // We need to update an existing object with all the new + // information. We can just remove all existing components + // and create new ones. + foreach ($existingObject->getComponents() as $component) { + $existingObject->remove($component); + } + foreach ($itipMessage->message->getComponents() as $component) { + $existingObject->add(clone $component); + } + } + return $existingObject; + + } + + /** + * Processes incoming CANCEL messages. + * + * This is a message from an organizer, and means that either an + * attendee got removed from an event, or an event got cancelled + * altogether. + * + * @param Message $itipMessage + * @param VCalendar $existingObject + * + * @return VCalendar|null + */ + protected function processMessageCancel(Message $itipMessage, VCalendar $existingObject = null) { + + if (!$existingObject) { + // The event didn't exist in the first place, so we're just + // ignoring this message. + } else { + foreach ($existingObject->VEVENT as $vevent) { + $vevent->STATUS = 'CANCELLED'; + $vevent->SEQUENCE = $itipMessage->sequence; + } + } + return $existingObject; + + } + + /** + * Processes incoming REPLY messages. + * + * The message is a reply. This is for example an attendee telling + * an organizer he accepted the invite, or declined it. + * + * @param Message $itipMessage + * @param VCalendar $existingObject + * + * @return VCalendar|null + */ + protected function processMessageReply(Message $itipMessage, VCalendar $existingObject = null) { + + // A reply can only be processed based on an existing object. + // If the object is not available, the reply is ignored. + if (!$existingObject) { + return; + } + $instances = []; + $requestStatus = '2.0'; + + // Finding all the instances the attendee replied to. + foreach ($itipMessage->message->VEVENT as $vevent) { + $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; + $attendee = $vevent->ATTENDEE; + $instances[$recurId] = $attendee['PARTSTAT']->getValue(); + if (isset($vevent->{'REQUEST-STATUS'})) { + $requestStatus = $vevent->{'REQUEST-STATUS'}->getValue(); + list($requestStatus) = explode(';', $requestStatus); + } + } + + // Now we need to loop through the original organizer event, to find + // all the instances where we have a reply for. + $masterObject = null; + foreach ($existingObject->VEVENT as $vevent) { + $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; + if ($recurId === 'master') { + $masterObject = $vevent; + } + if (isset($instances[$recurId])) { + $attendeeFound = false; + if (isset($vevent->ATTENDEE)) { + foreach ($vevent->ATTENDEE as $attendee) { + if ($attendee->getValue() === $itipMessage->sender) { + $attendeeFound = true; + $attendee['PARTSTAT'] = $instances[$recurId]; + $attendee['SCHEDULE-STATUS'] = $requestStatus; + // Un-setting the RSVP status, because we now know + // that the attendee already replied. + unset($attendee['RSVP']); + break; + } + } + } + if (!$attendeeFound) { + // Adding a new attendee. The iTip documentation calls this + // a party crasher. + $attendee = $vevent->add('ATTENDEE', $itipMessage->sender, [ + 'PARTSTAT' => $instances[$recurId] + ]); + if ($itipMessage->senderName) $attendee['CN'] = $itipMessage->senderName; + } + unset($instances[$recurId]); + } + } + + if (!$masterObject) { + // No master object, we can't add new instances. + return; + } + // If we got replies to instances that did not exist in the + // original list, it means that new exceptions must be created. + foreach ($instances as $recurId => $partstat) { + + $recurrenceIterator = new EventIterator($existingObject, $itipMessage->uid); + $found = false; + $iterations = 1000; + do { + + $newObject = $recurrenceIterator->getEventObject(); + $recurrenceIterator->next(); + + if (isset($newObject->{'RECURRENCE-ID'}) && $newObject->{'RECURRENCE-ID'}->getValue() === $recurId) { + $found = true; + } + $iterations--; + + } while ($recurrenceIterator->valid() && !$found && $iterations); + + // Invalid recurrence id. Skipping this object. + if (!$found) continue; + + unset( + $newObject->RRULE, + $newObject->EXDATE, + $newObject->RDATE + ); + $attendeeFound = false; + if (isset($newObject->ATTENDEE)) { + foreach ($newObject->ATTENDEE as $attendee) { + if ($attendee->getValue() === $itipMessage->sender) { + $attendeeFound = true; + $attendee['PARTSTAT'] = $partstat; + break; + } + } + } + if (!$attendeeFound) { + // Adding a new attendee + $attendee = $newObject->add('ATTENDEE', $itipMessage->sender, [ + 'PARTSTAT' => $partstat + ]); + if ($itipMessage->senderName) { + $attendee['CN'] = $itipMessage->senderName; + } + } + $existingObject->add($newObject); + + } + return $existingObject; + + } + + /** + * This method is used in cases where an event got updated, and we + * potentially need to send emails to attendees to let them know of updates + * in the events. + * + * We will detect which attendees got added, which got removed and create + * specific messages for these situations. + * + * @param VCalendar $calendar + * @param array $eventInfo + * @param array $oldEventInfo + * + * @return array + */ + protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) { + + // Merging attendee lists. + $attendees = []; + foreach ($oldEventInfo['attendees'] as $attendee) { + $attendees[$attendee['href']] = [ + 'href' => $attendee['href'], + 'oldInstances' => $attendee['instances'], + 'newInstances' => [], + 'name' => $attendee['name'], + 'forceSend' => null, + ]; + } + foreach ($eventInfo['attendees'] as $attendee) { + if (isset($attendees[$attendee['href']])) { + $attendees[$attendee['href']]['name'] = $attendee['name']; + $attendees[$attendee['href']]['newInstances'] = $attendee['instances']; + $attendees[$attendee['href']]['forceSend'] = $attendee['forceSend']; + } else { + $attendees[$attendee['href']] = [ + 'href' => $attendee['href'], + 'oldInstances' => [], + 'newInstances' => $attendee['instances'], + 'name' => $attendee['name'], + 'forceSend' => $attendee['forceSend'], + ]; + } + } + + $messages = []; + + foreach ($attendees as $attendee) { + + // An organizer can also be an attendee. We should not generate any + // messages for those. + if ($attendee['href'] === $eventInfo['organizer']) { + continue; + } + + $message = new Message(); + $message->uid = $eventInfo['uid']; + $message->component = 'VEVENT'; + $message->sequence = $eventInfo['sequence']; + $message->sender = $eventInfo['organizer']; + $message->senderName = $eventInfo['organizerName']; + $message->recipient = $attendee['href']; + $message->recipientName = $attendee['name']; + + if (!$attendee['newInstances']) { + + // If there are no instances the attendee is a part of, it + // means the attendee was removed and we need to send him a + // CANCEL. + $message->method = 'CANCEL'; + + // Creating the new iCalendar body. + $icalMsg = new VCalendar(); + $icalMsg->METHOD = $message->method; + $event = $icalMsg->add('VEVENT', [ + 'UID' => $message->uid, + 'SEQUENCE' => $message->sequence, + ]); + if (isset($calendar->VEVENT->SUMMARY)) { + $event->add('SUMMARY', $calendar->VEVENT->SUMMARY->getValue()); + } + $event->add(clone $calendar->VEVENT->DTSTART); + if (isset($calendar->VEVENT->DTEND)) { + $event->add(clone $calendar->VEVENT->DTEND); + } elseif (isset($calendar->VEVENT->DURATION)) { + $event->add(clone $calendar->VEVENT->DURATION); + } + $org = $event->add('ORGANIZER', $eventInfo['organizer']); + if ($eventInfo['organizerName']) $org['CN'] = $eventInfo['organizerName']; + $event->add('ATTENDEE', $attendee['href'], [ + 'CN' => $attendee['name'], + ]); + $message->significantChange = true; + + } else { + + // The attendee gets the updated event body + $message->method = 'REQUEST'; + + // Creating the new iCalendar body. + $icalMsg = new VCalendar(); + $icalMsg->METHOD = $message->method; + + foreach ($calendar->select('VTIMEZONE') as $timezone) { + $icalMsg->add(clone $timezone); + } + + // We need to find out that this change is significant. If it's + // not, systems may opt to not send messages. + // + // We do this based on the 'significantChangeHash' which is + // some value that changes if there's a certain set of + // properties changed in the event, or simply if there's a + // difference in instances that the attendee is invited to. + + $message->significantChange = + $attendee['forceSend'] === 'REQUEST' || + array_keys($attendee['oldInstances']) != array_keys($attendee['newInstances']) || + $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash']; + + foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) { + + $currentEvent = clone $eventInfo['instances'][$instanceId]; + if ($instanceId === 'master') { + + // We need to find a list of events that the attendee + // is not a part of to add to the list of exceptions. + $exceptions = []; + foreach ($eventInfo['instances'] as $instanceId => $vevent) { + if (!isset($attendee['newInstances'][$instanceId])) { + $exceptions[] = $instanceId; + } + } + + // If there were exceptions, we need to add it to an + // existing EXDATE property, if it exists. + if ($exceptions) { + if (isset($currentEvent->EXDATE)) { + $currentEvent->EXDATE->setParts(array_merge( + $currentEvent->EXDATE->getParts(), + $exceptions + )); + } else { + $currentEvent->EXDATE = $exceptions; + } + } + + // Cleaning up any scheduling information that + // shouldn't be sent along. + unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']); + unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']); + + foreach ($currentEvent->ATTENDEE as $attendee) { + unset($attendee['SCHEDULE-FORCE-SEND']); + unset($attendee['SCHEDULE-STATUS']); + + // We're adding PARTSTAT=NEEDS-ACTION to ensure that + // iOS shows an "Inbox Item" + if (!isset($attendee['PARTSTAT'])) { + $attendee['PARTSTAT'] = 'NEEDS-ACTION'; + } + + } + + } + + $icalMsg->add($currentEvent); + + } + + } + + $message->message = $icalMsg; + $messages[] = $message; + + } + + return $messages; + + } + + /** + * Parse an event update for an attendee. + * + * This function figures out if we need to send a reply to an organizer. + * + * @param VCalendar $calendar + * @param array $eventInfo + * @param array $oldEventInfo + * @param string $attendee + * + * @return Message[] + */ + protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee) { + + if ($this->scheduleAgentServerRules && $eventInfo['organizerScheduleAgent'] === 'CLIENT') { + return []; + } + + // Don't bother generating messages for events that have already been + // cancelled. + if ($eventInfo['status'] === 'CANCELLED') { + return []; + } + + $oldInstances = !empty($oldEventInfo['attendees'][$attendee]['instances']) ? + $oldEventInfo['attendees'][$attendee]['instances'] : + []; + + $instances = []; + foreach ($oldInstances as $instance) { + + $instances[$instance['id']] = [ + 'id' => $instance['id'], + 'oldstatus' => $instance['partstat'], + 'newstatus' => null, + ]; + + } + foreach ($eventInfo['attendees'][$attendee]['instances'] as $instance) { + + if (isset($instances[$instance['id']])) { + $instances[$instance['id']]['newstatus'] = $instance['partstat']; + } else { + $instances[$instance['id']] = [ + 'id' => $instance['id'], + 'oldstatus' => null, + 'newstatus' => $instance['partstat'], + ]; + } + + } + + // We need to also look for differences in EXDATE. If there are new + // items in EXDATE, it means that an attendee deleted instances of an + // event, which means we need to send DECLINED specifically for those + // instances. + // We only need to do that though, if the master event is not declined. + if (isset($instances['master']) && $instances['master']['newstatus'] !== 'DECLINED') { + foreach ($eventInfo['exdate'] as $exDate) { + + if (!in_array($exDate, $oldEventInfo['exdate'])) { + if (isset($instances[$exDate])) { + $instances[$exDate]['newstatus'] = 'DECLINED'; + } else { + $instances[$exDate] = [ + 'id' => $exDate, + 'oldstatus' => null, + 'newstatus' => 'DECLINED', + ]; + } + } + + } + } + + // Gathering a few extra properties for each instance. + foreach ($instances as $recurId => $instanceInfo) { + + if (isset($eventInfo['instances'][$recurId])) { + $instances[$recurId]['dtstart'] = clone $eventInfo['instances'][$recurId]->DTSTART; + } else { + $instances[$recurId]['dtstart'] = $recurId; + } + + } + + $message = new Message(); + $message->uid = $eventInfo['uid']; + $message->method = 'REPLY'; + $message->component = 'VEVENT'; + $message->sequence = $eventInfo['sequence']; + $message->sender = $attendee; + $message->senderName = $eventInfo['attendees'][$attendee]['name']; + $message->recipient = $eventInfo['organizer']; + $message->recipientName = $eventInfo['organizerName']; + + $icalMsg = new VCalendar(); + $icalMsg->METHOD = 'REPLY'; + + $hasReply = false; + + foreach ($instances as $instance) { + + if ($instance['oldstatus'] == $instance['newstatus'] && $eventInfo['organizerForceSend'] !== 'REPLY') { + // Skip + continue; + } + + $event = $icalMsg->add('VEVENT', [ + 'UID' => $message->uid, + 'SEQUENCE' => $message->sequence, + ]); + $summary = isset($calendar->VEVENT->SUMMARY) ? $calendar->VEVENT->SUMMARY->getValue() : ''; + // Adding properties from the correct source instance + if (isset($eventInfo['instances'][$instance['id']])) { + $instanceObj = $eventInfo['instances'][$instance['id']]; + $event->add(clone $instanceObj->DTSTART); + if (isset($instanceObj->DTEND)) { + $event->add(clone $instanceObj->DTEND); + } elseif (isset($instanceObj->DURATION)) { + $event->add(clone $instanceObj->DURATION); + } + if (isset($instanceObj->SUMMARY)) { + $event->add('SUMMARY', $instanceObj->SUMMARY->getValue()); + } elseif ($summary) { + $event->add('SUMMARY', $summary); + } + } else { + // This branch of the code is reached, when a reply is + // generated for an instance of a recurring event, through the + // fact that the instance has disappeared by showing up in + // EXDATE + $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']); + // Treat is as a DATE field + if (strlen($instance['id']) <= 8) { + $event->add('DTSTART', $dt, ['VALUE' => 'DATE']); + } else { + $event->add('DTSTART', $dt); + } + if ($summary) { + $event->add('SUMMARY', $summary); + } + } + if ($instance['id'] !== 'master') { + $dt = DateTimeParser::parse($instance['id'], $eventInfo['timezone']); + // Treat is as a DATE field + if (strlen($instance['id']) <= 8) { + $event->add('RECURRENCE-ID', $dt, ['VALUE' => 'DATE']); + } else { + $event->add('RECURRENCE-ID', $dt); + } + } + $organizer = $event->add('ORGANIZER', $message->recipient); + if ($message->recipientName) { + $organizer['CN'] = $message->recipientName; + } + $attendee = $event->add('ATTENDEE', $message->sender, [ + 'PARTSTAT' => $instance['newstatus'] + ]); + if ($message->senderName) { + $attendee['CN'] = $message->senderName; + } + $hasReply = true; + + } + + if ($hasReply) { + $message->message = $icalMsg; + return [$message]; + } else { + return []; + } + + } + + /** + * Returns attendee information and information about instances of an + * event. + * + * Returns an array with the following keys: + * + * 1. uid + * 2. organizer + * 3. organizerName + * 4. organizerScheduleAgent + * 5. organizerForceSend + * 6. instances + * 7. attendees + * 8. sequence + * 9. exdate + * 10. timezone - strictly the timezone on which the recurrence rule is + * based on. + * 11. significantChangeHash + * 12. status + * @param VCalendar $calendar + * + * @return array + */ + protected function parseEventInfo(VCalendar $calendar = null) { + + $uid = null; + $organizer = null; + $organizerName = null; + $organizerForceSend = null; + $sequence = null; + $timezone = null; + $status = null; + $organizerScheduleAgent = 'SERVER'; + + $significantChangeHash = ''; + + // Now we need to collect a list of attendees, and which instances they + // are a part of. + $attendees = []; + + $instances = []; + $exdate = []; + + foreach ($calendar->VEVENT as $vevent) { + + if (is_null($uid)) { + $uid = $vevent->UID->getValue(); + } else { + if ($uid !== $vevent->UID->getValue()) { + throw new ITipException('If a calendar contained more than one event, they must have the same UID.'); + } + } + + if (!isset($vevent->DTSTART)) { + throw new ITipException('An event MUST have a DTSTART property.'); + } + + if (isset($vevent->ORGANIZER)) { + if (is_null($organizer)) { + $organizer = $vevent->ORGANIZER->getNormalizedValue(); + $organizerName = isset($vevent->ORGANIZER['CN']) ? $vevent->ORGANIZER['CN'] : null; + } else { + if ($organizer !== $vevent->ORGANIZER->getNormalizedValue()) { + throw new SameOrganizerForAllComponentsException('Every instance of the event must have the same organizer.'); + } + } + $organizerForceSend = + isset($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) ? + strtoupper($vevent->ORGANIZER['SCHEDULE-FORCE-SEND']) : + null; + $organizerScheduleAgent = + isset($vevent->ORGANIZER['SCHEDULE-AGENT']) ? + strtoupper((string)$vevent->ORGANIZER['SCHEDULE-AGENT']) : + 'SERVER'; + } + if (is_null($sequence) && isset($vevent->SEQUENCE)) { + $sequence = $vevent->SEQUENCE->getValue(); + } + if (isset($vevent->EXDATE)) { + foreach ($vevent->select('EXDATE') as $val) { + $exdate = array_merge($exdate, $val->getParts()); + } + sort($exdate); + } + if (isset($vevent->STATUS)) { + $status = strtoupper($vevent->STATUS->getValue()); + } + + $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; + if (is_null($timezone)) { + if ($recurId === 'master') { + $timezone = $vevent->DTSTART->getDateTime()->getTimeZone(); + } else { + $timezone = $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimeZone(); + } + } + if (isset($vevent->ATTENDEE)) { + foreach ($vevent->ATTENDEE as $attendee) { + + if ($this->scheduleAgentServerRules && + isset($attendee['SCHEDULE-AGENT']) && + strtoupper($attendee['SCHEDULE-AGENT']->getValue()) === 'CLIENT' + ) { + continue; + } + $partStat = + isset($attendee['PARTSTAT']) ? + strtoupper($attendee['PARTSTAT']) : + 'NEEDS-ACTION'; + + $forceSend = + isset($attendee['SCHEDULE-FORCE-SEND']) ? + strtoupper($attendee['SCHEDULE-FORCE-SEND']) : + null; + + + if (isset($attendees[$attendee->getNormalizedValue()])) { + $attendees[$attendee->getNormalizedValue()]['instances'][$recurId] = [ + 'id' => $recurId, + 'partstat' => $partStat, + 'force-send' => $forceSend, + ]; + } else { + $attendees[$attendee->getNormalizedValue()] = [ + 'href' => $attendee->getNormalizedValue(), + 'instances' => [ + $recurId => [ + 'id' => $recurId, + 'partstat' => $partStat, + ], + ], + 'name' => isset($attendee['CN']) ? (string)$attendee['CN'] : null, + 'forceSend' => $forceSend, + ]; + } + + } + $instances[$recurId] = $vevent; + + } + + foreach ($this->significantChangeProperties as $prop) { + if (isset($vevent->$prop)) { + $propertyValues = $vevent->select($prop); + + $significantChangeHash .= $prop . ':'; + + if ($prop === 'EXDATE') { + + $significantChangeHash .= implode(',', $exdate) . ';'; + + } else { + + foreach ($propertyValues as $val) { + $significantChangeHash .= $val->getValue() . ';'; + } + + } + } + } + + } + $significantChangeHash = md5($significantChangeHash); + + return compact( + 'uid', + 'organizer', + 'organizerName', + 'organizerScheduleAgent', + 'organizerForceSend', + 'instances', + 'attendees', + 'sequence', + 'exdate', + 'timezone', + 'significantChangeHash', + 'status' + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/ITip/ITipException.php b/htdocs/includes/sabre/sabre/vobject/lib/ITip/ITipException.php new file mode 100644 index 00000000000..ad5e53ab4ae --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/ITip/ITipException.php @@ -0,0 +1,15 @@ +<?php + +namespace Sabre\VObject\ITip; + +use Exception; + +/** + * This message is emitted in case of serious problems with iTip messages. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ITipException extends Exception { +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/ITip/Message.php b/htdocs/includes/sabre/sabre/vobject/lib/ITip/Message.php new file mode 100644 index 00000000000..bebe2e4fc17 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/ITip/Message.php @@ -0,0 +1,141 @@ +<?php + +namespace Sabre\VObject\ITip; + +/** + * This class represents an iTip message. + * + * A message holds all the information relevant to the message, including the + * object itself. + * + * It should for the most part be treated as immutable. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Message { + + /** + * The object's UID. + * + * @var string + */ + public $uid; + + /** + * The component type, such as VEVENT. + * + * @var string + */ + public $component; + + /** + * Contains the ITip method, which is something like REQUEST, REPLY or + * CANCEL. + * + * @var string + */ + public $method; + + /** + * The current sequence number for the event. + * + * @var int + */ + public $sequence; + + /** + * The senders' email address. + * + * Note that this does not imply that this has to be used in a From: field + * if the message is sent by email. It may also be populated in Reply-To: + * or not at all. + * + * @var string + */ + public $sender; + + /** + * The name of the sender. This is often populated from a CN parameter from + * either the ORGANIZER or ATTENDEE, depending on the message. + * + * @var string|null + */ + public $senderName; + + /** + * The recipient's email address. + * + * @var string + */ + public $recipient; + + /** + * The name of the recipient. This is usually populated with the CN + * parameter from the ATTENDEE or ORGANIZER property, if it's available. + * + * @var string|null + */ + public $recipientName; + + /** + * After the message has been delivered, this should contain a string such + * as : 1.1;Sent or 1.2;Delivered. + * + * In case of a failure, this will hold the error status code. + * + * See: + * http://tools.ietf.org/html/rfc6638#section-7.3 + * + * @var string + */ + public $scheduleStatus; + + /** + * The iCalendar / iTip body. + * + * @var \Sabre\VObject\Component\VCalendar + */ + public $message; + + /** + * This will be set to true, if the iTip broker considers the change + * 'significant'. + * + * In practice, this means that we'll only mark it true, if for instance + * DTSTART changed. This allows systems to only send iTip messages when + * significant changes happened. This is especially useful for iMip, as + * normally a ton of messages may be generated for normal calendar use. + * + * To see the list of properties that are considered 'significant', check + * out Sabre\VObject\ITip\Broker::$significantChangeProperties. + * + * @var bool + */ + public $significantChange = true; + + /** + * Returns the schedule status as a string. + * + * For example: + * 1.2 + * + * @return mixed bool|string + */ + function getScheduleStatus() { + + if (!$this->scheduleStatus) { + + return false; + + } else { + + list($scheduleStatus) = explode(';', $this->scheduleStatus); + return $scheduleStatus; + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php b/htdocs/includes/sabre/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php new file mode 100644 index 00000000000..423b3983172 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/ITip/SameOrganizerForAllComponentsException.php @@ -0,0 +1,18 @@ +<?php + +namespace Sabre\VObject\ITip; + +/** + * SameOrganizerForAllComponentsException. + * + * This exception is emitted when an event is encountered with more than one + * component (e.g.: exceptions), but the organizer is not identical in every + * component. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class SameOrganizerForAllComponentsException extends ITipException { + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/InvalidDataException.php b/htdocs/includes/sabre/sabre/vobject/lib/InvalidDataException.php new file mode 100644 index 00000000000..50ebc0f4984 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/InvalidDataException.php @@ -0,0 +1,14 @@ +<?php + +namespace Sabre\VObject; + +/** + * This exception is thrown whenever an invalid value is found anywhere in a + * iCalendar or vCard object. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class InvalidDataException extends \Exception { +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Node.php b/htdocs/includes/sabre/sabre/vobject/lib/Node.php new file mode 100644 index 00000000000..e2845da75f7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Node.php @@ -0,0 +1,265 @@ +<?php + +namespace Sabre\VObject; + +use Sabre\Xml; + +/** + * A node is the root class for every element in an iCalendar of vCard object. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class Node + implements \IteratorAggregate, + \ArrayAccess, + \Countable, + \JsonSerializable, + Xml\XmlSerializable { + + /** + * The following constants are used by the validate() method. + * + * If REPAIR is set, the validator will attempt to repair any broken data + * (if possible). + */ + const REPAIR = 1; + + /** + * If this option is set, the validator will operate on the vcards on the + * assumption that the vcards need to be valid for CardDAV. + * + * This means for example that the UID is required, whereas it is not for + * regular vcards. + */ + const PROFILE_CARDDAV = 2; + + /** + * If this option is set, the validator will operate on iCalendar objects + * on the assumption that the vcards need to be valid for CalDAV. + * + * This means for example that calendars can only contain objects with + * identical component types and UIDs. + */ + const PROFILE_CALDAV = 4; + + /** + * Reference to the parent object, if this is not the top object. + * + * @var Node + */ + public $parent; + + /** + * Iterator override. + * + * @var ElementList + */ + protected $iterator = null; + + /** + * The root document. + * + * @var Component + */ + protected $root; + + /** + * Serializes the node into a mimedir format. + * + * @return string + */ + abstract function serialize(); + + /** + * This method returns an array, with the representation as it should be + * encoded in JSON. This is used to create jCard or jCal documents. + * + * @return array + */ + abstract function jsonSerialize(); + + /** + * This method serializes the data into XML. This is used to create xCard or + * xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + abstract function xmlSerialize(Xml\Writer $writer); + + /** + * Call this method on a document if you're done using it. + * + * It's intended to remove all circular references, so PHP can easily clean + * it up. + * + * @return void + */ + function destroy() { + + $this->parent = null; + $this->root = null; + + } + + /* {{{ IteratorAggregator interface */ + + /** + * Returns the iterator for this object. + * + * @return ElementList + */ + function getIterator() { + + if (!is_null($this->iterator)) { + return $this->iterator; + } + + return new ElementList([$this]); + + } + + /** + * Sets the overridden iterator. + * + * Note that this is not actually part of the iterator interface + * + * @param ElementList $iterator + * + * @return void + */ + function setIterator(ElementList $iterator) { + + $this->iterator = $iterator; + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on) + * 2 - An inconsequential issue + * 3 - A severe issue. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + return []; + + } + + /* }}} */ + + /* {{{ Countable interface */ + + /** + * Returns the number of elements. + * + * @return int + */ + function count() { + + $it = $this->getIterator(); + return $it->count(); + + } + + /* }}} */ + + /* {{{ ArrayAccess Interface */ + + + /** + * Checks if an item exists through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * + * @return bool + */ + function offsetExists($offset) { + + $iterator = $this->getIterator(); + return $iterator->offsetExists($offset); + + } + + /** + * Gets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * + * @return mixed + */ + function offsetGet($offset) { + + $iterator = $this->getIterator(); + return $iterator->offsetGet($offset); + + } + + /** + * Sets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * @param mixed $value + * + * @return void + */ + function offsetSet($offset, $value) { + + $iterator = $this->getIterator(); + $iterator->offsetSet($offset, $value); + + // @codeCoverageIgnoreStart + // + // This method always throws an exception, so we ignore the closing + // brace + } + // @codeCoverageIgnoreEnd + + /** + * Sets an item through ArrayAccess. + * + * This method just forwards the request to the inner iterator + * + * @param int $offset + * + * @return void + */ + function offsetUnset($offset) { + + $iterator = $this->getIterator(); + $iterator->offsetUnset($offset); + + // @codeCoverageIgnoreStart + // + // This method always throws an exception, so we ignore the closing + // brace + } + // @codeCoverageIgnoreEnd + + /* }}} */ +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/PHPUnitAssertions.php b/htdocs/includes/sabre/sabre/vobject/lib/PHPUnitAssertions.php new file mode 100644 index 00000000000..87ec75e8f5f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/PHPUnitAssertions.php @@ -0,0 +1,82 @@ +<?php + +namespace Sabre\VObject; + +/** + * PHPUnit Assertions + * + * This trait can be added to your unittest to make it easier to test iCalendar + * and/or vCards. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +trait PHPUnitAssertions { + + /** + * This method tests wether two vcards or icalendar objects are + * semantically identical. + * + * It supports objects being supplied as strings, streams or + * Sabre\VObject\Component instances. + * + * PRODID is removed from both objects as this is often changes and would + * just get in the way. + * + * CALSCALE will automatically get removed if it's set to GREGORIAN. + * + * Any property that has the value **ANY** will be treated as a wildcard. + * + * @param resource|string|Component $expected + * @param resource|string|Component $actual + * @param string $message + */ + function assertVObjectEqualsVObject($expected, $actual, $message = '') { + + $self = $this; + $getObj = function($input) use ($self) { + + if (is_resource($input)) { + $input = stream_get_contents($input); + } + if (is_string($input)) { + $input = Reader::read($input); + } + if (!$input instanceof Component) { + $this->fail('Input must be a string, stream or VObject component'); + } + unset($input->PRODID); + if ($input instanceof Component\VCalendar && (string)$input->CALSCALE === 'GREGORIAN') { + unset($input->CALSCALE); + } + return $input; + + }; + + $expected = $getObj($expected)->serialize(); + $actual = $getObj($actual)->serialize(); + + // Finding wildcards in expected. + preg_match_all('|^([A-Z]+):\\*\\*ANY\\*\\*\r$|m', $expected, $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + + $actual = preg_replace( + '|^' . preg_quote($match[1], '|') . ':(.*)\r$|m', + $match[1] . ':**ANY**' . "\r", + $actual + ); + + } + + $this->assertEquals( + $expected, + $actual, + $message + ); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Parameter.php b/htdocs/includes/sabre/sabre/vobject/lib/Parameter.php new file mode 100644 index 00000000000..a99a33eec0e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Parameter.php @@ -0,0 +1,394 @@ +<?php + +namespace Sabre\VObject; + +use ArrayIterator; +use Sabre\Xml; + +/** + * VObject Parameter. + * + * This class represents a parameter. A parameter is always tied to a property. + * In the case of: + * DTSTART;VALUE=DATE:20101108 + * VALUE=DATE would be the parameter name and value. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Parameter extends Node { + + /** + * Parameter name. + * + * @var string + */ + public $name; + + /** + * vCard 2.1 allows parameters to be encoded without a name. + * + * We can deduce the parameter name based on it's value. + * + * @var bool + */ + public $noName = false; + + /** + * Parameter value. + * + * @var string + */ + protected $value; + + /** + * Sets up the object. + * + * It's recommended to use the create:: factory method instead. + * + * @param string $name + * @param string $value + */ + function __construct(Document $root, $name, $value = null) { + + $this->name = strtoupper($name); + $this->root = $root; + if (is_null($name)) { + $this->noName = true; + $this->name = static::guessParameterNameByValue($value); + } + + // If guessParameterNameByValue() returns an empty string + // above, we're actually dealing with a parameter that has no value. + // In that case we have to move the value to the name. + if ($this->name === '') { + $this->noName = false; + $this->name = strtoupper($value); + } else { + $this->setValue($value); + } + + } + + /** + * Try to guess property name by value, can be used for vCard 2.1 nameless parameters. + * + * Figuring out what the name should have been. Note that a ton of + * these are rather silly in 2014 and would probably rarely be + * used, but we like to be complete. + * + * @param string $value + * + * @return string + */ + static function guessParameterNameByValue($value) { + switch (strtoupper($value)) { + + // Encodings + case '7-BIT' : + case 'QUOTED-PRINTABLE' : + case 'BASE64' : + $name = 'ENCODING'; + break; + + // Common types + case 'WORK' : + case 'HOME' : + case 'PREF' : + + // Delivery Label Type + case 'DOM' : + case 'INTL' : + case 'POSTAL' : + case 'PARCEL' : + + // Telephone types + case 'VOICE' : + case 'FAX' : + case 'MSG' : + case 'CELL' : + case 'PAGER' : + case 'BBS' : + case 'MODEM' : + case 'CAR' : + case 'ISDN' : + case 'VIDEO' : + + // EMAIL types (lol) + case 'AOL' : + case 'APPLELINK' : + case 'ATTMAIL' : + case 'CIS' : + case 'EWORLD' : + case 'INTERNET' : + case 'IBMMAIL' : + case 'MCIMAIL' : + case 'POWERSHARE' : + case 'PRODIGY' : + case 'TLX' : + case 'X400' : + + // Photo / Logo format types + case 'GIF' : + case 'CGM' : + case 'WMF' : + case 'BMP' : + case 'DIB' : + case 'PICT' : + case 'TIFF' : + case 'PDF' : + case 'PS' : + case 'JPEG' : + case 'MPEG' : + case 'MPEG2' : + case 'AVI' : + case 'QTIME' : + + // Sound Digital Audio Type + case 'WAVE' : + case 'PCM' : + case 'AIFF' : + + // Key types + case 'X509' : + case 'PGP' : + $name = 'TYPE'; + break; + + // Value types + case 'INLINE' : + case 'URL' : + case 'CONTENT-ID' : + case 'CID' : + $name = 'VALUE'; + break; + + default: + $name = ''; + } + + return $name; + } + + /** + * Updates the current value. + * + * This may be either a single, or multiple strings in an array. + * + * @param string|array $value + * + * @return void + */ + function setValue($value) { + + $this->value = $value; + + } + + /** + * Returns the current value. + * + * This method will always return a string, or null. If there were multiple + * values, it will automatically concatenate them (separated by comma). + * + * @return string|null + */ + function getValue() { + + if (is_array($this->value)) { + return implode(',', $this->value); + } else { + return $this->value; + } + + } + + /** + * Sets multiple values for this parameter. + * + * @param array $value + * + * @return void + */ + function setParts(array $value) { + + $this->value = $value; + + } + + /** + * Returns all values for this parameter. + * + * If there were no values, an empty array will be returned. + * + * @return array + */ + function getParts() { + + if (is_array($this->value)) { + return $this->value; + } elseif (is_null($this->value)) { + return []; + } else { + return [$this->value]; + } + + } + + /** + * Adds a value to this parameter. + * + * If the argument is specified as an array, all items will be added to the + * parameter value list. + * + * @param string|array $part + * + * @return void + */ + function addValue($part) { + + if (is_null($this->value)) { + $this->value = $part; + } else { + $this->value = array_merge((array)$this->value, (array)$part); + } + + } + + /** + * Checks if this parameter contains the specified value. + * + * This is a case-insensitive match. It makes sense to call this for for + * instance the TYPE parameter, to see if it contains a keyword such as + * 'WORK' or 'FAX'. + * + * @param string $value + * + * @return bool + */ + function has($value) { + + return in_array( + strtolower($value), + array_map('strtolower', (array)$this->value) + ); + + } + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + function serialize() { + + $value = $this->getParts(); + + if (count($value) === 0) { + return $this->name . '='; + } + + if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) { + + return implode(';', $value); + + } + + return $this->name . '=' . array_reduce( + $value, + function($out, $item) { + + if (!is_null($out)) $out .= ','; + + // If there's no special characters in the string, we'll use the simple + // format. + // + // The list of special characters is defined as: + // + // Any character except CONTROL, DQUOTE, ";", ":", "," + // + // by the iCalendar spec: + // https://tools.ietf.org/html/rfc5545#section-3.1 + // + // And we add ^ to that because of: + // https://tools.ietf.org/html/rfc6868 + // + // But we've found that iCal (7.0, shipped with OSX 10.9) + // severaly trips on + characters not being quoted, so we + // added + as well. + if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) { + return $out . $item; + } else { + // Enclosing in double-quotes, and using RFC6868 for encoding any + // special characters + $out .= '"' . strtr( + $item, + [ + '^' => '^^', + "\n" => '^n', + '"' => '^\'', + ] + ) . '"'; + return $out; + } + + } + ); + + } + + /** + * This method returns an array, with the representation as it should be + * encoded in JSON. This is used to create jCard or jCal documents. + * + * @return array + */ + function jsonSerialize() { + + return $this->value; + + } + + /** + * This method serializes the data into XML. This is used to create xCard or + * xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + foreach (explode(',', $this->value) as $value) { + $writer->writeElement('text', $value); + } + + } + + /** + * Called when this object is being cast to a string. + * + * @return string + */ + function __toString() { + + return (string)$this->getValue(); + + } + + /** + * Returns the iterator for this object. + * + * @return ElementList + */ + function getIterator() { + + if (!is_null($this->iterator)) + return $this->iterator; + + return $this->iterator = new ArrayIterator((array)$this->value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/ParseException.php b/htdocs/includes/sabre/sabre/vobject/lib/ParseException.php new file mode 100644 index 00000000000..d96d20720d6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/ParseException.php @@ -0,0 +1,13 @@ +<?php + +namespace Sabre\VObject; + +/** + * Exception thrown by Reader if an invalid object was attempted to be parsed. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class ParseException extends \Exception { +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Parser/Json.php b/htdocs/includes/sabre/sabre/vobject/lib/Parser/Json.php new file mode 100644 index 00000000000..a77258a2ef4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Parser/Json.php @@ -0,0 +1,197 @@ +<?php + +namespace Sabre\VObject\Parser; + +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\EofException; +use Sabre\VObject\ParseException; + +/** + * Json Parser. + * + * This parser parses both the jCal and jCard formats. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Json extends Parser { + + /** + * The input data. + * + * @var array + */ + protected $input; + + /** + * Root component. + * + * @var Document + */ + protected $root; + + /** + * This method starts the parsing process. + * + * If the input was not supplied during construction, it's possible to pass + * it here instead. + * + * If either input or options are not supplied, the defaults will be used. + * + * @param resource|string|array|null $input + * @param int $options + * + * @return Sabre\VObject\Document + */ + function parse($input = null, $options = 0) { + + if (!is_null($input)) { + $this->setInput($input); + } + if (is_null($this->input)) { + throw new EofException('End of input stream, or no input supplied'); + } + + if (0 !== $options) { + $this->options = $options; + } + + switch ($this->input[0]) { + case 'vcalendar' : + $this->root = new VCalendar([], false); + break; + case 'vcard' : + $this->root = new VCard([], false); + break; + default : + throw new ParseException('The root component must either be a vcalendar, or a vcard'); + + } + foreach ($this->input[1] as $prop) { + $this->root->add($this->parseProperty($prop)); + } + if (isset($this->input[2])) foreach ($this->input[2] as $comp) { + $this->root->add($this->parseComponent($comp)); + } + + // Resetting the input so we can throw an feof exception the next time. + $this->input = null; + + return $this->root; + + } + + /** + * Parses a component. + * + * @param array $jComp + * + * @return \Sabre\VObject\Component + */ + function parseComponent(array $jComp) { + + // We can remove $self from PHP 5.4 onward. + $self = $this; + + $properties = array_map( + function($jProp) use ($self) { + return $self->parseProperty($jProp); + }, + $jComp[1] + ); + + if (isset($jComp[2])) { + + $components = array_map( + function($jComp) use ($self) { + return $self->parseComponent($jComp); + }, + $jComp[2] + ); + + } else $components = []; + + return $this->root->createComponent( + $jComp[0], + array_merge($properties, $components), + $defaults = false + ); + + } + + /** + * Parses properties. + * + * @param array $jProp + * + * @return \Sabre\VObject\Property + */ + function parseProperty(array $jProp) { + + list( + $propertyName, + $parameters, + $valueType + ) = $jProp; + + $propertyName = strtoupper($propertyName); + + // This is the default class we would be using if we didn't know the + // value type. We're using this value later in this function. + $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName); + + $parameters = (array)$parameters; + + $value = array_slice($jProp, 3); + + $valueType = strtoupper($valueType); + + if (isset($parameters['group'])) { + $propertyName = $parameters['group'] . '.' . $propertyName; + unset($parameters['group']); + } + + $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType); + $prop->setJsonValue($value); + + // We have to do something awkward here. FlatText as well as Text + // represents TEXT values. We have to normalize these here. In the + // future we can get rid of FlatText once we're allowed to break BC + // again. + if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') { + $defaultPropertyClass = 'Sabre\VObject\Property\Text'; + } + + // If the value type we received (e.g.: TEXT) was not the default value + // type for the given property (e.g.: BDAY), we need to add a VALUE= + // parameter. + if ($defaultPropertyClass !== get_class($prop)) { + $prop["VALUE"] = $valueType; + } + + return $prop; + + } + + /** + * Sets the input data. + * + * @param resource|string|array $input + * + * @return void + */ + function setInput($input) { + + if (is_resource($input)) { + $input = stream_get_contents($input); + } + if (is_string($input)) { + $input = json_decode($input); + } + $this->input = $input; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Parser/MimeDir.php b/htdocs/includes/sabre/sabre/vobject/lib/Parser/MimeDir.php new file mode 100644 index 00000000000..fa75a1a3b60 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Parser/MimeDir.php @@ -0,0 +1,696 @@ +<?php + +namespace Sabre\VObject\Parser; + +use Sabre\VObject\Component; +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Document; +use Sabre\VObject\EofException; +use Sabre\VObject\ParseException; + +/** + * MimeDir parser. + * + * This class parses iCalendar 2.0 and vCard 2.1, 3.0 and 4.0 files. This + * parser will return one of the following two objects from the parse method: + * + * Sabre\VObject\Component\VCalendar + * Sabre\VObject\Component\VCard + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class MimeDir extends Parser { + + /** + * The input stream. + * + * @var resource + */ + protected $input; + + /** + * Root component. + * + * @var Component + */ + protected $root; + + /** + * By default all input will be assumed to be UTF-8. + * + * However, both iCalendar and vCard might be encoded using different + * character sets. The character set is usually set in the mime-type. + * + * If this is the case, use setEncoding to specify that a different + * encoding will be used. If this is set, the parser will automatically + * convert all incoming data to UTF-8. + * + * @var string + */ + protected $charset = 'UTF-8'; + + /** + * The list of character sets we support when decoding. + * + * This would be a const expression but for now we need to support PHP 5.5 + */ + protected static $SUPPORTED_CHARSETS = [ + 'UTF-8', + 'ISO-8859-1', + 'Windows-1252', + ]; + + /** + * Parses an iCalendar or vCard file. + * + * Pass a stream or a string. If null is parsed, the existing buffer is + * used. + * + * @param string|resource|null $input + * @param int $options + * + * @return Sabre\VObject\Document + */ + function parse($input = null, $options = 0) { + + $this->root = null; + + if (!is_null($input)) { + $this->setInput($input); + } + + if (0 !== $options) { + $this->options = $options; + } + + $this->parseDocument(); + + return $this->root; + + } + + /** + * By default all input will be assumed to be UTF-8. + * + * However, both iCalendar and vCard might be encoded using different + * character sets. The character set is usually set in the mime-type. + * + * If this is the case, use setEncoding to specify that a different + * encoding will be used. If this is set, the parser will automatically + * convert all incoming data to UTF-8. + * + * @param string $charset + */ + function setCharset($charset) { + + if (!in_array($charset, self::$SUPPORTED_CHARSETS)) { + throw new \InvalidArgumentException('Unsupported encoding. (Supported encodings: ' . implode(', ', self::$SUPPORTED_CHARSETS) . ')'); + } + $this->charset = $charset; + + } + + /** + * Sets the input buffer. Must be a string or stream. + * + * @param resource|string $input + * + * @return void + */ + function setInput($input) { + + // Resetting the parser + $this->lineIndex = 0; + $this->startLine = 0; + + if (is_string($input)) { + // Convering to a stream. + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $input); + rewind($stream); + $this->input = $stream; + } elseif (is_resource($input)) { + $this->input = $input; + } else { + throw new \InvalidArgumentException('This parser can only read from strings or streams.'); + } + + } + + /** + * Parses an entire document. + * + * @return void + */ + protected function parseDocument() { + + $line = $this->readLine(); + + // BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF). + // It's 0xEF 0xBB 0xBF in UTF-8 hex. + if (3 <= strlen($line) + && ord($line[0]) === 0xef + && ord($line[1]) === 0xbb + && ord($line[2]) === 0xbf) { + $line = substr($line, 3); + } + + switch (strtoupper($line)) { + case 'BEGIN:VCALENDAR' : + $class = VCalendar::$componentMap['VCALENDAR']; + break; + case 'BEGIN:VCARD' : + $class = VCard::$componentMap['VCARD']; + break; + default : + throw new ParseException('This parser only supports VCARD and VCALENDAR files'); + } + + $this->root = new $class([], false); + + while (true) { + + // Reading until we hit END: + $line = $this->readLine(); + if (strtoupper(substr($line, 0, 4)) === 'END:') { + break; + } + $result = $this->parseLine($line); + if ($result) { + $this->root->add($result); + } + + } + + $name = strtoupper(substr($line, 4)); + if ($name !== $this->root->name) { + throw new ParseException('Invalid MimeDir file. expected: "END:' . $this->root->name . '" got: "END:' . $name . '"'); + } + + } + + /** + * Parses a line, and if it hits a component, it will also attempt to parse + * the entire component. + * + * @param string $line Unfolded line + * + * @return Node + */ + protected function parseLine($line) { + + // Start of a new component + if (strtoupper(substr($line, 0, 6)) === 'BEGIN:') { + + $component = $this->root->createComponent(substr($line, 6), [], false); + + while (true) { + + // Reading until we hit END: + $line = $this->readLine(); + if (strtoupper(substr($line, 0, 4)) === 'END:') { + break; + } + $result = $this->parseLine($line); + if ($result) { + $component->add($result); + } + + } + + $name = strtoupper(substr($line, 4)); + if ($name !== $component->name) { + throw new ParseException('Invalid MimeDir file. expected: "END:' . $component->name . '" got: "END:' . $name . '"'); + } + + return $component; + + } else { + + // Property reader + $property = $this->readProperty($line); + if (!$property) { + // Ignored line + return false; + } + return $property; + + } + + } + + /** + * We need to look ahead 1 line every time to see if we need to 'unfold' + * the next line. + * + * If that was not the case, we store it here. + * + * @var null|string + */ + protected $lineBuffer; + + /** + * The real current line number. + */ + protected $lineIndex = 0; + + /** + * In the case of unfolded lines, this property holds the line number for + * the start of the line. + * + * @var int + */ + protected $startLine = 0; + + /** + * Contains a 'raw' representation of the current line. + * + * @var string + */ + protected $rawLine; + + /** + * Reads a single line from the buffer. + * + * This method strips any newlines and also takes care of unfolding. + * + * @throws \Sabre\VObject\EofException + * + * @return string + */ + protected function readLine() { + + if (!is_null($this->lineBuffer)) { + $rawLine = $this->lineBuffer; + $this->lineBuffer = null; + } else { + do { + $eof = feof($this->input); + + $rawLine = fgets($this->input); + + if ($eof || (feof($this->input) && $rawLine === false)) { + throw new EofException('End of document reached prematurely'); + } + if ($rawLine === false) { + throw new ParseException('Error reading from input stream'); + } + $rawLine = rtrim($rawLine, "\r\n"); + } while ($rawLine === ''); // Skipping empty lines + $this->lineIndex++; + } + $line = $rawLine; + + $this->startLine = $this->lineIndex; + + // Looking ahead for folded lines. + while (true) { + + $nextLine = rtrim(fgets($this->input), "\r\n"); + $this->lineIndex++; + if (!$nextLine) { + break; + } + if ($nextLine[0] === "\t" || $nextLine[0] === " ") { + $line .= substr($nextLine, 1); + $rawLine .= "\n " . substr($nextLine, 1); + } else { + $this->lineBuffer = $nextLine; + break; + } + + } + $this->rawLine = $rawLine; + return $line; + + } + + /** + * Reads a property or component from a line. + * + * @return void + */ + protected function readProperty($line) { + + if ($this->options & self::OPTION_FORGIVING) { + $propNameToken = 'A-Z0-9\-\._\\/'; + } else { + $propNameToken = 'A-Z0-9\-\.'; + } + + $paramNameToken = 'A-Z0-9\-'; + $safeChar = '^";:,'; + $qSafeChar = '^"'; + + $regex = "/ + ^(?P<name> [$propNameToken]+ ) (?=[;:]) # property name + | + (?<=:)(?P<propValue> .+)$ # property value + | + ;(?P<paramName> [$paramNameToken]+) (?=[=;:]) # parameter name + | + (=|,)(?P<paramValue> # parameter value + (?: [$safeChar]*) | + \"(?: [$qSafeChar]+)\" + ) (?=[;:,]) + /xi"; + + //echo $regex, "\n"; die(); + preg_match_all($regex, $line, $matches, PREG_SET_ORDER); + + $property = [ + 'name' => null, + 'parameters' => [], + 'value' => null + ]; + + $lastParam = null; + + /** + * Looping through all the tokens. + * + * Note that we are looping through them in reverse order, because if a + * sub-pattern matched, the subsequent named patterns will not show up + * in the result. + */ + foreach ($matches as $match) { + + if (isset($match['paramValue'])) { + if ($match['paramValue'] && $match['paramValue'][0] === '"') { + $value = substr($match['paramValue'], 1, -1); + } else { + $value = $match['paramValue']; + } + + $value = $this->unescapeParam($value); + + if (is_null($lastParam)) { + throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions'); + } + if (is_null($property['parameters'][$lastParam])) { + $property['parameters'][$lastParam] = $value; + } elseif (is_array($property['parameters'][$lastParam])) { + $property['parameters'][$lastParam][] = $value; + } else { + $property['parameters'][$lastParam] = [ + $property['parameters'][$lastParam], + $value + ]; + } + continue; + } + if (isset($match['paramName'])) { + $lastParam = strtoupper($match['paramName']); + if (!isset($property['parameters'][$lastParam])) { + $property['parameters'][$lastParam] = null; + } + continue; + } + if (isset($match['propValue'])) { + $property['value'] = $match['propValue']; + continue; + } + if (isset($match['name']) && $match['name']) { + $property['name'] = strtoupper($match['name']); + continue; + } + + // @codeCoverageIgnoreStart + throw new \LogicException('This code should not be reachable'); + // @codeCoverageIgnoreEnd + + } + + if (is_null($property['value'])) { + $property['value'] = ''; + } + if (!$property['name']) { + if ($this->options & self::OPTION_IGNORE_INVALID_LINES) { + return false; + } + throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions'); + } + + // vCard 2.1 states that parameters may appear without a name, and only + // a value. We can deduce the value based on it's name. + // + // Our parser will get those as parameters without a value instead, so + // we're filtering these parameters out first. + $namedParameters = []; + $namelessParameters = []; + + foreach ($property['parameters'] as $name => $value) { + if (!is_null($value)) { + $namedParameters[$name] = $value; + } else { + $namelessParameters[] = $name; + } + } + + $propObj = $this->root->createProperty($property['name'], null, $namedParameters); + + foreach ($namelessParameters as $namelessParameter) { + $propObj->add(null, $namelessParameter); + } + + if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') { + $propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue()); + } else { + $charset = $this->charset; + if ($this->root->getDocumentType() === Document::VCARD21 && isset($propObj['CHARSET'])) { + // vCard 2.1 allows the character set to be specified per property. + $charset = (string)$propObj['CHARSET']; + } + switch ($charset) { + case 'UTF-8' : + break; + case 'ISO-8859-1' : + $property['value'] = utf8_encode($property['value']); + break; + case 'Windows-1252' : + $property['value'] = mb_convert_encoding($property['value'], 'UTF-8', $charset); + break; + default : + throw new ParseException('Unsupported CHARSET: ' . $propObj['CHARSET']); + } + $propObj->setRawMimeDirValue($property['value']); + } + + return $propObj; + + } + + /** + * Unescapes a property value. + * + * vCard 2.1 says: + * * Semi-colons must be escaped in some property values, specifically + * ADR, ORG and N. + * * Semi-colons must be escaped in parameter values, because semi-colons + * are also use to separate values. + * * No mention of escaping backslashes with another backslash. + * * newlines are not escaped either, instead QUOTED-PRINTABLE is used to + * span values over more than 1 line. + * + * vCard 3.0 says: + * * (rfc2425) Backslashes, newlines (\n or \N) and comma's must be + * escaped, all time time. + * * Comma's are used for delimeters in multiple values + * * (rfc2426) Adds to to this that the semi-colon MUST also be escaped, + * as in some properties semi-colon is used for separators. + * * Properties using semi-colons: N, ADR, GEO, ORG + * * Both ADR and N's individual parts may be broken up further with a + * comma. + * * Properties using commas: NICKNAME, CATEGORIES + * + * vCard 4.0 (rfc6350) says: + * * Commas must be escaped. + * * Semi-colons may be escaped, an unescaped semi-colon _may_ be a + * delimiter, depending on the property. + * * Backslashes must be escaped + * * Newlines must be escaped as either \N or \n. + * * Some compound properties may contain multiple parts themselves, so a + * comma within a semi-colon delimited property may also be unescaped + * to denote multiple parts _within_ the compound property. + * * Text-properties using semi-colons: N, ADR, ORG, CLIENTPIDMAP. + * * Text-properties using commas: NICKNAME, RELATED, CATEGORIES, PID. + * + * Even though the spec says that commas must always be escaped, the + * example for GEO in Section 6.5.2 seems to violate this. + * + * iCalendar 2.0 (rfc5545) says: + * * Commas or semi-colons may be used as delimiters, depending on the + * property. + * * Commas, semi-colons, backslashes, newline (\N or \n) are always + * escaped, unless they are delimiters. + * * Colons shall not be escaped. + * * Commas can be considered the 'default delimiter' and is described as + * the delimiter in cases where the order of the multiple values is + * insignificant. + * * Semi-colons are described as the delimiter for 'structured values'. + * They are specifically used in Semi-colons are used as a delimiter in + * REQUEST-STATUS, RRULE, GEO and EXRULE. EXRULE is deprecated however. + * + * Now for the parameters + * + * If delimiter is not set (null) this method will just return a string. + * If it's a comma or a semi-colon the string will be split on those + * characters, and always return an array. + * + * @param string $input + * @param string $delimiter + * + * @return string|string[] + */ + static function unescapeValue($input, $delimiter = ';') { + + $regex = '# (?: (\\\\ (?: \\\\ | N | n | ; | , ) )'; + if ($delimiter) { + $regex .= ' | (' . $delimiter . ')'; + } + $regex .= ') #x'; + + $matches = preg_split($regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + $resultArray = []; + $result = ''; + + foreach ($matches as $match) { + + switch ($match) { + case '\\\\' : + $result .= '\\'; + break; + case '\N' : + case '\n' : + $result .= "\n"; + break; + case '\;' : + $result .= ';'; + break; + case '\,' : + $result .= ','; + break; + case $delimiter : + $resultArray[] = $result; + $result = ''; + break; + default : + $result .= $match; + break; + + } + + } + + $resultArray[] = $result; + return $delimiter ? $resultArray : $result; + + } + + /** + * Unescapes a parameter value. + * + * vCard 2.1: + * * Does not mention a mechanism for this. In addition, double quotes + * are never used to wrap values. + * * This means that parameters can simply not contain colons or + * semi-colons. + * + * vCard 3.0 (rfc2425, rfc2426): + * * Parameters _may_ be surrounded by double quotes. + * * If this is not the case, semi-colon, colon and comma may simply not + * occur (the comma used for multiple parameter values though). + * * If it is surrounded by double-quotes, it may simply not contain + * double-quotes. + * * This means that a parameter can in no case encode double-quotes, or + * newlines. + * + * vCard 4.0 (rfc6350) + * * Behavior seems to be identical to vCard 3.0 + * + * iCalendar 2.0 (rfc5545) + * * Behavior seems to be identical to vCard 3.0 + * + * Parameter escaping mechanism (rfc6868) : + * * This rfc describes a new way to escape parameter values. + * * New-line is encoded as ^n + * * ^ is encoded as ^^. + * * " is encoded as ^' + * + * @param string $input + * + * @return void + */ + private function unescapeParam($input) { + + return + preg_replace_callback( + '#(\^(\^|n|\'))#', + function($matches) { + switch ($matches[2]) { + case 'n' : + return "\n"; + case '^' : + return '^'; + case '\'' : + return '"'; + + // @codeCoverageIgnoreStart + } + // @codeCoverageIgnoreEnd + }, + $input + ); + } + + /** + * Gets the full quoted printable value. + * + * We need a special method for this, because newlines have both a meaning + * in vCards, and in QuotedPrintable. + * + * This method does not do any decoding. + * + * @return string + */ + private function extractQuotedPrintableValue() { + + // We need to parse the raw line again to get the start of the value. + // + // We are basically looking for the first colon (:), but we need to + // skip over the parameters first, as they may contain one. + $regex = '/^ + (?: [^:])+ # Anything but a colon + (?: "[^"]")* # A parameter in double quotes + : # start of the value we really care about + (.*)$ + /xs'; + + preg_match($regex, $this->rawLine, $matches); + + $value = $matches[1]; + // Removing the first whitespace character from every line. Kind of + // like unfolding, but we keep the newline. + $value = str_replace("\n ", "\n", $value); + + // Microsoft products don't always correctly fold lines, they may be + // missing a whitespace. So if 'forgiving' is turned on, we will take + // those as well. + if ($this->options & self::OPTION_FORGIVING) { + while (substr($value, -1) === '=') { + // Reading the line + $this->readLine(); + // Grabbing the raw form + $value .= "\n" . $this->rawLine; + } + } + + return $value; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Parser/Parser.php b/htdocs/includes/sabre/sabre/vobject/lib/Parser/Parser.php new file mode 100644 index 00000000000..ca8bc0addd3 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Parser/Parser.php @@ -0,0 +1,80 @@ +<?php + +namespace Sabre\VObject\Parser; + +/** + * Abstract parser. + * + * This class serves as a base-class for the different parsers. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class Parser { + + /** + * Turning on this option makes the parser more forgiving. + * + * In the case of the MimeDir parser, this means that the parser will + * accept slashes and underscores in property names, and it will also + * attempt to fix Microsoft vCard 2.1's broken line folding. + */ + const OPTION_FORGIVING = 1; + + /** + * If this option is turned on, any lines we cannot parse will be ignored + * by the reader. + */ + const OPTION_IGNORE_INVALID_LINES = 2; + + /** + * Bitmask of parser options. + * + * @var int + */ + protected $options; + + /** + * Creates the parser. + * + * Optionally, it's possible to parse the input stream here. + * + * @param mixed $input + * @param int $options Any parser options (OPTION constants). + * + * @return void + */ + function __construct($input = null, $options = 0) { + + if (!is_null($input)) { + $this->setInput($input); + } + $this->options = $options; + } + + /** + * This method starts the parsing process. + * + * If the input was not supplied during construction, it's possible to pass + * it here instead. + * + * If either input or options are not supplied, the defaults will be used. + * + * @param mixed $input + * @param int $options + * + * @return array + */ + abstract function parse($input = null, $options = 0); + + /** + * Sets the input data. + * + * @param mixed $input + * + * @return void + */ + abstract function setInput($input); + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Parser/XML.php b/htdocs/includes/sabre/sabre/vobject/lib/Parser/XML.php new file mode 100644 index 00000000000..5ac42398477 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Parser/XML.php @@ -0,0 +1,428 @@ +<?php + +namespace Sabre\VObject\Parser; + +use Sabre\VObject\Component; +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\EofException; +use Sabre\VObject\ParseException; +use Sabre\Xml as SabreXml; + +/** + * XML Parser. + * + * This parser parses both the xCal and xCard formats. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Ivan Enderlin + * @license http://sabre.io/license/ Modified BSD License + */ +class XML extends Parser { + + const XCAL_NAMESPACE = 'urn:ietf:params:xml:ns:icalendar-2.0'; + const XCARD_NAMESPACE = 'urn:ietf:params:xml:ns:vcard-4.0'; + + /** + * The input data. + * + * @var array + */ + protected $input; + + /** + * A pointer/reference to the input. + * + * @var array + */ + private $pointer; + + /** + * Document, root component. + * + * @var Sabre\VObject\Document + */ + protected $root; + + /** + * Creates the parser. + * + * Optionally, it's possible to parse the input stream here. + * + * @param mixed $input + * @param int $options Any parser options (OPTION constants). + * + * @return void + */ + function __construct($input = null, $options = 0) { + + if (0 === $options) { + $options = parent::OPTION_FORGIVING; + } + + parent::__construct($input, $options); + + } + + /** + * Parse xCal or xCard. + * + * @param resource|string $input + * @param int $options + * + * @throws \Exception + * + * @return Sabre\VObject\Document + */ + function parse($input = null, $options = 0) { + + if (!is_null($input)) { + $this->setInput($input); + } + + if (0 !== $options) { + $this->options = $options; + } + + if (is_null($this->input)) { + throw new EofException('End of input stream, or no input supplied'); + } + + switch ($this->input['name']) { + + case '{' . self::XCAL_NAMESPACE . '}icalendar': + $this->root = new VCalendar([], false); + $this->pointer = &$this->input['value'][0]; + $this->parseVCalendarComponents($this->root); + break; + + case '{' . self::XCARD_NAMESPACE . '}vcards': + foreach ($this->input['value'] as &$vCard) { + + $this->root = new VCard(['version' => '4.0'], false); + $this->pointer = &$vCard; + $this->parseVCardComponents($this->root); + + // We just parse the first <vcard /> element. + break; + + } + break; + + default: + throw new ParseException('Unsupported XML standard'); + + } + + return $this->root; + } + + /** + * Parse a xCalendar component. + * + * @param Component $parentComponent + * + * @return void + */ + protected function parseVCalendarComponents(Component $parentComponent) { + + foreach ($this->pointer['value'] ?: [] as $children) { + + switch (static::getTagName($children['name'])) { + + case 'properties': + $this->pointer = &$children['value']; + $this->parseProperties($parentComponent); + break; + + case 'components': + $this->pointer = &$children; + $this->parseComponent($parentComponent); + break; + } + } + + } + + /** + * Parse a xCard component. + * + * @param Component $parentComponent + * + * @return void + */ + protected function parseVCardComponents(Component $parentComponent) { + + $this->pointer = &$this->pointer['value']; + $this->parseProperties($parentComponent); + + } + + /** + * Parse xCalendar and xCard properties. + * + * @param Component $parentComponent + * @param string $propertyNamePrefix + * + * @return void + */ + protected function parseProperties(Component $parentComponent, $propertyNamePrefix = '') { + + foreach ($this->pointer ?: [] as $xmlProperty) { + + list($namespace, $tagName) = SabreXml\Service::parseClarkNotation($xmlProperty['name']); + + $propertyName = $tagName; + $propertyValue = []; + $propertyParameters = []; + $propertyType = 'text'; + + // A property which is not part of the standard. + if ($namespace !== self::XCAL_NAMESPACE + && $namespace !== self::XCARD_NAMESPACE) { + + $propertyName = 'xml'; + $value = '<' . $tagName . ' xmlns="' . $namespace . '"'; + + foreach ($xmlProperty['attributes'] as $attributeName => $attributeValue) { + $value .= ' ' . $attributeName . '="' . str_replace('"', '\"', $attributeValue) . '"'; + } + + $value .= '>' . $xmlProperty['value'] . '</' . $tagName . '>'; + + $propertyValue = [$value]; + + $this->createProperty( + $parentComponent, + $propertyName, + $propertyParameters, + $propertyType, + $propertyValue + ); + + continue; + } + + // xCard group. + if ($propertyName === 'group') { + + if (!isset($xmlProperty['attributes']['name'])) { + continue; + } + + $this->pointer = &$xmlProperty['value']; + $this->parseProperties( + $parentComponent, + strtoupper($xmlProperty['attributes']['name']) . '.' + ); + + continue; + + } + + // Collect parameters. + foreach ($xmlProperty['value'] as $i => $xmlPropertyChild) { + + if (!is_array($xmlPropertyChild) + || 'parameters' !== static::getTagName($xmlPropertyChild['name'])) + continue; + + $xmlParameters = $xmlPropertyChild['value']; + + foreach ($xmlParameters as $xmlParameter) { + + $propertyParameterValues = []; + + foreach ($xmlParameter['value'] as $xmlParameterValues) { + $propertyParameterValues[] = $xmlParameterValues['value']; + } + + $propertyParameters[static::getTagName($xmlParameter['name'])] + = implode(',', $propertyParameterValues); + + } + + array_splice($xmlProperty['value'], $i, 1); + + } + + $propertyNameExtended = ($this->root instanceof VCalendar + ? 'xcal' + : 'xcard') . ':' . $propertyName; + + switch ($propertyNameExtended) { + + case 'xcal:geo': + $propertyType = 'float'; + $propertyValue['latitude'] = 0; + $propertyValue['longitude'] = 0; + + foreach ($xmlProperty['value'] as $xmlRequestChild) { + $propertyValue[static::getTagName($xmlRequestChild['name'])] + = $xmlRequestChild['value']; + } + break; + + case 'xcal:request-status': + $propertyType = 'text'; + + foreach ($xmlProperty['value'] as $xmlRequestChild) { + $propertyValue[static::getTagName($xmlRequestChild['name'])] + = $xmlRequestChild['value']; + } + break; + + case 'xcal:freebusy': + $propertyType = 'freebusy'; + // We don't break because we only want to set + // another property type. + + case 'xcal:categories': + case 'xcal:resources': + case 'xcal:exdate': + foreach ($xmlProperty['value'] as $specialChild) { + $propertyValue[static::getTagName($specialChild['name'])] + = $specialChild['value']; + } + break; + + case 'xcal:rdate': + $propertyType = 'date-time'; + + foreach ($xmlProperty['value'] as $specialChild) { + + $tagName = static::getTagName($specialChild['name']); + + if ('period' === $tagName) { + + $propertyParameters['value'] = 'PERIOD'; + $propertyValue[] = implode('/', $specialChild['value']); + + } + else { + $propertyValue[] = $specialChild['value']; + } + } + break; + + default: + $propertyType = static::getTagName($xmlProperty['value'][0]['name']); + + foreach ($xmlProperty['value'] as $value) { + $propertyValue[] = $value['value']; + } + + if ('date' === $propertyType) { + $propertyParameters['value'] = 'DATE'; + } + break; + } + + $this->createProperty( + $parentComponent, + $propertyNamePrefix . $propertyName, + $propertyParameters, + $propertyType, + $propertyValue + ); + + } + + } + + /** + * Parse a component. + * + * @param Component $parentComponent + * + * @return void + */ + protected function parseComponent(Component $parentComponent) { + + $components = $this->pointer['value'] ?: []; + + foreach ($components as $component) { + + $componentName = static::getTagName($component['name']); + $currentComponent = $this->root->createComponent( + $componentName, + null, + false + ); + + $this->pointer = &$component; + $this->parseVCalendarComponents($currentComponent); + + $parentComponent->add($currentComponent); + + } + + } + + /** + * Create a property. + * + * @param Component $parentComponent + * @param string $name + * @param array $parameters + * @param string $type + * @param mixed $value + * + * @return void + */ + protected function createProperty(Component $parentComponent, $name, $parameters, $type, $value) { + + $property = $this->root->createProperty( + $name, + null, + $parameters, + $type + ); + $parentComponent->add($property); + $property->setXmlValue($value); + + } + + /** + * Sets the input data. + * + * @param resource|string $input + * + * @return void + */ + function setInput($input) { + + if (is_resource($input)) { + $input = stream_get_contents($input); + } + + if (is_string($input)) { + + $reader = new SabreXml\Reader(); + $reader->elementMap['{' . self::XCAL_NAMESPACE . '}period'] + = 'Sabre\VObject\Parser\XML\Element\KeyValue'; + $reader->elementMap['{' . self::XCAL_NAMESPACE . '}recur'] + = 'Sabre\VObject\Parser\XML\Element\KeyValue'; + $reader->xml($input); + $input = $reader->parse(); + + } + + $this->input = $input; + + } + + /** + * Get tag name from a Clark notation. + * + * @param string $clarkedTagName + * + * @return string + */ + protected static function getTagName($clarkedTagName) { + + list(, $tagName) = SabreXml\Service::parseClarkNotation($clarkedTagName); + return $tagName; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Parser/XML/Element/KeyValue.php b/htdocs/includes/sabre/sabre/vobject/lib/Parser/XML/Element/KeyValue.php new file mode 100644 index 00000000000..14d7984332b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Parser/XML/Element/KeyValue.php @@ -0,0 +1,70 @@ +<?php + +namespace Sabre\VObject\Parser\XML\Element; + +use Sabre\Xml as SabreXml; + +/** + * Our own sabre/xml key-value element. + * + * It just removes the clark notation. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Ivan Enderlin + * @license http://sabre.io/license/ Modified BSD License + */ +class KeyValue extends SabreXml\Element\KeyValue { + + /** + * The deserialize method is called during xml parsing. + * + * This method is called staticly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param XML\Reader $reader + * + * @return mixed + */ + static function xmlDeserialize(SabreXml\Reader $reader) { + + // If there's no children, we don't do anything. + if ($reader->isEmptyElement) { + $reader->next(); + return []; + } + + $values = []; + $reader->read(); + + do { + + if ($reader->nodeType === SabreXml\Reader::ELEMENT) { + + $name = $reader->localName; + $values[$name] = $reader->parseCurrentElement()['value']; + + } else { + $reader->read(); + } + + } while ($reader->nodeType !== SabreXml\Reader::END_ELEMENT); + + $reader->read(); + + return $values; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property.php b/htdocs/includes/sabre/sabre/vobject/lib/Property.php new file mode 100644 index 00000000000..1aaa3ed5869 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property.php @@ -0,0 +1,662 @@ +<?php + +namespace Sabre\VObject; + +use Sabre\Xml; + +/** + * Property. + * + * A property is always in a KEY:VALUE structure, and may optionally contain + * parameters. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class Property extends Node { + + /** + * Property name. + * + * This will contain a string such as DTSTART, SUMMARY, FN. + * + * @var string + */ + public $name; + + /** + * Property group. + * + * This is only used in vcards + * + * @var string + */ + public $group; + + /** + * List of parameters. + * + * @var array + */ + public $parameters = []; + + /** + * Current value. + * + * @var mixed + */ + protected $value; + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = ';'; + + /** + * Creates the generic property. + * + * Parameters must be specified in key=>value syntax. + * + * @param Component $root The root document + * @param string $name + * @param string|array|null $value + * @param array $parameters List of parameters + * @param string $group The vcard property group + * + * @return void + */ + function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) { + + $this->name = $name; + $this->group = $group; + + $this->root = $root; + + foreach ($parameters as $k => $v) { + $this->add($k, $v); + } + + if (!is_null($value)) { + $this->setValue($value); + } + + } + + /** + * Updates the current value. + * + * This may be either a single, or multiple strings in an array. + * + * @param string|array $value + * + * @return void + */ + function setValue($value) { + + $this->value = $value; + + } + + /** + * Returns the current value. + * + * This method will always return a singular value. If this was a + * multi-value object, some decision will be made first on how to represent + * it as a string. + * + * To get the correct multi-value version, use getParts. + * + * @return string + */ + function getValue() { + + if (is_array($this->value)) { + if (count($this->value) == 0) { + return; + } elseif (count($this->value) === 1) { + return $this->value[0]; + } else { + return $this->getRawMimeDirValue(); + } + } else { + return $this->value; + } + + } + + /** + * Sets a multi-valued property. + * + * @param array $parts + * + * @return void + */ + function setParts(array $parts) { + + $this->value = $parts; + + } + + /** + * Returns a multi-valued property. + * + * This method always returns an array, if there was only a single value, + * it will still be wrapped in an array. + * + * @return array + */ + function getParts() { + + if (is_null($this->value)) { + return []; + } elseif (is_array($this->value)) { + return $this->value; + } else { + return [$this->value]; + } + + } + + /** + * Adds a new parameter. + * + * If a parameter with same name already existed, the values will be + * combined. + * If nameless parameter is added, we try to guess it's name. + * + * @param string $name + * @param string|null|array $value + */ + function add($name, $value = null) { + $noName = false; + if ($name === null) { + $name = Parameter::guessParameterNameByValue($value); + $noName = true; + } + + if (isset($this->parameters[strtoupper($name)])) { + $this->parameters[strtoupper($name)]->addValue($value); + } + else { + $param = new Parameter($this->root, $name, $value); + $param->noName = $noName; + $this->parameters[$param->name] = $param; + } + } + + /** + * Returns an iterable list of children. + * + * @return array + */ + function parameters() { + + return $this->parameters; + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + abstract function getValueType(); + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + abstract function setRawMimeDirValue($val); + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + abstract function getRawMimeDirValue(); + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + function serialize() { + + $str = $this->name; + if ($this->group) $str = $this->group . '.' . $this->name; + + foreach ($this->parameters() as $param) { + + $str .= ';' . $param->serialize(); + + } + + $str .= ':' . $this->getRawMimeDirValue(); + + $out = ''; + while (strlen($str) > 0) { + if (strlen($str) > 75) { + $out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n"; + $str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8'); + } else { + $out .= $str . "\r\n"; + $str = ''; + break; + } + } + + return $out; + + } + + /** + * Returns the value, in the format it should be encoded for JSON. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + return $this->getParts(); + + } + + /** + * Sets the JSON value, as it would appear in a jCard or jCal object. + * + * The value must always be an array. + * + * @param array $value + * + * @return void + */ + function setJsonValue(array $value) { + + if (count($value) === 1) { + $this->setValue(reset($value)); + } else { + $this->setValue($value); + } + + } + + /** + * This method returns an array, with the representation as it should be + * encoded in JSON. This is used to create jCard or jCal documents. + * + * @return array + */ + function jsonSerialize() { + + $parameters = []; + + foreach ($this->parameters as $parameter) { + if ($parameter->name === 'VALUE') { + continue; + } + $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize(); + } + // In jCard, we need to encode the property-group as a separate 'group' + // parameter. + if ($this->group) { + $parameters['group'] = $this->group; + } + + return array_merge( + [ + strtolower($this->name), + (object)$parameters, + strtolower($this->getValueType()), + ], + $this->getJsonValue() + ); + } + + /** + * Hydrate data from a XML subtree, as it would appear in a xCard or xCal + * object. + * + * @param array $value + * + * @return void + */ + function setXmlValue(array $value) { + + $this->setJsonValue($value); + + } + + /** + * This method serializes the data into XML. This is used to create xCard or + * xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $parameters = []; + + foreach ($this->parameters as $parameter) { + + if ($parameter->name === 'VALUE') { + continue; + } + + $parameters[] = $parameter; + + } + + $writer->startElement(strtolower($this->name)); + + if (!empty($parameters)) { + + $writer->startElement('parameters'); + + foreach ($parameters as $parameter) { + + $writer->startElement(strtolower($parameter->name)); + $writer->write($parameter); + $writer->endElement(); + + } + + $writer->endElement(); + + } + + $this->xmlSerializeValue($writer); + $writer->endElement(); + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + $valueType = strtolower($this->getValueType()); + + foreach ($this->getJsonValue() as $values) { + foreach ((array)$values as $value) { + $writer->writeElement($valueType, $value); + } + } + + } + + /** + * Called when this object is being cast to a string. + * + * If the property only had a single value, you will get just that. In the + * case the property had multiple values, the contents will be escaped and + * combined with ,. + * + * @return string + */ + function __toString() { + + return (string)$this->getValue(); + + } + + /* ArrayAccess interface {{{ */ + + /** + * Checks if an array element exists. + * + * @param mixed $name + * + * @return bool + */ + function offsetExists($name) { + + if (is_int($name)) return parent::offsetExists($name); + + $name = strtoupper($name); + + foreach ($this->parameters as $parameter) { + if ($parameter->name == $name) return true; + } + return false; + + } + + /** + * Returns a parameter. + * + * If the parameter does not exist, null is returned. + * + * @param string $name + * + * @return Node + */ + function offsetGet($name) { + + if (is_int($name)) return parent::offsetGet($name); + $name = strtoupper($name); + + if (!isset($this->parameters[$name])) { + return; + } + + return $this->parameters[$name]; + + } + + /** + * Creates a new parameter. + * + * @param string $name + * @param mixed $value + * + * @return void + */ + function offsetSet($name, $value) { + + if (is_int($name)) { + parent::offsetSet($name, $value); + // @codeCoverageIgnoreStart + // This will never be reached, because an exception is always + // thrown. + return; + // @codeCoverageIgnoreEnd + } + + $param = new Parameter($this->root, $name, $value); + $this->parameters[$param->name] = $param; + + } + + /** + * Removes one or more parameters with the specified name. + * + * @param string $name + * + * @return void + */ + function offsetUnset($name) { + + if (is_int($name)) { + parent::offsetUnset($name); + // @codeCoverageIgnoreStart + // This will never be reached, because an exception is always + // thrown. + return; + // @codeCoverageIgnoreEnd + } + + unset($this->parameters[strtoupper($name)]); + + } + /* }}} */ + + /** + * This method is automatically called when the object is cloned. + * Specifically, this will ensure all child elements are also cloned. + * + * @return void + */ + function __clone() { + + foreach ($this->parameters as $key => $child) { + $this->parameters[$key] = clone $child; + $this->parameters[$key]->parent = $this; + } + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * - Node::REPAIR - If something is broken, and automatic repair may + * be attempted. + * + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $warnings = []; + + // Checking if our value is UTF-8 + if (!StringUtil::isUTF8($this->getRawMimeDirValue())) { + + $oldValue = $this->getRawMimeDirValue(); + $level = 3; + if ($options & self::REPAIR) { + $newValue = StringUtil::convertToUTF8($oldValue); + if (true || StringUtil::isUTF8($newValue)) { + $this->setRawMimeDirValue($newValue); + $level = 1; + } + + } + + + if (preg_match('%([\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', $oldValue, $matches)) { + $message = 'Property contained a control character (0x' . bin2hex($matches[1]) . ')'; + } else { + $message = 'Property is not valid UTF-8! ' . $oldValue; + } + + $warnings[] = [ + 'level' => $level, + 'message' => $message, + 'node' => $this, + ]; + } + + // Checking if the propertyname does not contain any invalid bytes. + if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { + $warnings[] = [ + 'level' => $options & self::REPAIR ? 1 : 3, + 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed', + 'node' => $this, + ]; + if ($options & self::REPAIR) { + // Uppercasing and converting underscores to dashes. + $this->name = strtoupper( + str_replace('_', '-', $this->name) + ); + // Removing every other invalid character + $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name); + + } + + } + + if ($encoding = $this->offsetGet('ENCODING')) { + + if ($this->root->getDocumentType() === Document::VCARD40) { + $warnings[] = [ + 'level' => 3, + 'message' => 'ENCODING parameter is not valid in vCard 4.', + 'node' => $this + ]; + } else { + + $encoding = (string)$encoding; + + $allowedEncoding = []; + + switch ($this->root->getDocumentType()) { + case Document::ICALENDAR20 : + $allowedEncoding = ['8BIT', 'BASE64']; + break; + case Document::VCARD21 : + $allowedEncoding = ['QUOTED-PRINTABLE', 'BASE64', '8BIT']; + break; + case Document::VCARD30 : + $allowedEncoding = ['B']; + break; + + } + if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding)) { + $warnings[] = [ + 'level' => 3, + 'message' => 'ENCODING=' . strtoupper($encoding) . ' is not valid for this document type.', + 'node' => $this + ]; + } + } + + } + + // Validating inner parameters + foreach ($this->parameters as $param) { + $warnings = array_merge($warnings, $param->validate($options)); + } + + return $warnings; + + } + + /** + * Call this method on a document if you're done using it. + * + * It's intended to remove all circular references, so PHP can easily clean + * it up. + * + * @return void + */ + function destroy() { + + parent::destroy(); + foreach ($this->parameters as $param) { + $param->destroy(); + } + $this->parameters = []; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/Binary.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/Binary.php new file mode 100644 index 00000000000..d54cae25da2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/Binary.php @@ -0,0 +1,128 @@ +<?php + +namespace Sabre\VObject\Property; + +use Sabre\VObject\Property; + +/** + * BINARY property. + * + * This object represents BINARY values. + * + * Binary values are most commonly used by the iCalendar ATTACH property, and + * the vCard PHOTO property. + * + * This property will transparently encode and decode to base64. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Binary extends Property { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = null; + + /** + * Updates the current value. + * + * This may be either a single, or multiple strings in an array. + * + * @param string|array $value + * + * @return void + */ + function setValue($value) { + + if (is_array($value)) { + + if (count($value) === 1) { + $this->value = $value[0]; + } else { + throw new \InvalidArgumentException('The argument must either be a string or an array with only one child'); + } + + } else { + + $this->value = $value; + + } + + } + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->value = base64_decode($val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return base64_encode($this->value); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'BINARY'; + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + return [base64_encode($this->getValue())]; + + } + + /** + * Sets the json value, as it would appear in a jCard or jCal object. + * + * The value must always be an array. + * + * @param array $value + * + * @return void + */ + function setJsonValue(array $value) { + + $value = array_map('base64_decode', $value); + parent::setJsonValue($value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/Boolean.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/Boolean.php new file mode 100644 index 00000000000..6f5887e25cd --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/Boolean.php @@ -0,0 +1,84 @@ +<?php + +namespace Sabre\VObject\Property; + +use + Sabre\VObject\Property; + +/** + * Boolean property. + * + * This object represents BOOLEAN values. These are always the case-insenstive + * string TRUE or FALSE. + * + * Automatic conversion to PHP's true and false are done. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Boolean extends Property { + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $val = strtoupper($val) === 'TRUE' ? true : false; + $this->setValue($val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return $this->value ? 'TRUE' : 'FALSE'; + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'BOOLEAN'; + + } + + /** + * Hydrate data from a XML subtree, as it would appear in a xCard or xCal + * object. + * + * @param array $value + * + * @return void + */ + function setXmlValue(array $value) { + + $value = array_map( + function($value) { + return 'true' === $value; + }, + $value + ); + parent::setXmlValue($value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/FlatText.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/FlatText.php new file mode 100644 index 00000000000..2c7b43c29d8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/FlatText.php @@ -0,0 +1,50 @@ +<?php + +namespace Sabre\VObject\Property; + +/** + * FlatText property. + * + * This object represents certain TEXT values. + * + * Specifically, this property is used for text values where there is only 1 + * part. Semi-colons and colons will be de-escaped when deserializing, but if + * any semi-colons or commas appear without a backslash, we will not assume + * that they are delimiters. + * + * vCard 2.1 specifically has a whole bunch of properties where this may + * happen, as it only defines a delimiter for a few properties. + * + * vCard 4.0 states something similar. An unescaped semi-colon _may_ be a + * delimiter, depending on the property. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class FlatText extends Text { + + /** + * Field separator. + * + * @var string + */ + public $delimiter = ','; + + /** + * Sets the value as a quoted-printable encoded string. + * + * Overriding this so we're not splitting on a ; delimiter. + * + * @param string $val + * + * @return void + */ + function setQuotedPrintableValue($val) { + + $val = quoted_printable_decode($val); + $this->setValue($val); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/FloatValue.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/FloatValue.php new file mode 100644 index 00000000000..15b1195491c --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/FloatValue.php @@ -0,0 +1,142 @@ +<?php + +namespace Sabre\VObject\Property; + +use Sabre\VObject\Property; +use Sabre\Xml; + +/** + * Float property. + * + * This object represents FLOAT values. These can be 1 or more floating-point + * numbers. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class FloatValue extends Property { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = ';'; + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $val = explode($this->delimiter, $val); + foreach ($val as &$item) { + $item = (float)$item; + } + $this->setParts($val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return implode( + $this->delimiter, + $this->getParts() + ); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'FLOAT'; + + } + + /** + * Returns the value, in the format it should be encoded for JSON. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $val = array_map('floatval', $this->getParts()); + + // Special-casing the GEO property. + // + // See: + // http://tools.ietf.org/html/draft-ietf-jcardcal-jcal-04#section-3.4.1.2 + if ($this->name === 'GEO') { + return [$val]; + } + + return $val; + + } + + /** + * Hydrate data from a XML subtree, as it would appear in a xCard or xCal + * object. + * + * @param array $value + * + * @return void + */ + function setXmlValue(array $value) { + + $value = array_map('floatval', $value); + parent::setXmlValue($value); + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + // Special-casing the GEO property. + // + // See: + // http://tools.ietf.org/html/rfc6321#section-3.4.1.2 + if ($this->name === 'GEO') { + + $value = array_map('floatval', $this->getParts()); + + $writer->writeElement('latitude', $value[0]); + $writer->writeElement('longitude', $value[1]); + + } + else { + parent::xmlSerializeValue($writer); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/CalAddress.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/CalAddress.php new file mode 100644 index 00000000000..a0c4a9b9add --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/CalAddress.php @@ -0,0 +1,61 @@ +<?php + +namespace Sabre\VObject\Property\ICalendar; + +use + Sabre\VObject\Property\Text; + +/** + * CalAddress property. + * + * This object encodes CAL-ADDRESS values, as defined in rfc5545 + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class CalAddress extends Text { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = null; + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'CAL-ADDRESS'; + + } + + /** + * This returns a normalized form of the value. + * + * This is primarily used right now to turn mixed-cased schemes in user + * uris to lower-case. + * + * Evolution in particular tends to encode mailto: as MAILTO:. + * + * @return string + */ + function getNormalizedValue() { + + $input = $this->getValue(); + if (!strpos($input, ':')) { + return $input; + } + list($schema, $everythingElse) = explode(':', $input, 2); + return strtolower($schema) . ':' . $everythingElse; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Date.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Date.php new file mode 100644 index 00000000000..378a0d60a3e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Date.php @@ -0,0 +1,18 @@ +<?php + +namespace Sabre\VObject\Property\ICalendar; + +/** + * DateTime property. + * + * This object represents DATE values, as defined here: + * + * http://tools.ietf.org/html/rfc5545#section-3.3.5 + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Date extends DateTime { + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/DateTime.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/DateTime.php new file mode 100644 index 00000000000..d580d4f68fe --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/DateTime.php @@ -0,0 +1,404 @@ +<?php + +namespace Sabre\VObject\Property\ICalendar; + +use DateTimeInterface; +use DateTimeZone; +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\InvalidDataException; +use Sabre\VObject\Property; +use Sabre\VObject\TimeZoneUtil; + +/** + * DateTime property. + * + * This object represents DATE-TIME values, as defined here: + * + * http://tools.ietf.org/html/rfc5545#section-3.3.4 + * + * This particular object has a bit of hackish magic that it may also in some + * cases represent a DATE value. This is because it's a common usecase to be + * able to change a DATE-TIME into a DATE. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class DateTime extends Property { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = ','; + + /** + * Sets a multi-valued property. + * + * You may also specify DateTime objects here. + * + * @param array $parts + * + * @return void + */ + function setParts(array $parts) { + + if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) { + $this->setDateTimes($parts); + } else { + parent::setParts($parts); + } + + } + + /** + * Updates the current value. + * + * This may be either a single, or multiple strings in an array. + * + * Instead of strings, you may also use DateTime here. + * + * @param string|array|DateTimeInterface $value + * + * @return void + */ + function setValue($value) { + + if (is_array($value) && isset($value[0]) && $value[0] instanceof DateTimeInterface) { + $this->setDateTimes($value); + } elseif ($value instanceof DateTimeInterface) { + $this->setDateTimes([$value]); + } else { + parent::setValue($value); + } + + } + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue(explode($this->delimiter, $val)); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return implode($this->delimiter, $this->getParts()); + + } + + /** + * Returns true if this is a DATE-TIME value, false if it's a DATE. + * + * @return bool + */ + function hasTime() { + + return strtoupper((string)$this['VALUE']) !== 'DATE'; + + } + + /** + * Returns true if this is a floating DATE or DATE-TIME. + * + * Note that DATE is always floating. + */ + function isFloating() { + + return + !$this->hasTime() || + ( + !isset($this['TZID']) && + strpos($this->getValue(), 'Z') === false + ); + + } + + /** + * Returns a date-time value. + * + * Note that if this property contained more than 1 date-time, only the + * first will be returned. To get an array with multiple values, call + * getDateTimes. + * + * If no timezone information is known, because it's either an all-day + * property or floating time, we will use the DateTimeZone argument to + * figure out the exact date. + * + * @param DateTimeZone $timeZone + * + * @return DateTimeImmutable + */ + function getDateTime(DateTimeZone $timeZone = null) { + + $dt = $this->getDateTimes($timeZone); + if (!$dt) return; + + return $dt[0]; + + } + + /** + * Returns multiple date-time values. + * + * If no timezone information is known, because it's either an all-day + * property or floating time, we will use the DateTimeZone argument to + * figure out the exact date. + * + * @param DateTimeZone $timeZone + * + * @return DateTimeImmutable[] + * @return \DateTime[] + */ + function getDateTimes(DateTimeZone $timeZone = null) { + + // Does the property have a TZID? + $tzid = $this['TZID']; + + if ($tzid) { + $timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root); + } + + $dts = []; + foreach ($this->getParts() as $part) { + $dts[] = DateTimeParser::parse($part, $timeZone); + } + return $dts; + + } + + /** + * Sets the property as a DateTime object. + * + * @param DateTimeInterface $dt + * @param bool isFloating If set to true, timezones will be ignored. + * + * @return void + */ + function setDateTime(DateTimeInterface $dt, $isFloating = false) { + + $this->setDateTimes([$dt], $isFloating); + + } + + /** + * Sets the property as multiple date-time objects. + * + * The first value will be used as a reference for the timezones, and all + * the otehr values will be adjusted for that timezone + * + * @param DateTimeInterface[] $dt + * @param bool isFloating If set to true, timezones will be ignored. + * + * @return void + */ + function setDateTimes(array $dt, $isFloating = false) { + + $values = []; + + if ($this->hasTime()) { + + $tz = null; + $isUtc = false; + + foreach ($dt as $d) { + + if ($isFloating) { + $values[] = $d->format('Ymd\\THis'); + continue; + } + if (is_null($tz)) { + $tz = $d->getTimeZone(); + $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']); + if (!$isUtc) { + $this->offsetSet('TZID', $tz->getName()); + } + } else { + $d = $d->setTimeZone($tz); + } + + if ($isUtc) { + $values[] = $d->format('Ymd\\THis\\Z'); + } else { + $values[] = $d->format('Ymd\\THis'); + } + + } + if ($isUtc || $isFloating) { + $this->offsetUnset('TZID'); + } + + } else { + + foreach ($dt as $d) { + + $values[] = $d->format('Ymd'); + + } + $this->offsetUnset('TZID'); + + } + + $this->value = $values; + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return $this->hasTime() ? 'DATE-TIME' : 'DATE'; + + } + + /** + * Returns the value, in the format it should be encoded for JSON. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $dts = $this->getDateTimes(); + $hasTime = $this->hasTime(); + $isFloating = $this->isFloating(); + + $tz = $dts[0]->getTimeZone(); + $isUtc = $isFloating ? false : in_array($tz->getName(), ['UTC', 'GMT', 'Z']); + + return array_map( + function(DateTimeInterface $dt) use ($hasTime, $isUtc) { + + if ($hasTime) { + return $dt->format('Y-m-d\\TH:i:s') . ($isUtc ? 'Z' : ''); + } else { + return $dt->format('Y-m-d'); + } + + }, + $dts + ); + + } + + /** + * Sets the json value, as it would appear in a jCard or jCal object. + * + * The value must always be an array. + * + * @param array $value + * + * @return void + */ + function setJsonValue(array $value) { + + // dates and times in jCal have one difference to dates and times in + // iCalendar. In jCal date-parts are separated by dashes, and + // time-parts are separated by colons. It makes sense to just remove + // those. + $this->setValue( + array_map( + function($item) { + + return strtr($item, [':' => '', '-' => '']); + + }, + $value + ) + ); + + } + + /** + * We need to intercept offsetSet, because it may be used to alter the + * VALUE from DATE-TIME to DATE or vice-versa. + * + * @param string $name + * @param mixed $value + * + * @return void + */ + function offsetSet($name, $value) { + + parent::offsetSet($name, $value); + if (strtoupper($name) !== 'VALUE') { + return; + } + + // This will ensure that dates are correctly encoded. + $this->setDateTimes($this->getDateTimes()); + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on) + * 2 - An inconsequential issue + * 3 - A severe issue. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $messages = parent::validate($options); + $valueType = $this->getValueType(); + $values = $this->getParts(); + try { + foreach ($values as $value) { + switch ($valueType) { + case 'DATE' : + DateTimeParser::parseDate($value); + break; + case 'DATE-TIME' : + DateTimeParser::parseDateTime($value); + break; + } + } + } catch (InvalidDataException $e) { + $messages[] = [ + 'level' => 3, + 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType, + 'node' => $this, + ]; + } + return $messages; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Duration.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Duration.php new file mode 100644 index 00000000000..7b7e1ce8ea5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Duration.php @@ -0,0 +1,85 @@ +<?php + +namespace Sabre\VObject\Property\ICalendar; + +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\Property; + +/** + * Duration property. + * + * This object represents DURATION values, as defined here: + * + * http://tools.ietf.org/html/rfc5545#section-3.3.6 + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Duration extends Property { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = ','; + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue(explode($this->delimiter, $val)); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return implode($this->delimiter, $this->getParts()); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'DURATION'; + + } + + /** + * Returns a DateInterval representation of the Duration property. + * + * If the property has more than one value, only the first is returned. + * + * @return \DateInterval + */ + function getDateInterval() { + + $parts = $this->getParts(); + $value = $parts[0]; + return DateTimeParser::parseDuration($value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Period.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Period.php new file mode 100644 index 00000000000..d35b425aa8e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Period.php @@ -0,0 +1,155 @@ +<?php + +namespace Sabre\VObject\Property\ICalendar; + +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\Property; +use Sabre\Xml; + +/** + * Period property. + * + * This object represents PERIOD values, as defined here: + * + * http://tools.ietf.org/html/rfc5545#section-3.8.2.6 + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Period extends Property { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = ','; + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue(explode($this->delimiter, $val)); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return implode($this->delimiter, $this->getParts()); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'PERIOD'; + + } + + /** + * Sets the json value, as it would appear in a jCard or jCal object. + * + * The value must always be an array. + * + * @param array $value + * + * @return void + */ + function setJsonValue(array $value) { + + $value = array_map( + function($item) { + + return strtr(implode('/', $item), [':' => '', '-' => '']); + + }, + $value + ); + parent::setJsonValue($value); + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $return = []; + foreach ($this->getParts() as $item) { + + list($start, $end) = explode('/', $item, 2); + + $start = DateTimeParser::parseDateTime($start); + + // This is a duration value. + if ($end[0] === 'P') { + $return[] = [ + $start->format('Y-m-d\\TH:i:s'), + $end + ]; + } else { + $end = DateTimeParser::parseDateTime($end); + $return[] = [ + $start->format('Y-m-d\\TH:i:s'), + $end->format('Y-m-d\\TH:i:s'), + ]; + } + + } + + return $return; + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + $writer->startElement(strtolower($this->getValueType())); + $value = $this->getJsonValue(); + $writer->writeElement('start', $value[0][0]); + + if ($value[0][1][0] === 'P') { + $writer->writeElement('duration', $value[0][1]); + } + else { + $writer->writeElement('end', $value[0][1]); + } + + $writer->endElement(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Recur.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Recur.php new file mode 100644 index 00000000000..434b770884b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/ICalendar/Recur.php @@ -0,0 +1,359 @@ +<?php + +namespace Sabre\VObject\Property\ICalendar; + +use Sabre\VObject\Property; +use Sabre\Xml; + +/** + * Recur property. + * + * This object represents RECUR properties. + * These values are just used for RRULE and the now deprecated EXRULE. + * + * The RRULE property may look something like this: + * + * RRULE:FREQ=MONTHLY;BYDAY=1,2,3;BYHOUR=5. + * + * This property exposes this as a key=>value array that is accessible using + * getParts, and may be set using setParts. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Recur extends Property { + + /** + * Updates the current value. + * + * This may be either a single, or multiple strings in an array. + * + * @param string|array $value + * + * @return void + */ + function setValue($value) { + + // If we're getting the data from json, we'll be receiving an object + if ($value instanceof \StdClass) { + $value = (array)$value; + } + + if (is_array($value)) { + $newVal = []; + foreach ($value as $k => $v) { + + if (is_string($v)) { + $v = strtoupper($v); + + // The value had multiple sub-values + if (strpos($v, ',') !== false) { + $v = explode(',', $v); + } + if (strcmp($k, 'until') === 0) { + $v = strtr($v, [':' => '', '-' => '']); + } + } elseif (is_array($v)) { + $v = array_map('strtoupper', $v); + } + + $newVal[strtoupper($k)] = $v; + } + $this->value = $newVal; + } elseif (is_string($value)) { + $this->value = self::stringToArray($value); + } else { + throw new \InvalidArgumentException('You must either pass a string, or a key=>value array'); + } + + } + + /** + * Returns the current value. + * + * This method will always return a singular value. If this was a + * multi-value object, some decision will be made first on how to represent + * it as a string. + * + * To get the correct multi-value version, use getParts. + * + * @return string + */ + function getValue() { + + $out = []; + foreach ($this->value as $key => $value) { + $out[] = $key . '=' . (is_array($value) ? implode(',', $value) : $value); + } + return strtoupper(implode(';', $out)); + + } + + /** + * Sets a multi-valued property. + * + * @param array $parts + * @return void + */ + function setParts(array $parts) { + + $this->setValue($parts); + + } + + /** + * Returns a multi-valued property. + * + * This method always returns an array, if there was only a single value, + * it will still be wrapped in an array. + * + * @return array + */ + function getParts() { + + return $this->value; + + } + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue($val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return $this->getValue(); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'RECUR'; + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $values = []; + foreach ($this->getParts() as $k => $v) { + if (strcmp($k, 'UNTIL') === 0) { + $date = new DateTime($this->root, null, $v); + $values[strtolower($k)] = $date->getJsonValue()[0]; + } elseif (strcmp($k, 'COUNT') === 0) { + $values[strtolower($k)] = intval($v); + } else { + $values[strtolower($k)] = $v; + } + } + return [$values]; + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + $valueType = strtolower($this->getValueType()); + + foreach ($this->getJsonValue() as $value) { + $writer->writeElement($valueType, $value); + } + + } + + /** + * Parses an RRULE value string, and turns it into a struct-ish array. + * + * @param string $value + * + * @return array + */ + static function stringToArray($value) { + + $value = strtoupper($value); + $newValue = []; + foreach (explode(';', $value) as $part) { + + // Skipping empty parts. + if (empty($part)) { + continue; + } + list($partName, $partValue) = explode('=', $part); + + // The value itself had multiple values.. + if (strpos($partValue, ',') !== false) { + $partValue = explode(',', $partValue); + } + $newValue[$partName] = $partValue; + + } + + return $newValue; + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on) + * 2 - An inconsequential issue + * 3 - A severe issue. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $repair = ($options & self::REPAIR); + + $warnings = parent::validate($options); + $values = $this->getParts(); + + foreach ($values as $key => $value) { + + if ($value === '') { + $warnings[] = [ + 'level' => $repair ? 1 : 3, + 'message' => 'Invalid value for ' . $key . ' in ' . $this->name, + 'node' => $this + ]; + if ($repair) { + unset($values[$key]); + } + } elseif ($key == 'BYMONTH') { + $byMonth = (array)$value; + foreach ($byMonth as $i => $v) { + if (!is_numeric($v) || (int)$v < 1 || (int)$v > 12) { + $warnings[] = [ + 'level' => $repair ? 1 : 3, + 'message' => 'BYMONTH in RRULE must have value(s) between 1 and 12!', + 'node' => $this + ]; + if ($repair) { + if (is_array($value)) { + unset($values[$key][$i]); + } else { + unset($values[$key]); + } + } + } + } + // if there is no valid entry left, remove the whole value + if (is_array($value) && empty($values[$key])) { + unset($values[$key]); + } + } elseif ($key == 'BYWEEKNO') { + $byWeekNo = (array)$value; + foreach ($byWeekNo as $i => $v) { + if (!is_numeric($v) || (int)$v < -53 || (int)$v == 0 || (int)$v > 53) { + $warnings[] = [ + 'level' => $repair ? 1 : 3, + 'message' => 'BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', + 'node' => $this + ]; + if ($repair) { + if (is_array($value)) { + unset($values[$key][$i]); + } else { + unset($values[$key]); + } + } + } + } + // if there is no valid entry left, remove the whole value + if (is_array($value) && empty($values[$key])) { + unset($values[$key]); + } + } elseif ($key == 'BYYEARDAY') { + $byYearDay = (array)$value; + foreach ($byYearDay as $i => $v) { + if (!is_numeric($v) || (int)$v < -366 || (int)$v == 0 || (int)$v > 366) { + $warnings[] = [ + 'level' => $repair ? 1 : 3, + 'message' => 'BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', + 'node' => $this + ]; + if ($repair) { + if (is_array($value)) { + unset($values[$key][$i]); + } else { + unset($values[$key]); + } + } + } + } + // if there is no valid entry left, remove the whole value + if (is_array($value) && empty($values[$key])) { + unset($values[$key]); + } + } + + } + if (!isset($values['FREQ'])) { + $warnings[] = [ + 'level' => $repair ? 1 : 3, + 'message' => 'FREQ is required in ' . $this->name, + 'node' => $this + ]; + if ($repair) { + $this->parent->remove($this); + } + } + if ($repair) { + $this->setValue($values); + } + + return $warnings; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/IntegerValue.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/IntegerValue.php new file mode 100644 index 00000000000..5bd1887fad2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/IntegerValue.php @@ -0,0 +1,88 @@ +<?php + +namespace Sabre\VObject\Property; + +use + Sabre\VObject\Property; + +/** + * Integer property. + * + * This object represents INTEGER values. These are always a single integer. + * They may be preceeded by either + or -. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class IntegerValue extends Property { + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue((int)$val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return $this->value; + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'INTEGER'; + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + return [(int)$this->getValue()]; + + } + + /** + * Hydrate data from a XML subtree, as it would appear in a xCard or xCal + * object. + * + * @param array $value + * + * @return void + */ + function setXmlValue(array $value) { + + $value = array_map('intval', $value); + parent::setXmlValue($value); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/Text.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/Text.php new file mode 100644 index 00000000000..476dcde4d33 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/Text.php @@ -0,0 +1,413 @@ +<?php + +namespace Sabre\VObject\Property; + +use Sabre\VObject\Component; +use Sabre\VObject\Document; +use Sabre\VObject\Parser\MimeDir; +use Sabre\VObject\Property; +use Sabre\Xml; + +/** + * Text property. + * + * This object represents TEXT values. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Text extends Property { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string + */ + public $delimiter = ','; + + /** + * List of properties that are considered 'structured'. + * + * @var array + */ + protected $structuredValues = [ + // vCard + 'N', + 'ADR', + 'ORG', + 'GENDER', + 'CLIENTPIDMAP', + + // iCalendar + 'REQUEST-STATUS', + ]; + + /** + * Some text components have a minimum number of components. + * + * N must for instance be represented as 5 components, separated by ;, even + * if the last few components are unused. + * + * @var array + */ + protected $minimumPropertyValues = [ + 'N' => 5, + 'ADR' => 7, + ]; + + /** + * Creates the property. + * + * You can specify the parameters either in key=>value syntax, in which case + * parameters will automatically be created, or you can just pass a list of + * Parameter objects. + * + * @param Component $root The root document + * @param string $name + * @param string|array|null $value + * @param array $parameters List of parameters + * @param string $group The vcard property group + * + * @return void + */ + function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) { + + // There's two types of multi-valued text properties: + // 1. multivalue properties. + // 2. structured value properties + // + // The former is always separated by a comma, the latter by semi-colon. + if (in_array($name, $this->structuredValues)) { + $this->delimiter = ';'; + } + + parent::__construct($root, $name, $value, $parameters, $group); + + } + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue(MimeDir::unescapeValue($val, $this->delimiter)); + + } + + /** + * Sets the value as a quoted-printable encoded string. + * + * @param string $val + * + * @return void + */ + function setQuotedPrintableValue($val) { + + $val = quoted_printable_decode($val); + + // Quoted printable only appears in vCard 2.1, and the only character + // that may be escaped there is ;. So we are simply splitting on just + // that. + // + // We also don't have to unescape \\, so all we need to look for is a ; + // that's not preceeded with a \. + $regex = '# (?<!\\\\) ; #x'; + $matches = preg_split($regex, $val); + $this->setValue($matches); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + $val = $this->getParts(); + + if (isset($this->minimumPropertyValues[$this->name])) { + $val = array_pad($val, $this->minimumPropertyValues[$this->name], ''); + } + + foreach ($val as &$item) { + + if (!is_array($item)) { + $item = [$item]; + } + + foreach ($item as &$subItem) { + $subItem = strtr( + $subItem, + [ + '\\' => '\\\\', + ';' => '\;', + ',' => '\,', + "\n" => '\n', + "\r" => "", + ] + ); + } + $item = implode(',', $item); + + } + + return implode($this->delimiter, $val); + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + // Structured text values should always be returned as a single + // array-item. Multi-value text should be returned as multiple items in + // the top-array. + if (in_array($this->name, $this->structuredValues)) { + return [$this->getParts()]; + } + return $this->getParts(); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'TEXT'; + + } + + /** + * Turns the object back into a serialized blob. + * + * @return string + */ + function serialize() { + + // We need to kick in a special type of encoding, if it's a 2.1 vcard. + if ($this->root->getDocumentType() !== Document::VCARD21) { + return parent::serialize(); + } + + $val = $this->getParts(); + + if (isset($this->minimumPropertyValues[$this->name])) { + $val = array_pad($val, $this->minimumPropertyValues[$this->name], ''); + } + + // Imploding multiple parts into a single value, and splitting the + // values with ;. + if (count($val) > 1) { + foreach ($val as $k => $v) { + $val[$k] = str_replace(';', '\;', $v); + } + $val = implode(';', $val); + } else { + $val = $val[0]; + } + + $str = $this->name; + if ($this->group) $str = $this->group . '.' . $this->name; + foreach ($this->parameters as $param) { + + if ($param->getValue() === 'QUOTED-PRINTABLE') { + continue; + } + $str .= ';' . $param->serialize(); + + } + + + + // If the resulting value contains a \n, we must encode it as + // quoted-printable. + if (strpos($val, "\n") !== false) { + + $str .= ';ENCODING=QUOTED-PRINTABLE:'; + $lastLine = $str; + $out = null; + + // The PHP built-in quoted-printable-encode does not correctly + // encode newlines for us. Specifically, the \r\n sequence must in + // vcards be encoded as =0D=OA and we must insert soft-newlines + // every 75 bytes. + for ($ii = 0;$ii < strlen($val);$ii++) { + $ord = ord($val[$ii]); + // These characters are encoded as themselves. + if ($ord >= 32 && $ord <= 126) { + $lastLine .= $val[$ii]; + } else { + $lastLine .= '=' . strtoupper(bin2hex($val[$ii])); + } + if (strlen($lastLine) >= 75) { + // Soft line break + $out .= $lastLine . "=\r\n "; + $lastLine = null; + } + + } + if (!is_null($lastLine)) $out .= $lastLine . "\r\n"; + return $out; + + } else { + $str .= ':' . $val; + $out = ''; + while (strlen($str) > 0) { + if (strlen($str) > 75) { + $out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n"; + $str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8'); + } else { + $out .= $str . "\r\n"; + $str = ''; + break; + } + } + + return $out; + + } + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + $values = $this->getParts(); + + $map = function($items) use ($values, $writer) { + foreach ($items as $i => $item) { + $writer->writeElement( + $item, + !empty($values[$i]) ? $values[$i] : null + ); + } + }; + + switch ($this->name) { + + // Special-casing the REQUEST-STATUS property. + // + // See: + // http://tools.ietf.org/html/rfc6321#section-3.4.1.3 + case 'REQUEST-STATUS': + $writer->writeElement('code', $values[0]); + $writer->writeElement('description', $values[1]); + + if (isset($values[2])) { + $writer->writeElement('data', $values[2]); + } + break; + + case 'N': + $map([ + 'surname', + 'given', + 'additional', + 'prefix', + 'suffix' + ]); + break; + + case 'GENDER': + $map([ + 'sex', + 'text' + ]); + break; + + case 'ADR': + $map([ + 'pobox', + 'ext', + 'street', + 'locality', + 'region', + 'code', + 'country' + ]); + break; + + case 'CLIENTPIDMAP': + $map([ + 'sourceid', + 'uri' + ]); + break; + + default: + parent::xmlSerializeValue($writer); + } + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * - Node::REPAIR - If something is broken, and automatic repair may + * be attempted. + * + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $warnings = parent::validate($options); + + if (isset($this->minimumPropertyValues[$this->name])) { + + $minimum = $this->minimumPropertyValues[$this->name]; + $parts = $this->getParts(); + if (count($parts) < $minimum) { + $warnings[] = [ + 'level' => $options & self::REPAIR ? 1 : 3, + 'message' => 'The ' . $this->name . ' property must have at least ' . $minimum . ' values. It only has ' . count($parts), + 'node' => $this, + ]; + if ($options & self::REPAIR) { + $parts = array_pad($parts, $minimum, ''); + $this->setParts($parts); + } + } + + } + return $warnings; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/Time.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/Time.php new file mode 100644 index 00000000000..dbafc3b85cd --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/Time.php @@ -0,0 +1,144 @@ +<?php + +namespace Sabre\VObject\Property; + +use Sabre\VObject\DateTimeParser; + +/** + * Time property. + * + * This object encodes TIME values. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Time extends Text { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = null; + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'TIME'; + + } + + /** + * Sets the JSON value, as it would appear in a jCard or jCal object. + * + * The value must always be an array. + * + * @param array $value + * + * @return void + */ + function setJsonValue(array $value) { + + // Removing colons from value. + $value = str_replace( + ':', + '', + $value + ); + + if (count($value) === 1) { + $this->setValue(reset($value)); + } else { + $this->setValue($value); + } + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $parts = DateTimeParser::parseVCardTime($this->getValue()); + $timeStr = ''; + + // Hour + if (!is_null($parts['hour'])) { + $timeStr .= $parts['hour']; + + if (!is_null($parts['minute'])) { + $timeStr .= ':'; + } + } else { + // We know either minute or second _must_ be set, so we insert a + // dash for an empty value. + $timeStr .= '-'; + } + + // Minute + if (!is_null($parts['minute'])) { + $timeStr .= $parts['minute']; + + if (!is_null($parts['second'])) { + $timeStr .= ':'; + } + } else { + if (isset($parts['second'])) { + // Dash for empty minute + $timeStr .= '-'; + } + } + + // Second + if (!is_null($parts['second'])) { + $timeStr .= $parts['second']; + } + + // Timezone + if (!is_null($parts['timezone'])) { + if ($parts['timezone'] === 'Z') { + $timeStr .= 'Z'; + } else { + $timeStr .= + preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', $parts['timezone']); + } + } + + return [$timeStr]; + + } + + /** + * Hydrate data from a XML subtree, as it would appear in a xCard or xCal + * object. + * + * @param array $value + * + * @return void + */ + function setXmlValue(array $value) { + + $value = array_map( + function($value) { + return str_replace(':', '', $value); + }, + $value + ); + parent::setXmlValue($value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/Unknown.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/Unknown.php new file mode 100644 index 00000000000..7a337386835 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/Unknown.php @@ -0,0 +1,44 @@ +<?php + +namespace Sabre\VObject\Property; + +/** + * Unknown property. + * + * This object represents any properties not recognized by the parser. + * This type of value has been introduced by the jCal, jCard specs. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Unknown extends Text { + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + return [$this->getRawMimeDirValue()]; + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'UNKNOWN'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/Uri.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/Uri.php new file mode 100644 index 00000000000..88fcfaab888 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/Uri.php @@ -0,0 +1,122 @@ +<?php + +namespace Sabre\VObject\Property; + +use Sabre\VObject\Parameter; +use Sabre\VObject\Property; + +/** + * URI property. + * + * This object encodes URI values. vCard 2.1 calls these URL. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Uri extends Text { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = null; + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'URI'; + + } + + /** + * Returns an iterable list of children. + * + * @return array + */ + function parameters() { + + $parameters = parent::parameters(); + if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'])) { + // If we are encoding a URI value, and this URI value has no + // VALUE=URI parameter, we add it anyway. + // + // This is not required by any spec, but both Apple iCal and Apple + // AddressBook (at least in version 10.8) will trip over this if + // this is not set, and so it improves compatibility. + // + // See Issue #227 and #235 + $parameters['VALUE'] = new Parameter($this->root, 'VALUE', 'URI'); + } + return $parameters; + + } + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + // Normally we don't need to do any type of unescaping for these + // properties, however.. we've noticed that Google Contacts + // specifically escapes the colon (:) with a blackslash. While I have + // no clue why they thought that was a good idea, I'm unescaping it + // anyway. + // + // Good thing backslashes are not allowed in urls. Makes it easy to + // assume that a backslash is always intended as an escape character. + if ($this->name === 'URL') { + $regex = '# (?: (\\\\ (?: \\\\ | : ) ) ) #x'; + $matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $newVal = ''; + foreach ($matches as $match) { + switch ($match) { + case '\:' : + $newVal .= ':'; + break; + default : + $newVal .= $match; + break; + } + } + $this->value = $newVal; + } else { + $this->value = strtr($val, ['\,' => ',']); + } + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + if (is_array($this->value)) { + $value = $this->value[0]; + } else { + $value = $this->value; + } + + return strtr($value, [',' => '\,']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/UtcOffset.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/UtcOffset.php new file mode 100644 index 00000000000..61895c48eb7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/UtcOffset.php @@ -0,0 +1,77 @@ +<?php + +namespace Sabre\VObject\Property; + +/** + * UtcOffset property. + * + * This object encodes UTC-OFFSET values. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class UtcOffset extends Text { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = null; + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'UTC-OFFSET'; + + } + + /** + * Sets the JSON value, as it would appear in a jCard or jCal object. + * + * The value must always be an array. + * + * @param array $value + * + * @return void + */ + function setJsonValue(array $value) { + + $value = array_map( + function($value) { + return str_replace(':', '', $value); + }, + $value + ); + parent::setJsonValue($value); + + } + + /** + * Returns the value, in the format it should be encoded for JSON. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + return array_map( + function($value) { + return substr($value, 0, -2) . ':' . + substr($value, -2); + }, + parent::getJsonValue() + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/Date.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/Date.php new file mode 100644 index 00000000000..1ef6dff3443 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/Date.php @@ -0,0 +1,43 @@ +<?php + +namespace Sabre\VObject\Property\VCard; + +/** + * Date property. + * + * This object encodes vCard DATE values. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Date extends DateAndOrTime { + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'DATE'; + + } + + /** + * Sets the property as a DateTime object. + * + * @param \DateTimeInterface $dt + * + * @return void + */ + function setDateTime(\DateTimeInterface $dt) { + + $this->value = $dt->format('Ymd'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/DateAndOrTime.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/DateAndOrTime.php new file mode 100644 index 00000000000..3b4ae3bb50f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/DateAndOrTime.php @@ -0,0 +1,405 @@ +<?php + +namespace Sabre\VObject\Property\VCard; + +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\InvalidDataException; +use Sabre\VObject\Property; +use Sabre\Xml; + +/** + * DateAndOrTime property. + * + * This object encodes DATE-AND-OR-TIME values. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class DateAndOrTime extends Property { + + /** + * Field separator. + * + * @var null|string + */ + public $delimiter = null; + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'DATE-AND-OR-TIME'; + + } + + /** + * Sets a multi-valued property. + * + * You may also specify DateTimeInterface objects here. + * + * @param array $parts + * + * @return void + */ + function setParts(array $parts) { + + if (count($parts) > 1) { + throw new \InvalidArgumentException('Only one value allowed'); + } + if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) { + $this->setDateTime($parts[0]); + } else { + parent::setParts($parts); + } + + } + + /** + * Updates the current value. + * + * This may be either a single, or multiple strings in an array. + * + * Instead of strings, you may also use DateTimeInterface here. + * + * @param string|array|DateTimeInterface $value + * + * @return void + */ + function setValue($value) { + + if ($value instanceof DateTimeInterface) { + $this->setDateTime($value); + } else { + parent::setValue($value); + } + + } + + /** + * Sets the property as a DateTime object. + * + * @param DateTimeInterface $dt + * + * @return void + */ + function setDateTime(DateTimeInterface $dt) { + + $tz = $dt->getTimeZone(); + $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z']); + + if ($isUtc) { + $value = $dt->format('Ymd\\THis\\Z'); + } else { + // Calculating the offset. + $value = $dt->format('Ymd\\THisO'); + } + + $this->value = $value; + + } + + /** + * Returns a date-time value. + * + * Note that if this property contained more than 1 date-time, only the + * first will be returned. To get an array with multiple values, call + * getDateTimes. + * + * If no time was specified, we will always use midnight (in the default + * timezone) as the time. + * + * If parts of the date were omitted, such as the year, we will grab the + * current values for those. So at the time of writing, if the year was + * omitted, we would have filled in 2014. + * + * @return DateTimeImmutable + */ + function getDateTime() { + + $now = new DateTime(); + + $tzFormat = $now->getTimezone()->getOffset($now) === 0 ? '\\Z' : 'O'; + $nowParts = DateTimeParser::parseVCardDateTime($now->format('Ymd\\This' . $tzFormat)); + + $dateParts = DateTimeParser::parseVCardDateTime($this->getValue()); + + // This sets all the missing parts to the current date/time. + // So if the year was missing for a birthday, we're making it 'this + // year'. + foreach ($dateParts as $k => $v) { + if (is_null($v)) { + $dateParts[$k] = $nowParts[$k]; + } + } + return new DateTimeImmutable("$dateParts[year]-$dateParts[month]-$dateParts[date] $dateParts[hour]:$dateParts[minute]:$dateParts[second] $dateParts[timezone]"); + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $parts = DateTimeParser::parseVCardDateTime($this->getValue()); + + $dateStr = ''; + + // Year + if (!is_null($parts['year'])) { + + $dateStr .= $parts['year']; + + if (!is_null($parts['month'])) { + // If a year and a month is set, we need to insert a separator + // dash. + $dateStr .= '-'; + } + + } else { + + if (!is_null($parts['month']) || !is_null($parts['date'])) { + // Inserting two dashes + $dateStr .= '--'; + } + + } + + // Month + if (!is_null($parts['month'])) { + + $dateStr .= $parts['month']; + + if (isset($parts['date'])) { + // If month and date are set, we need the separator dash. + $dateStr .= '-'; + } + + } elseif (isset($parts['date'])) { + // If the month is empty, and a date is set, we need a 'empty + // dash' + $dateStr .= '-'; + } + + // Date + if (!is_null($parts['date'])) { + $dateStr .= $parts['date']; + } + + + // Early exit if we don't have a time string. + if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) { + return [$dateStr]; + } + + $dateStr .= 'T'; + + // Hour + if (!is_null($parts['hour'])) { + + $dateStr .= $parts['hour']; + + if (!is_null($parts['minute'])) { + $dateStr .= ':'; + } + + } else { + // We know either minute or second _must_ be set, so we insert a + // dash for an empty value. + $dateStr .= '-'; + } + + // Minute + if (!is_null($parts['minute'])) { + + $dateStr .= $parts['minute']; + + if (!is_null($parts['second'])) { + $dateStr .= ':'; + } + + } elseif (isset($parts['second'])) { + // Dash for empty minute + $dateStr .= '-'; + } + + // Second + if (!is_null($parts['second'])) { + $dateStr .= $parts['second']; + } + + // Timezone + if (!is_null($parts['timezone'])) { + $dateStr .= $parts['timezone']; + } + + return [$dateStr]; + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + $valueType = strtolower($this->getValueType()); + $parts = DateTimeParser::parseVCardDateAndOrTime($this->getValue()); + $value = ''; + + // $d = defined + $d = function($part) use ($parts) { + return !is_null($parts[$part]); + }; + + // $r = read + $r = function($part) use ($parts) { + return $parts[$part]; + }; + + // From the Relax NG Schema. + // + // # 4.3.1 + // value-date = element date { + // xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" } + // } + if (($d('year') || $d('month') || $d('date')) + && (!$d('hour') && !$d('minute') && !$d('second') && !$d('timezone'))) { + + if ($d('year') && $d('month') && $d('date')) { + $value .= $r('year') . $r('month') . $r('date'); + } elseif ($d('year') && $d('month') && !$d('date')) { + $value .= $r('year') . '-' . $r('month'); + } elseif (!$d('year') && $d('month')) { + $value .= '--' . $r('month') . $r('date'); + } elseif (!$d('year') && !$d('month') && $d('date')) { + $value .= '---' . $r('date'); + } + + // # 4.3.2 + // value-time = element time { + // xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d?)|--\d\d)" + // ~ "(Z|[+\-]\d\d(\d\d)?)?" } + // } + } elseif ((!$d('year') && !$d('month') && !$d('date')) + && ($d('hour') || $d('minute') || $d('second'))) { + + if ($d('hour')) { + $value .= $r('hour') . $r('minute') . $r('second'); + } elseif ($d('minute')) { + $value .= '-' . $r('minute') . $r('second'); + } elseif ($d('second')) { + $value .= '--' . $r('second'); + } + + $value .= $r('timezone'); + + // # 4.3.3 + // value-date-time = element date-time { + // xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?" + // ~ "(Z|[+\-]\d\d(\d\d)?)?" } + // } + } elseif ($d('date') && $d('hour')) { + + if ($d('year') && $d('month') && $d('date')) { + $value .= $r('year') . $r('month') . $r('date'); + } elseif (!$d('year') && $d('month') && $d('date')) { + $value .= '--' . $r('month') . $r('date'); + } elseif (!$d('year') && !$d('month') && $d('date')) { + $value .= '---' . $r('date'); + } + + $value .= 'T' . $r('hour') . $r('minute') . $r('second') . + $r('timezone'); + + } + + $writer->writeElement($valueType, $value); + + } + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue($val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return implode($this->delimiter, $this->getParts()); + + } + + /** + * Validates the node for correctness. + * + * The following options are supported: + * Node::REPAIR - May attempt to automatically repair the problem. + * + * This method returns an array with detected problems. + * Every element has the following properties: + * + * * level - problem level. + * * message - A human-readable string describing the issue. + * * node - A reference to the problematic node. + * + * The level means: + * 1 - The issue was repaired (only happens if REPAIR was turned on) + * 2 - An inconsequential issue + * 3 - A severe issue. + * + * @param int $options + * + * @return array + */ + function validate($options = 0) { + + $messages = parent::validate($options); + $value = $this->getValue(); + + try { + DateTimeParser::parseVCardDateTime($value); + } catch (InvalidDataException $e) { + $messages[] = [ + 'level' => 3, + 'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property', + 'node' => $this, + ]; + } + + return $messages; + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/DateTime.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/DateTime.php new file mode 100644 index 00000000000..e7c804ca784 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/DateTime.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\VObject\Property\VCard; + +/** + * DateTime property. + * + * This object encodes DATE-TIME values for vCards. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class DateTime extends DateAndOrTime { + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'DATE-TIME'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/LanguageTag.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/LanguageTag.php new file mode 100644 index 00000000000..aa7e9178d51 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/LanguageTag.php @@ -0,0 +1,60 @@ +<?php + +namespace Sabre\VObject\Property\VCard; + +use + Sabre\VObject\Property; + +/** + * LanguageTag property. + * + * This object represents LANGUAGE-TAG values as used in vCards. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class LanguageTag extends Property { + + /** + * Sets a raw value coming from a mimedir (iCalendar/vCard) file. + * + * This has been 'unfolded', so only 1 line will be passed. Unescaping is + * not yet done, but parameters are not included. + * + * @param string $val + * + * @return void + */ + function setRawMimeDirValue($val) { + + $this->setValue($val); + + } + + /** + * Returns a raw mime-dir representation of the value. + * + * @return string + */ + function getRawMimeDirValue() { + + return $this->getValue(); + + } + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'LANGUAGE-TAG'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/TimeStamp.php b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/TimeStamp.php new file mode 100644 index 00000000000..9d311f99d1b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Property/VCard/TimeStamp.php @@ -0,0 +1,86 @@ +<?php + +namespace Sabre\VObject\Property\VCard; + +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\Property\Text; +use Sabre\Xml; + +/** + * TimeStamp property. + * + * This object encodes TIMESTAMP values. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class TimeStamp extends Text { + + /** + * In case this is a multi-value property. This string will be used as a + * delimiter. + * + * @var string|null + */ + public $delimiter = null; + + /** + * Returns the type of value. + * + * This corresponds to the VALUE= parameter. Every property also has a + * 'default' valueType. + * + * @return string + */ + function getValueType() { + + return 'TIMESTAMP'; + + } + + /** + * Returns the value, in the format it should be encoded for json. + * + * This method must always return an array. + * + * @return array + */ + function getJsonValue() { + + $parts = DateTimeParser::parseVCardDateTime($this->getValue()); + + $dateStr = + $parts['year'] . '-' . + $parts['month'] . '-' . + $parts['date'] . 'T' . + $parts['hour'] . ':' . + $parts['minute'] . ':' . + $parts['second']; + + // Timezone + if (!is_null($parts['timezone'])) { + $dateStr .= $parts['timezone']; + } + + return [$dateStr]; + + } + + /** + * This method serializes only the value of a property. This is used to + * create xCard or xCal documents. + * + * @param Xml\Writer $writer XML writer. + * + * @return void + */ + protected function xmlSerializeValue(Xml\Writer $writer) { + + // xCard is the only XML and JSON format that has the same date and time + // format than vCard. + $valueType = strtolower($this->getValueType()); + $writer->writeElement($valueType, $this->getValue()); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Reader.php b/htdocs/includes/sabre/sabre/vobject/lib/Reader.php new file mode 100644 index 00000000000..70992933796 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Reader.php @@ -0,0 +1,98 @@ +<?php + +namespace Sabre\VObject; + +/** + * iCalendar/vCard/jCal/jCard/xCal/xCard reader object. + * + * This object provides a few (static) convenience methods to quickly access + * the parsers. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Reader { + + /** + * If this option is passed to the reader, it will be less strict about the + * validity of the lines. + */ + const OPTION_FORGIVING = 1; + + /** + * If this option is turned on, any lines we cannot parse will be ignored + * by the reader. + */ + const OPTION_IGNORE_INVALID_LINES = 2; + + /** + * Parses a vCard or iCalendar object, and returns the top component. + * + * The options argument is a bitfield. Pass any of the OPTIONS constant to + * alter the parsers' behaviour. + * + * You can either supply a string, or a readable stream for input. + * + * @param string|resource $data + * @param int $options + * @param string $charset + * @return Document + */ + static function read($data, $options = 0, $charset = 'UTF-8') { + + $parser = new Parser\MimeDir(); + $parser->setCharset($charset); + $result = $parser->parse($data, $options); + + return $result; + + } + + /** + * Parses a jCard or jCal object, and returns the top component. + * + * The options argument is a bitfield. Pass any of the OPTIONS constant to + * alter the parsers' behaviour. + * + * You can either a string, a readable stream, or an array for it's input. + * Specifying the array is useful if json_decode was already called on the + * input. + * + * @param string|resource|array $data + * @param int $options + * + * @return Document + */ + static function readJson($data, $options = 0) { + + $parser = new Parser\Json(); + $result = $parser->parse($data, $options); + + return $result; + + } + + /** + * Parses a xCard or xCal object, and returns the top component. + * + * The options argument is a bitfield. Pass any of the OPTIONS constant to + * alter the parsers' behaviour. + * + * You can either supply a string, or a readable stream for input. + * + * @param string|resource $data + * @param int $options + * + * @return Document + */ + static function readXML($data, $options = 0) { + + $parser = new Parser\XML(); + $result = $parser->parse($data, $options); + + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Recur/EventIterator.php b/htdocs/includes/sabre/sabre/vobject/lib/Recur/EventIterator.php new file mode 100644 index 00000000000..d313305a0dd --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Recur/EventIterator.php @@ -0,0 +1,513 @@ +<?php + +namespace Sabre\VObject\Recur; + +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use InvalidArgumentException; +use Sabre\VObject\Component; +use Sabre\VObject\Component\VEvent; +use Sabre\VObject\Settings; + +/** + * This class is used to determine new for a recurring event, when the next + * events occur. + * + * This iterator may loop infinitely in the future, therefore it is important + * that if you use this class, you set hard limits for the amount of iterations + * you want to handle. + * + * Note that currently there is not full support for the entire iCalendar + * specification, as it's very complex and contains a lot of permutations + * that's not yet used very often in software. + * + * For the focus has been on features as they actually appear in Calendaring + * software, but this may well get expanded as needed / on demand + * + * The following RRULE properties are supported + * * UNTIL + * * INTERVAL + * * COUNT + * * FREQ=DAILY + * * BYDAY + * * BYHOUR + * * BYMONTH + * * FREQ=WEEKLY + * * BYDAY + * * BYHOUR + * * WKST + * * FREQ=MONTHLY + * * BYMONTHDAY + * * BYDAY + * * BYSETPOS + * * FREQ=YEARLY + * * BYMONTH + * * BYYEARDAY + * * BYWEEKNO + * * BYMONTHDAY (only if BYMONTH is also set) + * * BYDAY (only if BYMONTH is also set) + * + * Anything beyond this is 'undefined', which means that it may get ignored, or + * you may get unexpected results. The effect is that in some applications the + * specified recurrence may look incorrect, or is missing. + * + * The recurrence iterator also does not yet support THISANDFUTURE. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class EventIterator implements \Iterator { + + /** + * Reference timeZone for floating dates and times. + * + * @var DateTimeZone + */ + protected $timeZone; + + /** + * True if we're iterating an all-day event. + * + * @var bool + */ + protected $allDay = false; + + /** + * Creates the iterator. + * + * There's three ways to set up the iterator. + * + * 1. You can pass a VCALENDAR component and a UID. + * 2. You can pass an array of VEVENTs (all UIDS should match). + * 3. You can pass a single VEVENT component. + * + * Only the second method is recomended. The other 1 and 3 will be removed + * at some point in the future. + * + * The $uid parameter is only required for the first method. + * + * @param Component|array $input + * @param string|null $uid + * @param DateTimeZone $timeZone Reference timezone for floating dates and + * times. + */ + function __construct($input, $uid = null, DateTimeZone $timeZone = null) { + + if (is_null($timeZone)) { + $timeZone = new DateTimeZone('UTC'); + } + $this->timeZone = $timeZone; + + if (is_array($input)) { + $events = $input; + } elseif ($input instanceof VEvent) { + // Single instance mode. + $events = [$input]; + } else { + // Calendar + UID mode. + $uid = (string)$uid; + if (!$uid) { + throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor'); + } + if (!isset($input->VEVENT)) { + throw new InvalidArgumentException('No events found in this calendar'); + } + $events = $input->getByUID($uid); + + } + + foreach ($events as $vevent) { + + if (!isset($vevent->{'RECURRENCE-ID'})) { + + $this->masterEvent = $vevent; + + } else { + + $this->exceptions[ + $vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp() + ] = true; + $this->overriddenEvents[] = $vevent; + + } + + } + + if (!$this->masterEvent) { + // No base event was found. CalDAV does allow cases where only + // overridden instances are stored. + // + // In this particular case, we're just going to grab the first + // event and use that instead. This may not always give the + // desired result. + if (!count($this->overriddenEvents)) { + throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid); + } + $this->masterEvent = array_shift($this->overriddenEvents); + } + + $this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone); + $this->allDay = !$this->masterEvent->DTSTART->hasTime(); + + if (isset($this->masterEvent->EXDATE)) { + + foreach ($this->masterEvent->EXDATE as $exDate) { + + foreach ($exDate->getDateTimes($this->timeZone) as $dt) { + $this->exceptions[$dt->getTimeStamp()] = true; + } + + } + + } + + if (isset($this->masterEvent->DTEND)) { + $this->eventDuration = + $this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() - + $this->startDate->getTimeStamp(); + } elseif (isset($this->masterEvent->DURATION)) { + $duration = $this->masterEvent->DURATION->getDateInterval(); + $end = clone $this->startDate; + $end = $end->add($duration); + $this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp(); + } elseif ($this->allDay) { + $this->eventDuration = 3600 * 24; + } else { + $this->eventDuration = 0; + } + + if (isset($this->masterEvent->RDATE)) { + $this->recurIterator = new RDateIterator( + $this->masterEvent->RDATE->getParts(), + $this->startDate + ); + } elseif (isset($this->masterEvent->RRULE)) { + $this->recurIterator = new RRuleIterator( + $this->masterEvent->RRULE->getParts(), + $this->startDate + ); + } else { + $this->recurIterator = new RRuleIterator( + [ + 'FREQ' => 'DAILY', + 'COUNT' => 1, + ], + $this->startDate + ); + } + + $this->rewind(); + if (!$this->valid()) { + throw new NoInstancesException('This recurrence rule does not generate any valid instances'); + } + + } + + /** + * Returns the date for the current position of the iterator. + * + * @return DateTimeImmutable + */ + function current() { + + if ($this->currentDate) { + return clone $this->currentDate; + } + + } + + /** + * This method returns the start date for the current iteration of the + * event. + * + * @return DateTimeImmutable + */ + function getDtStart() { + + if ($this->currentDate) { + return clone $this->currentDate; + } + + } + + /** + * This method returns the end date for the current iteration of the + * event. + * + * @return DateTimeImmutable + */ + function getDtEnd() { + + if (!$this->valid()) { + return; + } + $end = clone $this->currentDate; + return $end->modify('+' . $this->eventDuration . ' seconds'); + + } + + /** + * Returns a VEVENT for the current iterations of the event. + * + * This VEVENT will have a recurrence id, and it's DTSTART and DTEND + * altered. + * + * @return VEvent + */ + function getEventObject() { + + if ($this->currentOverriddenEvent) { + return $this->currentOverriddenEvent; + } + + $event = clone $this->masterEvent; + + // Ignoring the following block, because PHPUnit's code coverage + // ignores most of these lines, and this messes with our stats. + // + // @codeCoverageIgnoreStart + unset( + $event->RRULE, + $event->EXDATE, + $event->RDATE, + $event->EXRULE, + $event->{'RECURRENCE-ID'} + ); + // @codeCoverageIgnoreEnd + + $event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating()); + if (isset($event->DTEND)) { + $event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating()); + } + $recurid = clone $event->DTSTART; + $recurid->name = 'RECURRENCE-ID'; + $event->add($recurid); + return $event; + + } + + /** + * Returns the current position of the iterator. + * + * This is for us simply a 0-based index. + * + * @return int + */ + function key() { + + // The counter is always 1 ahead. + return $this->counter - 1; + + } + + /** + * This is called after next, to see if the iterator is still at a valid + * position, or if it's at the end. + * + * @return bool + */ + function valid() { + + if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) { + throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences); + } + return !!$this->currentDate; + + } + + /** + * Sets the iterator back to the starting point. + */ + function rewind() { + + $this->recurIterator->rewind(); + // re-creating overridden event index. + $index = []; + foreach ($this->overriddenEvents as $key => $event) { + $stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp(); + $index[$stamp][] = $key; + } + krsort($index); + $this->counter = 0; + $this->overriddenEventsIndex = $index; + $this->currentOverriddenEvent = null; + + $this->nextDate = null; + $this->currentDate = clone $this->startDate; + + $this->next(); + + } + + /** + * Advances the iterator with one step. + * + * @return void + */ + function next() { + + $this->currentOverriddenEvent = null; + $this->counter++; + if ($this->nextDate) { + // We had a stored value. + $nextDate = $this->nextDate; + $this->nextDate = null; + } else { + // We need to ask rruleparser for the next date. + // We need to do this until we find a date that's not in the + // exception list. + do { + if (!$this->recurIterator->valid()) { + $nextDate = null; + break; + } + $nextDate = $this->recurIterator->current(); + $this->recurIterator->next(); + } while (isset($this->exceptions[$nextDate->getTimeStamp()])); + + } + + + // $nextDate now contains what rrule thinks is the next one, but an + // overridden event may cut ahead. + if ($this->overriddenEventsIndex) { + + $offsets = end($this->overriddenEventsIndex); + $timestamp = key($this->overriddenEventsIndex); + $offset = end($offsets); + if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) { + // Overridden event comes first. + $this->currentOverriddenEvent = $this->overriddenEvents[$offset]; + + // Putting the rrule next date aside. + $this->nextDate = $nextDate; + $this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone); + + // Ensuring that this item will only be used once. + array_pop($this->overriddenEventsIndex[$timestamp]); + if (!$this->overriddenEventsIndex[$timestamp]) { + array_pop($this->overriddenEventsIndex); + } + + // Exit point! + return; + + } + + } + + $this->currentDate = $nextDate; + + } + + /** + * Quickly jump to a date in the future. + * + * @param DateTimeInterface $dateTime + */ + function fastForward(DateTimeInterface $dateTime) { + + while ($this->valid() && $this->getDtEnd() <= $dateTime) { + $this->next(); + } + + } + + /** + * Returns true if this recurring event never ends. + * + * @return bool + */ + function isInfinite() { + + return $this->recurIterator->isInfinite(); + + } + + /** + * RRULE parser. + * + * @var RRuleIterator + */ + protected $recurIterator; + + /** + * The duration, in seconds, of the master event. + * + * We use this to calculate the DTEND for subsequent events. + */ + protected $eventDuration; + + /** + * A reference to the main (master) event. + * + * @var VEVENT + */ + protected $masterEvent; + + /** + * List of overridden events. + * + * @var array + */ + protected $overriddenEvents = []; + + /** + * Overridden event index. + * + * Key is timestamp, value is the list of indexes of the item in the $overriddenEvent + * property. + * + * @var array + */ + protected $overriddenEventsIndex; + + /** + * A list of recurrence-id's that are either part of EXDATE, or are + * overridden. + * + * @var array + */ + protected $exceptions = []; + + /** + * Internal event counter. + * + * @var int + */ + protected $counter; + + /** + * The very start of the iteration process. + * + * @var DateTimeImmutable + */ + protected $startDate; + + /** + * Where we are currently in the iteration process. + * + * @var DateTimeImmutable + */ + protected $currentDate; + + /** + * The next date from the rrule parser. + * + * Sometimes we need to temporary store the next date, because an + * overridden event came before. + * + * @var DateTimeImmutable + */ + protected $nextDate; + + /** + * The event that overwrites the current iteration + * + * @var VEVENT + */ + protected $currentOverriddenEvent; + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Recur/MaxInstancesExceededException.php b/htdocs/includes/sabre/sabre/vobject/lib/Recur/MaxInstancesExceededException.php new file mode 100644 index 00000000000..264df7d2bd5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Recur/MaxInstancesExceededException.php @@ -0,0 +1,16 @@ +<?php + +namespace Sabre\VObject\Recur; + +use Exception; + +/** + * This exception will get thrown when a recurrence rule generated more than + * the maximum number of instances. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class MaxInstancesExceededException extends Exception { +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Recur/NoInstancesException.php b/htdocs/includes/sabre/sabre/vobject/lib/Recur/NoInstancesException.php new file mode 100644 index 00000000000..8f8bb472bf5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Recur/NoInstancesException.php @@ -0,0 +1,18 @@ +<?php + +namespace Sabre\VObject\Recur; + +use Exception; + +/** + * This exception gets thrown when a recurrence iterator produces 0 instances. + * + * This may happen when every occurence in a rrule is also in EXDATE. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class NoInstancesException extends Exception { + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Recur/RDateIterator.php b/htdocs/includes/sabre/sabre/vobject/lib/Recur/RDateIterator.php new file mode 100644 index 00000000000..f44960e123f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Recur/RDateIterator.php @@ -0,0 +1,182 @@ +<?php + +namespace Sabre\VObject\Recur; + +use DateTimeInterface; +use Iterator; +use Sabre\VObject\DateTimeParser; + +/** + * RRuleParser. + * + * This class receives an RRULE string, and allows you to iterate to get a list + * of dates in that recurrence. + * + * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain + * 5 items, one for each day. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class RDateIterator implements Iterator { + + /** + * Creates the Iterator. + * + * @param string|array $rrule + * @param DateTimeInterface $start + */ + function __construct($rrule, DateTimeInterface $start) { + + $this->startDate = $start; + $this->parseRDate($rrule); + $this->currentDate = clone $this->startDate; + + } + + /* Implementation of the Iterator interface {{{ */ + + function current() { + + if (!$this->valid()) return; + return clone $this->currentDate; + + } + + /** + * Returns the current item number. + * + * @return int + */ + function key() { + + return $this->counter; + + } + + /** + * Returns whether the current item is a valid item for the recurrence + * iterator. + * + * @return bool + */ + function valid() { + + return ($this->counter <= count($this->dates)); + + } + + /** + * Resets the iterator. + * + * @return void + */ + function rewind() { + + $this->currentDate = clone $this->startDate; + $this->counter = 0; + + } + + /** + * Goes on to the next iteration. + * + * @return void + */ + function next() { + + $this->counter++; + if (!$this->valid()) return; + + $this->currentDate = + DateTimeParser::parse( + $this->dates[$this->counter - 1], + $this->startDate->getTimezone() + ); + + } + + /* End of Iterator implementation }}} */ + + /** + * Returns true if this recurring event never ends. + * + * @return bool + */ + function isInfinite() { + + return false; + + } + + /** + * This method allows you to quickly go to the next occurrence after the + * specified date. + * + * @param DateTimeInterface $dt + * + * @return void + */ + function fastForward(DateTimeInterface $dt) { + + while ($this->valid() && $this->currentDate < $dt) { + $this->next(); + } + + } + + /** + * The reference start date/time for the rrule. + * + * All calculations are based on this initial date. + * + * @var DateTimeInterface + */ + protected $startDate; + + /** + * The date of the current iteration. You can get this by calling + * ->current(). + * + * @var DateTimeInterface + */ + protected $currentDate; + + /** + * The current item in the list. + * + * You can get this number with the key() method. + * + * @var int + */ + protected $counter = 0; + + /* }}} */ + + /** + * This method receives a string from an RRULE property, and populates this + * class with all the values. + * + * @param string|array $rrule + * + * @return void + */ + protected function parseRDate($rdate) { + + if (is_string($rdate)) { + $rdate = explode(',', $rdate); + } + + $this->dates = $rdate; + + } + + /** + * Array with the RRULE dates + * + * @var array + */ + protected $dates = []; + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Recur/RRuleIterator.php b/htdocs/includes/sabre/sabre/vobject/lib/Recur/RRuleIterator.php new file mode 100644 index 00000000000..20f34ef42a7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Recur/RRuleIterator.php @@ -0,0 +1,1013 @@ +<?php + +namespace Sabre\VObject\Recur; + +use DateTimeImmutable; +use DateTimeInterface; +use Iterator; +use Sabre\VObject\DateTimeParser; +use Sabre\VObject\InvalidDataException; +use Sabre\VObject\Property; + +/** + * RRuleParser. + * + * This class receives an RRULE string, and allows you to iterate to get a list + * of dates in that recurrence. + * + * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain + * 5 items, one for each day. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class RRuleIterator implements Iterator { + + /** + * Creates the Iterator. + * + * @param string|array $rrule + * @param DateTimeInterface $start + */ + function __construct($rrule, DateTimeInterface $start) { + + $this->startDate = $start; + $this->parseRRule($rrule); + $this->currentDate = clone $this->startDate; + + } + + /* Implementation of the Iterator interface {{{ */ + + function current() { + + if (!$this->valid()) return; + return clone $this->currentDate; + + } + + /** + * Returns the current item number. + * + * @return int + */ + function key() { + + return $this->counter; + + } + + /** + * Returns whether the current item is a valid item for the recurrence + * iterator. This will return false if we've gone beyond the UNTIL or COUNT + * statements. + * + * @return bool + */ + function valid() { + + if (!is_null($this->count)) { + return $this->counter < $this->count; + } + return is_null($this->until) || $this->currentDate <= $this->until; + + } + + /** + * Resets the iterator. + * + * @return void + */ + function rewind() { + + $this->currentDate = clone $this->startDate; + $this->counter = 0; + + } + + /** + * Goes on to the next iteration. + * + * @return void + */ + function next() { + + // Otherwise, we find the next event in the normal RRULE + // sequence. + switch ($this->frequency) { + + case 'hourly' : + $this->nextHourly(); + break; + + case 'daily' : + $this->nextDaily(); + break; + + case 'weekly' : + $this->nextWeekly(); + break; + + case 'monthly' : + $this->nextMonthly(); + break; + + case 'yearly' : + $this->nextYearly(); + break; + + } + $this->counter++; + + } + + /* End of Iterator implementation }}} */ + + /** + * Returns true if this recurring event never ends. + * + * @return bool + */ + function isInfinite() { + + return !$this->count && !$this->until; + + } + + /** + * This method allows you to quickly go to the next occurrence after the + * specified date. + * + * @param DateTimeInterface $dt + * + * @return void + */ + function fastForward(DateTimeInterface $dt) { + + while ($this->valid() && $this->currentDate < $dt) { + $this->next(); + } + + } + + /** + * The reference start date/time for the rrule. + * + * All calculations are based on this initial date. + * + * @var DateTimeInterface + */ + protected $startDate; + + /** + * The date of the current iteration. You can get this by calling + * ->current(). + * + * @var DateTimeInterface + */ + protected $currentDate; + + /** + * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly, + * yearly. + * + * @var string + */ + protected $frequency; + + /** + * The number of recurrences, or 'null' if infinitely recurring. + * + * @var int + */ + protected $count; + + /** + * The interval. + * + * If for example frequency is set to daily, interval = 2 would mean every + * 2 days. + * + * @var int + */ + protected $interval = 1; + + /** + * The last instance of this recurrence, inclusively. + * + * @var DateTimeInterface|null + */ + protected $until; + + /** + * Which seconds to recur. + * + * This is an array of integers (between 0 and 60) + * + * @var array + */ + protected $bySecond; + + /** + * Which minutes to recur. + * + * This is an array of integers (between 0 and 59) + * + * @var array + */ + protected $byMinute; + + /** + * Which hours to recur. + * + * This is an array of integers (between 0 and 23) + * + * @var array + */ + protected $byHour; + + /** + * The current item in the list. + * + * You can get this number with the key() method. + * + * @var int + */ + protected $counter = 0; + + /** + * Which weekdays to recur. + * + * This is an array of weekdays + * + * This may also be preceeded by a positive or negative integer. If present, + * this indicates the nth occurrence of a specific day within the monthly or + * yearly rrule. For instance, -2TU indicates the second-last tuesday of + * the month, or year. + * + * @var array + */ + protected $byDay; + + /** + * Which days of the month to recur. + * + * This is an array of days of the months (1-31). The value can also be + * negative. -5 for instance means the 5th last day of the month. + * + * @var array + */ + protected $byMonthDay; + + /** + * Which days of the year to recur. + * + * This is an array with days of the year (1 to 366). The values can also + * be negative. For instance, -1 will always represent the last day of the + * year. (December 31st). + * + * @var array + */ + protected $byYearDay; + + /** + * Which week numbers to recur. + * + * This is an array of integers from 1 to 53. The values can also be + * negative. -1 will always refer to the last week of the year. + * + * @var array + */ + protected $byWeekNo; + + /** + * Which months to recur. + * + * This is an array of integers from 1 to 12. + * + * @var array + */ + protected $byMonth; + + /** + * Which items in an existing st to recur. + * + * These numbers work together with an existing by* rule. It specifies + * exactly which items of the existing by-rule to filter. + * + * Valid values are 1 to 366 and -1 to -366. As an example, this can be + * used to recur the last workday of the month. + * + * This would be done by setting frequency to 'monthly', byDay to + * 'MO,TU,WE,TH,FR' and bySetPos to -1. + * + * @var array + */ + protected $bySetPos; + + /** + * When the week starts. + * + * @var string + */ + protected $weekStart = 'MO'; + + /* Functions that advance the iterator {{{ */ + + /** + * Does the processing for advancing the iterator for hourly frequency. + * + * @return void + */ + protected function nextHourly() { + + $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' hours'); + + } + + /** + * Does the processing for advancing the iterator for daily frequency. + * + * @return void + */ + protected function nextDaily() { + + if (!$this->byHour && !$this->byDay) { + $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days'); + return; + } + + if (!empty($this->byHour)) { + $recurrenceHours = $this->getHours(); + } + + if (!empty($this->byDay)) { + $recurrenceDays = $this->getDays(); + } + + if (!empty($this->byMonth)) { + $recurrenceMonths = $this->getMonths(); + } + + do { + if ($this->byHour) { + if ($this->currentDate->format('G') == '23') { + // to obey the interval rule + $this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' days'); + } + + $this->currentDate = $this->currentDate->modify('+1 hours'); + + } else { + $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days'); + + } + + // Current month of the year + $currentMonth = $this->currentDate->format('n'); + + // Current day of the week + $currentDay = $this->currentDate->format('w'); + + // Current hour of the day + $currentHour = $this->currentDate->format('G'); + + } while ( + ($this->byDay && !in_array($currentDay, $recurrenceDays)) || + ($this->byHour && !in_array($currentHour, $recurrenceHours)) || + ($this->byMonth && !in_array($currentMonth, $recurrenceMonths)) + ); + + } + + /** + * Does the processing for advancing the iterator for weekly frequency. + * + * @return void + */ + protected function nextWeekly() { + + if (!$this->byHour && !$this->byDay) { + $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' weeks'); + return; + } + + if ($this->byHour) { + $recurrenceHours = $this->getHours(); + } + + if ($this->byDay) { + $recurrenceDays = $this->getDays(); + } + + // First day of the week: + $firstDay = $this->dayMap[$this->weekStart]; + + do { + + if ($this->byHour) { + $this->currentDate = $this->currentDate->modify('+1 hours'); + } else { + $this->currentDate = $this->currentDate->modify('+1 days'); + } + + // Current day of the week + $currentDay = (int)$this->currentDate->format('w'); + + // Current hour of the day + $currentHour = (int)$this->currentDate->format('G'); + + // We need to roll over to the next week + if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) { + $this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' weeks'); + + // We need to go to the first day of this week, but only if we + // are not already on this first day of this week. + if ($this->currentDate->format('w') != $firstDay) { + $this->currentDate = $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]); + } + } + + // We have a match + } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours))); + } + + /** + * Does the processing for advancing the iterator for monthly frequency. + * + * @return void + */ + protected function nextMonthly() { + + $currentDayOfMonth = $this->currentDate->format('j'); + if (!$this->byMonthDay && !$this->byDay) { + + // If the current day is higher than the 28th, rollover can + // occur to the next month. We Must skip these invalid + // entries. + if ($currentDayOfMonth < 29) { + $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' months'); + } else { + $increase = 0; + do { + $increase++; + $tempDate = clone $this->currentDate; + $tempDate = $tempDate->modify('+ ' . ($this->interval * $increase) . ' months'); + } while ($tempDate->format('j') != $currentDayOfMonth); + $this->currentDate = $tempDate; + } + return; + } + + while (true) { + + $occurrences = $this->getMonthlyOccurrences(); + + foreach ($occurrences as $occurrence) { + + // The first occurrence thats higher than the current + // day of the month wins. + if ($occurrence > $currentDayOfMonth) { + break 2; + } + + } + + // If we made it all the way here, it means there were no + // valid occurrences, and we need to advance to the next + // month. + // + // This line does not currently work in hhvm. Temporary workaround + // follows: + // $this->currentDate->modify('first day of this month'); + $this->currentDate = new DateTimeImmutable($this->currentDate->format('Y-m-1 H:i:s'), $this->currentDate->getTimezone()); + // end of workaround + $this->currentDate = $this->currentDate->modify('+ ' . $this->interval . ' months'); + + // This goes to 0 because we need to start counting at the + // beginning. + $currentDayOfMonth = 0; + + } + + $this->currentDate = $this->currentDate->setDate( + (int)$this->currentDate->format('Y'), + (int)$this->currentDate->format('n'), + (int)$occurrence + ); + + } + + /** + * Does the processing for advancing the iterator for yearly frequency. + * + * @return void + */ + protected function nextYearly() { + + $currentMonth = $this->currentDate->format('n'); + $currentYear = $this->currentDate->format('Y'); + $currentDayOfMonth = $this->currentDate->format('j'); + + // No sub-rules, so we just advance by year + if (empty($this->byMonth)) { + + // Unless it was a leap day! + if ($currentMonth == 2 && $currentDayOfMonth == 29) { + + $counter = 0; + do { + $counter++; + // Here we increase the year count by the interval, until + // we hit a date that's also in a leap year. + // + // We could just find the next interval that's dividable by + // 4, but that would ignore the rule that there's no leap + // year every year that's dividable by a 100, but not by + // 400. (1800, 1900, 2100). So we just rely on the datetime + // functions instead. + $nextDate = clone $this->currentDate; + $nextDate = $nextDate->modify('+ ' . ($this->interval * $counter) . ' years'); + } while ($nextDate->format('n') != 2); + + $this->currentDate = $nextDate; + + return; + + } + + if ($this->byWeekNo !== null) { // byWeekNo is an array with values from -53 to -1, or 1 to 53 + $dayOffsets = []; + if ($this->byDay) { + foreach ($this->byDay as $byDay) { + $dayOffsets[] = $this->dayMap[$byDay]; + } + } else { // default is Monday + $dayOffsets[] = 1; + } + + $currentYear = $this->currentDate->format('Y'); + + while (true) { + $checkDates = []; + + // loop through all WeekNo and Days to check all the combinations + foreach ($this->byWeekNo as $byWeekNo) { + foreach ($dayOffsets as $dayOffset) { + $date = clone $this->currentDate; + $date->setISODate($currentYear, $byWeekNo, $dayOffset); + + if ($date > $this->currentDate) { + $checkDates[] = $date; + } + } + } + + if (count($checkDates) > 0) { + $this->currentDate = min($checkDates); + return; + } + + // if there is no date found, check the next year + $currentYear += $this->interval; + } + } + + if ($this->byYearDay !== null) { // byYearDay is an array with values from -366 to -1, or 1 to 366 + $dayOffsets = []; + if ($this->byDay) { + foreach ($this->byDay as $byDay) { + $dayOffsets[] = $this->dayMap[$byDay]; + } + } else { // default is Monday-Sunday + $dayOffsets = [1,2,3,4,5,6,7]; + } + + $currentYear = $this->currentDate->format('Y'); + + while (true) { + $checkDates = []; + + // loop through all YearDay and Days to check all the combinations + foreach ($this->byYearDay as $byYearDay) { + $date = clone $this->currentDate; + $date->setDate($currentYear, 1, 1); + if ($byYearDay > 0) { + $date->add(new \DateInterval('P' . $byYearDay . 'D')); + } else { + $date->sub(new \DateInterval('P' . abs($byYearDay) . 'D')); + } + + if ($date > $this->currentDate && in_array($date->format('N'), $dayOffsets)) { + $checkDates[] = $date; + } + } + + if (count($checkDates) > 0) { + $this->currentDate = min($checkDates); + return; + } + + // if there is no date found, check the next year + $currentYear += $this->interval; + } + } + + // The easiest form + $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' years'); + return; + + } + + $currentMonth = $this->currentDate->format('n'); + $currentYear = $this->currentDate->format('Y'); + $currentDayOfMonth = $this->currentDate->format('j'); + + $advancedToNewMonth = false; + + // If we got a byDay or getMonthDay filter, we must first expand + // further. + if ($this->byDay || $this->byMonthDay) { + + while (true) { + + $occurrences = $this->getMonthlyOccurrences(); + + foreach ($occurrences as $occurrence) { + + // The first occurrence that's higher than the current + // day of the month wins. + // If we advanced to the next month or year, the first + // occurrence is always correct. + if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { + break 2; + } + + } + + // If we made it here, it means we need to advance to + // the next month or year. + $currentDayOfMonth = 1; + $advancedToNewMonth = true; + do { + + $currentMonth++; + if ($currentMonth > 12) { + $currentYear += $this->interval; + $currentMonth = 1; + } + } while (!in_array($currentMonth, $this->byMonth)); + + $this->currentDate = $this->currentDate->setDate( + (int)$currentYear, + (int)$currentMonth, + (int)$currentDayOfMonth + ); + + } + + // If we made it here, it means we got a valid occurrence + $this->currentDate = $this->currentDate->setDate( + (int)$currentYear, + (int)$currentMonth, + (int)$occurrence + ); + return; + + } else { + + // These are the 'byMonth' rules, if there are no byDay or + // byMonthDay sub-rules. + do { + + $currentMonth++; + if ($currentMonth > 12) { + $currentYear += $this->interval; + $currentMonth = 1; + } + } while (!in_array($currentMonth, $this->byMonth)); + $this->currentDate = $this->currentDate->setDate( + (int)$currentYear, + (int)$currentMonth, + (int)$currentDayOfMonth + ); + + return; + + } + + } + + /* }}} */ + + /** + * This method receives a string from an RRULE property, and populates this + * class with all the values. + * + * @param string|array $rrule + * + * @return void + */ + protected function parseRRule($rrule) { + + if (is_string($rrule)) { + $rrule = Property\ICalendar\Recur::stringToArray($rrule); + } + + foreach ($rrule as $key => $value) { + + $key = strtoupper($key); + switch ($key) { + + case 'FREQ' : + $value = strtolower($value); + if (!in_array( + $value, + ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly'] + )) { + throw new InvalidDataException('Unknown value for FREQ=' . strtoupper($value)); + } + $this->frequency = $value; + break; + + case 'UNTIL' : + $this->until = DateTimeParser::parse($value, $this->startDate->getTimezone()); + + // In some cases events are generated with an UNTIL= + // parameter before the actual start of the event. + // + // Not sure why this is happening. We assume that the + // intention was that the event only recurs once. + // + // So we are modifying the parameter so our code doesn't + // break. + if ($this->until < $this->startDate) { + $this->until = $this->startDate; + } + break; + + case 'INTERVAL' : + // No break + + case 'COUNT' : + $val = (int)$value; + if ($val < 1) { + throw new InvalidDataException(strtoupper($key) . ' in RRULE must be a positive integer!'); + } + $key = strtolower($key); + $this->$key = $val; + break; + + case 'BYSECOND' : + $this->bySecond = (array)$value; + break; + + case 'BYMINUTE' : + $this->byMinute = (array)$value; + break; + + case 'BYHOUR' : + $this->byHour = (array)$value; + break; + + case 'BYDAY' : + $value = (array)$value; + foreach ($value as $part) { + if (!preg_match('#^ (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) { + throw new InvalidDataException('Invalid part in BYDAY clause: ' . $part); + } + } + $this->byDay = $value; + break; + + case 'BYMONTHDAY' : + $this->byMonthDay = (array)$value; + break; + + case 'BYYEARDAY' : + $this->byYearDay = (array)$value; + foreach ($this->byYearDay as $byYearDay) { + if (!is_numeric($byYearDay) || (int)$byYearDay < -366 || (int)$byYearDay == 0 || (int)$byYearDay > 366) { + throw new InvalidDataException('BYYEARDAY in RRULE must have value(s) from 1 to 366, or -366 to -1!'); + } + } + break; + + case 'BYWEEKNO' : + $this->byWeekNo = (array)$value; + foreach ($this->byWeekNo as $byWeekNo) { + if (!is_numeric($byWeekNo) || (int)$byWeekNo < -53 || (int)$byWeekNo == 0 || (int)$byWeekNo > 53) { + throw new InvalidDataException('BYWEEKNO in RRULE must have value(s) from 1 to 53, or -53 to -1!'); + } + } + break; + + case 'BYMONTH' : + $this->byMonth = (array)$value; + foreach ($this->byMonth as $byMonth) { + if (!is_numeric($byMonth) || (int)$byMonth < 1 || (int)$byMonth > 12) { + throw new InvalidDataException('BYMONTH in RRULE must have value(s) betweeen 1 and 12!'); + } + } + break; + + case 'BYSETPOS' : + $this->bySetPos = (array)$value; + break; + + case 'WKST' : + $this->weekStart = strtoupper($value); + break; + + default: + throw new InvalidDataException('Not supported: ' . strtoupper($key)); + + } + + } + + } + + /** + * Mappings between the day number and english day name. + * + * @var array + */ + protected $dayNames = [ + 0 => 'Sunday', + 1 => 'Monday', + 2 => 'Tuesday', + 3 => 'Wednesday', + 4 => 'Thursday', + 5 => 'Friday', + 6 => 'Saturday', + ]; + + /** + * Returns all the occurrences for a monthly frequency with a 'byDay' or + * 'byMonthDay' expansion for the current month. + * + * The returned list is an array of integers with the day of month (1-31). + * + * @return array + */ + protected function getMonthlyOccurrences() { + + $startDate = clone $this->currentDate; + + $byDayResults = []; + + // Our strategy is to simply go through the byDays, advance the date to + // that point and add it to the results. + if ($this->byDay) foreach ($this->byDay as $day) { + + $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]]; + + + // Dayname will be something like 'wednesday'. Now we need to find + // all wednesdays in this month. + $dayHits = []; + + // workaround for missing 'first day of the month' support in hhvm + $checkDate = new \DateTime($startDate->format('Y-m-1')); + // workaround modify always advancing the date even if the current day is a $dayName in hhvm + if ($checkDate->format('l') !== $dayName) { + $checkDate = $checkDate->modify($dayName); + } + + do { + $dayHits[] = $checkDate->format('j'); + $checkDate = $checkDate->modify('next ' . $dayName); + } while ($checkDate->format('n') === $startDate->format('n')); + + // So now we have 'all wednesdays' for month. It is however + // possible that the user only really wanted the 1st, 2nd or last + // wednesday. + if (strlen($day) > 2) { + $offset = (int)substr($day, 0, -2); + + if ($offset > 0) { + // It is possible that the day does not exist, such as a + // 5th or 6th wednesday of the month. + if (isset($dayHits[$offset - 1])) { + $byDayResults[] = $dayHits[$offset - 1]; + } + } else { + + // if it was negative we count from the end of the array + // might not exist, fx. -5th tuesday + if (isset($dayHits[count($dayHits) + $offset])) { + $byDayResults[] = $dayHits[count($dayHits) + $offset]; + } + } + } else { + // There was no counter (first, second, last wednesdays), so we + // just need to add the all to the list). + $byDayResults = array_merge($byDayResults, $dayHits); + + } + + } + + $byMonthDayResults = []; + if ($this->byMonthDay) foreach ($this->byMonthDay as $monthDay) { + + // Removing values that are out of range for this month + if ($monthDay > $startDate->format('t') || + $monthDay < 0 - $startDate->format('t')) { + continue; + } + if ($monthDay > 0) { + $byMonthDayResults[] = $monthDay; + } else { + // Negative values + $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay; + } + } + + // If there was just byDay or just byMonthDay, they just specify our + // (almost) final list. If both were provided, then byDay limits the + // list. + if ($this->byMonthDay && $this->byDay) { + $result = array_intersect($byMonthDayResults, $byDayResults); + } elseif ($this->byMonthDay) { + $result = $byMonthDayResults; + } else { + $result = $byDayResults; + } + $result = array_unique($result); + sort($result, SORT_NUMERIC); + + // The last thing that needs checking is the BYSETPOS. If it's set, it + // means only certain items in the set survive the filter. + if (!$this->bySetPos) { + return $result; + } + + $filteredResult = []; + foreach ($this->bySetPos as $setPos) { + + if ($setPos < 0) { + $setPos = count($result) + ($setPos + 1); + } + if (isset($result[$setPos - 1])) { + $filteredResult[] = $result[$setPos - 1]; + } + } + + sort($filteredResult, SORT_NUMERIC); + return $filteredResult; + + } + + /** + * Simple mapping from iCalendar day names to day numbers. + * + * @var array + */ + protected $dayMap = [ + 'SU' => 0, + 'MO' => 1, + 'TU' => 2, + 'WE' => 3, + 'TH' => 4, + 'FR' => 5, + 'SA' => 6, + ]; + + protected function getHours() { + + $recurrenceHours = []; + foreach ($this->byHour as $byHour) { + $recurrenceHours[] = $byHour; + } + + return $recurrenceHours; + } + + protected function getDays() { + + $recurrenceDays = []; + foreach ($this->byDay as $byDay) { + + // The day may be preceeded with a positive (+n) or + // negative (-n) integer. However, this does not make + // sense in 'weekly' so we ignore it here. + $recurrenceDays[] = $this->dayMap[substr($byDay, -2)]; + + } + + return $recurrenceDays; + } + + protected function getMonths() { + + $recurrenceMonths = []; + foreach ($this->byMonth as $byMonth) { + $recurrenceMonths[] = $byMonth; + } + + return $recurrenceMonths; + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Settings.php b/htdocs/includes/sabre/sabre/vobject/lib/Settings.php new file mode 100644 index 00000000000..3f274ba8eee --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Settings.php @@ -0,0 +1,56 @@ +<?php + +namespace Sabre\VObject; + +/** + * This class provides a list of global defaults for vobject. + * + * Some of these started to appear in various classes, so it made a bit more + * sense to centralize them, so it's easier for user to find and change these. + * + * The global nature of them does mean that changing the settings for one + * instance has a global influence. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Settings { + + /** + * The minimum date we accept for various calculations with dates, such as + * recurrences. + * + * The choice of 1900 is pretty arbitrary, but it covers most common + * use-cases. In particular, it covers birthdates for virtually everyone + * alive on earth, which is less than 5 people at the time of writing. + */ + static $minDate = '1900-01-01'; + + /** + * The maximum date we accept for various calculations with dates, such as + * recurrences. + * + * The choice of 2100 is pretty arbitrary, but should cover most + * appointments made for many years to come. + */ + static $maxDate = '2100-01-01'; + + /** + * The maximum number of recurrences that will be generated. + * + * This setting limits the maximum of recurring events that this library + * generates in its recurrence iterators. + * + * This is a security measure. Without this, it would be possible to craft + * specific events that recur many, many times, potentially DDOSing the + * server. + * + * The default (3500) allows creation of a dialy event that goes on for 10 + * years, which is hopefully long enough for most. + * + * Set this value to -1 to disable this control altogether. + */ + static $maxRecurrences = 3500; + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Splitter/ICalendar.php b/htdocs/includes/sabre/sabre/vobject/lib/Splitter/ICalendar.php new file mode 100644 index 00000000000..c0007ba01ae --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Splitter/ICalendar.php @@ -0,0 +1,113 @@ +<?php + +namespace Sabre\VObject\Splitter; + +use Sabre\VObject; +use Sabre\VObject\Component\VCalendar; + +/** + * Splitter. + * + * This class is responsible for splitting up iCalendar objects. + * + * This class expects a single VCALENDAR object with one or more + * calendar-objects inside. Objects with identical UID's will be combined into + * a single object. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Dominik Tobschall (http://tobschall.de/) + * @author Armin Hackmann + * @license http://sabre.io/license/ Modified BSD License + */ +class ICalendar implements SplitterInterface { + + /** + * Timezones. + * + * @var array + */ + protected $vtimezones = []; + + /** + * iCalendar objects. + * + * @var array + */ + protected $objects = []; + + /** + * Constructor. + * + * The splitter should receive an readable file stream as it's input. + * + * @param resource $input + * @param int $options Parser options, see the OPTIONS constants. + */ + function __construct($input, $options = 0) { + + $data = VObject\Reader::read($input, $options); + + if (!$data instanceof VObject\Component\VCalendar) { + throw new VObject\ParseException('Supplied input could not be parsed as VCALENDAR.'); + } + + foreach ($data->children() as $component) { + if (!$component instanceof VObject\Component) { + continue; + } + + // Get all timezones + if ($component->name === 'VTIMEZONE') { + $this->vtimezones[(string)$component->TZID] = $component; + continue; + } + + // Get component UID for recurring Events search + if (!$component->UID) { + $component->UID = sha1(microtime()) . '-vobjectimport'; + } + $uid = (string)$component->UID; + + // Take care of recurring events + if (!array_key_exists($uid, $this->objects)) { + $this->objects[$uid] = new VCalendar(); + } + + $this->objects[$uid]->add(clone $component); + } + + } + + /** + * Every time getNext() is called, a new object will be parsed, until we + * hit the end of the stream. + * + * When the end is reached, null will be returned. + * + * @return Sabre\VObject\Component|null + */ + function getNext() { + + if ($object = array_shift($this->objects)) { + + // create our baseobject + $object->version = '2.0'; + $object->prodid = '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN'; + $object->calscale = 'GREGORIAN'; + + // add vtimezone information to obj (if we have it) + foreach ($this->vtimezones as $vtimezone) { + $object->add($vtimezone); + } + + return $object; + + } else { + + return; + + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Splitter/SplitterInterface.php b/htdocs/includes/sabre/sabre/vobject/lib/Splitter/SplitterInterface.php new file mode 100644 index 00000000000..8f827cc4b47 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Splitter/SplitterInterface.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\VObject\Splitter; + +/** + * VObject splitter. + * + * The splitter is responsible for reading a large vCard or iCalendar object, + * and splitting it into multiple objects. + * + * This is for example for Card and CalDAV, which require every event and vcard + * to exist in their own objects, instead of one large one. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Dominik Tobschall (http://tobschall.de/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface SplitterInterface { + + /** + * Constructor. + * + * The splitter should receive an readable file stream as it's input. + * + * @param resource $input + */ + function __construct($input); + + /** + * Every time getNext() is called, a new object will be parsed, until we + * hit the end of the stream. + * + * When the end is reached, null will be returned. + * + * @return Sabre\VObject\Component|null + */ + function getNext(); + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Splitter/VCard.php b/htdocs/includes/sabre/sabre/vobject/lib/Splitter/VCard.php new file mode 100644 index 00000000000..0bb82abe93b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Splitter/VCard.php @@ -0,0 +1,78 @@ +<?php + +namespace Sabre\VObject\Splitter; + +use Sabre\VObject; +use Sabre\VObject\Parser\MimeDir; + +/** + * Splitter. + * + * This class is responsible for splitting up VCard objects. + * + * It is assumed that the input stream contains 1 or more VCARD objects. This + * class checks for BEGIN:VCARD and END:VCARD and parses each encountered + * component individually. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Dominik Tobschall (http://tobschall.de/) + * @author Armin Hackmann + * @license http://sabre.io/license/ Modified BSD License + */ +class VCard implements SplitterInterface { + + /** + * File handle. + * + * @var resource + */ + protected $input; + + /** + * Persistent parser. + * + * @var MimeDir + */ + protected $parser; + + /** + * Constructor. + * + * The splitter should receive an readable file stream as it's input. + * + * @param resource $input + * @param int $options Parser options, see the OPTIONS constants. + */ + function __construct($input, $options = 0) { + + $this->input = $input; + $this->parser = new MimeDir($input, $options); + + } + + /** + * Every time getNext() is called, a new object will be parsed, until we + * hit the end of the stream. + * + * When the end is reached, null will be returned. + * + * @return Sabre\VObject\Component|null + */ + function getNext() { + + try { + $object = $this->parser->parse(); + + if (!$object instanceof VObject\Component\VCard) { + throw new VObject\ParseException('The supplied input contained non-VCARD data.'); + } + + } catch (VObject\EofException $e) { + return; + } + + return $object; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/StringUtil.php b/htdocs/includes/sabre/sabre/vobject/lib/StringUtil.php new file mode 100644 index 00000000000..b8615f2ba12 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/StringUtil.php @@ -0,0 +1,66 @@ +<?php + +namespace Sabre\VObject; + +/** + * Useful utilities for working with various strings. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class StringUtil { + + /** + * Returns true or false depending on if a string is valid UTF-8. + * + * @param string $str + * + * @return bool + */ + static function isUTF8($str) { + + // Control characters + if (preg_match('%[\x00-\x08\x0B-\x0C\x0E\x0F]%', $str)) { + return false; + } + + return (bool)preg_match('%%u', $str); + + } + + /** + * This method tries its best to convert the input string to UTF-8. + * + * Currently only ISO-5991-1 input and UTF-8 input is supported, but this + * may be expanded upon if we receive other examples. + * + * @param string $str + * + * @return string + */ + static function convertToUTF8($str) { + + $encoding = mb_detect_encoding($str, ['UTF-8', 'ISO-8859-1', 'WINDOWS-1252'], true); + + switch ($encoding) { + case 'ISO-8859-1' : + $newStr = utf8_encode($str); + break; + /* Unreachable code. Not sure yet how we can improve this + * situation. + case 'WINDOWS-1252' : + $newStr = iconv('cp1252', 'UTF-8', $str); + break; + */ + default : + $newStr = $str; + + } + + // Removing any control characters + return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', '', $newStr)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/TimeZoneUtil.php b/htdocs/includes/sabre/sabre/vobject/lib/TimeZoneUtil.php new file mode 100644 index 00000000000..925183e8d94 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/TimeZoneUtil.php @@ -0,0 +1,276 @@ +<?php + +namespace Sabre\VObject; + +/** + * Time zone name translation. + * + * This file translates well-known time zone names into "Olson database" time zone names. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Frank Edelhaeuser (fedel@users.sourceforge.net) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class TimeZoneUtil { + + static $map = null; + + /** + * List of microsoft exchange timezone ids. + * + * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx + */ + static $microsoftExchangeMap = [ + 0 => 'UTC', + 31 => 'Africa/Casablanca', + + // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo. + // I'm not even kidding.. We handle this special case in the + // getTimeZone method. + 2 => 'Europe/Lisbon', + 1 => 'Europe/London', + 4 => 'Europe/Berlin', + 6 => 'Europe/Prague', + 3 => 'Europe/Paris', + 69 => 'Africa/Luanda', // This was a best guess + 7 => 'Europe/Athens', + 5 => 'Europe/Bucharest', + 49 => 'Africa/Cairo', + 50 => 'Africa/Harare', + 59 => 'Europe/Helsinki', + 27 => 'Asia/Jerusalem', + 26 => 'Asia/Baghdad', + 74 => 'Asia/Kuwait', + 51 => 'Europe/Moscow', + 56 => 'Africa/Nairobi', + 25 => 'Asia/Tehran', + 24 => 'Asia/Muscat', // Best guess + 54 => 'Asia/Baku', + 48 => 'Asia/Kabul', + 58 => 'Asia/Yekaterinburg', + 47 => 'Asia/Karachi', + 23 => 'Asia/Calcutta', + 62 => 'Asia/Kathmandu', + 46 => 'Asia/Almaty', + 71 => 'Asia/Dhaka', + 66 => 'Asia/Colombo', + 61 => 'Asia/Rangoon', + 22 => 'Asia/Bangkok', + 64 => 'Asia/Krasnoyarsk', + 45 => 'Asia/Shanghai', + 63 => 'Asia/Irkutsk', + 21 => 'Asia/Singapore', + 73 => 'Australia/Perth', + 75 => 'Asia/Taipei', + 20 => 'Asia/Tokyo', + 72 => 'Asia/Seoul', + 70 => 'Asia/Yakutsk', + 19 => 'Australia/Adelaide', + 44 => 'Australia/Darwin', + 18 => 'Australia/Brisbane', + 76 => 'Australia/Sydney', + 43 => 'Pacific/Guam', + 42 => 'Australia/Hobart', + 68 => 'Asia/Vladivostok', + 41 => 'Asia/Magadan', + 17 => 'Pacific/Auckland', + 40 => 'Pacific/Fiji', + 67 => 'Pacific/Tongatapu', + 29 => 'Atlantic/Azores', + 53 => 'Atlantic/Cape_Verde', + 30 => 'America/Noronha', + 8 => 'America/Sao_Paulo', // Best guess + 32 => 'America/Argentina/Buenos_Aires', + 60 => 'America/Godthab', + 28 => 'America/St_Johns', + 9 => 'America/Halifax', + 33 => 'America/Caracas', + 65 => 'America/Santiago', + 35 => 'America/Bogota', + 10 => 'America/New_York', + 34 => 'America/Indiana/Indianapolis', + 55 => 'America/Guatemala', + 11 => 'America/Chicago', + 37 => 'America/Mexico_City', + 36 => 'America/Edmonton', + 38 => 'America/Phoenix', + 12 => 'America/Denver', // Best guess + 13 => 'America/Los_Angeles', // Best guess + 14 => 'America/Anchorage', + 15 => 'Pacific/Honolulu', + 16 => 'Pacific/Midway', + 39 => 'Pacific/Kwajalein', + ]; + + /** + * This method will try to find out the correct timezone for an iCalendar + * date-time value. + * + * You must pass the contents of the TZID parameter, as well as the full + * calendar. + * + * If the lookup fails, this method will return the default PHP timezone + * (as configured using date_default_timezone_set, or the date.timezone ini + * setting). + * + * Alternatively, if $failIfUncertain is set to true, it will throw an + * exception if we cannot accurately determine the timezone. + * + * @param string $tzid + * @param Sabre\VObject\Component $vcalendar + * + * @return DateTimeZone + */ + static function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) { + + // First we will just see if the tzid is a support timezone identifier. + // + // The only exception is if the timezone starts with (. This is to + // handle cases where certain microsoft products generate timezone + // identifiers that for instance look like: + // + // (GMT+01.00) Sarajevo/Warsaw/Zagreb + // + // Since PHP 5.5.10, the first bit will be used as the timezone and + // this method will return just GMT+01:00. This is wrong, because it + // doesn't take DST into account. + if ($tzid[0] !== '(') { + + // PHP has a bug that logs PHP warnings even it shouldn't: + // https://bugs.php.net/bug.php?id=67881 + // + // That's why we're checking if we'll be able to successfull instantiate + // \DateTimeZone() before doing so. Otherwise we could simply instantiate + // and catch the exception. + $tzIdentifiers = \DateTimeZone::listIdentifiers(); + + try { + if ( + (in_array($tzid, $tzIdentifiers)) || + (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) || + (in_array($tzid, self::getIdentifiersBC())) + ) { + return new \DateTimeZone($tzid); + } + } catch (\Exception $e) { + } + + } + + self::loadTzMaps(); + + // Next, we check if the tzid is somewhere in our tzid map. + if (isset(self::$map[$tzid])) { + return new \DateTimeZone(self::$map[$tzid]); + } + + // Some Microsoft products prefix the offset first, so let's strip that off + // and see if it is our tzid map. We don't want to check for this first just + // in case there are overrides in our tzid map. + if (preg_match('/^\((UTC|GMT)(\+|\-)[\d]{2}\:[\d]{2}\) (.*)/', $tzid, $matches)) { + $tzidAlternate = $matches[3]; + if (isset(self::$map[$tzidAlternate])) { + return new \DateTimeZone(self::$map[$tzidAlternate]); + } + } + + // Maybe the author was hyper-lazy and just included an offset. We + // support it, but we aren't happy about it. + if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) { + + // Note that the path in the source will never be taken from PHP 5.5.10 + // onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it + // already gets returned early in this function. Once we drop support + // for versions under PHP 5.5.10, this bit can be taken out of the + // source. + // @codeCoverageIgnoreStart + return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2], 0, 2), '0')); + // @codeCoverageIgnoreEnd + } + + if ($vcalendar) { + + // If that didn't work, we will scan VTIMEZONE objects + foreach ($vcalendar->select('VTIMEZONE') as $vtimezone) { + + if ((string)$vtimezone->TZID === $tzid) { + + // Some clients add 'X-LIC-LOCATION' with the olson name. + if (isset($vtimezone->{'X-LIC-LOCATION'})) { + + $lic = (string)$vtimezone->{'X-LIC-LOCATION'}; + + // Libical generators may specify strings like + // "SystemV/EST5EDT". For those we must remove the + // SystemV part. + if (substr($lic, 0, 8) === 'SystemV/') { + $lic = substr($lic, 8); + } + + return self::getTimeZone($lic, null, $failIfUncertain); + + } + // Microsoft may add a magic number, which we also have an + // answer for. + if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) { + $cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue(); + + // 2 can mean both Europe/Lisbon and Europe/Sarajevo. + if ($cdoId === 2 && strpos((string)$vtimezone->TZID, 'Sarajevo') !== false) { + return new \DateTimeZone('Europe/Sarajevo'); + } + + if (isset(self::$microsoftExchangeMap[$cdoId])) { + return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]); + } + } + + } + + } + + } + + if ($failIfUncertain) { + throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid); + } + + // If we got all the way here, we default to UTC. + return new \DateTimeZone(date_default_timezone_get()); + + } + + /** + * This method will load in all the tz mapping information, if it's not yet + * done. + */ + static function loadTzMaps() { + + if (!is_null(self::$map)) return; + + self::$map = array_merge( + include __DIR__ . '/timezonedata/windowszones.php', + include __DIR__ . '/timezonedata/lotuszones.php', + include __DIR__ . '/timezonedata/exchangezones.php', + include __DIR__ . '/timezonedata/php-workaround.php' + ); + + } + + /** + * This method returns an array of timezone identifiers, that are supported + * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers(). + * + * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because: + * - It's not supported by some PHP versions as well as HHVM. + * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions. + * (See timezonedata/php-bc.php and timezonedata php-workaround.php) + * + * @return array + */ + static function getIdentifiersBC() { + return include __DIR__ . '/timezonedata/php-bc.php'; + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/UUIDUtil.php b/htdocs/includes/sabre/sabre/vobject/lib/UUIDUtil.php new file mode 100644 index 00000000000..24ebe3cf80d --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/UUIDUtil.php @@ -0,0 +1,69 @@ +<?php + +namespace Sabre\VObject; + +/** + * UUID Utility. + * + * This class has static methods to generate and validate UUID's. + * UUIDs are used a decent amount within various *DAV standards, so it made + * sense to include it. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class UUIDUtil { + + /** + * Returns a pseudo-random v4 UUID. + * + * This function is based on a comment by Andrew Moore on php.net + * + * @see http://www.php.net/manual/en/function.uniqid.php#94959 + * + * @return string + */ + static function getUUID() { + + return sprintf( + + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + + // 32 bits for "time_low" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), + + // 16 bits for "time_mid" + mt_rand(0, 0xffff), + + // 16 bits for "time_hi_and_version", + // four most significant bits holds version number 4 + mt_rand(0, 0x0fff) | 0x4000, + + // 16 bits, 8 bits for "clk_seq_hi_res", + // 8 bits for "clk_seq_low", + // two most significant bits holds zero and one for variant DCE1.1 + mt_rand(0, 0x3fff) | 0x8000, + + // 48 bits for "node" + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) + ); + } + + /** + * Checks if a string is a valid UUID. + * + * @param string $uuid + * + * @return bool + */ + static function validateUUID($uuid) { + + return preg_match( + '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i', + $uuid + ) !== 0; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/VCardConverter.php b/htdocs/includes/sabre/sabre/vobject/lib/VCardConverter.php new file mode 100644 index 00000000000..1f6d016f142 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/VCardConverter.php @@ -0,0 +1,467 @@ +<?php + +namespace Sabre\VObject; + +/** + * This utility converts vcards from one version to another. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class VCardConverter { + + /** + * Converts a vCard object to a new version. + * + * targetVersion must be one of: + * Document::VCARD21 + * Document::VCARD30 + * Document::VCARD40 + * + * Currently only 3.0 and 4.0 as input and output versions. + * + * 2.1 has some minor support for the input version, it's incomplete at the + * moment though. + * + * If input and output version are identical, a clone is returned. + * + * @param Component\VCard $input + * @param int $targetVersion + */ + function convert(Component\VCard $input, $targetVersion) { + + $inputVersion = $input->getDocumentType(); + if ($inputVersion === $targetVersion) { + return clone $input; + } + + if (!in_array($inputVersion, [Document::VCARD21, Document::VCARD30, Document::VCARD40])) { + throw new \InvalidArgumentException('Only vCard 2.1, 3.0 and 4.0 are supported for the input data'); + } + if (!in_array($targetVersion, [Document::VCARD30, Document::VCARD40])) { + throw new \InvalidArgumentException('You can only use vCard 3.0 or 4.0 for the target version'); + } + + $newVersion = $targetVersion === Document::VCARD40 ? '4.0' : '3.0'; + + $output = new Component\VCard([ + 'VERSION' => $newVersion, + ]); + + // We might have generated a default UID. Remove it! + unset($output->UID); + + foreach ($input->children() as $property) { + + $this->convertProperty($input, $output, $property, $targetVersion); + + } + + return $output; + + } + + /** + * Handles conversion of a single property. + * + * @param Component\VCard $input + * @param Component\VCard $output + * @param Property $property + * @param int $targetVersion + * + * @return void + */ + protected function convertProperty(Component\VCard $input, Component\VCard $output, Property $property, $targetVersion) { + + // Skipping these, those are automatically added. + if (in_array($property->name, ['VERSION', 'PRODID'])) { + return; + } + + $parameters = $property->parameters(); + $valueType = null; + if (isset($parameters['VALUE'])) { + $valueType = $parameters['VALUE']->getValue(); + unset($parameters['VALUE']); + } + if (!$valueType) { + $valueType = $property->getValueType(); + } + $newProperty = $output->createProperty( + $property->name, + $property->getParts(), + [], // parameters will get added a bit later. + $valueType + ); + + + if ($targetVersion === Document::VCARD30) { + + if ($property instanceof Property\Uri && in_array($property->name, ['PHOTO', 'LOGO', 'SOUND'])) { + + $newProperty = $this->convertUriToBinary($output, $newProperty); + + } elseif ($property instanceof Property\VCard\DateAndOrTime) { + + // In vCard 4, the birth year may be optional. This is not the + // case for vCard 3. Apple has a workaround for this that + // allows applications that support Apple's extension still + // omit birthyears in vCard 3, but applications that do not + // support this, will just use a random birthyear. We're + // choosing 1604 for the birthyear, because that's what apple + // uses. + $parts = DateTimeParser::parseVCardDateTime($property->getValue()); + if (is_null($parts['year'])) { + $newValue = '1604-' . $parts['month'] . '-' . $parts['date']; + $newProperty->setValue($newValue); + $newProperty['X-APPLE-OMIT-YEAR'] = '1604'; + } + + if ($newProperty->name == 'ANNIVERSARY') { + // Microsoft non-standard anniversary + $newProperty->name = 'X-ANNIVERSARY'; + + // We also need to add a new apple property for the same + // purpose. This apple property needs a 'label' in the same + // group, so we first need to find a groupname that doesn't + // exist yet. + $x = 1; + while ($output->select('ITEM' . $x . '.')) { + $x++; + } + $output->add('ITEM' . $x . '.X-ABDATE', $newProperty->getValue(), ['VALUE' => 'DATE-AND-OR-TIME']); + $output->add('ITEM' . $x . '.X-ABLABEL', '_$!<Anniversary>!$_'); + } + + } elseif ($property->name === 'KIND') { + + switch (strtolower($property->getValue())) { + case 'org' : + // vCard 3.0 does not have an equivalent to KIND:ORG, + // but apple has an extension that means the same + // thing. + $newProperty = $output->createProperty('X-ABSHOWAS', 'COMPANY'); + break; + + case 'individual' : + // Individual is implicit, so we skip it. + return; + + case 'group' : + // OS X addressbook property + $newProperty = $output->createProperty('X-ADDRESSBOOKSERVER-KIND', 'GROUP'); + break; + } + + + } + + } elseif ($targetVersion === Document::VCARD40) { + + // These properties were removed in vCard 4.0 + if (in_array($property->name, ['NAME', 'MAILER', 'LABEL', 'CLASS'])) { + return; + } + + if ($property instanceof Property\Binary) { + + $newProperty = $this->convertBinaryToUri($output, $newProperty, $parameters); + + } elseif ($property instanceof Property\VCard\DateAndOrTime && isset($parameters['X-APPLE-OMIT-YEAR'])) { + + // If a property such as BDAY contained 'X-APPLE-OMIT-YEAR', + // then we're stripping the year from the vcard 4 value. + $parts = DateTimeParser::parseVCardDateTime($property->getValue()); + if ($parts['year'] === $property['X-APPLE-OMIT-YEAR']->getValue()) { + $newValue = '--' . $parts['month'] . '-' . $parts['date']; + $newProperty->setValue($newValue); + } + + // Regardless if the year matched or not, we do need to strip + // X-APPLE-OMIT-YEAR. + unset($parameters['X-APPLE-OMIT-YEAR']); + + } + switch ($property->name) { + case 'X-ABSHOWAS' : + if (strtoupper($property->getValue()) === 'COMPANY') { + $newProperty = $output->createProperty('KIND', 'ORG'); + } + break; + case 'X-ADDRESSBOOKSERVER-KIND' : + if (strtoupper($property->getValue()) === 'GROUP') { + $newProperty = $output->createProperty('KIND', 'GROUP'); + } + break; + case 'X-ANNIVERSARY' : + $newProperty->name = 'ANNIVERSARY'; + // If we already have an anniversary property with the same + // value, ignore. + foreach ($output->select('ANNIVERSARY') as $anniversary) { + if ($anniversary->getValue() === $newProperty->getValue()) { + return; + } + } + break; + case 'X-ABDATE' : + // Find out what the label was, if it exists. + if (!$property->group) { + break; + } + $label = $input->{$property->group . '.X-ABLABEL'}; + + // We only support converting anniversaries. + if (!$label || $label->getValue() !== '_$!<Anniversary>!$_') { + break; + } + + // If we already have an anniversary property with the same + // value, ignore. + foreach ($output->select('ANNIVERSARY') as $anniversary) { + if ($anniversary->getValue() === $newProperty->getValue()) { + return; + } + } + $newProperty->name = 'ANNIVERSARY'; + break; + // Apple's per-property label system. + case 'X-ABLABEL' : + if ($newProperty->getValue() === '_$!<Anniversary>!$_') { + // We can safely remove these, as they are converted to + // ANNIVERSARY properties. + return; + } + break; + + } + + } + + // set property group + $newProperty->group = $property->group; + + if ($targetVersion === Document::VCARD40) { + $this->convertParameters40($newProperty, $parameters); + } else { + $this->convertParameters30($newProperty, $parameters); + } + + // Lastly, we need to see if there's a need for a VALUE parameter. + // + // We can do that by instantating a empty property with that name, and + // seeing if the default valueType is identical to the current one. + $tempProperty = $output->createProperty($newProperty->name); + if ($tempProperty->getValueType() !== $newProperty->getValueType()) { + $newProperty['VALUE'] = $newProperty->getValueType(); + } + + $output->add($newProperty); + + + } + + /** + * Converts a BINARY property to a URI property. + * + * vCard 4.0 no longer supports BINARY properties. + * + * @param Component\VCard $output + * @param Property\Uri $property The input property. + * @param $parameters List of parameters that will eventually be added to + * the new property. + * + * @return Property\Uri + */ + protected function convertBinaryToUri(Component\VCard $output, Property\Binary $newProperty, array &$parameters) { + + $value = $newProperty->getValue(); + $newProperty = $output->createProperty( + $newProperty->name, + null, // no value + [], // no parameters yet + 'URI' // Forcing the BINARY type + ); + + $mimeType = 'application/octet-stream'; + + // See if we can find a better mimetype. + if (isset($parameters['TYPE'])) { + + $newTypes = []; + foreach ($parameters['TYPE']->getParts() as $typePart) { + if (in_array( + strtoupper($typePart), + ['JPEG', 'PNG', 'GIF'] + )) { + $mimeType = 'image/' . strtolower($typePart); + } else { + $newTypes[] = $typePart; + } + } + + // If there were any parameters we're not converting to a + // mime-type, we need to keep them. + if ($newTypes) { + $parameters['TYPE']->setParts($newTypes); + } else { + unset($parameters['TYPE']); + } + + } + + $newProperty->setValue('data:' . $mimeType . ';base64,' . base64_encode($value)); + return $newProperty; + + } + + /** + * Converts a URI property to a BINARY property. + * + * In vCard 4.0 attachments are encoded as data: uri. Even though these may + * be valid in vCard 3.0 as well, we should convert those to BINARY if + * possible, to improve compatibility. + * + * @param Component\VCard $output + * @param Property\Uri $property The input property. + * + * @return Property\Binary|null + */ + protected function convertUriToBinary(Component\VCard $output, Property\Uri $newProperty) { + + $value = $newProperty->getValue(); + + // Only converting data: uris + if (substr($value, 0, 5) !== 'data:') { + return $newProperty; + } + + $newProperty = $output->createProperty( + $newProperty->name, + null, // no value + [], // no parameters yet + 'BINARY' + ); + + $mimeType = substr($value, 5, strpos($value, ',') - 5); + if (strpos($mimeType, ';')) { + $mimeType = substr($mimeType, 0, strpos($mimeType, ';')); + $newProperty->setValue(base64_decode(substr($value, strpos($value, ',') + 1))); + } else { + $newProperty->setValue(substr($value, strpos($value, ',') + 1)); + } + unset($value); + + $newProperty['ENCODING'] = 'b'; + switch ($mimeType) { + + case 'image/jpeg' : + $newProperty['TYPE'] = 'JPEG'; + break; + case 'image/png' : + $newProperty['TYPE'] = 'PNG'; + break; + case 'image/gif' : + $newProperty['TYPE'] = 'GIF'; + break; + + } + + + return $newProperty; + + } + + /** + * Adds parameters to a new property for vCard 4.0. + * + * @param Property $newProperty + * @param array $parameters + * + * @return void + */ + protected function convertParameters40(Property $newProperty, array $parameters) { + + // Adding all parameters. + foreach ($parameters as $param) { + + // vCard 2.1 allowed parameters with no name + if ($param->noName) $param->noName = false; + + switch ($param->name) { + + // We need to see if there's any TYPE=PREF, because in vCard 4 + // that's now PREF=1. + case 'TYPE' : + foreach ($param->getParts() as $paramPart) { + + if (strtoupper($paramPart) === 'PREF') { + $newProperty->add('PREF', '1'); + } else { + $newProperty->add($param->name, $paramPart); + } + + } + break; + // These no longer exist in vCard 4 + case 'ENCODING' : + case 'CHARSET' : + break; + + default : + $newProperty->add($param->name, $param->getParts()); + break; + + } + + } + + } + + /** + * Adds parameters to a new property for vCard 3.0. + * + * @param Property $newProperty + * @param array $parameters + * + * @return void + */ + protected function convertParameters30(Property $newProperty, array $parameters) { + + // Adding all parameters. + foreach ($parameters as $param) { + + // vCard 2.1 allowed parameters with no name + if ($param->noName) $param->noName = false; + + switch ($param->name) { + + case 'ENCODING' : + // This value only existed in vCard 2.1, and should be + // removed for anything else. + if (strtoupper($param->getValue()) !== 'QUOTED-PRINTABLE') { + $newProperty->add($param->name, $param->getParts()); + } + break; + + /* + * Converting PREF=1 to TYPE=PREF. + * + * Any other PREF numbers we'll drop. + */ + case 'PREF' : + if ($param->getValue() == '1') { + $newProperty->add('TYPE', 'PREF'); + } + break; + + default : + $newProperty->add($param->name, $param->getParts()); + break; + + } + + } + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Version.php b/htdocs/includes/sabre/sabre/vobject/lib/Version.php new file mode 100644 index 00000000000..346e2044df1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Version.php @@ -0,0 +1,19 @@ +<?php + +namespace Sabre\VObject; + +/** + * This class contains the version number for the VObject package. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Version { + + /** + * Full version number. + */ + const VERSION = '4.1.2'; + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/Writer.php b/htdocs/includes/sabre/sabre/vobject/lib/Writer.php new file mode 100644 index 00000000000..f8a58758d6c --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/Writer.php @@ -0,0 +1,81 @@ +<?php + +namespace Sabre\VObject; + +use Sabre\Xml; + +/** + * iCalendar/vCard/jCal/jCard/xCal/xCard writer object. + * + * This object provides a few (static) convenience methods to quickly access + * the serializers. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Ivan Enderlin + * @license http://sabre.io/license/ Modified BSD License + */ +class Writer { + + /** + * Serializes a vCard or iCalendar object. + * + * @param Component $component + * + * @return string + */ + static function write(Component $component) { + + return $component->serialize(); + + } + + /** + * Serializes a jCal or jCard object. + * + * @param Component $component + * @param int $options + * + * @return string + */ + static function writeJson(Component $component, $options = 0) { + + return json_encode($component, $options); + + } + + /** + * Serializes a xCal or xCard object. + * + * @param Component $component + * + * @return string + */ + static function writeXml(Component $component) { + + $writer = new Xml\Writer(); + $writer->openMemory(); + $writer->setIndent(true); + + $writer->startDocument('1.0', 'utf-8'); + + if ($component instanceof Component\VCalendar) { + + $writer->startElement('icalendar'); + $writer->writeAttribute('xmlns', Parser\Xml::XCAL_NAMESPACE); + + } else { + + $writer->startElement('vcards'); + $writer->writeAttribute('xmlns', Parser\Xml::XCARD_NAMESPACE); + + } + + $component->xmlSerialize($writer); + + $writer->endElement(); + + return $writer->outputMemory(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/exchangezones.php b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/exchangezones.php new file mode 100644 index 00000000000..38138354a76 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/exchangezones.php @@ -0,0 +1,93 @@ +<?php + +/** + * Microsoft exchange timezones + * Source: + * http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx. + * + * Correct timezones deduced with help from: + * http://en.wikipedia.org/wiki/List_of_tz_database_time_zones + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +return [ + 'Universal Coordinated Time' => 'UTC', + 'Casablanca, Monrovia' => 'Africa/Casablanca', + 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon', + 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London', + 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin', + 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague', + 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris', + 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris', + 'Prague, Central Europe' => 'Europe/Prague', + 'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo', + 'West Central Africa' => 'Africa/Luanda', // This was a best guess + 'Athens, Istanbul, Minsk' => 'Europe/Athens', + 'Bucharest' => 'Europe/Bucharest', + 'Cairo' => 'Africa/Cairo', + 'Harare, Pretoria' => 'Africa/Harare', + 'Helsinki, Riga, Tallinn' => 'Europe/Helsinki', + 'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem', + 'Baghdad' => 'Asia/Baghdad', + 'Arab, Kuwait, Riyadh' => 'Asia/Kuwait', + 'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow', + 'East Africa, Nairobi' => 'Africa/Nairobi', + 'Tehran' => 'Asia/Tehran', + 'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess + 'Baku, Tbilisi, Yerevan' => 'Asia/Baku', + 'Kabul' => 'Asia/Kabul', + 'Ekaterinburg' => 'Asia/Yekaterinburg', + 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi', + 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta', + 'Kathmandu, Nepal' => 'Asia/Kathmandu', + 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty', + 'Astana, Dhaka' => 'Asia/Dhaka', + 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo', + 'Rangoon' => 'Asia/Rangoon', + 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok', + 'Krasnoyarsk' => 'Asia/Krasnoyarsk', + 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai', + 'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk', + 'Kuala Lumpur, Singapore' => 'Asia/Singapore', + 'Perth, Western Australia' => 'Australia/Perth', + 'Taipei' => 'Asia/Taipei', + 'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo', + 'Seoul, Korea Standard time' => 'Asia/Seoul', + 'Yakutsk' => 'Asia/Yakutsk', + 'Adelaide, Central Australia' => 'Australia/Adelaide', + 'Darwin' => 'Australia/Darwin', + 'Brisbane, East Australia' => 'Australia/Brisbane', + 'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney', + 'Guam, Port Moresby' => 'Pacific/Guam', + 'Hobart, Tasmania' => 'Australia/Hobart', + 'Vladivostok' => 'Asia/Vladivostok', + 'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan', + 'Auckland, Wellington' => 'Pacific/Auckland', + 'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji', + 'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu', + 'Azores' => 'Atlantic/Azores', + 'Cape Verde Is.' => 'Atlantic/Cape_Verde', + 'Mid-Atlantic' => 'America/Noronha', + 'Brasilia' => 'America/Sao_Paulo', // Best guess + 'Buenos Aires' => 'America/Argentina/Buenos_Aires', + 'Greenland' => 'America/Godthab', + 'Newfoundland' => 'America/St_Johns', + 'Atlantic Time (Canada)' => 'America/Halifax', + 'Caracas, La Paz' => 'America/Caracas', + 'Santiago' => 'America/Santiago', + 'Bogota, Lima, Quito' => 'America/Bogota', + 'Eastern Time (US & Canada)' => 'America/New_York', + 'Indiana (East)' => 'America/Indiana/Indianapolis', + 'Central America' => 'America/Guatemala', + 'Central Time (US & Canada)' => 'America/Chicago', + 'Mexico City, Tegucigalpa' => 'America/Mexico_City', + 'Saskatchewan' => 'America/Edmonton', + 'Arizona' => 'America/Phoenix', + 'Mountain Time (US & Canada)' => 'America/Denver', // Best guess + 'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess + 'Alaska' => 'America/Anchorage', + 'Hawaii' => 'Pacific/Honolulu', + 'Midway Island, Samoa' => 'Pacific/Midway', + 'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein', +]; diff --git a/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/lotuszones.php b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/lotuszones.php new file mode 100644 index 00000000000..79d555a92f0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/lotuszones.php @@ -0,0 +1,101 @@ +<?php + +/** + * The following list are timezone names that could be generated by + * Lotus / Domino. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +return [ + 'Dateline' => 'Etc/GMT-12', + 'Samoa' => 'Pacific/Apia', + 'Hawaiian' => 'Pacific/Honolulu', + 'Alaskan' => 'America/Anchorage', + 'Pacific' => 'America/Los_Angeles', + 'Pacific Standard Time' => 'America/Los_Angeles', + 'Mexico Standard Time 2' => 'America/Chihuahua', + 'Mountain' => 'America/Denver', + // 'Mountain Standard Time' => 'America/Chihuahua', // conflict with windows timezones. + 'US Mountain' => 'America/Phoenix', + 'Canada Central' => 'America/Edmonton', + 'Central America' => 'America/Guatemala', + 'Central' => 'America/Chicago', + // 'Central Standard Time' => 'America/Mexico_City', // conflict with windows timezones. + 'Mexico' => 'America/Mexico_City', + 'Eastern' => 'America/New_York', + 'SA Pacific' => 'America/Bogota', + 'US Eastern' => 'America/Indiana/Indianapolis', + 'Venezuela' => 'America/Caracas', + 'Atlantic' => 'America/Halifax', + 'Central Brazilian' => 'America/Manaus', + 'Pacific SA' => 'America/Santiago', + 'SA Western' => 'America/La_Paz', + 'Newfoundland' => 'America/St_Johns', + 'Argentina' => 'America/Argentina/Buenos_Aires', + 'E. South America' => 'America/Belem', + 'Greenland' => 'America/Godthab', + 'Montevideo' => 'America/Montevideo', + 'SA Eastern' => 'America/Belem', + // 'Mid-Atlantic' => 'Etc/GMT-2', // conflict with windows timezones. + 'Azores' => 'Atlantic/Azores', + 'Cape Verde' => 'Atlantic/Cape_Verde', + 'Greenwich' => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT. + 'Morocco' => 'Africa/Casablanca', + 'Central Europe' => 'Europe/Prague', + 'Central European' => 'Europe/Sarajevo', + 'Romance' => 'Europe/Paris', + 'W. Central Africa' => 'Africa/Lagos', // Best guess + 'W. Europe' => 'Europe/Amsterdam', + 'E. Europe' => 'Europe/Minsk', + 'Egypt' => 'Africa/Cairo', + 'FLE' => 'Europe/Helsinki', + 'GTB' => 'Europe/Athens', + 'Israel' => 'Asia/Jerusalem', + 'Jordan' => 'Asia/Amman', + 'Middle East' => 'Asia/Beirut', + 'Namibia' => 'Africa/Windhoek', + 'South Africa' => 'Africa/Harare', + 'Arab' => 'Asia/Kuwait', + 'Arabic' => 'Asia/Baghdad', + 'E. Africa' => 'Africa/Nairobi', + 'Georgian' => 'Asia/Tbilisi', + 'Russian' => 'Europe/Moscow', + 'Iran' => 'Asia/Tehran', + 'Arabian' => 'Asia/Muscat', + 'Armenian' => 'Asia/Yerevan', + 'Azerbijan' => 'Asia/Baku', + 'Caucasus' => 'Asia/Yerevan', + 'Mauritius' => 'Indian/Mauritius', + 'Afghanistan' => 'Asia/Kabul', + 'Ekaterinburg' => 'Asia/Yekaterinburg', + 'Pakistan' => 'Asia/Karachi', + 'West Asia' => 'Asia/Tashkent', + 'India' => 'Asia/Calcutta', + 'Sri Lanka' => 'Asia/Colombo', + 'Nepal' => 'Asia/Kathmandu', + 'Central Asia' => 'Asia/Dhaka', + 'N. Central Asia' => 'Asia/Almaty', + 'Myanmar' => 'Asia/Rangoon', + 'North Asia' => 'Asia/Krasnoyarsk', + 'SE Asia' => 'Asia/Bangkok', + 'China' => 'Asia/Shanghai', + 'North Asia East' => 'Asia/Irkutsk', + 'Singapore' => 'Asia/Singapore', + 'Taipei' => 'Asia/Taipei', + 'W. Australia' => 'Australia/Perth', + 'Korea' => 'Asia/Seoul', + 'Tokyo' => 'Asia/Tokyo', + 'Yakutsk' => 'Asia/Yakutsk', + 'AUS Central' => 'Australia/Darwin', + 'Cen. Australia' => 'Australia/Adelaide', + 'AUS Eastern' => 'Australia/Sydney', + 'E. Australia' => 'Australia/Brisbane', + 'Tasmania' => 'Australia/Hobart', + 'Vladivostok' => 'Asia/Vladivostok', + 'West Pacific' => 'Pacific/Guam', + 'Central Pacific' => 'Asia/Magadan', + 'Fiji' => 'Pacific/Fiji', + 'New Zealand' => 'Pacific/Auckland', + 'Tonga' => 'Pacific/Tongatapu', +]; diff --git a/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/php-bc.php b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/php-bc.php new file mode 100644 index 00000000000..906ccb0e4d4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/php-bc.php @@ -0,0 +1,154 @@ +<?php + +/** + * A list of additional PHP timezones that are returned by + * DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) + * valid for new DateTimeZone(). + * + * This list does not include those timezone identifiers that we have to map to + * a different identifier for some PHP versions (see php-workaround.php). + * + * Instead of using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) + * directly, we use this file because DateTimeZone::ALL_WITH_BC is not properly + * supported by all PHP version and HHVM. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +return [ + 'Africa/Asmera', + 'Africa/Timbuktu', + 'America/Argentina/ComodRivadavia', + 'America/Atka', + 'America/Buenos_Aires', + 'America/Catamarca', + 'America/Coral_Harbour', + 'America/Cordoba', + 'America/Ensenada', + 'America/Fort_Wayne', + 'America/Indianapolis', + 'America/Jujuy', + 'America/Knox_IN', + 'America/Louisville', + 'America/Mendoza', + 'America/Montreal', + 'America/Porto_Acre', + 'America/Rosario', + 'America/Shiprock', + 'America/Virgin', + 'Antarctica/South_Pole', + 'Asia/Ashkhabad', + 'Asia/Calcutta', + 'Asia/Chungking', + 'Asia/Dacca', + 'Asia/Istanbul', + 'Asia/Katmandu', + 'Asia/Macao', + 'Asia/Saigon', + 'Asia/Tel_Aviv', + 'Asia/Thimbu', + 'Asia/Ujung_Pandang', + 'Asia/Ulan_Bator', + 'Atlantic/Faeroe', + 'Atlantic/Jan_Mayen', + 'Australia/ACT', + 'Australia/Canberra', + 'Australia/LHI', + 'Australia/North', + 'Australia/NSW', + 'Australia/Queensland', + 'Australia/South', + 'Australia/Tasmania', + 'Australia/Victoria', + 'Australia/West', + 'Australia/Yancowinna', + 'Brazil/Acre', + 'Brazil/DeNoronha', + 'Brazil/East', + 'Brazil/West', + 'Canada/Atlantic', + 'Canada/Central', + 'Canada/East-Saskatchewan', + 'Canada/Eastern', + 'Canada/Mountain', + 'Canada/Newfoundland', + 'Canada/Pacific', + 'Canada/Saskatchewan', + 'Canada/Yukon', + 'CET', + 'Chile/Continental', + 'Chile/EasterIsland', + 'EET', + 'EST', + 'Etc/GMT', + 'Etc/GMT+0', + 'Etc/GMT+1', + 'Etc/GMT+10', + 'Etc/GMT+11', + 'Etc/GMT+12', + 'Etc/GMT+2', + 'Etc/GMT+3', + 'Etc/GMT+4', + 'Etc/GMT+5', + 'Etc/GMT+6', + 'Etc/GMT+7', + 'Etc/GMT+8', + 'Etc/GMT+9', + 'Etc/GMT-0', + 'Etc/GMT-1', + 'Etc/GMT-10', + 'Etc/GMT-11', + 'Etc/GMT-12', + 'Etc/GMT-13', + 'Etc/GMT-14', + 'Etc/GMT-2', + 'Etc/GMT-3', + 'Etc/GMT-4', + 'Etc/GMT-5', + 'Etc/GMT-6', + 'Etc/GMT-7', + 'Etc/GMT-8', + 'Etc/GMT-9', + 'Etc/GMT0', + 'Etc/Greenwich', + 'Etc/UCT', + 'Etc/Universal', + 'Etc/UTC', + 'Etc/Zulu', + 'Europe/Belfast', + 'Europe/Nicosia', + 'Europe/Tiraspol', + 'GB', + 'GMT', + 'GMT+0', + 'GMT-0', + 'HST', + 'MET', + 'Mexico/BajaNorte', + 'Mexico/BajaSur', + 'Mexico/General', + 'MST', + 'NZ', + 'Pacific/Ponape', + 'Pacific/Samoa', + 'Pacific/Truk', + 'Pacific/Yap', + 'PRC', + 'ROC', + 'ROK', + 'UCT', + 'US/Alaska', + 'US/Aleutian', + 'US/Arizona', + 'US/Central', + 'US/East-Indiana', + 'US/Eastern', + 'US/Hawaii', + 'US/Indiana-Starke', + 'US/Michigan', + 'US/Mountain', + 'US/Pacific', + 'US/Pacific-New', + 'US/Samoa', + 'WET', +]; diff --git a/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/php-workaround.php b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/php-workaround.php new file mode 100644 index 00000000000..6b9cb6ef745 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/php-workaround.php @@ -0,0 +1,46 @@ +<?php + +/** + * A list of PHP timezones that were supported until 5.5.9, removed in + * PHP 5.5.10 and re-introduced in PHP 5.5.17. + * + * DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) returns them, + * but they are invalid for new DateTimeZone(). Fixed in PHP 5.5.17. + * https://bugs.php.net/bug.php?id=66985 + * + * Some more info here: + * http://evertpot.com/php-5-5-10-timezone-changes/ + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +return [ + 'CST6CDT' => 'America/Chicago', + 'Cuba' => 'America/Havana', + 'Egypt' => 'Africa/Cairo', + 'Eire' => 'Europe/Dublin', + 'EST5EDT' => 'America/New_York', + 'Factory' => 'UTC', + 'GB-Eire' => 'Europe/London', + 'GMT0' => 'UTC', + 'Greenwich' => 'UTC', + 'Hongkong' => 'Asia/Hong_Kong', + 'Iceland' => 'Atlantic/Reykjavik', + 'Iran' => 'Asia/Tehran', + 'Israel' => 'Asia/Jerusalem', + 'Jamaica' => 'America/Jamaica', + 'Japan' => 'Asia/Tokyo', + 'Kwajalein' => 'Pacific/Kwajalein', + 'Libya' => 'Africa/Tripoli', + 'MST7MDT' => 'America/Denver', + 'Navajo' => 'America/Denver', + 'NZ-CHAT' => 'Pacific/Chatham', + 'Poland' => 'Europe/Warsaw', + 'Portugal' => 'Europe/Lisbon', + 'PST8PDT' => 'America/Los_Angeles', + 'Singapore' => 'Asia/Singapore', + 'Turkey' => 'Europe/Istanbul', + 'Universal' => 'UTC', + 'W-SU' => 'Europe/Moscow', + 'Zulu' => 'UTC', +]; diff --git a/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/windowszones.php b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/windowszones.php new file mode 100644 index 00000000000..29f3a6cb809 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/lib/timezonedata/windowszones.php @@ -0,0 +1,143 @@ +<?php + +/** + * Automatically generated timezone file + * + * Last update: 2016-08-24T17:35:38-04:00 + * Source: http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml + * + * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/). + * @license http://sabre.io/license/ Modified BSD License + */ + +return [ + 'AUS Central Standard Time' => 'Australia/Darwin', + 'AUS Eastern Standard Time' => 'Australia/Sydney', + 'Afghanistan Standard Time' => 'Asia/Kabul', + 'Alaskan Standard Time' => 'America/Anchorage', + 'Aleutian Standard Time' => 'America/Adak', + 'Altai Standard Time' => 'Asia/Barnaul', + 'Arab Standard Time' => 'Asia/Riyadh', + 'Arabian Standard Time' => 'Asia/Dubai', + 'Arabic Standard Time' => 'Asia/Baghdad', + 'Argentina Standard Time' => 'America/Buenos_Aires', + 'Astrakhan Standard Time' => 'Europe/Astrakhan', + 'Atlantic Standard Time' => 'America/Halifax', + 'Aus Central W. Standard Time' => 'Australia/Eucla', + 'Azerbaijan Standard Time' => 'Asia/Baku', + 'Azores Standard Time' => 'Atlantic/Azores', + 'Bahia Standard Time' => 'America/Bahia', + 'Bangladesh Standard Time' => 'Asia/Dhaka', + 'Belarus Standard Time' => 'Europe/Minsk', + 'Bougainville Standard Time' => 'Pacific/Bougainville', + 'Canada Central Standard Time' => 'America/Regina', + 'Cape Verde Standard Time' => 'Atlantic/Cape_Verde', + 'Caucasus Standard Time' => 'Asia/Yerevan', + 'Cen. Australia Standard Time' => 'Australia/Adelaide', + 'Central America Standard Time' => 'America/Guatemala', + 'Central Asia Standard Time' => 'Asia/Almaty', + 'Central Brazilian Standard Time' => 'America/Cuiaba', + 'Central Europe Standard Time' => 'Europe/Budapest', + 'Central European Standard Time' => 'Europe/Warsaw', + 'Central Pacific Standard Time' => 'Pacific/Guadalcanal', + 'Central Standard Time' => 'America/Chicago', + 'Central Standard Time (Mexico)' => 'America/Mexico_City', + 'Chatham Islands Standard Time' => 'Pacific/Chatham', + 'China Standard Time' => 'Asia/Shanghai', + 'Cuba Standard Time' => 'America/Havana', + 'Dateline Standard Time' => 'Etc/GMT+12', + 'E. Africa Standard Time' => 'Africa/Nairobi', + 'E. Australia Standard Time' => 'Australia/Brisbane', + 'E. Europe Standard Time' => 'Europe/Chisinau', + 'E. South America Standard Time' => 'America/Sao_Paulo', + 'Easter Island Standard Time' => 'Pacific/Easter', + 'Eastern Standard Time' => 'America/New_York', + 'Eastern Standard Time (Mexico)' => 'America/Cancun', + 'Egypt Standard Time' => 'Africa/Cairo', + 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg', + 'FLE Standard Time' => 'Europe/Kiev', + 'Fiji Standard Time' => 'Pacific/Fiji', + 'GMT Standard Time' => 'Europe/London', + 'GTB Standard Time' => 'Europe/Bucharest', + 'Georgian Standard Time' => 'Asia/Tbilisi', + 'Greenland Standard Time' => 'America/Godthab', + 'Greenwich Standard Time' => 'Atlantic/Reykjavik', + 'Haiti Standard Time' => 'America/Port-au-Prince', + 'Hawaiian Standard Time' => 'Pacific/Honolulu', + 'India Standard Time' => 'Asia/Calcutta', + 'Iran Standard Time' => 'Asia/Tehran', + 'Israel Standard Time' => 'Asia/Jerusalem', + 'Jordan Standard Time' => 'Asia/Amman', + 'Kaliningrad Standard Time' => 'Europe/Kaliningrad', + 'Korea Standard Time' => 'Asia/Seoul', + 'Libya Standard Time' => 'Africa/Tripoli', + 'Line Islands Standard Time' => 'Pacific/Kiritimati', + 'Lord Howe Standard Time' => 'Australia/Lord_Howe', + 'Magadan Standard Time' => 'Asia/Magadan', + 'Marquesas Standard Time' => 'Pacific/Marquesas', + 'Mauritius Standard Time' => 'Indian/Mauritius', + 'Middle East Standard Time' => 'Asia/Beirut', + 'Montevideo Standard Time' => 'America/Montevideo', + 'Morocco Standard Time' => 'Africa/Casablanca', + 'Mountain Standard Time' => 'America/Denver', + 'Mountain Standard Time (Mexico)' => 'America/Chihuahua', + 'Myanmar Standard Time' => 'Asia/Rangoon', + 'N. Central Asia Standard Time' => 'Asia/Novosibirsk', + 'Namibia Standard Time' => 'Africa/Windhoek', + 'Nepal Standard Time' => 'Asia/Katmandu', + 'New Zealand Standard Time' => 'Pacific/Auckland', + 'Newfoundland Standard Time' => 'America/St_Johns', + 'Norfolk Standard Time' => 'Pacific/Norfolk', + 'North Asia East Standard Time' => 'Asia/Irkutsk', + 'North Asia Standard Time' => 'Asia/Krasnoyarsk', + 'North Korea Standard Time' => 'Asia/Pyongyang', + 'Pacific SA Standard Time' => 'America/Santiago', + 'Pacific Standard Time' => 'America/Los_Angeles', + 'Pacific Standard Time (Mexico)' => 'America/Tijuana', + 'Pakistan Standard Time' => 'Asia/Karachi', + 'Paraguay Standard Time' => 'America/Asuncion', + 'Romance Standard Time' => 'Europe/Paris', + 'Russia Time Zone 10' => 'Asia/Srednekolymsk', + 'Russia Time Zone 11' => 'Asia/Kamchatka', + 'Russia Time Zone 3' => 'Europe/Samara', + 'Russian Standard Time' => 'Europe/Moscow', + 'SA Eastern Standard Time' => 'America/Cayenne', + 'SA Pacific Standard Time' => 'America/Bogota', + 'SA Western Standard Time' => 'America/La_Paz', + 'SE Asia Standard Time' => 'Asia/Bangkok', + 'Saint Pierre Standard Time' => 'America/Miquelon', + 'Sakhalin Standard Time' => 'Asia/Sakhalin', + 'Samoa Standard Time' => 'Pacific/Apia', + 'Singapore Standard Time' => 'Asia/Singapore', + 'South Africa Standard Time' => 'Africa/Johannesburg', + 'Sri Lanka Standard Time' => 'Asia/Colombo', + 'Syria Standard Time' => 'Asia/Damascus', + 'Taipei Standard Time' => 'Asia/Taipei', + 'Tasmania Standard Time' => 'Australia/Hobart', + 'Tocantins Standard Time' => 'America/Araguaina', + 'Tokyo Standard Time' => 'Asia/Tokyo', + 'Tomsk Standard Time' => 'Asia/Tomsk', + 'Tonga Standard Time' => 'Pacific/Tongatapu', + 'Transbaikal Standard Time' => 'Asia/Chita', + 'Turkey Standard Time' => 'Europe/Istanbul', + 'Turks And Caicos Standard Time' => 'America/Grand_Turk', + 'US Eastern Standard Time' => 'America/Indianapolis', + 'US Mountain Standard Time' => 'America/Phoenix', + 'UTC' => 'Etc/GMT', + 'UTC+12' => 'Etc/GMT-12', + 'UTC-02' => 'Etc/GMT+2', + 'UTC-08' => 'Etc/GMT+8', + 'UTC-09' => 'Etc/GMT+9', + 'UTC-11' => 'Etc/GMT+11', + 'Ulaanbaatar Standard Time' => 'Asia/Ulaanbaatar', + 'Venezuela Standard Time' => 'America/Caracas', + 'Vladivostok Standard Time' => 'Asia/Vladivostok', + 'W. Australia Standard Time' => 'Australia/Perth', + 'W. Central Africa Standard Time' => 'Africa/Lagos', + 'W. Europe Standard Time' => 'Europe/Berlin', + 'W. Mongolia Standard Time' => 'Asia/Hovd', + 'West Asia Standard Time' => 'Asia/Tashkent', + 'West Bank Standard Time' => 'Asia/Hebron', + 'West Pacific Standard Time' => 'Pacific/Port_Moresby', + 'Yakutsk Standard Time' => 'Asia/Yakutsk', +]; diff --git a/htdocs/includes/sabre/sabre/vobject/resources/schema/xcal.rng b/htdocs/includes/sabre/sabre/vobject/resources/schema/xcal.rng new file mode 100644 index 00000000000..4a51460e742 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/resources/schema/xcal.rng @@ -0,0 +1,1192 @@ +# RELAX NG Schema for iCalendar in XML +# Extract from RFC6321. +# Erratum 3042 applied. +# Erratum 3050 applied. +# Erratum 3314 applied. + +default namespace = "urn:ietf:params:xml:ns:icalendar-2.0" + +# 3.2 Property Parameters + +# 3.2.1 Alternate Text Representation + +altrepparam = element altrep { + value-uri +} + +# 3.2.2 Common Name + +cnparam = element cn { + value-text +} + +# 3.2.3 Calendar User Type + +cutypeparam = element cutype { + element text { + "INDIVIDUAL" | + "GROUP" | + "RESOURCE" | + "ROOM" | + "UNKNOWN" + } +} + +# 3.2.4 Delegators + +delfromparam = element delegated-from { + value-cal-address+ +} + +# 3.2.5 Delegatees + +deltoparam = element delegated-to { + value-cal-address+ +} + +# 3.2.6 Directory Entry Reference + +dirparam = element dir { + value-uri +} + +# 3.2.7 Inline Encoding + +encodingparam = element encoding { + element text { + "8BIT" | + "BASE64" + } +} + +# 3.2.8 Format Type + +fmttypeparam = element fmttype { + value-text +} + +# 3.2.9 Free/Busy Time Type + +fbtypeparam = element fbtype { + element text { + "FREE" | + "BUSY" | + "BUSY-UNAVAILABLE" | + "BUSY-TENTATIVE" + } +} + +# 3.2.10 Language + +languageparam = element language { + value-text +} + +# 3.2.11 Group or List Membership + +memberparam = element member { + value-cal-address+ +} + +# 3.2.12 Participation Status + +partstatparam = element partstat { + type-partstat-event | + type-partstat-todo | + type-partstat-jour +} + +type-partstat-event = ( + element text { + "NEEDS-ACTION" | + "ACCEPTED" | + "DECLINED" | + "TENTATIVE" | + "DELEGATED" + } +) + +type-partstat-todo = ( + element text { + "NEEDS-ACTION" | + "ACCEPTED" | + "DECLINED" | + "TENTATIVE" | + "DELEGATED" | + "COMPLETED" | + "IN-PROCESS" + } +) + +type-partstat-jour = ( + element text { + "NEEDS-ACTION" | + "ACCEPTED" | + "DECLINED" + } +) + +# 3.2.13 Recurrence Identifier Range + +rangeparam = element range { + element text { + "THISANDFUTURE" + } +} + +# 3.2.14 Alarm Trigger Relationship + +trigrelparam = element related { + element text { + "START" | + "END" + } +} + +# 3.2.15 Relationship Type + +reltypeparam = element reltype { + element text { + "PARENT" | + "CHILD" | + "SIBLING" + } +} + +# 3.2.16 Participation Role + +roleparam = element role { + element text { + "CHAIR" | + "REQ-PARTICIPANT" | + "OPT-PARTICIPANT" | + "NON-PARTICIPANT" + } +} + +# 3.2.17 RSVP Expectation + +rsvpparam = element rsvp { + value-boolean +} + +# 3.2.18 Sent By + +sentbyparam = element sent-by { + value-cal-address +} + +# 3.2.19 Time Zone Identifier + +tzidparam = element tzid { + value-text +} + +# 3.3 Property Value Data Types + +# 3.3.1 BINARY + +value-binary = element binary { + xsd:string +} + +# 3.3.2 BOOLEAN + +value-boolean = element boolean { + xsd:boolean +} + +# 3.3.3 CAL-ADDRESS + +value-cal-address = element cal-address { + xsd:anyURI +} + +# 3.3.4 DATE + +pattern-date = xsd:string { + pattern = "\d\d\d\d-\d\d-\d\d" +} + +value-date = element date { + pattern-date +} + +# 3.3.5 DATE-TIME + +pattern-date-time = xsd:string { + pattern = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ?" +} + +value-date-time = element date-time { + pattern-date-time +} + +# 3.3.6 DURATION + +pattern-duration = xsd:string { + pattern = "(+|-)?P(\d+W)|(\d+D)?" + ~ "(T(\d+H(\d+M)?(\d+S)?)|" + ~ "(\d+M(\d+S)?)|" + ~ "(\d+S))?" +} + +value-duration = element duration { + pattern-duration +} + +# 3.3.7 FLOAT + +value-float = element float { + xsd:float +} + +# 3.3.8 INTEGER + +value-integer = element integer { + xsd:integer +} + +# 3.3.9 PERIOD + +value-period = element period { + element start { + pattern-date-time + }, + ( + element end { + pattern-date-time + } | + element duration { + pattern-duration + } + ) +} + +# 3.3.10 RECUR + +value-recur = element recur { + type-freq, + (type-until | type-count)?, + element interval { + xsd:positiveInteger + }?, + type-bysecond*, + type-byminute*, + type-byhour*, + type-byday*, + type-bymonthday*, + type-byyearday*, + type-byweekno*, + type-bymonth*, + type-bysetpos*, + element wkst { type-weekday }? +} + +type-freq = element freq { + "SECONDLY" | + "MINUTELY" | + "HOURLY" | + "DAILY" | + "WEEKLY" | + "MONTHLY" | + "YEARLY" +} + +type-until = element until { + type-date | + type-date-time +} + +type-count = element count { + xsd:positiveInteger +} + +type-bysecond = element bysecond { + xsd:nonNegativeInteger +} + +type-byminute = element byminute { + xsd:nonNegativeInteger +} + +type-byhour = element byhour { + xsd:nonNegativeInteger +} + +type-weekday = ( + "SU" | + "MO" | + "TU" | + "WE" | + "TH" | + "FR" | + "SA" +) + +type-byday = element byday { + xsd:integer?, + type-weekday +} + +type-bymonthday = element bymonthday { + xsd:integer +} + +type-byyearday = element byyearday { + xsd:integer +} + +type-byweekno = element byweekno { + xsd:integer +} + +type-bymonth = element bymonth { + xsd:positiveInteger +} + +type-bysetpos = element bysetpos { + xsd:integer +} + +# 3.3.11 TEXT + +value-text = element text { + xsd:string +} + +# 3.3.12 TIME + +pattern-time = xsd:string { + pattern = "\d\d:\d\d:\d\dZ?" +} + +value-time = element time { + pattern-time +} + +# 3.3.13 URI + +value-uri = element uri { + xsd:anyURI +} + +# 3.3.14 UTC-OFFSET + +value-utc-offset = element utc-offset { + xsd:string { pattern = "(+|-)\d\d:\d\d(:\d\d)?" } +} + +# UNKNOWN + +value-unknown = element unknown { + xsd:string +} + +# 3.4 iCalendar Stream + +start = element icalendar { + vcalendar+ +} + +# 3.6 Calendar Components + +vcalendar = element vcalendar { + type-calprops, + type-component +} + +type-calprops = element properties { + property-prodid & + property-version & + property-calscale? & + property-method? +} + +type-component = element components { + ( + component-vevent | + component-vtodo | + component-vjournal | + component-vfreebusy | + component-vtimezone + )* +} + +# 3.6.1 Event Component + +component-vevent = element vevent { + type-eventprop, + element components { + component-valarm+ + }? +} + +type-eventprop = element properties { + property-dtstamp & + property-dtstart & + property-uid & + + property-class? & + property-created? & + property-description? & + property-geo? & + property-last-mod? & + property-location? & + property-organizer? & + property-priority? & + property-seq? & + property-status-event? & + property-summary? & + property-transp? & + property-url? & + property-recurid? & + + property-rrule? & + + (property-dtend | property-duration)? & + + property-attach* & + property-attendee* & + property-categories* & + property-comment* & + property-contact* & + property-exdate* & + property-rstatus* & + property-related* & + property-resources* & + property-rdate* +} + +# 3.6.2 To-do Component + +component-vtodo = element vtodo { + type-todoprop, + element components { + component-valarm+ + }? +} + +type-todoprop = element properties { + property-dtstamp & + property-uid & + + property-class? & + property-completed? & + property-created? & + property-description? & + property-geo? & + property-last-mod? & + property-location? & + property-organizer? & + property-percent? & + property-priority? & + property-recurid? & + property-seq? & + property-status-todo? & + property-summary? & + property-url? & + + property-rrule? & + + ( + (property-dtstart?, property-dtend? ) | + (property-dtstart, property-duration)? + ) & + + property-attach* & + property-attendee* & + property-categories* & + property-comment* & + property-contact* & + property-exdate* & + property-rstatus* & + property-related* & + property-resources* & + property-rdate* +} + +# 3.6.3 Journal Component + +component-vjournal = element vjournal { + type-jourprop +} + +type-jourprop = element properties { + property-dtstamp & + property-uid & + + property-class? & + property-created? & + property-dtstart? & + property-last-mod? & + property-organizer? & + property-recurid? & + property-seq? & + property-status-jour? & + property-summary? & + property-url? & + + property-rrule? & + + property-attach* & + property-attendee* & + property-categories* & + property-comment* & + property-contact* & + property-description? & + property-exdate* & + property-related* & + property-rdate* & + property-rstatus* +} + +# 3.6.4 Free/Busy Component + +component-vfreebusy = element vfreebusy { + type-fbprop +} + +type-fbprop = element properties { + property-dtstamp & + property-uid & + + property-contact? & + property-dtstart? & + property-dtend? & + property-duration? & + property-organizer? & + property-url? & + + property-attendee* & + property-comment* & + property-freebusy* & + property-rstatus* +} + +# 3.6.5 Time Zone Component + +component-vtimezone = element vtimezone { + element properties { + property-tzid & + + property-last-mod? & + property-tzurl? + }, + element components { + (component-standard | component-daylight) & + component-standard* & + component-daylight* + } +} + +component-standard = element standard { + type-tzprop +} + +component-daylight = element daylight { + type-tzprop +} + +type-tzprop = element properties { + property-dtstart & + property-tzoffsetto & + property-tzoffsetfrom & + + property-rrule? & + + property-comment* & + property-rdate* & + property-tzname* +} + +# 3.6.6 Alarm Component + +component-valarm = element valarm { + type-audioprop | type-dispprop | type-emailprop +} + +type-audioprop = element properties { + property-action & + + property-trigger & + + (property-duration, property-repeat)? & + + property-attach? +} + +type-emailprop = element properties { + property-action & + property-description & + property-trigger & + property-summary & + + property-attendee+ & + + (property-duration, property-repeat)? & + + property-attach* +} + +type-dispprop = element properties { + property-action & + property-description & + property-trigger & + + (property-duration, property-repeat)? +} + +# 3.7 Calendar Properties + +# 3.7.1 Calendar Scale + +property-calscale = element calscale { + + element parameters { empty }?, + + element text { "GREGORIAN" } +} + +# 3.7.2 Method + +property-method = element method { + + element parameters { empty }?, + + value-text +} + +# 3.7.3 Product Identifier + +property-prodid = element prodid { + + element parameters { empty }?, + + value-text +} + +# 3.7.4 Version + +property-version = element version { + + element parameters { empty }?, + + element text { "2.0" } +} + +# 3.8 Component Properties + +# 3.8.1 Descriptive Component Properties + +# 3.8.1.1 Attachment + +property-attach = element attach { + + element parameters { + fmttypeparam? & + encodingparam? + }?, + + value-uri | value-binary +} + +# 3.8.1.2 Categories + +property-categories = element categories { + + element parameters { + languageparam? & + }?, + + value-text+ +} + +# 3.8.1.3 Classification + +property-class = element class { + + element parameters { empty }?, + + element text { + "PUBLIC" | + "PRIVATE" | + "CONFIDENTIAL" + } +} + +# 3.8.1.4 Comment + +property-comment = element comment { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text +} + +# 3.8.1.5 Description + +property-description = element description { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text +} + +# 3.8.1.6 Geographic Position + +property-geo = element geo { + + element parameters { empty }?, + + element latitude { xsd:float }, + element longitude { xsd:float } +} + +# 3.8.1.7 Location + +property-location = element location { + + element parameters { + + altrepparam? & + languageparam? + }?, + + value-text +} + +# 3.8.1.8 Percent Complete + +property-percent = element percent-complete { + + element parameters { empty }?, + + value-integer +} + +# 3.8.1.9 Priority + +property-priority = element priority { + + element parameters { empty }?, + + value-integer +} + +# 3.8.1.10 Resources + +property-resources = element resources { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text+ +} + +# 3.8.1.11 Status + +property-status-event = element status { + + element parameters { empty }?, + + element text { + "TENTATIVE" | + "CONFIRMED" | + "CANCELLED" + } +} + +property-status-todo = element status { + + element parameters { empty }?, + + element text { + "NEEDS-ACTION" | + "COMPLETED" | + "IN-PROCESS" | + "CANCELLED" + } +} + +property-status-jour = element status { + + element parameters { empty }?, + + element text { + "DRAFT" | + "FINAL" | + "CANCELLED" + } +} + +# 3.8.1.12 Summary + +property-summary = element summary { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text +} + +# 3.8.2 Date and Time Component Properties + +# 3.8.2.1 Date/Time Completed + +property-completed = element completed { + + element parameters { empty }?, + + value-date-time +} + +# 3.8.2.2 Date/Time End + +property-dtend = element dtend { + + element parameters { + tzidparam? + }?, + + value-date-time | + value-date +} + +# 3.8.2.3 Date/Time Due + +property-due = element due { + + element parameters { + tzidparam? + }?, + + value-date-time | + value-date +} + +# 3.8.2.4 Date/Time Start + +property-dtstart = element dtstart { + + element parameters { + tzidparam? + }?, + + value-date-time | + value-date +} + +# 3.8.2.5 Duration + +property-duration = element duration { + + element parameters { empty }?, + + value-duration +} + +# 3.8.2.6 Free/Busy Time + +property-freebusy = element freebusy { + + element parameters { + fbtypeparam? + }?, + + + value-period+ +} + +# 3.8.2.7 Time Transparency + +property-transp = element transp { + + element parameters { empty }?, + + element text { + "OPAQUE" | + "TRANSPARENT" + } +} + +# 3.8.3 Time Zone Component Properties + +# 3.8.3.1 Time Zone Identifier + +property-tzid = element tzid { + + element parameters { empty }?, + + value-text +} + +# 3.8.3.2 Time Zone Name + +property-tzname = element tzname { + + element parameters { + languageparam? + }?, + + value-text +} + +# 3.8.3.3 Time Zone Offset From + +property-tzoffsetfrom = element tzoffsetfrom { + + element parameters { empty }?, + + value-utc-offset +} + +# 3.8.3.4 Time Zone Offset To + +property-tzoffsetto = element tzoffsetto { + + element parameters { empty }?, + + value-utc-offset +} + +# 3.8.3.5 Time Zone URL + +property-tzurl = element tzurl { + + element parameters { empty }?, + + value-uri +} + +# 3.8.4 Relationship Component Properties + +# 3.8.4.1 Attendee + +property-attendee = element attendee { + + element parameters { + cutypeparam? & + memberparam? & + roleparam? & + partstatparam? & + rsvpparam? & + deltoparam? & + delfromparam? & + sentbyparam? & + cnparam? & + dirparam? & + languageparam? + }?, + + value-cal-address +} + +# 3.8.4.2 Contact + +property-contact = element contact { + + element parameters { + altrepparam? & + languageparam? + }?, + + value-text +} + +# 3.8.4.3 Organizer + +property-organizer = element organizer { + + element parameters { + cnparam? & + dirparam? & + sentbyparam? & + languageparam? + }?, + + value-cal-address +} + +# 3.8.4.4 Recurrence ID + +property-recurid = element recurrence-id { + + element parameters { + tzidparam? & + rangeparam? + }?, + + value-date-time | + value-date +} + +# 3.8.4.5 Related-To + +property-related = element related-to { + + element parameters { + reltypeparam? + }?, + + value-text +} + +# 3.8.4.6 Uniform Resource Locator + +property-url = element url { + + element parameters { empty }?, + + value-uri +} + +# 3.8.4.7 Unique Identifier + +property-uid = element uid { + + element parameters { empty }?, + + value-text +} + +# 3.8.5 Recurrence Component Properties + +# 3.8.5.1 Exception Date/Times + +property-exdate = element exdate { + + element parameters { + tzidparam? + }?, + + value-date-time+ | + value-date+ +} + +# 3.8.5.2 Recurrence Date/Times + +property-rdate = element rdate { + + element parameters { + tzidparam? + }?, + + value-date-time+ | + value-date+ | + value-period+ +} + +# 3.8.5.3 Recurrence Rule + +property-rrule = element rrule { + + element parameters { empty }?, + + value-recur +} + +# 3.8.6 Alarm Component Properties + +# 3.8.6.1 Action + +property-action = element action { + + element parameters { empty }?, + + element text { + "AUDIO" | + "DISPLAY" | + "EMAIL" + } +} + +# 3.8.6.2 Repeat Count + +property-repeat = element repeat { + + element parameters { empty }?, + + value-integer +} + +# 3.8.6.3 Trigger + +property-trigger = element trigger { + + ( + element parameters { + trigrelparam? + }?, + + value-duration + ) | + ( + element parameters { empty }?, + + value-date-time + ) +} + +# 3.8.7 Change Management Component Properties + +# 3.8.7.1 Date/Time Created + +property-created = element created { + + element parameters { empty }?, + + value-date-time +} + +# 3.8.7.2 Date/Time Stamp + +property-dtstamp = element dtstamp { + + element parameters { empty }?, + + value-date-time +} + +# 3.8.7.3 Last Modified + +property-last-mod = element last-modified { + + element parameters { empty }?, + + value-date-time +} + +# 3.8.7.4 Sequence Number + +property-seq = element sequence { + + element parameters { empty }?, + + value-integer +} + +# 3.8.8 Miscellaneous Component Properties + +# 3.8.8.3 Request Status + +property-rstatus = element request-status { + + element parameters { + languageparam? + }?, + + element code { xsd:string }, + element description { xsd:string }, + element data { xsd:string }? +} diff --git a/htdocs/includes/sabre/sabre/vobject/resources/schema/xcard.rng b/htdocs/includes/sabre/sabre/vobject/resources/schema/xcard.rng new file mode 100644 index 00000000000..c0b7cfb3545 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/resources/schema/xcard.rng @@ -0,0 +1,388 @@ +# RELAX NG Schema for vCard in XML +# Extract from RFC6351. +# Erratum 2994 applied. +# Erratum 3047 applied. +# Erratum 3008 applied. +# Erratum 4247 applied. + +default namespace = "urn:ietf:params:xml:ns:vcard-4.0" + +### Section 3.3: vCard Format Specification +# +# 3.3 +iana-token = xsd:string { pattern = "[a-zA-Z0-9\-]+" } +x-name = xsd:string { pattern = "x-[a-zA-Z0-9\-]+" } + +### Section 4: Value types +# +# 4.1 +value-text = element text { text } +value-text-list = value-text+ + +# 4.2 +value-uri = element uri { xsd:anyURI } + +# 4.3.1 +value-date = element date { + xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" } + } + +# 4.3.2 +value-time = element time { + xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)" + ~ "(Z|[+\-]\d\d(\d\d)?)?" } + } + +# 4.3.3 +value-date-time = element date-time { + xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?" + ~ "(Z|[+\-]\d\d(\d\d)?)?" } + } + +# 4.3.4 +value-date-and-or-time = value-date | value-date-time | value-time + +# 4.3.5 +value-timestamp = element timestamp { + xsd:string { pattern = "\d{8}T\d{6}(Z|[+\-]\d\d(\d\d)?)?" } + } + +# 4.4 +value-boolean = element boolean { xsd:boolean } + +# 4.5 +value-integer = element integer { xsd:integer } + +# 4.6 +value-float = element float { xsd:float } + +# 4.7 +value-utc-offset = element utc-offset { + xsd:string { pattern = "[+\-]\d\d(\d\d)?" } + } + +# 4.8 +value-language-tag = element language-tag { + xsd:string { pattern = "([a-z]{2,3}((-[a-z]{3}){0,3})?|[a-z]{4,8})" + ~ "(-[a-z]{4})?(-([a-z]{2}|\d{3}))?" + ~ "(-([0-9a-z]{5,8}|\d[0-9a-z]{3}))*" + ~ "(-[0-9a-wyz](-[0-9a-z]{2,8})+)*" + ~ "(-x(-[0-9a-z]{1,8})+)?|x(-[0-9a-z]{1,8})+|" + ~ "[a-z]{1,3}(-[0-9a-z]{2,8}){1,2}" } + } + +### Section 5: Parameters +# +# 5.1 +param-language = element language { value-language-tag }? + +# 5.2 +param-pref = element pref { + element integer { + xsd:integer { minInclusive = "1" maxInclusive = "100" } + } + }? + +# 5.4 +param-altid = element altid { value-text }? + +# 5.5 +param-pid = element pid { + element text { xsd:string { pattern = "\d+(\.\d+)?" } }+ + }? + +# 5.6 +param-type = element type { element text { "work" | "home" }+ }? + +# 5.7 +param-mediatype = element mediatype { value-text }? + +# 5.8 +param-calscale = element calscale { element text { "gregorian" } }? + +# 5.9 +param-sort-as = element sort-as { value-text+ }? + +# 5.10 +param-geo = element geo { value-uri }? + +# 5.11 +param-tz = element tz { value-text | value-uri }? + +### Section 6: Properties +# +# 6.1.3 +property-source = element source { + element parameters { param-altid, param-pid, param-pref, + param-mediatype }?, + value-uri + } + +# 6.1.4 +property-kind = element kind { + element text { "individual" | "group" | "org" | "location" | + x-name | iana-token }* + } + +# 6.2.1 +property-fn = element fn { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.2.2 +property-n = element n { + element parameters { param-language, param-sort-as, param-altid }?, + element surname { text }+, + element given { text }+, + element additional { text }+, + element prefix { text }+, + element suffix { text }+ + } + +# 6.2.3 +property-nickname = element nickname { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text-list + } + +# 6.2.4 +property-photo = element photo { + element parameters { param-altid, param-pid, param-pref, param-type, + param-mediatype }?, + value-uri + } + +# 6.2.5 +property-bday = element bday { + element parameters { param-altid, param-calscale }?, + (value-date-and-or-time | value-text) + } + +# 6.2.6 +property-anniversary = element anniversary { + element parameters { param-altid, param-calscale }?, + (value-date-and-or-time | value-text) + } + +# 6.2.7 +property-gender = element gender { + element sex { "" | "M" | "F" | "O" | "N" | "U" }, + element identity { text }? + } + +# 6.3.1 +param-label = element label { value-text }? +property-adr = element adr { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-geo, param-tz, + param-label }?, + element pobox { text }+, + element ext { text }+, + element street { text }+, + element locality { text }+, + element region { text }+, + element code { text }+, + element country { text }+ + } + +# 6.4.1 +property-tel = element tel { + element parameters { + param-altid, + param-pid, + param-pref, + element type { + element text { "work" | "home" | "text" | "voice" + | "fax" | "cell" | "video" | "pager" + | "textphone" | x-name | iana-token }+ + }?, + param-mediatype + }?, + (value-text | value-uri) + } + +# 6.4.2 +property-email = element email { + element parameters { param-altid, param-pid, param-pref, + param-type }?, + value-text + } + +# 6.4.3 +property-impp = element impp { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.4.4 +property-lang = element lang { + element parameters { param-altid, param-pid, param-pref, + param-type }?, + value-language-tag + } + +# 6.5.1 +property-tz = element tz { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + (value-text | value-uri | value-utc-offset) + } + +# 6.5.2 +property-geo = element geo { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.6.1 +property-title = element title { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.6.2 +property-role = element role { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.6.3 +property-logo = element logo { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-mediatype }?, + value-uri + } + +# 6.6.4 +property-org = element org { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-sort-as }?, + value-text-list + } + +# 6.6.5 +property-member = element member { + element parameters { param-altid, param-pid, param-pref, + param-mediatype }?, + value-uri + } + +# 6.6.6 +property-related = element related { + element parameters { + param-altid, + param-pid, + param-pref, + element type { + element text { + "work" | "home" | "contact" | "acquaintance" | + "friend" | "met" | "co-worker" | "colleague" | "co-resident" | + "neighbor" | "child" | "parent" | "sibling" | "spouse" | + "kin" | "muse" | "crush" | "date" | "sweetheart" | "me" | + "agent" | "emergency" + }+ + }?, + param-mediatype + }?, + (value-uri | value-text) + } + +# 6.7.1 +property-categories = element categories { + element parameters { param-altid, param-pid, param-pref, + param-type }?, + value-text-list + } + +# 6.7.2 +property-note = element note { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type }?, + value-text + } + +# 6.7.3 +property-prodid = element prodid { value-text } + +# 6.7.4 +property-rev = element rev { value-timestamp } + +# 6.7.5 +property-sound = element sound { + element parameters { param-language, param-altid, param-pid, + param-pref, param-type, param-mediatype }?, + value-uri + } + +# 6.7.6 +property-uid = element uid { value-uri } + +# 6.7.7 +property-clientpidmap = element clientpidmap { + element sourceid { xsd:positiveInteger }, + value-uri + } + +# 6.7.8 +property-url = element url { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.8.1 +property-key = element key { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + (value-uri | value-text) + } + +# 6.9.1 +property-fburl = element fburl { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.9.2 +property-caladruri = element caladruri { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# 6.9.3 +property-caluri = element caluri { + element parameters { param-altid, param-pid, param-pref, + param-type, param-mediatype }?, + value-uri + } + +# Top-level grammar +property = property-adr | property-anniversary | property-bday + | property-caladruri | property-caluri | property-categories + | property-clientpidmap | property-email | property-fburl + | property-fn | property-geo | property-impp | property-key + | property-kind | property-lang | property-logo + | property-member | property-n | property-nickname + | property-note | property-org | property-photo + | property-prodid | property-related | property-rev + | property-role | property-gender | property-sound + | property-source | property-tel | property-title + | property-tz | property-uid | property-url +start = element vcards { + element vcard { + (property + | element group { + attribute name { text }, + property* + })+ + }+ + } diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/AttachIssueTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/AttachIssueTest.php new file mode 100644 index 00000000000..68c9872bb5b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/AttachIssueTest.php @@ -0,0 +1,22 @@ +<?php + +namespace Sabre\VObject; + +class AttachIssueTest extends \PHPUnit_Framework_TestCase { + + function testRead() { + + $event = <<<ICS +BEGIN:VCALENDAR\r +BEGIN:VEVENT\r +ATTACH;FMTTYPE=;ENCODING=:Zm9v\r +END:VEVENT\r +END:VCALENDAR\r + +ICS; + $obj = Reader::read($event); + $this->assertEquals($event, $obj->serialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/BirthdayCalendarGeneratorTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/BirthdayCalendarGeneratorTest.php new file mode 100644 index 00000000000..54c478eaf46 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/BirthdayCalendarGeneratorTest.php @@ -0,0 +1,562 @@ +<?php + +namespace Sabre\VObject; + +class BirthdayCalendarGeneratorTest extends \PHPUnit_Framework_TestCase { + + use PHPUnitAssertions; + + function testVcardStringWithValidBirthday() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:19850407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testArrayOfVcardStringsWithValidBirthdays() { + + $generator = new BirthdayCalendarGenerator(); + $input = []; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Doe;John;;Mr. +FN:John Doe +BDAY:19820210 +UID:bar +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:19850407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VEVENT +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:John Doe's Birthday +DTSTART;VALUE=DATE:19820210 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=bar;X-SABRE-VCARD-FN=John Doe:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testArrayOfVcardStringsWithValidBirthdaysViaConstructor() { + + $input = []; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Doe;John;;Mr. +FN:John Doe +BDAY:19820210 +UID:bar +END:VCARD +VCF; + + $generator = new BirthdayCalendarGenerator($input); + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:19850407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VEVENT +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:John Doe's Birthday +DTSTART;VALUE=DATE:19820210 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=bar;X-SABRE-VCARD-FN=John Doe:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardObjectWithValidBirthday() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $input = Reader::read($input); + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:19850407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testArrayOfVcardObjectsWithValidBirthdays() { + + $generator = new BirthdayCalendarGenerator(); + $input = []; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Doe;John;;Mr. +FN:John Doe +BDAY:19820210 +UID:bar +END:VCARD +VCF; + + foreach ($input as $key => $value) { + $input[$key] = Reader::read($value); + } + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:19850407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VEVENT +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:John Doe's Birthday +DTSTART;VALUE=DATE:19820210 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=bar;X-SABRE-VCARD-FN=John Doe:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardStringWithValidBirthdayWithXAppleOmitYear() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY;X-APPLE-OMIT-YEAR=1604:1604-04-07 +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:20000407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump;X-SABRE-OMIT-YEAR=2000:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardStringWithValidBirthdayWithoutYear() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:4.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:--04-07 +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Birthday +DTSTART;VALUE=DATE:20000407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump;X-SABRE-OMIT-YEAR=2000:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardStringWithInvalidBirthday() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:foo +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardStringWithNoBirthday() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardStringWithValidBirthdayLocalized() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:**ANY** +DTSTAMP:**ANY** +SUMMARY:Forrest Gump's Geburtstag +DTSTART;VALUE=DATE:19850407 +RRULE:FREQ=YEARLY +TRANSP:TRANSPARENT +X-SABRE-BDAY;X-SABRE-VCARD-UID=foo;X-SABRE-VCARD-FN=Forrest Gump:BDAY +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $generator->setFormat('%1$s\'s Geburtstag'); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + function testVcardStringWithEmptyBirthdayProperty() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY: +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + + /** + * @expectedException \Sabre\VObject\ParseException + */ + function testParseException() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<FOO +BEGIN:FOO +FOO:Bar +END:FOO +FOO; + + $generator->setObjects($input); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testInvalidArgumentException() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SUMMARY:Foo +DTSTART;VALUE=DATE:19850407 +END:VEVENT +END:VCALENDAR +ICS; + + $generator->setObjects($input); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testInvalidArgumentExceptionForPartiallyInvalidArray() { + + $generator = new BirthdayCalendarGenerator(); + $input = []; + + $input[] = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +FN:Forrest Gump +BDAY:19850407 +UID:foo +END:VCARD +VCF; + $calendar = new Component\VCalendar(); + + $input = $calendar->add('VEVENT', [ + 'SUMMARY' => 'Foo', + 'DTSTART' => new \DateTime('NOW'), + ]); + + $generator->setObjects($input); + + } + + function testBrokenVcardWithoutFN() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;;Mr. +BDAY:19850407 +UID:foo +END:VCARD +VCF; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +END:VCALENDAR +ICS; + + $generator->setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjectEqualsVObject( + $expected, + $output + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/CliTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/CliTest.php new file mode 100644 index 00000000000..67037873e37 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/CliTest.php @@ -0,0 +1,642 @@ +<?php + +namespace Sabre\VObject; + +/** + * Tests the cli. + * + * Warning: these tests are very rudimentary. + */ +class CliTest extends \PHPUnit_Framework_TestCase { + + function setUp() { + + $this->cli = new CliMock(); + $this->cli->stderr = fopen('php://memory', 'r+'); + $this->cli->stdout = fopen('php://memory', 'r+'); + + } + + function testInvalidArg() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', '--hi']) + ); + rewind($this->cli->stderr); + $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100); + + } + + function testQuiet() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', '-q']) + ); + $this->assertTrue($this->cli->quiet); + + rewind($this->cli->stderr); + $this->assertEquals(0, strlen(stream_get_contents($this->cli->stderr))); + + } + + function testHelp() { + + $this->assertEquals( + 0, + $this->cli->main(['vobject', '-h']) + ); + rewind($this->cli->stderr); + $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100); + + } + + function testFormat() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', '--format=jcard']) + ); + + rewind($this->cli->stderr); + $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100); + + $this->assertEquals('jcard', $this->cli->format); + + } + + function testFormatInvalid() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', '--format=foo']) + ); + + rewind($this->cli->stderr); + $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100); + + $this->assertNull($this->cli->format); + + } + + function testInputFormatInvalid() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', '--inputformat=foo']) + ); + + rewind($this->cli->stderr); + $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100); + + $this->assertNull($this->cli->format); + + } + + + function testNoInputFile() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', 'color']) + ); + + rewind($this->cli->stderr); + $this->assertTrue(strlen(stream_get_contents($this->cli->stderr)) > 100); + + } + + function testTooManyArgs() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', 'color', 'a', 'b', 'c']) + ); + + } + + function testUnknownCommand() { + + $this->assertEquals( + 1, + $this->cli->main(['vobject', 'foo', '-']) + ); + + } + + function testConvertJson() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<ICS +BEGIN:VCARD +VERSION:3.0 +FN:Cowboy Henk +END:VCARD +ICS + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 0, + $this->cli->main(['vobject', 'convert', '--format=json', '-']) + ); + + rewind($this->cli->stdout); + $version = Version::VERSION; + $this->assertEquals( + '["vcard",[["version",{},"text","4.0"],["prodid",{},"text","-\/\/Sabre\/\/Sabre VObject ' . $version . '\/\/EN"],["fn",{},"text","Cowboy Henk"]]]', + stream_get_contents($this->cli->stdout) + ); + + } + + function testConvertJCardPretty() { + + if (version_compare(PHP_VERSION, '5.4.0') < 0) { + $this->markTestSkipped('This test required PHP 5.4.0'); + } + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<ICS +BEGIN:VCARD +VERSION:3.0 +FN:Cowboy Henk +END:VCARD +ICS + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 0, + $this->cli->main(['vobject', 'convert', '--format=jcard', '--pretty', '-']) + ); + + rewind($this->cli->stdout); + + // PHP 5.5.12 changed the output + + $expected = <<<JCARD +[ + "vcard", + [ + [ + "versi +JCARD; + + $this->assertStringStartsWith( + $expected, + stream_get_contents($this->cli->stdout) + ); + + } + + function testConvertJCalFail() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<ICS +BEGIN:VCARD +VERSION:3.0 +FN:Cowboy Henk +END:VCARD +ICS + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'convert', '--format=jcal', '--inputformat=mimedir', '-']) + ); + + } + + function testConvertMimeDir() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<JCARD +[ + "vcard", + [ + [ + "version", + { + + }, + "text", + "4.0" + ], + [ + "prodid", + { + + }, + "text", + "-\/\/Sabre\/\/Sabre VObject 3.1.0\/\/EN" + ], + [ + "fn", + { + + }, + "text", + "Cowboy Henk" + ] + ] +] +JCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 0, + $this->cli->main(['vobject', 'convert', '--format=mimedir', '--inputformat=json', '--pretty', '-']) + ); + + rewind($this->cli->stdout); + $expected = <<<VCF +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject 3.1.0//EN +FN:Cowboy Henk +END:VCARD + +VCF; + + $this->assertEquals( + strtr($expected, ["\n" => "\r\n"]), + stream_get_contents($this->cli->stdout) + ); + + } + + function testConvertDefaultFormats() { + + $outputFile = SABRE_TEMPDIR . 'bar.json'; + + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'convert', 'foo.json', $outputFile]) + ); + + $this->assertEquals('json', $this->cli->inputFormat); + $this->assertEquals('json', $this->cli->format); + + } + + function testConvertDefaultFormats2() { + + $outputFile = SABRE_TEMPDIR . 'bar.ics'; + + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'convert', 'foo.ics', $outputFile]) + ); + + $this->assertEquals('mimedir', $this->cli->inputFormat); + $this->assertEquals('mimedir', $this->cli->format); + + } + + function testVCard3040() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCARD +VERSION:3.0 +PRODID:-//Sabre//Sabre VObject 3.1.0//EN +FN:Cowboy Henk +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 0, + $this->cli->main(['vobject', 'convert', '--format=vcard40', '--pretty', '-']) + ); + + rewind($this->cli->stdout); + + $version = Version::VERSION; + $expected = <<<VCF +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject $version//EN +FN:Cowboy Henk +END:VCARD + +VCF; + + $this->assertEquals( + strtr($expected, ["\n" => "\r\n"]), + stream_get_contents($this->cli->stdout) + ); + + } + + function testVCard4030() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject 3.1.0//EN +FN:Cowboy Henk +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 0, + $this->cli->main(['vobject', 'convert', '--format=vcard30', '--pretty', '-']) + ); + + $version = Version::VERSION; + + rewind($this->cli->stdout); + $expected = <<<VCF +BEGIN:VCARD +VERSION:3.0 +PRODID:-//Sabre//Sabre VObject $version//EN +FN:Cowboy Henk +END:VCARD + +VCF; + + $this->assertEquals( + strtr($expected, ["\n" => "\r\n"]), + stream_get_contents($this->cli->stdout) + ); + + } + + function testVCard4021() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject 3.1.0//EN +FN:Cowboy Henk +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'convert', '--format=vcard21', '--pretty', '-']) + ); + + } + + function testValidate() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject 3.1.0//EN +UID:foo +FN:Cowboy Henk +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + $result = $this->cli->main(['vobject', 'validate', '-']); + + $this->assertEquals( + 0, + $result + ); + + } + + function testValidateFail() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCALENDAR +VERSION:2.0 +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + // vCard 2.0 is not supported yet, so this returns a failure. + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'validate', '-']) + ); + + } + + function testValidateFail2() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCALENDAR +VERSION:5.0 +END:VCALENDAR + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'validate', '-']) + ); + + } + + function testRepair() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCARD +VERSION:5.0 +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $this->assertEquals( + 2, + $this->cli->main(['vobject', 'repair', '-']) + ); + + rewind($this->cli->stdout); + $this->assertRegExp("/^BEGIN:VCARD\r\nVERSION:2.1\r\nUID:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\r\nEND:VCARD\r\n$/", stream_get_contents($this->cli->stdout)); + } + + function testRepairNothing() { + + $inputStream = fopen('php://memory', 'r+'); + + fwrite($inputStream, <<<VCARD +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject 3.1.0//EN +BEGIN:VEVENT +UID:foo +DTSTAMP:20140122T233226Z +DTSTART:20140101T120000Z +END:VEVENT +END:VCALENDAR + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $result = $this->cli->main(['vobject', 'repair', '-']); + + rewind($this->cli->stderr); + $error = stream_get_contents($this->cli->stderr); + + $this->assertEquals( + 0, + $result, + "This should have been error free. stderr output:\n" . $error + ); + + } + + /** + * Note: this is a very shallow test, doesn't dig into the actual output, + * but just makes sure there's no errors thrown. + * + * The colorizer is not a critical component, it's mostly a debugging tool. + */ + function testColorCalendar() { + + $inputStream = fopen('php://memory', 'r+'); + + $version = Version::VERSION; + + /** + * This object is not valid, but it's designed to hit every part of the + * colorizer source. + */ + fwrite($inputStream, <<<VCARD +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject {$version}//EN +BEGIN:VTIMEZONE +END:VTIMEZONE +BEGIN:VEVENT +ATTENDEE;RSVP=TRUE:mailto:foo@example.org +REQUEST-STATUS:5;foo +ATTACH:blabla +END:VEVENT +END:VCALENDAR + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $result = $this->cli->main(['vobject', 'color', '-']); + + rewind($this->cli->stderr); + $error = stream_get_contents($this->cli->stderr); + + $this->assertEquals( + 0, + $result, + "This should have been error free. stderr output:\n" . $error + ); + + } + + /** + * Note: this is a very shallow test, doesn't dig into the actual output, + * but just makes sure there's no errors thrown. + * + * The colorizer is not a critical component, it's mostly a debugging tool. + */ + function testColorVCard() { + + $inputStream = fopen('php://memory', 'r+'); + + $version = Version::VERSION; + + /** + * This object is not valid, but it's designed to hit every part of the + * colorizer source. + */ + fwrite($inputStream, <<<VCARD +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject {$version}//EN +ADR:1;2;3;4a,4b;5;6 +group.TEL:123454768 +END:VCARD + +VCARD + ); + rewind($inputStream); + $this->cli->stdin = $inputStream; + + $result = $this->cli->main(['vobject', 'color', '-']); + + rewind($this->cli->stderr); + $error = stream_get_contents($this->cli->stderr); + + $this->assertEquals( + 0, + $result, + "This should have been error free. stderr output:\n" . $error + ); + + } +} + +class CliMock extends Cli { + + public $quiet = false; + + public $format; + + public $pretty; + + public $stdin; + + public $stdout; + + public $stderr; + + public $inputFormat; + + public $outputFormat; + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/AvailableTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/AvailableTest.php new file mode 100644 index 00000000000..a13f67ac23b --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/AvailableTest.php @@ -0,0 +1,73 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeImmutable; +use DateTimeZone; +use Sabre\VObject\Reader; + +/** + * We use `RFCxxx` has a placeholder for the + * https://tools.ietf.org/html/draft-daboo-calendar-availability-05 name. + */ +class AvailableTest extends \PHPUnit_Framework_TestCase { + + function testAvailableComponent() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:AVAILABLE +END:AVAILABLE +END:VCALENDAR +VCAL; + $document = Reader::read($vcal); + $this->assertInstanceOf(__NAMESPACE__ . '\Available', $document->AVAILABLE); + + } + + function testGetEffectiveStartEnd() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:AVAILABLE +DTSTART:20150717T162200Z +DTEND:20150717T172200Z +END:AVAILABLE +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $tz = new DateTimeZone('UTC'); + $this->assertEquals( + [ + new DateTimeImmutable('2015-07-17 16:22:00', $tz), + new DateTimeImmutable('2015-07-17 17:22:00', $tz), + ], + $document->AVAILABLE->getEffectiveStartEnd() + ); + + } + + function testGetEffectiveStartEndDuration() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:AVAILABLE +DTSTART:20150717T162200Z +DURATION:PT1H +END:AVAILABLE +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $tz = new DateTimeZone('UTC'); + $this->assertEquals( + [ + new DateTimeImmutable('2015-07-17 16:22:00', $tz), + new DateTimeImmutable('2015-07-17 17:22:00', $tz), + ], + $document->AVAILABLE->getEffectiveStartEnd() + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VAlarmTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VAlarmTest.php new file mode 100644 index 00000000000..b398e71e824 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VAlarmTest.php @@ -0,0 +1,177 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTime; +use Sabre\VObject\Reader; + +class VAlarmTest extends \PHPUnit_Framework_TestCase { + + /** + * @dataProvider timeRangeTestData + */ + function testInTimeRange(VAlarm $valarm, $start, $end, $outcome) { + + $this->assertEquals($outcome, $valarm->isInTimeRange($start, $end)); + + } + + function timeRangeTestData() { + + $tests = []; + + $calendar = new VCalendar(); + + // Hard date and time + $valarm1 = $calendar->createComponent('VALARM'); + $valarm1->add( + $calendar->createProperty('TRIGGER', '20120312T130000Z', ['VALUE' => 'DATE-TIME']) + ); + + $tests[] = [$valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true]; + $tests[] = [$valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false]; + + // Relation to start time of event + $valarm2 = $calendar->createComponent('VALARM'); + $valarm2->add( + $calendar->createProperty('TRIGGER', '-P1D', ['VALUE' => 'DURATION']) + ); + + $vevent2 = $calendar->createComponent('VEVENT'); + $vevent2->DTSTART = '20120313T130000Z'; + $vevent2->add($valarm2); + + $tests[] = [$valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true]; + $tests[] = [$valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false]; + + // Relation to end time of event + $valarm3 = $calendar->createComponent('VALARM'); + $valarm3->add($calendar->createProperty('TRIGGER', '-P1D', ['VALUE' => 'DURATION', 'RELATED' => 'END'])); + + $vevent3 = $calendar->createComponent('VEVENT'); + $vevent3->DTSTART = '20120301T130000Z'; + $vevent3->DTEND = '20120401T130000Z'; + $vevent3->add($valarm3); + + $tests[] = [$valarm3, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false]; + $tests[] = [$valarm3, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true]; + + // Relation to end time of todo + $valarm4 = $calendar->createComponent('VALARM'); + $valarm4->TRIGGER = '-P1D'; + $valarm4->TRIGGER['VALUE'] = 'DURATION'; + $valarm4->TRIGGER['RELATED'] = 'END'; + + $vtodo4 = $calendar->createComponent('VTODO'); + $vtodo4->DTSTART = '20120301T130000Z'; + $vtodo4->DUE = '20120401T130000Z'; + $vtodo4->add($valarm4); + + $tests[] = [$valarm4, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false]; + $tests[] = [$valarm4, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true]; + + // Relation to start time of event + repeat + $valarm5 = $calendar->createComponent('VALARM'); + $valarm5->TRIGGER = '-P1D'; + $valarm5->TRIGGER['VALUE'] = 'DURATION'; + $valarm5->REPEAT = 10; + $valarm5->DURATION = 'P1D'; + + $vevent5 = $calendar->createComponent('VEVENT'); + $vevent5->DTSTART = '20120301T130000Z'; + $vevent5->add($valarm5); + + $tests[] = [$valarm5, new DateTime('2012-03-09 01:00:00'), new DateTime('2012-03-10 01:00:00'), true]; + + // Relation to start time of event + duration, but no repeat + $valarm6 = $calendar->createComponent('VALARM'); + $valarm6->TRIGGER = '-P1D'; + $valarm6->TRIGGER['VALUE'] = 'DURATION'; + $valarm6->DURATION = 'P1D'; + + $vevent6 = $calendar->createComponent('VEVENT'); + $vevent6->DTSTART = '20120313T130000Z'; + $vevent6->add($valarm6); + + $tests[] = [$valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true]; + $tests[] = [$valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false]; + + + // Relation to end time of event (DURATION instead of DTEND) + $valarm7 = $calendar->createComponent('VALARM'); + $valarm7->TRIGGER = '-P1D'; + $valarm7->TRIGGER['VALUE'] = 'DURATION'; + $valarm7->TRIGGER['RELATED'] = 'END'; + + $vevent7 = $calendar->createComponent('VEVENT'); + $vevent7->DTSTART = '20120301T130000Z'; + $vevent7->DURATION = 'P30D'; + $vevent7->add($valarm7); + + $tests[] = [$valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false]; + $tests[] = [$valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true]; + + // Relation to end time of event (No DTEND or DURATION) + $valarm7 = $calendar->createComponent('VALARM'); + $valarm7->TRIGGER = '-P1D'; + $valarm7->TRIGGER['VALUE'] = 'DURATION'; + $valarm7->TRIGGER['RELATED'] = 'END'; + + $vevent7 = $calendar->createComponent('VEVENT'); + $vevent7->DTSTART = '20120301T130000Z'; + $vevent7->add($valarm7); + + $tests[] = [$valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), true]; + $tests[] = [$valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), false]; + + + return $tests; + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testInTimeRangeInvalidComponent() { + + $calendar = new VCalendar(); + $valarm = $calendar->createComponent('VALARM'); + $valarm->TRIGGER = '-P1D'; + $valarm->TRIGGER['RELATED'] = 'END'; + + $vjournal = $calendar->createComponent('VJOURNAL'); + $vjournal->add($valarm); + + $valarm->isInTimeRange(new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00')); + + } + + /** + * This bug was found and reported on the mailing list. + */ + function testInTimeRangeBuggy() { + +$input = <<<BLA +BEGIN:VCALENDAR +BEGIN:VTODO +DTSTAMP:20121003T064931Z +UID:b848cb9a7bb16e464a06c222ca1f8102@examle.com +STATUS:NEEDS-ACTION +DUE:20121005T000000Z +SUMMARY:Task 1 +CATEGORIES:AlarmCategory +BEGIN:VALARM +TRIGGER:-PT10M +ACTION:DISPLAY +DESCRIPTION:Task 1 +END:VALARM +END:VTODO +END:VCALENDAR +BLA; + + $vobj = Reader::read($input); + + $this->assertTrue($vobj->VTODO->VALARM->isInTimeRange(new \DateTime('2012-10-01 00:00:00'), new \DateTime('2012-11-01 00:00:00'))); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VAvailabilityTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VAvailabilityTest.php new file mode 100644 index 00000000000..021100ecb88 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VAvailabilityTest.php @@ -0,0 +1,490 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeImmutable; +use DateTimeZone; +use Sabre\VObject; +use Sabre\VObject\Reader; + +/** + * We use `RFCxxx` has a placeholder for the + * https://tools.ietf.org/html/draft-daboo-calendar-availability-05 name. + */ +class VAvailabilityTest extends \PHPUnit_Framework_TestCase { + + function testVAvailabilityComponent() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +END:VAVAILABILITY +END:VCALENDAR +VCAL; + $document = Reader::read($vcal); + + $this->assertInstanceOf(__NAMESPACE__ . '\VAvailability', $document->VAVAILABILITY); + + } + + function testGetEffectiveStartEnd() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20150717T162200Z +DTEND:20150717T172200Z +END:VAVAILABILITY +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $tz = new DateTimeZone('UTC'); + $this->assertEquals( + [ + new DateTimeImmutable('2015-07-17 16:22:00', $tz), + new DateTimeImmutable('2015-07-17 17:22:00', $tz), + ], + $document->VAVAILABILITY->getEffectiveStartEnd() + ); + + } + + function testGetEffectiveStartDuration() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20150717T162200Z +DURATION:PT1H +END:VAVAILABILITY +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $tz = new DateTimeZone('UTC'); + $this->assertEquals( + [ + new DateTimeImmutable('2015-07-17 16:22:00', $tz), + new DateTimeImmutable('2015-07-17 17:22:00', $tz), + ], + $document->VAVAILABILITY->getEffectiveStartEnd() + ); + + } + + function testGetEffectiveStartEndUnbound() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +END:VAVAILABILITY +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $this->assertEquals( + [ + null, + null, + ], + $document->VAVAILABILITY->getEffectiveStartEnd() + ); + + } + + function testIsInTimeRangeUnbound() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +END:VAVAILABILITY +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $this->assertTrue( + $document->VAVAILABILITY->isInTimeRange(new DateTimeImmutable('2015-07-17'), new DateTimeImmutable('2015-07-18')) + ); + + } + + function testIsInTimeRangeOutside() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20140101T000000Z +DTEND:20140102T000000Z +END:VAVAILABILITY +END:VCALENDAR +VCAL; + + $document = Reader::read($vcal); + $this->assertFalse( + $document->VAVAILABILITY->isInTimeRange(new DateTimeImmutable('2015-07-17'), new DateTimeImmutable('2015-07-18')) + ); + + } + + function testRFCxxxSection3_1_availabilityprop_required() { + + // UID and DTSTAMP are present. + $this->assertIsValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // UID and DTSTAMP are missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // DTSTAMP is missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // UID is missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +DTSTAMP:20111005T133225Z +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + } + + function testRFCxxxSection3_1_availabilityprop_optional_once() { + + $properties = [ + 'BUSYTYPE:BUSY', + 'CLASS:PUBLIC', + 'CREATED:20111005T135125Z', + 'DESCRIPTION:Long bla bla', + 'DTSTART:20111005T020000', + 'LAST-MODIFIED:20111005T135325Z', + 'ORGANIZER:mailto:foo@example.com', + 'PRIORITY:1', + 'SEQUENCE:0', + 'SUMMARY:Bla bla', + 'URL:http://example.org/' + ]; + + // They are all present, only once. + $this->assertIsValid(Reader::read($this->template($properties))); + + // We duplicate each one to see if it fails. + foreach ($properties as $property) { + $this->assertIsNotValid(Reader::read($this->template([ + $property, + $property + ]))); + } + + } + + function testRFCxxxSection3_1_availabilityprop_dtend_duration() { + + // Only DTEND. + $this->assertIsValid(Reader::read($this->template([ + 'DTEND:21111005T133225Z' + ]))); + + // Only DURATION. + $this->assertIsValid(Reader::read($this->template([ + 'DURATION:PT1H' + ]))); + + // Both (not allowed). + $this->assertIsNotValid(Reader::read($this->template([ + 'DTEND:21111005T133225Z', + 'DURATION:PT1H' + ]))); + } + + function testAvailableSubComponent() { + + $vcal = <<<VCAL +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +BEGIN:AVAILABLE +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL; + $document = Reader::read($vcal); + + $this->assertInstanceOf(__NAMESPACE__, $document->VAVAILABILITY->AVAILABLE); + + } + + function testRFCxxxSection3_1_availableprop_required() { + + // UID, DTSTAMP and DTSTART are present. + $this->assertIsValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +BEGIN:AVAILABLE +UID:foo@test +DTSTAMP:20111005T133225Z +DTSTART:20111005T133225Z +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // UID, DTSTAMP and DTSTART are missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +BEGIN:AVAILABLE +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // UID is missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +BEGIN:AVAILABLE +DTSTAMP:20111005T133225Z +DTSTART:20111005T133225Z +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // DTSTAMP is missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +BEGIN:AVAILABLE +UID:foo@test +DTSTART:20111005T133225Z +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + // DTSTART is missing. + $this->assertIsNotValid(Reader::read( +<<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +BEGIN:AVAILABLE +UID:foo@test +DTSTAMP:20111005T133225Z +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL + )); + + } + + function testRFCxxxSection3_1_available_dtend_duration() { + + // Only DTEND. + $this->assertIsValid(Reader::read($this->templateAvailable([ + 'DTEND:21111005T133225Z' + ]))); + + // Only DURATION. + $this->assertIsValid(Reader::read($this->templateAvailable([ + 'DURATION:PT1H' + ]))); + + // Both (not allowed). + $this->assertIsNotValid(Reader::read($this->templateAvailable([ + 'DTEND:21111005T133225Z', + 'DURATION:PT1H' + ]))); + } + + function testRFCxxxSection3_1_available_optional_once() { + + $properties = [ + 'CREATED:20111005T135125Z', + 'DESCRIPTION:Long bla bla', + 'LAST-MODIFIED:20111005T135325Z', + 'RECURRENCE-ID;RANGE=THISANDFUTURE:19980401T133000Z', + 'RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', + 'SUMMARY:Bla bla' + ]; + + // They are all present, only once. + $this->assertIsValid(Reader::read($this->templateAvailable($properties))); + + // We duplicate each one to see if it fails. + foreach ($properties as $property) { + $this->assertIsNotValid(Reader::read($this->templateAvailable([ + $property, + $property + ]))); + } + + } + function testRFCxxxSection3_2() { + + $this->assertEquals( + 'BUSY', + Reader::read($this->templateAvailable([ + 'BUSYTYPE:BUSY' + ])) + ->VAVAILABILITY + ->AVAILABLE + ->BUSYTYPE + ->getValue() + ); + + $this->assertEquals( + 'BUSY-UNAVAILABLE', + Reader::read($this->templateAvailable([ + 'BUSYTYPE:BUSY-UNAVAILABLE' + ])) + ->VAVAILABILITY + ->AVAILABLE + ->BUSYTYPE + ->getValue() + ); + + $this->assertEquals( + 'BUSY-TENTATIVE', + Reader::read($this->templateAvailable([ + 'BUSYTYPE:BUSY-TENTATIVE' + ])) + ->VAVAILABILITY + ->AVAILABLE + ->BUSYTYPE + ->getValue() + ); + + } + + protected function assertIsValid(VObject\Document $document) { + + $validationResult = $document->validate(); + if ($validationResult) { + $messages = array_map(function($item) { return $item['message']; }, $validationResult); + $this->fail('Failed to assert that the supplied document is a valid document. Validation messages: ' . implode(', ', $messages)); + } + $this->assertEmpty($document->validate()); + + } + + protected function assertIsNotValid(VObject\Document $document) { + + $this->assertNotEmpty($document->validate()); + + } + + protected function template(array $properties) { + + return $this->_template( + <<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +… +END:VAVAILABILITY +END:VCALENDAR +VCAL +, + $properties + ); + + } + + protected function templateAvailable(array $properties) { + + return $this->_template( + <<<VCAL +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//id +BEGIN:VAVAILABILITY +UID:foo@test +DTSTAMP:20111005T133225Z +BEGIN:AVAILABLE +UID:foo@test +DTSTAMP:20111005T133225Z +DTSTART:20111005T133225Z +… +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +VCAL +, + $properties + ); + + } + + protected function _template($template, array $properties) { + + return str_replace('…', implode("\r\n", $properties), $template); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VCalendarTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VCalendarTest.php new file mode 100644 index 00000000000..abb387d9572 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VCalendarTest.php @@ -0,0 +1,782 @@ +<?php + +namespace Sabre\VObject\Component; + +use DateTimeZone; +use Sabre\VObject; + +class VCalendarTest extends \PHPUnit_Framework_TestCase { + + use VObject\PHPUnitAssertions; + + /** + * @dataProvider expandData + */ + function testExpand($input, $output, $timeZone = 'UTC', $start = '2011-12-01', $end = '2011-12-31') { + + $vcal = VObject\Reader::read($input); + + $timeZone = new DateTimeZone($timeZone); + + $vcal = $vcal->expand( + new \DateTime($start), + new \DateTime($end), + $timeZone + ); + + // This will normalize the output + $output = VObject\Reader::read($output)->serialize(); + + $this->assertVObjectEqualsVObject($output, $vcal->serialize()); + + } + + function expandData() { + + $tests = []; + + // No data + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +END:VCALENDAR +'; + + $output = $input; + $tests[] = [$input,$output]; + + + // Simple events + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla +SUMMARY:InExpand +DTSTART;VALUE=DATE:20111202 +END:VEVENT +BEGIN:VEVENT +UID:bla2 +SUMMARY:NotInExpand +DTSTART;VALUE=DATE:20120101 +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla +SUMMARY:InExpand +DTSTART;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + + $tests[] = [$input, $output]; + + // Removing timezone info + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/Paris +END:VTIMEZONE +BEGIN:VEVENT +UID:bla4 +SUMMARY:RemoveTZ info +DTSTART;TZID=Europe/Paris:20111203T130102 +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla4 +SUMMARY:RemoveTZ info +DTSTART:20111203T120102Z +END:VEVENT +END:VCALENDAR +'; + + $tests[] = [$input, $output]; + + // Removing timezone info from sub-components. See Issue #278 + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/Paris +END:VTIMEZONE +BEGIN:VEVENT +UID:bla4 +SUMMARY:RemoveTZ info +DTSTART;TZID=Europe/Paris:20111203T130102 +BEGIN:VALARM +TRIGGER;VALUE=DATE-TIME;TZID=America/New_York:20151209T133200 +END:VALARM +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla4 +SUMMARY:RemoveTZ info +DTSTART:20111203T120102Z +BEGIN:VALARM +TRIGGER;VALUE=DATE-TIME:20151209T183200Z +END:VALARM +END:VEVENT +END:VCALENDAR +'; + + $tests[] = [$input, $output]; + + // Recurrence rule + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111125T120000Z +DTEND:20111125T130000Z +RRULE:FREQ=WEEKLY +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111202T120000Z +DTEND:20111202T130000Z +RECURRENCE-ID:20111202T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111209T120000Z +DTEND:20111209T130000Z +RECURRENCE-ID:20111209T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111216T120000Z +DTEND:20111216T130000Z +RECURRENCE-ID:20111216T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111223T120000Z +DTEND:20111223T130000Z +RECURRENCE-ID:20111223T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule +DTSTART:20111230T120000Z +DTEND:20111230T130000Z +RECURRENCE-ID:20111230T120000Z +END:VEVENT +END:VCALENDAR +'; + + $tests[] = [$input, $output]; + + // Recurrence rule + override + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART:20111125T120000Z +DTEND:20111125T130000Z +RRULE:FREQ=WEEKLY +END:VEVENT +BEGIN:VEVENT +UID:bla6 +RECURRENCE-ID:20111209T120000Z +DTSTART:20111209T140000Z +DTEND:20111209T150000Z +SUMMARY:Override! +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART:20111202T120000Z +DTEND:20111202T130000Z +RECURRENCE-ID:20111202T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +RECURRENCE-ID:20111209T120000Z +DTSTART:20111209T140000Z +DTEND:20111209T150000Z +SUMMARY:Override! +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART:20111216T120000Z +DTEND:20111216T130000Z +RECURRENCE-ID:20111216T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART:20111223T120000Z +DTEND:20111223T130000Z +RECURRENCE-ID:20111223T120000Z +END:VEVENT +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule2 +DTSTART:20111230T120000Z +DTEND:20111230T130000Z +RECURRENCE-ID:20111230T120000Z +END:VEVENT +END:VCALENDAR +'; + + $tests[] = [$input, $output]; + + // Floating dates and times. + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:bla1 +DTSTART:20141112T195000 +END:VEVENT +BEGIN:VEVENT +UID:bla2 +DTSTART;VALUE=DATE:20141112 +END:VEVENT +BEGIN:VEVENT +UID:bla3 +DTSTART;VALUE=DATE:20141112 +RRULE:FREQ=DAILY;COUNT=2 +END:VEVENT +END:VCALENDAR +ICS; + + $output = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:bla1 +DTSTART:20141112T225000Z +END:VEVENT +BEGIN:VEVENT +UID:bla2 +DTSTART;VALUE=DATE:20141112 +END:VEVENT +BEGIN:VEVENT +UID:bla3 +DTSTART;VALUE=DATE:20141112 +RECURRENCE-ID;VALUE=DATE:20141112 +END:VEVENT +BEGIN:VEVENT +UID:bla3 +DTSTART;VALUE=DATE:20141113 +RECURRENCE-ID;VALUE=DATE:20141113 +END:VEVENT +END:VCALENDAR +ICS; + + $tests[] = [$input, $output, 'America/Argentina/Buenos_Aires', '2014-01-01', '2015-01-01']; + + // Recurrence rule with no valid instances + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +UID:bla6 +SUMMARY:Testing RRule3 +DTSTART:20111125T120000Z +DTEND:20111125T130000Z +RRULE:FREQ=WEEKLY;COUNT=1 +EXDATE:20111125T120000Z +END:VEVENT +END:VCALENDAR +'; + + $output = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +END:VCALENDAR +'; + + $tests[] = [$input, $output]; + return $tests; + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testBrokenEventExpand() { + + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +RRULE:FREQ=WEEKLY +DTSTART;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + $vcal = VObject\Reader::read($input); + $vcal->expand( + new \DateTime('2011-12-01'), + new \DateTime('2011-12-31') + ); + + } + + function testGetDocumentType() { + + $vcard = new VCalendar(); + $vcard->VERSION = '2.0'; + $this->assertEquals(VCalendar::ICALENDAR20, $vcard->getDocumentType()); + + } + + function testValidateCorrect() { + + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +PRODID:foo +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +DTSTAMP:20140122T233226Z +UID:foo +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals([], $vcal->validate(), 'Got an error'); + + } + + function testValidateNoVersion() { + + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +PRODID:foo +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(1, count($vcal->validate())); + + } + + function testValidateWrongVersion() { + + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:3.0 +PRODID:foo +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(1, count($vcal->validate())); + + } + + function testValidateNoProdId() { + + $input = 'BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(1, count($vcal->validate())); + + } + + function testValidateDoubleCalScale() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +CALSCALE:GREGORIAN +CALSCALE:GREGORIAN +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(1, count($vcal->validate())); + + } + + function testValidateDoubleMethod() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +METHOD:REQUEST +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(1, count($vcal->validate())); + + } + + function testValidateTwoMasterEvents() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(1, count($vcal->validate())); + + } + + function testValidateOneMasterEvent() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +RECURRENCE-ID;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + $this->assertEquals(0, count($vcal->validate())); + + } + + function testGetBaseComponent() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +BEGIN:VEVENT +SUMMARY:test +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +RECURRENCE-ID;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + + $result = $vcal->getBaseComponent(); + $this->assertEquals('test', $result->SUMMARY->getValue()); + + } + + function testGetBaseComponentNoResult() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +BEGIN:VEVENT +SUMMARY:test +RECURRENCE-ID;VALUE=DATE:20111202 +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +RECURRENCE-ID;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + + $result = $vcal->getBaseComponent(); + $this->assertNull($result); + + } + + function testGetBaseComponentWithFilter() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +BEGIN:VEVENT +SUMMARY:test +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20111202 +UID:foo +DTSTAMP:20140122T234434Z +RECURRENCE-ID;VALUE=DATE:20111202 +END:VEVENT +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + + $result = $vcal->getBaseComponent('VEVENT'); + $this->assertEquals('test', $result->SUMMARY->getValue()); + + } + + function testGetBaseComponentWithFilterNoResult() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foo +METHOD:REQUEST +BEGIN:VTODO +SUMMARY:test +UID:foo +DTSTAMP:20140122T234434Z +END:VTODO +END:VCALENDAR +'; + + $vcal = VObject\Reader::read($input); + + $result = $vcal->getBaseComponent('VEVENT'); + $this->assertNull($result); + + } + + function testNoComponents() { + + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:vobject +END:VCALENDAR +ICS; + + $this->assertValidate( + $input, + 0, + 3, + "An iCalendar object must have at least 1 component." + ); + + } + + function testCalDAVNoComponents() { + + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:vobject +BEGIN:VTIMEZONE +TZID:America/Toronto +END:VTIMEZONE +END:VCALENDAR +ICS; + + $this->assertValidate( + $input, + VCalendar::PROFILE_CALDAV, + 3, + "A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL)." + ); + + } + + function testCalDAVMultiUID() { + + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:vobject +BEGIN:VEVENT +UID:foo +DTSTAMP:20150109T184500Z +DTSTART:20150109T184500Z +END:VEVENT +BEGIN:VEVENT +UID:bar +DTSTAMP:20150109T184500Z +DTSTART:20150109T184500Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertValidate( + $input, + VCalendar::PROFILE_CALDAV, + 3, + "A calendar object on a CalDAV server may only have components with the same UID." + ); + + } + + function testCalDAVMultiComponent() { + + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:vobject +BEGIN:VEVENT +UID:foo +RECURRENCE-ID:20150109T185200Z +DTSTAMP:20150109T184500Z +DTSTART:20150109T184500Z +END:VEVENT +BEGIN:VTODO +UID:foo +DTSTAMP:20150109T184500Z +DTSTART:20150109T184500Z +END:VTODO +END:VCALENDAR +ICS; + + $this->assertValidate( + $input, + VCalendar::PROFILE_CALDAV, + 3, + "A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL)." + ); + + } + + function testCalDAVMETHOD() { + + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +PRODID:vobject +BEGIN:VEVENT +UID:foo +RECURRENCE-ID:20150109T185200Z +DTSTAMP:20150109T184500Z +DTSTART:20150109T184500Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertValidate( + $input, + VCalendar::PROFILE_CALDAV, + 3, + "A calendar object on a CalDAV server MUST NOT have a METHOD property." + ); + + } + + function assertValidate($ics, $options, $expectedLevel, $expectedMessage = null) { + + $vcal = VObject\Reader::read($ics); + $result = $vcal->validate($options); + + $this->assertValidateResult($result, $expectedLevel, $expectedMessage); + + } + + function assertValidateResult($input, $expectedLevel, $expectedMessage = null) { + + $messages = []; + foreach ($input as $warning) { + $messages[] = $warning['message']; + } + + if ($expectedLevel === 0) { + $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages)); + } else { + $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages)); + + $this->assertEquals($expectedMessage, $input[0]['message']); + $this->assertEquals($expectedLevel, $input[0]['level']); + } + + } + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VCardTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VCardTest.php new file mode 100644 index 00000000000..baa7f490d44 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VCardTest.php @@ -0,0 +1,304 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; + +class VCardTest extends \PHPUnit_Framework_TestCase { + + /** + * @dataProvider validateData + */ + function testValidate($input, $expectedWarnings, $expectedRepairedOutput) { + + $vcard = VObject\Reader::read($input); + + $warnings = $vcard->validate(); + + $warnMsg = []; + foreach ($warnings as $warning) { + $warnMsg[] = $warning['message']; + } + + $this->assertEquals($expectedWarnings, $warnMsg); + + $vcard->validate(VObject\Component::REPAIR); + + $this->assertEquals( + $expectedRepairedOutput, + $vcard->serialize() + ); + + } + + function validateData() { + + $tests = []; + + // Correct + $tests[] = [ + "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n", + [], + "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n", + ]; + + // No VERSION + $tests[] = [ + "BEGIN:VCARD\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n", + [ + 'VERSION MUST appear exactly once in a VCARD component', + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n", + ]; + + // Unknown version + $tests[] = [ + "BEGIN:VCARD\r\nVERSION:2.2\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n", + [ + 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', + ], + "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n", + ]; + + // No FN + $tests[] = [ + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n", + [ + 'The FN property must appear in the VCARD component exactly 1 time', + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n", + ]; + // No FN, N fallback + $tests[] = [ + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nEND:VCARD\r\n", + [ + 'The FN property must appear in the VCARD component exactly 1 time', + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nFN:John Doe\r\nEND:VCARD\r\n", + ]; + // No FN, N fallback, no first name + $tests[] = [ + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nEND:VCARD\r\n", + [ + 'The FN property must appear in the VCARD component exactly 1 time', + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nFN:Doe\r\nEND:VCARD\r\n", + ]; + + // No FN, ORG fallback + $tests[] = [ + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nEND:VCARD\r\n", + [ + 'The FN property must appear in the VCARD component exactly 1 time', + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nFN:Acme Co.\r\nEND:VCARD\r\n", + ]; + return $tests; + + } + + function testGetDocumentType() { + + $vcard = new VCard([], false); + $vcard->VERSION = '2.1'; + $this->assertEquals(VCard::VCARD21, $vcard->getDocumentType()); + + $vcard = new VCard([], false); + $vcard->VERSION = '3.0'; + $this->assertEquals(VCard::VCARD30, $vcard->getDocumentType()); + + $vcard = new VCard([], false); + $vcard->VERSION = '4.0'; + $this->assertEquals(VCard::VCARD40, $vcard->getDocumentType()); + + $vcard = new VCard([], false); + $this->assertEquals(VCard::UNKNOWN, $vcard->getDocumentType()); + } + + function testGetByType() { + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +EMAIL;TYPE=home:1@example.org +EMAIL;TYPE=work:2@example.org +END:VCARD +VCF; + + $vcard = VObject\Reader::read($vcard); + $this->assertEquals('1@example.org', $vcard->getByType('EMAIL', 'home')->getValue()); + $this->assertEquals('2@example.org', $vcard->getByType('EMAIL', 'work')->getValue()); + $this->assertNull($vcard->getByType('EMAIL', 'non-existant')); + $this->assertNull($vcard->getByType('ADR', 'non-existant')); + } + + function testPreferredNoPref() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +EMAIL:1@example.org +EMAIL:2@example.org +END:VCARD +VCF; + + $vcard = VObject\Reader::read($vcard); + $this->assertEquals('1@example.org', $vcard->preferred('EMAIL')->getValue()); + + } + + function testPreferredWithPref() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +EMAIL:1@example.org +EMAIL;TYPE=PREF:2@example.org +END:VCARD +VCF; + + $vcard = VObject\Reader::read($vcard); + $this->assertEquals('2@example.org', $vcard->preferred('EMAIL')->getValue()); + + } + + function testPreferredWith40Pref() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:4.0 +EMAIL:1@example.org +EMAIL;PREF=3:2@example.org +EMAIL;PREF=2:3@example.org +END:VCARD +VCF; + + $vcard = VObject\Reader::read($vcard); + $this->assertEquals('3@example.org', $vcard->preferred('EMAIL')->getValue()); + + } + + function testPreferredNotFound() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:4.0 +END:VCARD +VCF; + + $vcard = VObject\Reader::read($vcard); + $this->assertNull($vcard->preferred('EMAIL')); + + } + + function testNoUIDCardDAV() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:4.0 +FN:John Doe +END:VCARD +VCF; + $this->assertValidate( + $vcard, + VCARD::PROFILE_CARDDAV, + 3, + 'vCards on CardDAV servers MUST have a UID property.' + ); + + } + + function testNoUIDNoCardDAV() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:4.0 +FN:John Doe +END:VCARD +VCF; + $this->assertValidate( + $vcard, + 0, + 2, + 'Adding a UID to a vCard property is recommended.' + ); + + } + function testNoUIDNoCardDAVRepair() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:4.0 +FN:John Doe +END:VCARD +VCF; + $this->assertValidate( + $vcard, + VCARD::REPAIR, + 1, + 'Adding a UID to a vCard property is recommended.' + ); + + } + + function testVCard21CardDAV() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:2.1 +FN:John Doe +UID:foo +END:VCARD +VCF; + $this->assertValidate( + $vcard, + VCARD::PROFILE_CARDDAV, + 3, + 'CardDAV servers are not allowed to accept vCard 2.1.' + ); + + } + + function testVCard21NoCardDAV() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:2.1 +FN:John Doe +UID:foo +END:VCARD +VCF; + $this->assertValidate( + $vcard, + 0, + 0 + ); + + } + + function assertValidate($vcf, $options, $expectedLevel, $expectedMessage = null) { + + $vcal = VObject\Reader::read($vcf); + $result = $vcal->validate($options); + + $this->assertValidateResult($result, $expectedLevel, $expectedMessage); + + } + + function assertValidateResult($input, $expectedLevel, $expectedMessage = null) { + + $messages = []; + foreach ($input as $warning) { + $messages[] = $warning['message']; + } + + if ($expectedLevel === 0) { + $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages)); + } else { + $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages)); + + $this->assertEquals($expectedMessage, $input[0]['message']); + $this->assertEquals($expectedLevel, $input[0]['level']); + } + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VEventTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VEventTest.php new file mode 100644 index 00000000000..11acd3f6b72 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VEventTest.php @@ -0,0 +1,95 @@ +<?php + +namespace Sabre\VObject\Component; + +class VEventTest extends \PHPUnit_Framework_TestCase { + + /** + * @dataProvider timeRangeTestData + */ + function testInTimeRange(VEvent $vevent, $start, $end, $outcome) { + + $this->assertEquals($outcome, $vevent->isInTimeRange($start, $end)); + + } + + function timeRangeTestData() { + + $tests = []; + + $calendar = new VCalendar(); + + $vevent = $calendar->createComponent('VEVENT'); + $vevent->DTSTART = '20111223T120000Z'; + $tests[] = [$vevent, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vevent, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vevent2 = clone $vevent; + $vevent2->DTEND = '20111225T120000Z'; + $tests[] = [$vevent2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vevent2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vevent3 = clone $vevent; + $vevent3->DURATION = 'P1D'; + $tests[] = [$vevent3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vevent3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vevent4 = clone $vevent; + $vevent4->DTSTART = '20111225'; + $vevent4->DTSTART['VALUE'] = 'DATE'; + $tests[] = [$vevent4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vevent4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + // Event with no end date should be treated as lasting the entire day. + $tests[] = [$vevent4, new \DateTime('2011-12-25 16:00:00'), new \DateTime('2011-12-25 17:00:00'), true]; + // DTEND is non inclusive so all day events should not be returned on the next day. + $tests[] = [$vevent4, new \DateTime('2011-12-26 00:00:00'), new \DateTime('2011-12-26 17:00:00'), false]; + // The timezone of timerange in question also needs to be considered. + $tests[] = [$vevent4, new \DateTime('2011-12-26 00:00:00', new \DateTimeZone('Europe/Berlin')), new \DateTime('2011-12-26 17:00:00', new \DateTimeZone('Europe/Berlin')), false]; + + $vevent5 = clone $vevent; + $vevent5->DURATION = 'P1D'; + $vevent5->RRULE = 'FREQ=YEARLY'; + $tests[] = [$vevent5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vevent5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + $tests[] = [$vevent5, new \DateTime('2013-12-01'), new \DateTime('2013-12-31'), true]; + + $vevent6 = clone $vevent; + $vevent6->DTSTART = '20111225'; + $vevent6->DTSTART['VALUE'] = 'DATE'; + $vevent6->DTEND = '20111225'; + $vevent6->DTEND['VALUE'] = 'DATE'; + + $tests[] = [$vevent6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vevent6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + // Added this test to ensure that recurrence rules with no DTEND also + // get checked for the entire day. + $vevent7 = clone $vevent; + $vevent7->DTSTART = '20120101'; + $vevent7->DTSTART['VALUE'] = 'DATE'; + $vevent7->RRULE = 'FREQ=MONTHLY'; + $tests[] = [$vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true]; + // The timezone of timerange in question should also be considered. + $tests[] = [$vevent7, new \DateTime('2012-02-02 00:00:00', new \DateTimeZone('Europe/Berlin')), new \DateTime('2012-02-03 00:00:00', new \DateTimeZone('Europe/Berlin')), false]; + + // Added this test to check recurring events that have no instances. + $vevent8 = clone $vevent; + $vevent8->DTSTART = '20130329T140000'; + $vevent8->DTEND = '20130329T153000'; + $vevent8->RRULE = ['FREQ' => 'WEEKLY', 'BYDAY' => ['FR'], 'UNTIL' => '20130412T115959Z']; + $vevent8->add('EXDATE', '20130405T140000'); + $vevent8->add('EXDATE', '20130329T140000'); + $tests[] = [$vevent8, new \DateTime('2013-03-01'), new \DateTime('2013-04-01'), false]; + + // Added this test to check recurring all day event that repeat every day + $vevent9 = clone $vevent; + $vevent9->DTSTART = '20161027'; + $vevent9->DTEND = '20161028'; + $vevent9->RRULE = 'FREQ=DAILY'; + $tests[] = [$vevent9, new \DateTime('2016-10-31'), new \DateTime('2016-12-12'), true]; + + return $tests; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VFreeBusyTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VFreeBusyTest.php new file mode 100644 index 00000000000..2d463fd09ad --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VFreeBusyTest.php @@ -0,0 +1,66 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; +use Sabre\VObject\Reader; + +class VFreeBusyTest extends \PHPUnit_Framework_TestCase { + + function testIsFree() { + + $input = <<<BLA +BEGIN:VCALENDAR +BEGIN:VFREEBUSY +FREEBUSY;FBTYPE=FREE:20120912T000500Z/PT1H +FREEBUSY;FBTYPE=BUSY:20120912T010000Z/20120912T020000Z +FREEBUSY;FBTYPE=BUSY-TENTATIVE:20120912T020000Z/20120912T030000Z +FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20120912T030000Z/20120912T040000Z +FREEBUSY;FBTYPE=BUSY:20120912T050000Z/20120912T060000Z,20120912T080000Z/20120912T090000Z +FREEBUSY;FBTYPE=BUSY:20120912T100000Z/PT1H +END:VFREEBUSY +END:VCALENDAR +BLA; + + $obj = VObject\Reader::read($input); + $vfb = $obj->VFREEBUSY; + + $tz = new \DateTimeZone('UTC'); + + $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 01:15:00', $tz), new \DateTime('2012-09-12 01:45:00', $tz))); + $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 08:05:00', $tz), new \DateTime('2012-09-12 08:10:00', $tz))); + $this->assertFalse($vfb->isFree(new \DateTime('2012-09-12 10:15:00', $tz), new \DateTime('2012-09-12 10:45:00', $tz))); + + // Checking whether the end time is treated as non-inclusive + $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:00:00', $tz), new \DateTime('2012-09-12 09:15:00', $tz))); + $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 09:45:00', $tz), new \DateTime('2012-09-12 10:00:00', $tz))); + $this->assertTrue($vfb->isFree(new \DateTime('2012-09-12 11:00:00', $tz), new \DateTime('2012-09-12 12:00:00', $tz))); + + } + + function testValidate() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VFREEBUSY +UID:some-random-id +DTSTAMP:20140402T180200Z +END:VFREEBUSY +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([], $messages); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VJournalTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VJournalTest.php new file mode 100644 index 00000000000..1a2362d4a73 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VJournalTest.php @@ -0,0 +1,100 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject\Component; +use Sabre\VObject\Reader; + +class VJournalTest extends \PHPUnit_Framework_TestCase { + + /** + * @dataProvider timeRangeTestData + */ + function testInTimeRange(VJournal $vtodo, $start, $end, $outcome) { + + $this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); + + } + + function testValidate() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VJOURNAL +UID:12345678 +DTSTAMP:20140402T174100Z +END:VJOURNAL +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([], $messages); + + } + + function testValidateBroken() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VJOURNAL +UID:12345678 +DTSTAMP:20140402T174100Z +URL:http://example.org/ +URL:http://example.com/ +END:VJOURNAL +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals( + ["URL MUST NOT appear more than once in a VJOURNAL component"], + $messages + ); + + } + + function timeRangeTestData() { + + $calendar = new VCalendar(); + + $tests = []; + + $vjournal = $calendar->createComponent('VJOURNAL'); + $vjournal->DTSTART = '20111223T120000Z'; + $tests[] = [$vjournal, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vjournal, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vjournal2 = $calendar->createComponent('VJOURNAL'); + $vjournal2->DTSTART = '20111223'; + $vjournal2->DTSTART['VALUE'] = 'DATE'; + $tests[] = [$vjournal2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vjournal2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vjournal3 = $calendar->createComponent('VJOURNAL'); + $tests[] = [$vjournal3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), false]; + $tests[] = [$vjournal3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + return $tests; + } + + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VTimeZoneTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VTimeZoneTest.php new file mode 100644 index 00000000000..f320fd3d6e8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VTimeZoneTest.php @@ -0,0 +1,56 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject\Reader; + +class VTimeZoneTest extends \PHPUnit_Framework_TestCase { + + function testValidate() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VTIMEZONE +TZID:America/Toronto +END:VTIMEZONE +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([], $messages); + + } + + function testGetTimeZone() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VTIMEZONE +TZID:America/Toronto +END:VTIMEZONE +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $tz = new \DateTimeZone('America/Toronto'); + + $this->assertEquals( + $tz, + $obj->VTIMEZONE->getTimeZone() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VTodoTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VTodoTest.php new file mode 100644 index 00000000000..85c6e07842f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Component/VTodoTest.php @@ -0,0 +1,178 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject\Component; +use Sabre\VObject\Reader; + +class VTodoTest extends \PHPUnit_Framework_TestCase { + + /** + * @dataProvider timeRangeTestData + */ + function testInTimeRange(VTodo $vtodo, $start, $end, $outcome) { + + $this->assertEquals($outcome, $vtodo->isInTimeRange($start, $end)); + + } + + function timeRangeTestData() { + + $tests = []; + + $calendar = new VCalendar(); + + $vtodo = $calendar->createComponent('VTODO'); + $vtodo->DTSTART = '20111223T120000Z'; + $tests[] = [$vtodo, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo2 = clone $vtodo; + $vtodo2->DURATION = 'P1D'; + $tests[] = [$vtodo2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo3 = clone $vtodo; + $vtodo3->DUE = '20111225'; + $tests[] = [$vtodo3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo4 = $calendar->createComponent('VTODO'); + $vtodo4->DUE = '20111225'; + $tests[] = [$vtodo4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo5 = $calendar->createComponent('VTODO'); + $vtodo5->COMPLETED = '20111225'; + $tests[] = [$vtodo5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo6 = $calendar->createComponent('VTODO'); + $vtodo6->CREATED = '20111225'; + $tests[] = [$vtodo6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo7 = $calendar->createComponent('VTODO'); + $vtodo7->CREATED = '20111225'; + $vtodo7->COMPLETED = '20111226'; + $tests[] = [$vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false]; + + $vtodo7 = $calendar->createComponent('VTODO'); + $tests[] = [$vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true]; + $tests[] = [$vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), true]; + + return $tests; + + } + + function testValidate() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VTODO +UID:1234-21355-123156 +DTSTAMP:20140402T183400Z +END:VTODO +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([], $messages); + + } + + function testValidateInvalid() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VTODO +END:VTODO +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([ + "UID MUST appear exactly once in a VTODO component", + "DTSTAMP MUST appear exactly once in a VTODO component", + ], $messages); + + } + + function testValidateDUEDTSTARTMisMatch() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VTODO +UID:FOO +DTSTART;VALUE=DATE-TIME:20140520T131600Z +DUE;VALUE=DATE:20140520 +DTSTAMP;VALUE=DATE-TIME:20140520T131600Z +END:VTODO +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([ + "The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART", + ], $messages); + + } + + function testValidateDUEbeforeDTSTART() { + + $input = <<<HI +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:YoYo +BEGIN:VTODO +UID:FOO +DTSTART;VALUE=DATE:20140520 +DUE;VALUE=DATE:20140518 +DTSTAMP;VALUE=DATE-TIME:20140520T131600Z +END:VTODO +END:VCALENDAR +HI; + + $obj = Reader::read($input); + + $warnings = $obj->validate(); + $messages = []; + foreach ($warnings as $warning) { + $messages[] = $warning['message']; + } + + $this->assertEquals([ + "DUE must occur after DTSTART", + ], $messages); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ComponentTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ComponentTest.php new file mode 100644 index 00000000000..9323a43d1c1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ComponentTest.php @@ -0,0 +1,527 @@ +<?php + +namespace Sabre\VObject; + +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Component\VCard; + +class ComponentTest extends \PHPUnit_Framework_TestCase { + + function testIterate() { + + $comp = new VCalendar([], false); + + $sub = $comp->createComponent('VEVENT'); + $comp->add($sub); + + $sub = $comp->createComponent('VTODO'); + $comp->add($sub); + + $count = 0; + foreach ($comp->children() as $key => $subcomponent) { + + $count++; + $this->assertInstanceOf('Sabre\\VObject\\Component', $subcomponent); + + } + $this->assertEquals(2, $count); + $this->assertEquals(1, $key); + + } + + function testMagicGet() { + + $comp = new VCalendar([], false); + + $sub = $comp->createComponent('VEVENT'); + $comp->add($sub); + + $sub = $comp->createComponent('VTODO'); + $comp->add($sub); + + $event = $comp->vevent; + $this->assertInstanceOf('Sabre\\VObject\\Component', $event); + $this->assertEquals('VEVENT', $event->name); + + $this->assertInternalType('null', $comp->vjournal); + + } + + function testMagicGetGroups() { + + $comp = new VCard(); + + $sub = $comp->createProperty('GROUP1.EMAIL', '1@1.com'); + $comp->add($sub); + + $sub = $comp->createProperty('GROUP2.EMAIL', '2@2.com'); + $comp->add($sub); + + $sub = $comp->createProperty('EMAIL', '3@3.com'); + $comp->add($sub); + + $emails = $comp->email; + $this->assertEquals(3, count($emails)); + + $email1 = $comp->{"group1.email"}; + $this->assertEquals('EMAIL', $email1[0]->name); + $this->assertEquals('GROUP1', $email1[0]->group); + + $email3 = $comp->{".email"}; + $this->assertEquals('EMAIL', $email3[0]->name); + $this->assertEquals(null, $email3[0]->group); + + } + + function testMagicIsset() { + + $comp = new VCalendar(); + + $sub = $comp->createComponent('VEVENT'); + $comp->add($sub); + + $sub = $comp->createComponent('VTODO'); + $comp->add($sub); + + $this->assertTrue(isset($comp->vevent)); + $this->assertTrue(isset($comp->vtodo)); + $this->assertFalse(isset($comp->vjournal)); + + } + + function testMagicSetScalar() { + + $comp = new VCalendar(); + $comp->myProp = 'myValue'; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $comp->MYPROP); + $this->assertEquals('myValue', (string)$comp->MYPROP); + + + } + + function testMagicSetScalarTwice() { + + $comp = new VCalendar([], false); + $comp->myProp = 'myValue'; + $comp->myProp = 'myValue'; + + $this->assertEquals(1, count($comp->children())); + $this->assertInstanceOf('Sabre\\VObject\\Property', $comp->MYPROP); + $this->assertEquals('myValue', (string)$comp->MYPROP); + + } + + function testMagicSetArray() { + + $comp = new VCalendar(); + $comp->ORG = ['Acme Inc', 'Section 9']; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $comp->ORG); + $this->assertEquals(['Acme Inc', 'Section 9'], $comp->ORG->getParts()); + + } + + function testMagicSetComponent() { + + $comp = new VCalendar(); + + // Note that 'myProp' is ignored here. + $comp->myProp = $comp->createComponent('VEVENT'); + + $this->assertEquals(1, count($comp)); + + $this->assertEquals('VEVENT', $comp->VEVENT->name); + + } + + function testMagicSetTwice() { + + $comp = new VCalendar([], false); + + $comp->VEVENT = $comp->createComponent('VEVENT'); + $comp->VEVENT = $comp->createComponent('VEVENT'); + + $this->assertEquals(1, count($comp->children())); + + $this->assertEquals('VEVENT', $comp->VEVENT->name); + + } + + function testArrayAccessGet() { + + $comp = new VCalendar([], false); + + $event = $comp->createComponent('VEVENT'); + $event->summary = 'Event 1'; + + $comp->add($event); + + $event2 = clone $event; + $event2->summary = 'Event 2'; + + $comp->add($event2); + + $this->assertEquals(2, count($comp->children())); + $this->assertTrue($comp->vevent[1] instanceof Component); + $this->assertEquals('Event 2', (string)$comp->vevent[1]->summary); + + } + + function testArrayAccessExists() { + + $comp = new VCalendar(); + + $event = $comp->createComponent('VEVENT'); + $event->summary = 'Event 1'; + + $comp->add($event); + + $event2 = clone $event; + $event2->summary = 'Event 2'; + + $comp->add($event2); + + $this->assertTrue(isset($comp->vevent[0])); + $this->assertTrue(isset($comp->vevent[1])); + + } + + /** + * @expectedException LogicException + */ + function testArrayAccessSet() { + + $comp = new VCalendar(); + $comp['hey'] = 'hi there'; + + } + /** + * @expectedException LogicException + */ + function testArrayAccessUnset() { + + $comp = new VCalendar(); + unset($comp[0]); + + } + + function testAddScalar() { + + $comp = new VCalendar([], false); + + $comp->add('myprop', 'value'); + + $this->assertEquals(1, count($comp->children())); + + $bla = $comp->children()[0]; + + $this->assertTrue($bla instanceof Property); + $this->assertEquals('MYPROP', $bla->name); + $this->assertEquals('value', (string)$bla); + + } + + function testAddScalarParams() { + + $comp = new VCalendar([], false); + + $comp->add('myprop', 'value', ['param1' => 'value1']); + + $this->assertEquals(1, count($comp->children())); + + $bla = $comp->children()[0]; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $bla); + $this->assertEquals('MYPROP', $bla->name); + $this->assertEquals('value', (string)$bla); + + $this->assertEquals(1, count($bla->parameters())); + + $this->assertEquals('PARAM1', $bla->parameters['PARAM1']->name); + $this->assertEquals('value1', $bla->parameters['PARAM1']->getValue()); + + } + + + function testAddComponent() { + + $comp = new VCalendar([], false); + + $comp->add($comp->createComponent('VEVENT')); + + $this->assertEquals(1, count($comp->children())); + + $this->assertEquals('VEVENT', $comp->VEVENT->name); + + } + + function testAddComponentTwice() { + + $comp = new VCalendar([], false); + + $comp->add($comp->createComponent('VEVENT')); + $comp->add($comp->createComponent('VEVENT')); + + $this->assertEquals(2, count($comp->children())); + + $this->assertEquals('VEVENT', $comp->VEVENT->name); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail() { + + $comp = new VCalendar(); + $comp->add($comp->createComponent('VEVENT'), 'hello'); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testAddArgFail2() { + + $comp = new VCalendar(); + $comp->add([]); + + } + + function testMagicUnset() { + + $comp = new VCalendar([], false); + $comp->add($comp->createComponent('VEVENT')); + + unset($comp->vevent); + + $this->assertEquals(0, count($comp->children())); + + } + + + function testCount() { + + $comp = new VCalendar(); + $this->assertEquals(1, $comp->count()); + + } + + function testChildren() { + + $comp = new VCalendar([], false); + + // Note that 'myProp' is ignored here. + $comp->add($comp->createComponent('VEVENT')); + $comp->add($comp->createComponent('VTODO')); + + $r = $comp->children(); + $this->assertInternalType('array', $r); + $this->assertEquals(2, count($r)); + } + + function testGetComponents() { + + $comp = new VCalendar(); + + $comp->add($comp->createProperty('FOO', 'BAR')); + $comp->add($comp->createComponent('VTODO')); + + $r = $comp->getComponents(); + $this->assertInternalType('array', $r); + $this->assertEquals(1, count($r)); + $this->assertEquals('VTODO', $r[0]->name); + } + + function testSerialize() { + + $comp = new VCalendar([], false); + $this->assertEquals("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n", $comp->serialize()); + + } + + function testSerializeChildren() { + + $comp = new VCalendar([], false); + $event = $comp->add($comp->createComponent('VEVENT')); + unset($event->DTSTAMP, $event->UID); + $todo = $comp->add($comp->createComponent('VTODO')); + unset($todo->DTSTAMP, $todo->UID); + + $str = $comp->serialize(); + + $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", $str); + + } + + function testSerializeOrderCompAndProp() { + + $comp = new VCalendar([], false); + $comp->add($event = $comp->createComponent('VEVENT')); + $comp->add('PROP1', 'BLABLA'); + $comp->add('VERSION', '2.0'); + $comp->add($comp->createComponent('VTIMEZONE')); + + unset($event->DTSTAMP, $event->UID); + $str = $comp->serialize(); + + $this->assertEquals("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPROP1:BLABLA\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $str); + + } + + function testAnotherSerializeOrderProp() { + + $prop4s = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; + + $comp = new VCard([], false); + + $comp->__set('SOMEPROP', 'FOO'); + $comp->__set('ANOTHERPROP', 'FOO'); + $comp->__set('THIRDPROP', 'FOO'); + foreach ($prop4s as $prop4) { + $comp->add('PROP4', 'FOO ' . $prop4); + } + $comp->__set('PROPNUMBERFIVE', 'FOO'); + $comp->__set('PROPNUMBERSIX', 'FOO'); + $comp->__set('PROPNUMBERSEVEN', 'FOO'); + $comp->__set('PROPNUMBEREIGHT', 'FOO'); + $comp->__set('PROPNUMBERNINE', 'FOO'); + $comp->__set('PROPNUMBERTEN', 'FOO'); + $comp->__set('VERSION', '2.0'); + $comp->__set('UID', 'FOO'); + + $str = $comp->serialize(); + + $this->assertEquals("BEGIN:VCARD\r\nVERSION:2.0\r\nSOMEPROP:FOO\r\nANOTHERPROP:FOO\r\nTHIRDPROP:FOO\r\nPROP4:FOO 1\r\nPROP4:FOO 2\r\nPROP4:FOO 3\r\nPROP4:FOO 4\r\nPROP4:FOO 5\r\nPROP4:FOO 6\r\nPROP4:FOO 7\r\nPROP4:FOO 8\r\nPROP4:FOO 9\r\nPROP4:FOO 10\r\nPROPNUMBERFIVE:FOO\r\nPROPNUMBERSIX:FOO\r\nPROPNUMBERSEVEN:FOO\r\nPROPNUMBEREIGHT:FOO\r\nPROPNUMBERNINE:FOO\r\nPROPNUMBERTEN:FOO\r\nUID:FOO\r\nEND:VCARD\r\n", $str); + + } + + function testInstantiateWithChildren() { + + $comp = new VCard([ + 'ORG' => ['Acme Inc.', 'Section 9'], + 'FN' => 'Finn The Human', + ]); + + $this->assertEquals(['Acme Inc.', 'Section 9'], $comp->ORG->getParts()); + $this->assertEquals('Finn The Human', $comp->FN->getValue()); + + } + + function testInstantiateSubComponent() { + + $comp = new VCalendar(); + $event = $comp->createComponent('VEVENT', [ + $comp->createProperty('UID', '12345'), + ]); + $comp->add($event); + + $this->assertEquals('12345', $comp->VEVENT->UID->getValue()); + + } + + function testRemoveByName() { + + $comp = new VCalendar([], false); + $comp->add('prop1', 'val1'); + $comp->add('prop2', 'val2'); + $comp->add('prop2', 'val2'); + + $comp->remove('prop2'); + $this->assertFalse(isset($comp->prop2)); + $this->assertTrue(isset($comp->prop1)); + + } + + function testRemoveByObj() { + + $comp = new VCalendar([], false); + $comp->add('prop1', 'val1'); + $prop = $comp->add('prop2', 'val2'); + + $comp->remove($prop); + $this->assertFalse(isset($comp->prop2)); + $this->assertTrue(isset($comp->prop1)); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testRemoveNotFound() { + + $comp = new VCalendar([], false); + $prop = $comp->createProperty('A', 'B'); + $comp->remove($prop); + + } + + /** + * @dataProvider ruleData + */ + function testValidateRules($componentList, $errorCount) { + + $vcard = new Component\VCard(); + + $component = new FakeComponent($vcard, 'Hi', [], $defaults = false); + foreach ($componentList as $v) { + $component->add($v, 'Hello.'); + } + + $this->assertEquals($errorCount, count($component->validate())); + + } + + function testValidateRepair() { + + $vcard = new Component\VCard(); + + $component = new FakeComponent($vcard, 'Hi', [], $defaults = false); + $component->validate(Component::REPAIR); + $this->assertEquals('yow', $component->BAR->getValue()); + + } + + function ruleData() { + + return [ + + [[], 2], + [['FOO'], 3], + [['BAR'], 1], + [['BAZ'], 1], + [['BAR','BAZ'], 0], + [['BAR','BAZ','ZIM',], 0], + [['BAR','BAZ','ZIM','GIR'], 0], + [['BAR','BAZ','ZIM','GIR','GIR'], 1], + + ]; + + } + +} + +class FakeComponent extends Component { + + function getValidationRules() { + + return [ + 'FOO' => '0', + 'BAR' => '1', + 'BAZ' => '+', + 'ZIM' => '*', + 'GIR' => '?', + ]; + + } + + function getDefaults() { + + return [ + 'BAR' => 'yow', + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/DateTimeParserTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/DateTimeParserTest.php new file mode 100644 index 00000000000..677c2893677 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/DateTimeParserTest.php @@ -0,0 +1,699 @@ +<?php + +namespace Sabre\VObject; + +use DateInterval; +use DateTimeImmutable; +use DateTimeZone; + +class DateTimeParserTest extends \PHPUnit_Framework_TestCase { + + function testParseICalendarDuration() { + + $this->assertEquals('+1 weeks', DateTimeParser::parseDuration('P1W', true)); + $this->assertEquals('+5 days', DateTimeParser::parseDuration('P5D', true)); + $this->assertEquals('+5 days 3 hours 50 minutes 12 seconds', DateTimeParser::parseDuration('P5DT3H50M12S', true)); + $this->assertEquals('-1 weeks 50 minutes', DateTimeParser::parseDuration('-P1WT50M', true)); + $this->assertEquals('+50 days 3 hours 2 seconds', DateTimeParser::parseDuration('+P50DT3H2S', true)); + $this->assertEquals('+0 seconds', DateTimeParser::parseDuration('+PT0S', true)); + $this->assertEquals(new DateInterval('PT0S'), DateTimeParser::parseDuration('PT0S')); + + } + + function testParseICalendarDurationDateInterval() { + + $expected = new DateInterval('P7D'); + $this->assertEquals($expected, DateTimeParser::parseDuration('P1W')); + $this->assertEquals($expected, DateTimeParser::parse('P1W')); + + $expected = new DateInterval('PT3M'); + $expected->invert = true; + $this->assertEquals($expected, DateTimeParser::parseDuration('-PT3M')); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testParseICalendarDurationFail() { + + DateTimeParser::parseDuration('P1X', true); + + } + + function testParseICalendarDateTime() { + + $dateTime = DateTimeParser::parseDateTime('20100316T141405'); + + $compare = new DateTimeImmutable('2010-03-16 14:14:05', new DateTimeZone('UTC')); + + $this->assertEquals($compare, $dateTime); + + } + + /** + * @depends testParseICalendarDateTime + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testParseICalendarDateTimeBadFormat() { + + $dateTime = DateTimeParser::parseDateTime('20100316T141405 '); + + } + + /** + * @depends testParseICalendarDateTime + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testParseICalendarDateTimeInvalidTime() { + + $dateTime = DateTimeParser::parseDateTime('20100316T251405'); + + } + + /** + * @depends testParseICalendarDateTime + */ + function testParseICalendarDateTimeUTC() { + + $dateTime = DateTimeParser::parseDateTime('20100316T141405Z'); + + $compare = new DateTimeImmutable('2010-03-16 14:14:05', new DateTimeZone('UTC')); + $this->assertEquals($compare, $dateTime); + + } + + /** + * @depends testParseICalendarDateTime + */ + function testParseICalendarDateTimeUTC2() { + + $dateTime = DateTimeParser::parseDateTime('20101211T160000Z'); + + $compare = new DateTimeImmutable('2010-12-11 16:00:00', new DateTimeZone('UTC')); + $this->assertEquals($compare, $dateTime); + + } + + /** + * @depends testParseICalendarDateTime + */ + function testParseICalendarDateTimeCustomTimeZone() { + + $dateTime = DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam')); + + $compare = new DateTimeImmutable('2010-03-16 14:14:05', new DateTimeZone('Europe/Amsterdam')); + $this->assertEquals($compare, $dateTime); + + } + + function testParseICalendarDate() { + + $dateTime = DateTimeParser::parseDate('20100316'); + + $expected = new DateTimeImmutable('2010-03-16 00:00:00', new DateTimeZone('UTC')); + + $this->assertEquals($expected, $dateTime); + + $dateTime = DateTimeParser::parse('20100316'); + $this->assertEquals($expected, $dateTime); + + } + + /** + * TCheck if a date with year > 4000 will not throw an exception. iOS seems to use 45001231 in yearly recurring events + */ + function testParseICalendarDateGreaterThan4000() { + + $dateTime = DateTimeParser::parseDate('45001231'); + + $expected = new DateTimeImmutable('4500-12-31 00:00:00', new DateTimeZone('UTC')); + + $this->assertEquals($expected, $dateTime); + + $dateTime = DateTimeParser::parse('45001231'); + $this->assertEquals($expected, $dateTime); + + } + + /** + * Check if a datetime with year > 4000 will not throw an exception. iOS seems to use 45001231T235959 in yearly recurring events + */ + function testParseICalendarDateTimeGreaterThan4000() { + + $dateTime = DateTimeParser::parseDateTime('45001231T235959'); + + $expected = new DateTimeImmutable('4500-12-31 23:59:59', new DateTimeZone('UTC')); + + $this->assertEquals($expected, $dateTime); + + $dateTime = DateTimeParser::parse('45001231T235959'); + $this->assertEquals($expected, $dateTime); + + } + + /** + * @depends testParseICalendarDate + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testParseICalendarDateBadFormat() { + + $dateTime = DateTimeParser::parseDate('20100316T141405'); + + } + + /** + * @depends testParseICalendarDate + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testParseICalendarDateInvalidDate() { + + $dateTime = DateTimeParser::parseDate('20101331'); + + } + + /** + * @dataProvider vcardDates + */ + function testVCardDate($input, $output) { + + $this->assertEquals( + $output, + DateTimeParser::parseVCardDateTime($input) + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testBadVCardDate() { + + DateTimeParser::parseVCardDateTime('1985---01'); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testBadVCardTime() { + + DateTimeParser::parseVCardTime('23:12:166'); + + } + + function vcardDates() { + + return [ + [ + "19961022T140000", + [ + "year" => 1996, + "month" => 10, + "date" => 22, + "hour" => 14, + "minute" => 00, + "second" => 00, + "timezone" => null + ], + ], + [ + "--1022T1400", + [ + "year" => null, + "month" => 10, + "date" => 22, + "hour" => 14, + "minute" => 00, + "second" => null, + "timezone" => null + ], + ], + [ + "---22T14", + [ + "year" => null, + "month" => null, + "date" => 22, + "hour" => 14, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "19850412", + [ + "year" => 1985, + "month" => 4, + "date" => 12, + "hour" => null, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "1985-04", + [ + "year" => 1985, + "month" => 04, + "date" => null, + "hour" => null, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "1985", + [ + "year" => 1985, + "month" => null, + "date" => null, + "hour" => null, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "--0412", + [ + "year" => null, + "month" => 4, + "date" => 12, + "hour" => null, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "---12", + [ + "year" => null, + "month" => null, + "date" => 12, + "hour" => null, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "T102200", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => 10, + "minute" => 22, + "second" => 0, + "timezone" => null + ], + ], + [ + "T1022", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => 10, + "minute" => 22, + "second" => null, + "timezone" => null + ], + ], + [ + "T10", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => 10, + "minute" => null, + "second" => null, + "timezone" => null + ], + ], + [ + "T-2200", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => null, + "minute" => 22, + "second" => 00, + "timezone" => null + ], + ], + [ + "T--00", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => null, + "minute" => null, + "second" => 00, + "timezone" => null + ], + ], + [ + "T102200Z", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => 10, + "minute" => 22, + "second" => 00, + "timezone" => 'Z' + ], + ], + [ + "T102200-0800", + [ + "year" => null, + "month" => null, + "date" => null, + "hour" => 10, + "minute" => 22, + "second" => 00, + "timezone" => '-0800' + ], + ], + + // extended format + [ + "2012-11-29T15:10:53Z", + [ + "year" => 2012, + "month" => 11, + "date" => 29, + "hour" => 15, + "minute" => 10, + "second" => 53, + "timezone" => 'Z' + ], + ], + + // with milliseconds + [ + "20121129T151053.123Z", + [ + "year" => 2012, + "month" => 11, + "date" => 29, + "hour" => 15, + "minute" => 10, + "second" => 53, + "timezone" => 'Z' + ], + ], + + // extended format with milliseconds + [ + "2012-11-29T15:10:53.123Z", + [ + "year" => 2012, + "month" => 11, + "date" => 29, + "hour" => 15, + "minute" => 10, + "second" => 53, + "timezone" => 'Z' + ], + ], + ]; + + } + + function testDateAndOrTime_DateWithYearMonthDay() { + + $this->assertDateAndOrTimeEqualsTo( + '20150128', + [ + 'year' => '2015', + 'month' => '01', + 'date' => '28' + ] + ); + + } + + function testDateAndOrTime_DateWithYearMonth() { + + $this->assertDateAndOrTimeEqualsTo( + '2015-01', + [ + 'year' => '2015', + 'month' => '01' + ] + ); + + } + + function testDateAndOrTime_DateWithMonth() { + + $this->assertDateAndOrTimeEqualsTo( + '--01', + [ + 'month' => '01' + ] + ); + + } + + function testDateAndOrTime_DateWithMonthDay() { + + $this->assertDateAndOrTimeEqualsTo( + '--0128', + [ + 'month' => '01', + 'date' => '28' + ] + ); + + } + + function testDateAndOrTime_DateWithDay() { + + $this->assertDateAndOrTimeEqualsTo( + '---28', + [ + 'date' => '28' + ] + ); + + } + + function testDateAndOrTime_TimeWithHour() { + + $this->assertDateAndOrTimeEqualsTo( + '13', + [ + 'hour' => '13' + ] + ); + + } + + function testDateAndOrTime_TimeWithHourMinute() { + + $this->assertDateAndOrTimeEqualsTo( + '1353', + [ + 'hour' => '13', + 'minute' => '53' + ] + ); + + } + + function testDateAndOrTime_TimeWithHourSecond() { + + $this->assertDateAndOrTimeEqualsTo( + '135301', + [ + 'hour' => '13', + 'minute' => '53', + 'second' => '01' + ] + + ); + + } + + function testDateAndOrTime_TimeWithMinute() { + + $this->assertDateAndOrTimeEqualsTo( + '-53', + [ + 'minute' => '53' + ] + ); + + } + + function testDateAndOrTime_TimeWithMinuteSecond() { + + $this->assertDateAndOrTimeEqualsTo( + '-5301', + [ + 'minute' => '53', + 'second' => '01' + ] + ); + + } + + function testDateAndOrTime_TimeWithSecond() { + + $this->assertTrue(true); + + /** + * This is unreachable due to a conflict between date and time pattern. + * This is an error in the specification, not in our implementation. + */ + } + + function testDateAndOrTime_TimeWithSecondZ() { + + $this->assertDateAndOrTimeEqualsTo( + '--01Z', + [ + 'second' => '01', + 'timezone' => 'Z' + ] + ); + + } + + function testDateAndOrTime_TimeWithSecondTZ() { + + $this->assertDateAndOrTimeEqualsTo( + '--01+1234', + [ + 'second' => '01', + 'timezone' => '+1234' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithYearMonthDayHour() { + + $this->assertDateAndOrTimeEqualsTo( + '20150128T13', + [ + 'year' => '2015', + 'month' => '01', + 'date' => '28', + 'hour' => '13' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithMonthDayHour() { + + $this->assertDateAndOrTimeEqualsTo( + '--0128T13', + [ + 'month' => '01', + 'date' => '28', + 'hour' => '13' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithDayHour() { + + $this->assertDateAndOrTimeEqualsTo( + '---28T13', + [ + 'date' => '28', + 'hour' => '13' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithDayHourMinute() { + + $this->assertDateAndOrTimeEqualsTo( + '---28T1353', + [ + 'date' => '28', + 'hour' => '13', + 'minute' => '53' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithDayHourMinuteSecond() { + + $this->assertDateAndOrTimeEqualsTo( + '---28T135301', + [ + 'date' => '28', + 'hour' => '13', + 'minute' => '53', + 'second' => '01' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithDayHourZ() { + + $this->assertDateAndOrTimeEqualsTo( + '---28T13Z', + [ + 'date' => '28', + 'hour' => '13', + 'timezone' => 'Z' + ] + ); + + } + + function testDateAndOrTime_DateTimeWithDayHourTZ() { + + $this->assertDateAndOrTimeEqualsTo( + '---28T13+1234', + [ + 'date' => '28', + 'hour' => '13', + 'timezone' => '+1234' + ] + ); + + } + + protected function assertDateAndOrTimeEqualsTo($date, $parts) { + + $this->assertSame( + DateTimeParser::parseVCardDateAndOrTime($date), + array_merge( + [ + 'year' => null, + 'month' => null, + 'date' => null, + 'hour' => null, + 'minute' => null, + 'second' => null, + 'timezone' => null + ], + $parts + ) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/DocumentTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/DocumentTest.php new file mode 100644 index 00000000000..f1730fdeab1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/DocumentTest.php @@ -0,0 +1,91 @@ +<?php + +namespace Sabre\VObject; + +class DocumentTest extends \PHPUnit_Framework_TestCase { + + function testGetDocumentType() { + + $doc = new MockDocument(); + $this->assertEquals(Document::UNKNOWN, $doc->getDocumentType()); + + } + + function testConstruct() { + + $doc = new MockDocument('VLIST'); + $this->assertEquals('VLIST', $doc->name); + + } + + function testCreateComponent() { + + $vcal = new Component\VCalendar([], false); + + $event = $vcal->createComponent('VEVENT'); + + $this->assertInstanceOf('Sabre\VObject\Component\VEvent', $event); + $vcal->add($event); + + $prop = $vcal->createProperty('X-PROP', '1234256', ['X-PARAM' => '3']); + $this->assertInstanceOf('Sabre\VObject\Property', $prop); + + $event->add($prop); + + unset( + $event->DTSTAMP, + $event->UID + ); + + $out = $vcal->serialize(); + $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nX-PROP;X-PARAM=3:1234256\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $out); + + } + + function testCreate() { + + $vcal = new Component\VCalendar([], false); + + $event = $vcal->create('VEVENT'); + $this->assertInstanceOf('Sabre\VObject\Component\VEvent', $event); + + $prop = $vcal->create('CALSCALE'); + $this->assertInstanceOf('Sabre\VObject\Property\Text', $prop); + + } + + function testGetClassNameForPropertyValue() { + + $vcal = new Component\VCalendar([], false); + $this->assertEquals('Sabre\\VObject\\Property\\Text', $vcal->getClassNameForPropertyValue('TEXT')); + $this->assertNull($vcal->getClassNameForPropertyValue('FOO')); + + } + + function testDestroy() { + + $vcal = new Component\VCalendar([], false); + $event = $vcal->createComponent('VEVENT'); + + $this->assertInstanceOf('Sabre\VObject\Component\VEvent', $event); + $vcal->add($event); + + $prop = $vcal->createProperty('X-PROP', '1234256', ['X-PARAM' => '3']); + + $event->add($prop); + + $this->assertEquals($event, $prop->parent); + + $vcal->destroy(); + + $this->assertNull($prop->parent); + + + } + +} + + +class MockDocument extends Document { + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ElementListTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ElementListTest.php new file mode 100644 index 00000000000..e63231133ad --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ElementListTest.php @@ -0,0 +1,33 @@ +<?php + +namespace Sabre\VObject; + +class ElementListTest extends \PHPUnit_Framework_TestCase { + + function testIterate() { + + $cal = new Component\VCalendar(); + $sub = $cal->createComponent('VEVENT'); + + $elems = [ + $sub, + clone $sub, + clone $sub + ]; + + $elemList = new ElementList($elems); + + $count = 0; + foreach ($elemList as $key => $subcomponent) { + + $count++; + $this->assertInstanceOf('Sabre\\VObject\\Component', $subcomponent); + + } + $this->assertEquals(3, $count); + $this->assertEquals(2, $key); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmClientTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmClientTest.php new file mode 100644 index 00000000000..5743d48d815 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmClientTest.php @@ -0,0 +1,56 @@ +<?php + +namespace Sabre\VObject; + +use DateTimeImmutable; + +class EmClientTest extends \PHPUnit_Framework_TestCase { + + function testParseTz() { + + $str = 'BEGIN:VCALENDAR +X-WR-CALNAME:Blackhawks Schedule 2011-12 +X-APPLE-CALENDAR-COLOR:#E51717 +X-WR-TIMEZONE:America/Chicago +CALSCALE:GREGORIAN +PRODID:-//eM Client/4.0.13961.0 +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +DTSTART:20070311T020000 +TZNAME:CDT +TZOFFSETTO:-0500 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +DTSTART:20071104T020000 +TZNAME:CST +TZOFFSETTO:-0600 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20110624T181236Z +UID:be3bbfff-96e8-4c66-9908-ab791a62231d +DTEND;TZID="America/Chicago":20111008T223000 +TRANSP:OPAQUE +SUMMARY:Stars @ Blackhawks (Home Opener) +DTSTART;TZID="America/Chicago":20111008T193000 +DTSTAMP:20120330T013232Z +SEQUENCE:2 +X-MICROSOFT-CDO-BUSYSTATUS:BUSY +LAST-MODIFIED:20120330T013237Z +CLASS:PUBLIC +END:VEVENT +END:VCALENDAR'; + + $vObject = Reader::read($str); + $dt = $vObject->VEVENT->DTSTART->getDateTime(); + $this->assertEquals(new DateTimeImmutable('2011-10-08 19:30:00', new \DateTimeZone('America/Chicago')), $dt); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmptyParameterTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmptyParameterTest.php new file mode 100644 index 00000000000..a9e9fcc5cd9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmptyParameterTest.php @@ -0,0 +1,69 @@ +<?php + +namespace Sabre\VObject; + +class EmptyParameterTest extends \PHPUnit_Framework_TestCase { + + function testRead() { + + $input = <<<VCF +BEGIN:VCARD +VERSION:2.1 +N:Doe;Jon;;; +FN:Jon Doe +EMAIL;X-INTERN:foo@example.org +UID:foo +END:VCARD +VCF; + + $vcard = Reader::read($input); + + $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard); + $vcard = $vcard->convert(\Sabre\VObject\Document::VCARD30); + $vcard = $vcard->serialize(); + + $converted = Reader::read($vcard); + $converted->validate(); + + $this->assertTrue(isset($converted->EMAIL['X-INTERN'])); + + $version = Version::VERSION; + + $expected = <<<VCF +BEGIN:VCARD +VERSION:3.0 +PRODID:-//Sabre//Sabre VObject $version//EN +N:Doe;Jon;;; +FN:Jon Doe +EMAIL;X-INTERN=:foo@example.org +UID:foo +END:VCARD + +VCF; + + $this->assertEquals($expected, str_replace("\r", "", $vcard)); + + } + + function testVCard21Parameter() { + + $vcard = new Component\VCard([], false); + $vcard->VERSION = '2.1'; + $vcard->PHOTO = 'random_stuff'; + $vcard->PHOTO->add(null, 'BASE64'); + $vcard->UID = 'foo-bar'; + + $result = $vcard->serialize(); + $expected = [ + "BEGIN:VCARD", + "VERSION:2.1", + "PHOTO;BASE64:" . base64_encode('random_stuff'), + "UID:foo-bar", + "END:VCARD", + "", + ]; + + $this->assertEquals(implode("\r\n", $expected), $result); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmptyValueIssueTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmptyValueIssueTest.php new file mode 100644 index 00000000000..7a34944992c --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/EmptyValueIssueTest.php @@ -0,0 +1,30 @@ +<?php + +namespace Sabre\VObject; + +/** + * This test is written for Issue 68: + * + * https://github.com/fruux/sabre-vobject/issues/68 + */ +class EmptyValueIssueTest extends \PHPUnit_Framework_TestCase { + + function testDecodeValue() { + + $input = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +DESCRIPTION:This is a descpription\\nwith a linebreak and a \\; \\, and : +END:VEVENT +END:VCALENDAR +ICS; + + $vobj = Reader::read($input); + + // Before this bug was fixed, getValue() would return nothing. + $this->assertEquals("This is a descpription\nwith a linebreak and a ; , and :", $vobj->VEVENT->DESCRIPTION->getValue()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/FreeBusyDataTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/FreeBusyDataTest.php new file mode 100644 index 00000000000..9b5f541b9f6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/FreeBusyDataTest.php @@ -0,0 +1,318 @@ +<?php + +namespace Sabre\VObject; + +class FreeBusyDataTest extends \PHPUnit_Framework_TestCase { + + function testGetData() { + + $fb = new FreeBusyData(100, 200); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 200, + 'type' => 'FREE', + ] + ], + $fb->getData() + ); + + } + + /** + * @depends testGetData + */ + function testAddBeginning() { + + $fb = new FreeBusyData(100, 200); + + // Overwriting the first half + $fb->add(100, 150, 'BUSY'); + + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 150, + 'type' => 'BUSY', + ], + [ + 'start' => 150, + 'end' => 200, + 'type' => 'FREE', + ] + ], + $fb->getData() + ); + + // Overwriting the first half again + $fb->add(100, 150, 'BUSY-TENTATIVE'); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 150, + 'type' => 'BUSY-TENTATIVE', + ], + [ + 'start' => 150, + 'end' => 200, + 'type' => 'FREE', + ] + ], + $fb->getData() + ); + + } + + /** + * @depends testAddBeginning + */ + function testAddEnd() { + + $fb = new FreeBusyData(100, 200); + + // Overwriting the first half + $fb->add(150, 200, 'BUSY'); + + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 150, + 'type' => 'FREE', + ], + [ + 'start' => 150, + 'end' => 200, + 'type' => 'BUSY', + ], + ], + $fb->getData() + ); + + + } + + /** + * @depends testAddEnd + */ + function testAddMiddle() { + + $fb = new FreeBusyData(100, 200); + + // Overwriting the first half + $fb->add(150, 160, 'BUSY'); + + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 150, + 'type' => 'FREE', + ], + [ + 'start' => 150, + 'end' => 160, + 'type' => 'BUSY', + ], + [ + 'start' => 160, + 'end' => 200, + 'type' => 'FREE', + ], + ], + $fb->getData() + ); + + } + + /** + * @depends testAddMiddle + */ + function testAddMultiple() { + + $fb = new FreeBusyData(100, 200); + + $fb->add(110, 120, 'BUSY'); + $fb->add(130, 140, 'BUSY'); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 110, + 'type' => 'FREE', + ], + [ + 'start' => 110, + 'end' => 120, + 'type' => 'BUSY', + ], + [ + 'start' => 120, + 'end' => 130, + 'type' => 'FREE', + ], + [ + 'start' => 130, + 'end' => 140, + 'type' => 'BUSY', + ], + [ + 'start' => 140, + 'end' => 200, + 'type' => 'FREE', + ], + ], + $fb->getData() + ); + + } + + /** + * @depends testAddMultiple + */ + function testAddMultipleOverlap() { + + $fb = new FreeBusyData(100, 200); + + $fb->add(110, 120, 'BUSY'); + $fb->add(130, 140, 'BUSY'); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 110, + 'type' => 'FREE', + ], + [ + 'start' => 110, + 'end' => 120, + 'type' => 'BUSY', + ], + [ + 'start' => 120, + 'end' => 130, + 'type' => 'FREE', + ], + [ + 'start' => 130, + 'end' => 140, + 'type' => 'BUSY', + ], + [ + 'start' => 140, + 'end' => 200, + 'type' => 'FREE', + ], + ], + $fb->getData() + ); + + $fb->add(115, 135, 'BUSY-TENTATIVE'); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 110, + 'type' => 'FREE', + ], + [ + 'start' => 110, + 'end' => 115, + 'type' => 'BUSY', + ], + [ + 'start' => 115, + 'end' => 135, + 'type' => 'BUSY-TENTATIVE', + ], + [ + 'start' => 135, + 'end' => 140, + 'type' => 'BUSY', + ], + [ + 'start' => 140, + 'end' => 200, + 'type' => 'FREE', + ], + ], + $fb->getData() + ); + } + + /** + * @depends testAddMultipleOverlap + */ + function testAddMultipleOverlapAndMerge() { + + $fb = new FreeBusyData(100, 200); + + $fb->add(110, 120, 'BUSY'); + $fb->add(130, 140, 'BUSY'); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 110, + 'type' => 'FREE', + ], + [ + 'start' => 110, + 'end' => 120, + 'type' => 'BUSY', + ], + [ + 'start' => 120, + 'end' => 130, + 'type' => 'FREE', + ], + [ + 'start' => 130, + 'end' => 140, + 'type' => 'BUSY', + ], + [ + 'start' => 140, + 'end' => 200, + 'type' => 'FREE', + ], + ], + $fb->getData() + ); + + $fb->add(115, 135, 'BUSY'); + + $this->assertEquals( + [ + [ + 'start' => 100, + 'end' => 110, + 'type' => 'FREE', + ], + [ + 'start' => 110, + 'end' => 140, + 'type' => 'BUSY', + ], + [ + 'start' => 140, + 'end' => 200, + 'type' => 'FREE', + ], + ], + $fb->getData() + ); + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/FreeBusyGeneratorTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/FreeBusyGeneratorTest.php new file mode 100644 index 00000000000..70e83ab2f30 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/FreeBusyGeneratorTest.php @@ -0,0 +1,751 @@ +<?php + +namespace Sabre\VObject; + +class FreeBusyGeneratorTest extends \PHPUnit_Framework_TestCase { + + use PHPUnitAssertions; + + function testGeneratorBaseObject() { + + $obj = new Component\VCalendar(); + $obj->METHOD = 'PUBLISH'; + + $gen = new FreeBusyGenerator(); + $gen->setObjects([]); + $gen->setBaseObject($obj); + + $result = $gen->getResult(); + + $this->assertEquals('PUBLISH', $result->METHOD->getValue()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testInvalidArg() { + + $gen = new FreeBusyGenerator( + new \DateTime('2012-01-01'), + new \DateTime('2012-12-31'), + new \StdClass() + ); + + } + + /** + * This function takes a list of objects (icalendar objects), and turns + * them into a freebusy report. + * + * Then it takes the expected output and compares it to what we actually + * got. + * + * It only generates the freebusy report for the following time-range: + * 2011-01-01 11:00:00 until 2011-01-03 11:11:11 + * + * @param string $expected + * @param array $input + * @param string|null $timeZone + * @param string $vavailability + * @return void + */ + function assertFreeBusyReport($expected, $input, $timeZone = null, $vavailability = null) { + + $gen = new FreeBusyGenerator( + new \DateTime('20110101T110000Z', new \DateTimeZone('UTC')), + new \DateTime('20110103T110000Z', new \DateTimeZone('UTC')), + $input, + $timeZone + ); + + if ($vavailability) { + if (is_string($vavailability)) { + $vavailability = Reader::read($vavailability); + } + $gen->setVAvailability($vavailability); + } + + $output = $gen->getResult(); + + // Removing DTSTAMP because it changes every time. + unset($output->VFREEBUSY->DTSTAMP); + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VFREEBUSY +DTSTART:20110101T110000Z +DTEND:20110103T110000Z +$expected +END:VFREEBUSY +END:VCALENDAR +ICS; + + $this->assertVObjectEqualsVObject($expected, $output); + + } + + function testSimple() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T120000Z/20110101T130000Z", + $blob + ); + + } + + function testSource() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + $h = fopen('php://memory', 'r+'); + fwrite($h, $blob); + rewind($h); + + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T120000Z/20110101T130000Z", + $h + ); + + } + + /** + * Testing TRANSP:OPAQUE + */ + function testOpaque() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar2 +TRANSP:OPAQUE +DTSTART:20110101T130000Z +DTEND:20110101T140000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T130000Z/20110101T140000Z", + $blob + ); + + } + + /** + * Testing TRANSP:TRANSPARENT + */ + function testTransparent() { + + // transparent, hidden + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar3 +TRANSP:TRANSPARENT +DTSTART:20110101T140000Z +DTEND:20110101T150000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "", + $blob + ); + + } + + /** + * Testing STATUS:CANCELLED + */ + function testCancelled() { + + // transparent, hidden + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar4 +STATUS:CANCELLED +DTSTART:20110101T160000Z +DTEND:20110101T170000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "", + $blob + ); + + } + + /** + * Testing STATUS:TENTATIVE + */ + function testTentative() { + + // tentative, shows up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar5 +STATUS:TENTATIVE +DTSTART:20110101T180000Z +DTEND:20110101T190000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + 'FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T180000Z/20110101T190000Z', + $blob + ); + + } + + /** + * Testing an event that falls outside of the report time-range. + */ + function testOutsideTimeRange() { + + // outside of time-range, hidden + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar6 +DTSTART:20110101T090000Z +DTEND:20110101T100000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + '', + $blob + ); + + } + + /** + * Testing an event that falls outside of the report time-range. + */ + function testOutsideTimeRange2() { + + // outside of time-range, hidden + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar7 +DTSTART:20110104T090000Z +DTEND:20110104T100000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + '', + $blob + ); + + } + + /** + * Testing an event that uses DURATION + */ + function testDuration() { + + // using duration, shows up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar8 +DTSTART:20110101T190000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + 'FREEBUSY:20110101T190000Z/20110101T200000Z', + $blob + ); + + } + + /** + * Testing an all-day event + */ + function testAllDay() { + + // Day-long event, shows up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar9 +DTSTART;VALUE=DATE:20110102 +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + 'FREEBUSY:20110102T000000Z/20110103T000000Z', + $blob + ); + + } + + /** + * Testing an event that has no end or duration. + */ + function testNoDuration() { + + // No duration, does not show up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar10 +DTSTART:20110101T200000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + '', + $blob + ); + + } + + /** + * Testing feeding the freebusy generator an object instead of a string. + */ + function testObject() { + + // encoded as object, shows up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar11 +DTSTART:20110101T210000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + 'FREEBUSY:20110101T210000Z/20110101T220000Z', + Reader::read($blob) + ); + + + } + + /** + * Testing feeding VFREEBUSY objects instead of VEVENT + */ + function testVFreeBusy() { + + // Freebusy. Some parts show up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VFREEBUSY +FREEBUSY:20110103T010000Z/20110103T020000Z +FREEBUSY;FBTYPE=FREE:20110103T020000Z/20110103T030000Z +FREEBUSY:20110103T030000Z/20110103T040000Z,20110103T040000Z/20110103T050000Z +FREEBUSY:20120101T000000Z/20120101T010000Z +FREEBUSY:20110103T050000Z/PT1H +END:VFREEBUSY +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110103T010000Z/20110103T020000Z\n" . + 'FREEBUSY:20110103T030000Z/20110103T060000Z', + $blob + ); + + } + + function testYearlyRecurrence() { + + // Yearly recurrence rule, shows up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar13 +DTSTART:20100101T220000Z +DTEND:20100101T230000Z +RRULE:FREQ=YEARLY +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + 'FREEBUSY:20110101T220000Z/20110101T230000Z', + $blob + ); + + } + + function testYearlyRecurrenceDuration() { + + // Yearly recurrence rule + duration, shows up + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar14 +DTSTART:20100101T230000Z +DURATION:PT1H +RRULE:FREQ=YEARLY +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + 'FREEBUSY:20110101T230000Z/20110102T000000Z', + $blob + ); + + } + + function testFloatingTime() { + + // Floating time, no timezone + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20110101T120000 +DTEND:20110101T130000 +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T120000Z/20110101T130000Z", + $blob + ); + + } + + function testFloatingTimeReferenceTimeZone() { + + // Floating time + reference timezone + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20110101T120000 +DTEND:20110101T130000 +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T170000Z/20110101T180000Z", + $blob, + new \DateTimeZone('America/Toronto') + ); + + } + + function testAllDay2() { + + // All-day event, slightly outside of the VFREEBUSY range. + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART;VALUE=DATE:20110101 +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T110000Z/20110102T000000Z", + $blob + ); + + } + + function testAllDayReferenceTimeZone() { + + // All-day event + reference timezone + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART;VALUE=DATE:20110101 +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T110000Z/20110102T050000Z", + $blob, + new \DateTimeZone('America/Toronto') + ); + + } + + function testNoValidInstances() { + + // Recurrence rule with no valid instances + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20110101T100000Z +DTEND:20110103T120000Z +RRULE:FREQ=WEEKLY;COUNT=1 +EXDATE:20110101T100000Z +END:VEVENT +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "", + $blob + ); + + } + + /** + * This VAVAILABILITY object overlaps with the time-range, but we're just + * busy the entire time. + */ + function testVAvailabilitySimple() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:lalala +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $vavail = <<<ICS +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20110101T000000Z +DTEND:20120101T000000Z +BEGIN:AVAILABLE +DTSTART:20110101T000000Z +DTEND:20110101T010000Z +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T110000Z/20110101T120000Z\n" . + "FREEBUSY:20110101T120000Z/20110101T130000Z\n" . + "FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T130000Z/20110103T110000Z", + $blob, + null, + $vavail + ); + + } + + /** + * This VAVAILABILITY object does not overlap at all with the freebusy + * report, so it should be ignored. + */ + function testVAvailabilityIrrelevant() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:lalala +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $vavail = <<<ICS +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20150101T000000Z +DTEND:20160101T000000Z +BEGIN:AVAILABLE +DTSTART:20150101T000000Z +DTEND:20150101T010000Z +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T120000Z/20110101T130000Z", + $blob, + null, + $vavail + ); + + } + + /** + * This VAVAILABILITY object has a 9am-5pm AVAILABLE object for office + * hours. + */ + function testVAvailabilityOfficeHours() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:lalala +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $vavail = <<<ICS +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20100101T000000Z +DTEND:20120101T000000Z +BUSYTYPE:BUSY-TENTATIVE +BEGIN:AVAILABLE +DTSTART:20101213T090000Z +DTEND:20101213T170000Z +RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR +END:AVAILABLE +END:VAVAILABILITY +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T110000Z/20110101T120000Z\n" . + "FREEBUSY:20110101T120000Z/20110101T130000Z\n" . + "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T130000Z/20110103T090000Z\n", + $blob, + null, + $vavail + ); + + } + + /** + * This test has the same office hours, but has a vacation blocked off for + * the relevant time, using a higher priority. (lower number). + */ + function testVAvailabilityOfficeHoursVacation() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:lalala +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $vavail = <<<ICS +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20100101T000000Z +DTEND:20120101T000000Z +BUSYTYPE:BUSY-TENTATIVE +PRIORITY:2 +BEGIN:AVAILABLE +DTSTART:20101213T090000Z +DTEND:20101213T170000Z +RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR +END:AVAILABLE +END:VAVAILABILITY +BEGIN:VAVAILABILITY +PRIORITY:1 +DTSTART:20101214T000000Z +DTEND:20110107T000000Z +BUSYTYPE:BUSY +END:VAVAILABILITY +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY:20110101T110000Z/20110103T110000Z", + $blob, + null, + $vavail + ); + + } + + /** + * This test has the same input as the last, except somebody mixed up the + * PRIORITY values. + * + * The end-result is that the vacation VAVAILABILITY is completely ignored. + */ + function testVAvailabilityOfficeHoursVacation2() { + + $blob = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:lalala +DTSTART:20110101T120000Z +DTEND:20110101T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $vavail = <<<ICS +BEGIN:VCALENDAR +BEGIN:VAVAILABILITY +DTSTART:20100101T000000Z +DTEND:20120101T000000Z +BUSYTYPE:BUSY-TENTATIVE +PRIORITY:1 +BEGIN:AVAILABLE +DTSTART:20101213T090000Z +DTEND:20101213T170000Z +RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR +END:AVAILABLE +END:VAVAILABILITY +BEGIN:VAVAILABILITY +PRIORITY:2 +DTSTART:20101214T000000Z +DTEND:20110107T000000Z +BUSYTYPE:BUSY +END:VAVAILABILITY +END:VCALENDAR +ICS; + + $this->assertFreeBusyReport( + "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T110000Z/20110101T120000Z\n" . + "FREEBUSY:20110101T120000Z/20110101T130000Z\n" . + "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T130000Z/20110103T090000Z\n", + $blob, + null, + $vavail + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/GoogleColonEscapingTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/GoogleColonEscapingTest.php new file mode 100644 index 00000000000..ee37aa8875e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/GoogleColonEscapingTest.php @@ -0,0 +1,31 @@ +<?php + +namespace Sabre\VObject; + +/** + * Google produces vcards with a weird escaping of urls. + * + * VObject will provide a workaround for this, so end-user still get expected + * values. + */ +class GoogleColonEscapingTest extends \PHPUnit_Framework_TestCase { + + function testDecode() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +FN:Evert Pot +N:Pot;Evert;;; +EMAIL;TYPE=INTERNET;TYPE=WORK:evert@fruux.com +BDAY:1985-04-07 +item7.URL:http\://www.rooftopsolutions.nl/ +END:VCARD +VCF; + + $vobj = Reader::read($vcard); + $this->assertEquals('http://www.rooftopsolutions.nl/', $vobj->URL->getValue()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ICalendar/AttachParseTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ICalendar/AttachParseTest.php new file mode 100644 index 00000000000..0c4fc8790e5 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ICalendar/AttachParseTest.php @@ -0,0 +1,31 @@ +<?php + +namespace Sabre\VObject\ICalendar; + +use Sabre\VObject\Reader; + +class AttachParseTest extends \PHPUnit_Framework_TestCase { + + /** + * See issue #128 for more info. + */ + function testParseAttach() { + + $vcal = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +ATTACH;FMTTYPE=application/postscript:ftp://example.com/pub/reports/r-960812.ps +END:VEVENT +END:VCALENDAR +ICS; + + $vcal = Reader::read($vcal); + $prop = $vcal->VEVENT->ATTACH; + + $this->assertInstanceOf('Sabre\\VObject\\Property\\URI', $prop); + $this->assertEquals('ftp://example.com/pub/reports/r-960812.ps', $prop->getValue()); + + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerAttendeeReplyTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerAttendeeReplyTest.php new file mode 100644 index 00000000000..9519ed36828 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerAttendeeReplyTest.php @@ -0,0 +1,1146 @@ +<?php + +namespace Sabre\VObject\ITip; + +class BrokerAttendeeReplyTest extends BrokerTester { + + function testAccepted() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SUMMARY:B-day party +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SUMMARY:B-day party +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140716T120000Z +SUMMARY:B-day party +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testRecurringReply() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140724T120000Z +SUMMARY:Daily sprint +RRULE;FREQ=DAILY +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org +DTSTART:20140724T120000Z +SUMMARY:Daily sprint +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART:20140726T120000Z +RECURRENCE-ID:20140726T120000Z +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +DTSTART:20140724T120000Z +RECURRENCE-ID:20140724T120000Z +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org +DTSTART:20140728T120000Z +RECURRENCE-ID:20140728T120000Z +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART:20140729T120000Z +RECURRENCE-ID:20140729T120000Z +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +DTSTART:20140725T120000Z +RECURRENCE-ID:20140725T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140726T120000Z +SUMMARY:Daily sprint +RECURRENCE-ID:20140726T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140724T120000Z +SUMMARY:Daily sprint +RECURRENCE-ID:20140724T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140728T120000Z +SUMMARY:Daily sprint +RECURRENCE-ID:20140728T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140729T120000Z +SUMMARY:Daily sprint +RECURRENCE-ID:20140729T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140725T120000Z +SUMMARY:Daily sprint +RECURRENCE-ID:20140725T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testRecurringAllDay() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140724 +RRULE;FREQ=DAILY +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140724 +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140726 +RECURRENCE-ID;VALUE=DATE:20140726 +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140724 +RECURRENCE-ID;VALUE=DATE:20140724 +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140728 +RECURRENCE-ID;VALUE=DATE:20140728 +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140729 +RECURRENCE-ID;VALUE=DATE:20140729 +END:VEVENT +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140725 +RECURRENCE-ID;VALUE=DATE:20140725 +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140726 +RECURRENCE-ID;VALUE=DATE:20140726 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140724 +RECURRENCE-ID;VALUE=DATE:20140724 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140728 +RECURRENCE-ID;VALUE=DATE:20140728 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=TENTATIVE;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140729 +RECURRENCE-ID;VALUE=DATE:20140729 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140725 +RECURRENCE-ID;VALUE=DATE:20140725 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testNoChange() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $expected = []; + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testNoChangeForceSend() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;SCHEDULE-FORCE-SEND=REPLY;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140716T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=NEEDS-ACTION;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + ] + + ]; + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testNoRelevantAttendee() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $expected = []; + $this->parse($oldMessage, $newMessage, $expected); + + } + + /** + * In this test, an event exists in an attendees calendar. The event + * is recurring, and the attendee deletes 1 instance of the event. + * This instance shows up in EXDATE + * + * This should automatically generate a DECLINED message for that + * specific instance. + */ + function testCreateReplyByException() { + + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140811T200000Z +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140811T200000Z +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +EXDATE:20140818T200000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => null, + 'recipient' => 'mailto:organizer@example.org', + 'recipientName' => null, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140818T200000Z +RECURRENCE-ID:20140818T200000Z +ORGANIZER:mailto:organizer@example.org +ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + $this->parse($oldMessage, $newMessage, $expected); + + } + + /** + * This test is identical to the last, but now we're working with + * timezones. + * + * @depends testCreateReplyByException + */ + function testCreateReplyByExceptionTz() { + + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART;TZID=America/Toronto:20140811T200000 +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART;TZID=America/Toronto:20140811T200000 +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +EXDATE;TZID=America/Toronto:20140818T200000 +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => null, + 'recipient' => 'mailto:organizer@example.org', + 'recipientName' => null, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;TZID=America/Toronto:20140818T200000 +RECURRENCE-ID;TZID=America/Toronto:20140818T200000 +ORGANIZER:mailto:organizer@example.org +ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + $this->parse($oldMessage, $newMessage, $expected); + + } + + /** + * @depends testCreateReplyByException + */ + function testCreateReplyByExceptionAllDay() { + + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SUMMARY:Weekly meeting +UID:foobar +SEQUENCE:1 +DTSTART;VALUE=DATE:20140811 +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SUMMARY:Weekly meeting +UID:foobar +SEQUENCE:1 +DTSTART;VALUE=DATE:20140811 +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +EXDATE;VALUE=DATE:20140818 +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => null, + 'recipient' => 'mailto:organizer@example.org', + 'recipientName' => null, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140818 +SUMMARY:Weekly meeting +RECURRENCE-ID;VALUE=DATE:20140818 +ORGANIZER:mailto:organizer@example.org +ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testDeclined() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140716T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testDeclinedCancelledEvent() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +STATUS:CANCELLED +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +STATUS:CANCELLED +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = []; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + /** + * In this test, a new exception is created by an attendee as well. + * + * Except in this case, there was already an overridden event, and the + * overridden event was marked as cancelled by the attendee. + * + * For any other attendence status, the new status would have been + * declined, but for this, no message should we sent. + */ + function testDontCreateReplyWhenEventWasDeclined() { + + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140811T200000Z +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +END:VEVENT +BEGIN:VEVENT +RECURRENCE-ID:20140818T200000Z +UID:foobar +SEQUENCE:1 +DTSTART:20140818T200000Z +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE;PARTSTAT=DECLINED:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140811T200000Z +RRULE:FREQ=WEEKLY +ORGANIZER:mailto:organizer@example.org +ATTENDEE:mailto:one@example.org +EXDATE:20140818T200000Z +END:VEVENT +END:VCALENDAR +ICS; + + $expected = []; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testScheduleAgentOnOrganizer() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;SCHEDULE-AGENT=CLIENT;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = []; + $this->parse($oldMessage, $newMessage, $expected); + + } + + function testAcceptedAllDay() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140716 +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART;VALUE=DATE:20140716 +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART;VALUE=DATE:20140716 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + /** + * This function tests an attendee updating their status to an event where + * they don't have the master event of. + * + * This is possible in cases an organizer created a recurring event, and + * invited an attendee for one instance of the event. + */ + function testReplyNoMasterEvent() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +RECURRENCE-ID:20140724T120000Z +DTSTART:20140724T120000Z +SUMMARY:Daily sprint +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +RECURRENCE-ID:20140724T120000Z +DTSTART:20140724T120000Z +SUMMARY:Daily sprint +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140724T120000Z +SUMMARY:Daily sprint +RECURRENCE-ID:20140724T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } + + /** + * A party crasher is an attendee that accepted an event, but was not in + * any original invite. + * + * @depends testAccepted + */ + function testPartyCrasher() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SUMMARY:B-day party +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +DTSTART:20140716T120000Z +RRULE:FREQ=DAILY +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140717T120000Z +SUMMARY:B-day party +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +DTSTART:20140717T120000Z +RRULE:FREQ=DAILY +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SUMMARY:B-day party +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +DTSTART:20140716T120000Z +RRULE:FREQ=DAILY +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140717T120000Z +SUMMARY:B-day party +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +DTSTART:20140717T120000Z +RRULE:FREQ=DAILY +END:VEVENT +END:VCALENDAR +ICS; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140717T120000Z +SUMMARY:B-day party +RECURRENCE-ID:20140717T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=ACCEPTED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR + +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerDeleteEventTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerDeleteEventTest.php new file mode 100644 index 00000000000..935c451fe76 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerDeleteEventTest.php @@ -0,0 +1,344 @@ +<?php + +namespace Sabre\VObject\ITip; + +class BrokerDeleteEventTest extends BrokerTester { + + function testOrganizerDeleteWithDtend() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = null; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + ], + + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testOrganizerDeleteWithDuration() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = null; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DURATION:PT1H +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + ], + + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DURATION:PT1H +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testAttendeeDeleteWithDtend() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = null; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:one@example.org'); + + + } + + function testAttendeeReplyWithDuration() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = null; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REPLY', + 'component' => 'VEVENT', + 'sender' => 'mailto:one@example.org', + 'senderName' => 'One', + 'recipient' => 'mailto:strunk@example.org', + 'recipientName' => 'Strunk', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140716T120000Z +DURATION:PT1H +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;PARTSTAT=DECLINED;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:one@example.org'); + + + } + + function testAttendeeDeleteCancelledEvent() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +STATUS:CANCELLED +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = null; + + $expected = []; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:one@example.org'); + + + } + + function testNoCalendar() { + + $this->parse(null, null, [], 'mailto:one@example.org'); + + } + + function testVTodo() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VTODO +UID:foobar +SEQUENCE:1 +END:VTODO +END:VCALENDAR +ICS; + $this->parse($oldMessage, null, [], 'mailto:one@example.org'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerNewEventTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerNewEventTest.php new file mode 100644 index 00000000000..05cf452a85c --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerNewEventTest.php @@ -0,0 +1,496 @@ +<?php + +namespace Sabre\VObject\ITip; + +class BrokerNewEventTest extends BrokerTester { + + function testNoAttendee() { + + $message = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20140811T220000Z +DTEND:20140811T230000Z +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->parse(null, $message, []); + + } + + function testVTODO() { + + $message = <<<ICS +BEGIN:VCALENDAR +BEGIN:VTODO +UID:foobar +END:VTODO +END:VCALENDAR +ICS; + + $result = $this->parse(null, $message, []); + + } + + function testSimpleInvite() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTSTART:20140811T220000Z +DTEND:20140811T230000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=White:mailto:white@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $expectedMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +DTSTART:20140811T220000Z +DTEND:20140811T230000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=White;PARTSTAT=NEEDS-ACTION:mailto:white@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:white@example.org', + 'recipientName' => 'White', + 'message' => $expectedMessage, + ], + ]; + + $this->parse(null, $message, $expected, 'mailto:strunk@example.org'); + + } + + /** + * @expectedException \Sabre\VObject\ITip\ITipException + */ + function testBrokenEventUIDMisMatch() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=White:mailto:white@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=White:mailto:white@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $this->parse(null, $message, [], 'mailto:strunk@example.org'); + + } + /** + * @expectedException \Sabre\VObject\ITip\ITipException + */ + function testBrokenEventOrganizerMisMatch() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=White:mailto:white@example.org +END:VEVENT +BEGIN:VEVENT +UID:foobar +ORGANIZER:mailto:foo@example.org +ATTENDEE;CN=White:mailto:white@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $this->parse(null, $message, [], 'mailto:strunk@example.org'); + + } + + function testRecurrenceInvite() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DURATION:PT1H +RRULE:FREQ=DAILY +EXDATE:20140717T120000Z +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +DTSTART:20140716T120000Z +DURATION:PT1H +RRULE:FREQ=DAILY +EXDATE:20140717T120000Z,20140718T120000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +DTSTART:20140716T120000Z +DURATION:PT1H +RRULE:FREQ=DAILY +EXDATE:20140717T120000Z +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:three@example.org', + 'recipientName' => 'Three', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DURATION:PT1H +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse(null, $message, $expected, 'mailto:strunk@example.org'); + + } + + function testRecurrenceInvite2() { + + // This method tests a nearly identical path, but in this case the + // master event does not have an EXDATE. + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +RRULE:FREQ=DAILY +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DTEND:20140718T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +RRULE:FREQ=DAILY +EXDATE:20140718T120000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +RRULE:FREQ=DAILY +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DTEND:20140718T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:three@example.org', + 'recipientName' => 'Three', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DTEND:20140718T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse(null, $message, $expected, 'mailto:strunk@example.org'); + + } + + function testScheduleAgentClient() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DTSTART:20140811T220000Z +DTEND:20140811T230000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=White;SCHEDULE-AGENT=CLIENT:mailto:white@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $this->parse(null, $message, [], 'mailto:strunk@example.org'); + + } + + /** + * @expectedException Sabre\VObject\ITip\ITipException + */ + function testMultipleUID() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +RRULE:FREQ=DAILY +END:VEVENT +BEGIN:VEVENT +UID:foobar2 +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DTEND:20140718T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $this->parse(null, $message, [], 'mailto:strunk@example.org'); + + } + + /** + * @expectedException Sabre\VObject\ITip\SameOrganizerForAllComponentsException + */ + function testChangingOrganizers() { + + $message = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +RRULE:FREQ=DAILY +END:VEVENT +BEGIN:VEVENT +UID:foobar +RECURRENCE-ID:20140718T120000Z +ORGANIZER;CN=Strunk:mailto:ew@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140718T120000Z +DTEND:20140718T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $this->parse(null, $message, [], 'mailto:strunk@example.org'); + + } + function testNoOrganizerHasAttendee() { + + $message = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +DTSTART:20140811T220000Z +DTEND:20140811T230000Z +ATTENDEE;CN=Two:mailto:two@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $this->parse(null, $message, [], 'mailto:strunk@example.org'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerProcessMessageTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerProcessMessageTest.php new file mode 100644 index 00000000000..691574a8991 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerProcessMessageTest.php @@ -0,0 +1,164 @@ +<?php + +namespace Sabre\VObject\ITip; + +class BrokerProcessMessageTest extends BrokerTester { + + function testRequestNew() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REQUEST +BEGIN:VEVENT +SEQUENCE:1 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:1 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, null, $expected); + + } + + function testRequestUpdate() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REQUEST +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +SEQUENCE:1 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testCancel() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:CANCEL +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +SEQUENCE:1 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +BEGIN:VEVENT +UID:foobar +STATUS:CANCELLED +SEQUENCE:2 +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testCancelNoExistingEvent() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:CANCEL +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = null; + $expected = null; + + $result = $this->process($itip, $old, $expected); + + } + + function testUnsupportedComponent() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VTODO +SEQUENCE:2 +UID:foobar +END:VTODO +END:VCALENDAR +ICS; + + $old = null; + $expected = null; + + $result = $this->process($itip, $old, $expected); + + } + + function testUnsupportedMethod() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = null; + $expected = null; + + $result = $this->process($itip, $old, $expected); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerProcessReplyTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerProcessReplyTest.php new file mode 100644 index 00000000000..533fdce1512 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerProcessReplyTest.php @@ -0,0 +1,496 @@ +<?php + +namespace Sabre\VObject\ITip; + +class BrokerProcessReplyTest extends BrokerTester { + + function testReplyNoOriginal() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $old = null; + $expected = null; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyAccept() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyRequestStatus() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +UID:foobar +REQUEST-STATUS:2.3;foo-bar! +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.3:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + + function testReplyPartyCrasher() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED:mailto:crasher@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +ATTENDEE;PARTSTAT=ACCEPTED:mailto:crasher@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyNewException() { + + // This is a reply to 1 instance of a recurring event. This should + // automatically create an exception. + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +RECURRENCE-ID:20140725T000000Z +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +DTSTART:20140725T000000Z +DTEND:20140725T010000Z +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +RECURRENCE-ID:20140725T000000Z +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyNewExceptionTz() { + + // This is a reply to 1 instance of a recurring event. This should + // automatically create an exception. + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +RECURRENCE-ID;TZID=America/Toronto:20140725T000000 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART;TZID=America/Toronto:20140724T000000 +DTEND;TZID=America/Toronto:20140724T010000 +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART;TZID=America/Toronto:20140724T000000 +DTEND;TZID=America/Toronto:20140724T010000 +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +DTSTART;TZID=America/Toronto:20140725T000000 +DTEND;TZID=America/Toronto:20140725T010000 +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +RECURRENCE-ID;TZID=America/Toronto:20140725T000000 +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyPartyCrashCreateExcepton() { + + // IN this test there's a recurring event that has an exception. The + // exception is missing the attendee. + // + // The attendee party crashes the instance, so it should show up in the + // resulting object. + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED;CN=Crasher!:mailto:crasher@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +RECURRENCE-ID:20140725T000000Z +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ORGANIZER:mailto:bar@example.org +END:VEVENT +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +DTSTART:20140725T000000Z +DTEND:20140725T010000Z +ORGANIZER:mailto:bar@example.org +RECURRENCE-ID:20140725T000000Z +ATTENDEE;PARTSTAT=ACCEPTED;CN=Crasher!:mailto:crasher@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyNewExceptionNoMasterEvent() { + + /** + * This iTip message would normally create a new exception, but the + * server is not able to create this new instance, because there's no + * master event to clone from. + * + * This test checks if the message is ignored. + */ + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED;CN=Crasher!:mailto:crasher@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +RECURRENCE-ID:20140725T000000Z +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +RECURRENCE-ID:20140724T000000Z +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = null; + $result = $this->process($itip, $old, $expected); + + } + + /** + * @depends testReplyAccept + */ + function testReplyAcceptUpdateRSVP() { + + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE;RSVP=TRUE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + + function testReplyNewExceptionFirstOccurence() { + + // This is a reply to 1 instance of a recurring event. This should + // automatically create an exception. + $itip = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +METHOD:REPLY +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +SEQUENCE:2 +RECURRENCE-ID:20140724T000000Z +UID:foobar +END:VEVENT +END:VCALENDAR +ICS; + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $expected = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +RRULE:FREQ=DAILY +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ATTENDEE:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +END:VEVENT +BEGIN:VEVENT +SEQUENCE:2 +UID:foobar +DTSTART:20140724T000000Z +DTEND:20140724T010000Z +ATTENDEE;PARTSTAT=ACCEPTED:mailto:foo@example.org +ORGANIZER:mailto:bar@example.org +RECURRENCE-ID:20140724T000000Z +END:VEVENT +END:VCALENDAR +ICS; + + $result = $this->process($itip, $old, $expected); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerTester.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerTester.php new file mode 100644 index 00000000000..6dbb517496e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerTester.php @@ -0,0 +1,96 @@ +<?php + +namespace Sabre\VObject\ITip; + +use Sabre\VObject\Reader; + +/** + * Utilities for testing the broker + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +abstract class BrokerTester extends \PHPUnit_Framework_TestCase { + + use \Sabre\VObject\PHPUnitAssertions; + + function parse($oldMessage, $newMessage, $expected = [], $currentUser = 'mailto:one@example.org') { + + $broker = new Broker(); + $result = $broker->parseEvent($newMessage, $currentUser, $oldMessage); + + $this->assertEquals(count($expected), count($result)); + + foreach ($expected as $index => $ex) { + + $message = $result[$index]; + + foreach ($ex as $key => $val) { + + if ($key === 'message') { + $this->assertVObjectEqualsVObject( + $val, + $message->message->serialize() + ); + } else { + $this->assertEquals($val, $message->$key); + } + + } + + } + + } + + function process($input, $existingObject = null, $expected = false) { + + $version = \Sabre\VObject\Version::VERSION; + + $vcal = Reader::read($input); + + foreach ($vcal->getComponents() as $mainComponent) { + break; + } + + $message = new Message(); + $message->message = $vcal; + $message->method = isset($vcal->METHOD) ? $vcal->METHOD->getValue() : null; + $message->component = $mainComponent->name; + $message->uid = $mainComponent->UID->getValue(); + $message->sequence = isset($vcal->VEVENT[0]) ? (string)$vcal->VEVENT[0]->SEQUENCE : null; + + if ($message->method === 'REPLY') { + + $message->sender = $mainComponent->ATTENDEE->getValue(); + $message->senderName = isset($mainComponent->ATTENDEE['CN']) ? $mainComponent->ATTENDEE['CN']->getValue() : null; + $message->recipient = $mainComponent->ORGANIZER->getValue(); + $message->recipientName = isset($mainComponent->ORGANIZER['CN']) ? $mainComponent->ORGANIZER['CN'] : null; + + } + + $broker = new Broker(); + + if (is_string($existingObject)) { + $existingObject = str_replace( + '%foo%', + "VERSION:2.0\nPRODID:-//Sabre//Sabre VObject $version//EN\nCALSCALE:GREGORIAN", + $existingObject + ); + $existingObject = Reader::read($existingObject); + } + + $result = $broker->processMessage($message, $existingObject); + + if (is_null($expected)) { + $this->assertTrue(!$result); + return; + } + + $this->assertVObjectEqualsVObject( + $expected, + $result + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php new file mode 100644 index 00000000000..255a84e8c1e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerTimezoneInParseEventInfoWithoutMasterTest.php @@ -0,0 +1,77 @@ +<?php + +namespace Sabre\VObject\ITip; + +use Sabre\VObject\Reader; + +class BrokerTimezoneInParseEventInfoWithoutMasterTest extends \PHPUnit_Framework_TestCase { + + function testTimezoneInParseEventInfoWithoutMaster() + { + $calendar = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//Mac OS X 10.9.5//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/Minsk +BEGIN:DAYLIGHT +TZOFFSETFROM:+0200 +RRULE:FREQ=YEARLY;UNTIL=20100328T000000Z;BYMONTH=3;BYDAY=-1SU +DTSTART:19930328T020000 +TZNAME:GMT+3 +TZOFFSETTO:+0300 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +DTSTART:20110327T020000 +TZNAME:GMT+3 +TZOFFSETTO:+0300 +RDATE:20110327T020000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20160331T163031Z +UID:B9301437-417C-4136-8DB3-8D1555863791 +DTEND;TZID=Europe/Minsk:20160405T100000 +TRANSP:OPAQUE +ATTENDEE;CN=User Invitee;CUTYPE=INDIVIDUAL;EMAIL=invitee@test.com;PARTSTAT= + ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:invitee@test.com +ATTENDEE;CN=User Organizer;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:organ + izer@test.com +SUMMARY:Event title +DTSTART;TZID=Europe/Minsk:20160405T090000 +DTSTAMP:20160331T164108Z +ORGANIZER;CN=User Organizer:mailto:organizer@test.com +SEQUENCE:6 +RECURRENCE-ID;TZID=Europe/Minsk:20160405T090000 +END:VEVENT +BEGIN:VEVENT +CREATED:20160331T163031Z +UID:B9301437-417C-4136-8DB3-8D1555863791 +DTEND;TZID=Europe/Minsk:20160406T100000 +TRANSP:OPAQUE +ATTENDEE;CN=User Invitee;CUTYPE=INDIVIDUAL;EMAIL=invitee@test.com;PARTSTAT= + ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:invitee@test.com +ATTENDEE;CN=User Organizer;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:organ + izer@test.com +SUMMARY:Event title +DTSTART;TZID=Europe/Minsk:20160406T090000 +DTSTAMP:20160331T165845Z +ORGANIZER;CN=User Organizer:mailto:organizer@test.com +SEQUENCE:6 +RECURRENCE-ID;TZID=Europe/Minsk:20160406T090000 +END:VEVENT +END:VCALENDAR +ICS; + + $calendar = Reader::read($calendar); + $broker = new Broker(); + + $reflectionMethod = new \ReflectionMethod($broker, 'parseEventInfo'); + $reflectionMethod->setAccessible(true); + $data = $reflectionMethod->invoke($broker, $calendar); + $this->assertInstanceOf('DateTimeZone', $data['timezone']); + $this->assertEquals($data['timezone']->getName(), 'Europe/Minsk'); + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerUpdateEventTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerUpdateEventTest.php new file mode 100644 index 00000000000..bc109009e70 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/BrokerUpdateEventTest.php @@ -0,0 +1,846 @@ +<?php + +namespace Sabre\VObject\ITip; + +class BrokerUpdateEventTest extends BrokerTester { + + function testInviteChange() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'significantChange' => false, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:three@example.org', + 'recipientName' => 'Three', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testInviteChangeFromNonSchedulingToSchedulingObject() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testInviteChangeFromSchedulingToNonSchedulingObject() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:1 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testNoAttendees() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = []; + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testRemoveInstance() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART;TZID=America/Toronto:20140716T120000 +DTEND;TZID=America/Toronto:20140716T130000 +RRULE:FREQ=WEEKLY +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART;TZID=America/Toronto:20140716T120000 +DTEND;TZID=America/Toronto:20140716T130000 +RRULE:FREQ=WEEKLY +EXDATE;TZID=America/Toronto:20140724T120000 +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +DTSTART;TZID=America/Toronto:20140716T120000 +DTEND;TZID=America/Toronto:20140716T130000 +RRULE:FREQ=WEEKLY +EXDATE;TZID=America/Toronto:20140724T120000 +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + /** + * This test is identical to the first test, except this time we change the + * DURATION property. + * + * This should ensure that the message is significant for every attendee, + */ + function testInviteChangeSignificantChange() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DURATION:PT1H +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +DURATION:PT2H +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +ATTENDEE;CN=Three:mailto:three@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +DURATION:PT2H +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:three@example.org', + 'recipientName' => 'Three', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +DURATION:PT2H +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=Two;PARTSTAT=NEEDS-ACTION:mailto:two@example.org +ATTENDEE;CN=Three;PARTSTAT=NEEDS-ACTION:mailto:three@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testInviteNoChange() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'significantChange' => false, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testInviteNoChangeForceSend() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;SCHEDULE-FORCE-SEND=REQUEST;CN=One:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;PARTSTAT=ACCEPTED:mailto:strunk@example.org +ATTENDEE;CN=One;PARTSTAT=NEEDS-ACTION:mailto:one@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testInviteRemoveAttendees() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +SUMMARY:foo +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +ATTENDEE;CN=Two:mailto:two@example.org +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:foobar +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=One:mailto:one@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + [ + 'uid' => 'foobar', + 'method' => 'CANCEL', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:two@example.org', + 'recipientName' => 'Two', + 'significantChange' => true, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:CANCEL +BEGIN:VEVENT +UID:foobar +DTSTAMP:**ANY** +SEQUENCE:2 +SUMMARY:foo +DTSTART:20140716T120000Z +DTEND:20140716T130000Z +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Two:mailto:two@example.org +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $result = $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } + + function testInviteChangeExdateOrder() { + + $oldMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//Mac OS X 10.10.1//EN +CALSCALE:GREGORIAN +BEGIN:VEVENT +UID:foobar +SEQUENCE:0 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;CUTYPE=INDIVIDUAL;EMAIL=strunk@example.org;PARTSTAT=ACCE + PTED:mailto:strunk@example.org +ATTENDEE;CN=One;CUTYPE=INDIVIDUAL;EMAIL=one@example.org;PARTSTAT=ACCEPTED;R + OLE=REQ-PARTICIPANT;SCHEDULE-STATUS="1.2;Message delivered locally":mailto + :one@example.org +SUMMARY:foo +DTSTART:20141211T160000Z +DTEND:20141211T170000Z +RRULE:FREQ=WEEKLY +EXDATE:20141225T160000Z,20150101T160000Z +EXDATE:20150108T160000Z +END:VEVENT +END:VCALENDAR +ICS; + + + $newMessage = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//Mac OS X 10.10.1//EN +CALSCALE:GREGORIAN +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;CUTYPE=INDIVIDUAL;EMAIL=strunk@example.org;PARTSTAT=ACCE + PTED:mailto:strunk@example.org +ATTENDEE;CN=One;CUTYPE=INDIVIDUAL;EMAIL=one@example.org;PARTSTAT=ACCEPTED;R + OLE=REQ-PARTICIPANT;SCHEDULE-STATUS=1.2:mailto:one@example.org +DTSTART:20141211T160000Z +DTEND:20141211T170000Z +RRULE:FREQ=WEEKLY +EXDATE:20150101T160000Z +EXDATE:20150108T160000Z,20141225T160000Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + + $expected = [ + [ + 'uid' => 'foobar', + 'method' => 'REQUEST', + 'component' => 'VEVENT', + 'sender' => 'mailto:strunk@example.org', + 'senderName' => 'Strunk', + 'recipient' => 'mailto:one@example.org', + 'recipientName' => 'One', + 'significantChange' => false, + 'message' => <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +UID:foobar +SEQUENCE:1 +ORGANIZER;CN=Strunk:mailto:strunk@example.org +ATTENDEE;CN=Strunk;CUTYPE=INDIVIDUAL;EMAIL=strunk@example.org;PARTSTAT=ACCE + PTED:mailto:strunk@example.org +ATTENDEE;CN=One;CUTYPE=INDIVIDUAL;EMAIL=one@example.org;PARTSTAT=ACCEPTED;R + OLE=REQ-PARTICIPANT:mailto:one@example.org +DTSTART:20141211T160000Z +DTEND:20141211T170000Z +RRULE:FREQ=WEEKLY +EXDATE:20150101T160000Z +EXDATE:20150108T160000Z,20141225T160000Z +END:VEVENT +END:VCALENDAR +ICS + + ], + ]; + + $this->parse($oldMessage, $newMessage, $expected, 'mailto:strunk@example.org'); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/EvolutionTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/EvolutionTest.php new file mode 100644 index 00000000000..3afe560d508 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/EvolutionTest.php @@ -0,0 +1,2653 @@ +<?php + +namespace Sabre\VObject\ITip; + +class EvolutionTest extends BrokerTester { + + /** + * Evolution does things as usual a little bit differently. + * + * We're adding a seprate test just for it. + */ + function testNewEvolutionEvent() { + + $ics = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +PRODID:-//Ximian//NONSGML Evolution Calendar//EN +BEGIN:VTIMEZONE +TZID:/freeassociation.sourceforge.net/Tzfile/America/Toronto +X-LIC-LOCATION:America/Toronto +BEGIN:STANDARD +TZNAME:EST +DTSTART:19691026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19700426T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19701025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19710425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19711031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19720430T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19721029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19730429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19731028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19740428T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19741027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19750427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19751026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19760425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19761031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19770424T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19771030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19780430T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19781029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19790429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19791028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19800427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19801026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19810426T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19811025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19820425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19821031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19830424T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19831030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19840429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19841028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19850428T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19851027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19860427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19861026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19870405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19871025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19880403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19881030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19890402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19891029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19900401T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19901028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19910407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19911027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19920405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19921025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19930404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19931031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19940403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19941030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19950402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19951029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19960407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19961027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19970406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19971026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19980405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19981025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19990404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19991031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20000402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20001029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20010401T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20011028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20020407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20021027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20030406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20031026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20040404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20041031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20050403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20051030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20060402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20061029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20070311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20071104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20080309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20081102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20090308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20091101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20100314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20101107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20110313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20111106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20120311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20121104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20130310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20131103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20140309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20141102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20150308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20151101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20160313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20161106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20170312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20171105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20180311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20181104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20190310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20191103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20200308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20201101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20210314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20211107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20220313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20221106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20230312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20231105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20240310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20241103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20250309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20251102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20260308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20261101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20270314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20271107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20280312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20281105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20290311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20291104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20300310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20301103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20310309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20311102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20320314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20321107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20330313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20331106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20340312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20341105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20350311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20351104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20360309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20361102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20370308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20371101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:20140813T153116Z-12176-1000-1065-6@johnny-lubuntu +DTSTAMP:20140813T142829Z +DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:201408 + 15T110000 +DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:20140815 + T113000 +TRANSP:OPAQUE +SEQUENCE:2 +SUMMARY:Evo makes a Meeting (fruux HQ) (fruux HQ) +LOCATION:fruux HQ +CLASS:PUBLIC +ORGANIZER;SENT-BY="MAILTO:martin+johnny@fruux.com":MAILTO:martin@fruux.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE + ;SENT-BY="MAILTO:martin+johnny@fruux.com";LANGUAGE=en:MAILTO:martin@fruux. + com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP= + TRUE;LANGUAGE=en:MAILTO:dominik@fruux.com +CREATED:20140813T153211Z +LAST-MODIFIED:20140813T155353Z +END:VEVENT +END:VCALENDAR +ICS; + + $version = \Sabre\VObject\Version::VERSION; + $expectedICS = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:/freeassociation.sourceforge.net/Tzfile/America/Toronto +X-LIC-LOCATION:America/Toronto +BEGIN:STANDARD +TZNAME:EST +DTSTART:19691026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19700426T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19701025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19710425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19711031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19720430T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19721029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19730429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19731028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19740428T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19741027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19750427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19751026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19760425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19761031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19770424T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19771030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19780430T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19781029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19790429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19791028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19800427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19801026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19810426T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19811025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19820425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19821031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19830424T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19831030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19840429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19841028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19850428T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19851027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19860427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19861026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19870405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19871025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19880403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19881030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19890402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19891029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19900401T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19901028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19910407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19911027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19920405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19921025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19930404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19931031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19940403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19941030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19950402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19951029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19960407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19961027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19970406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19971026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19980405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19981025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19990404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19991031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20000402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20001029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20010401T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20011028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20020407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20021027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20030406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20031026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20040404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20041031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20050403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20051030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20060402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20061029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20070311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20071104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20080309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20081102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20090308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20091101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20100314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20101107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20110313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20111106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20120311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20121104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20130310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20131103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20140309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20141102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20150308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20151101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20160313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20161106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20170312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20171105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20180311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20181104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20190310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20191103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20200308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20201101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20210314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20211107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20220313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20221106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20230312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20231105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20240310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20241103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20250309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20251102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20260308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20261101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20270314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20271107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20280312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20281105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20290311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20291104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20300310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20301103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20310309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20311102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20320314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20321107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20330313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20331106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20340312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20341105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20350311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20351104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20360309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20361102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20370308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20371101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:20140813T153116Z-12176-1000-1065-6@johnny-lubuntu +DTSTAMP:20140813T142829Z +DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:201408 + 15T110000 +DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:20140815 + T113000 +TRANSP:OPAQUE +SEQUENCE:2 +SUMMARY:Evo makes a Meeting (fruux HQ) (fruux HQ) +LOCATION:fruux HQ +CLASS:PUBLIC +ORGANIZER;SENT-BY="MAILTO:martin+johnny@fruux.com":MAILTO:martin@fruux.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE + ;SENT-BY="MAILTO:martin+johnny@fruux.com";LANGUAGE=en:MAILTO:martin@fruux. + com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP= + TRUE;LANGUAGE=en:MAILTO:dominik@fruux.com +CREATED:20140813T153211Z +LAST-MODIFIED:20140813T155353Z +END:VEVENT +END:VCALENDAR +ICS; + + $expected = [ + [ + 'uid' => '20140813T153116Z-12176-1000-1065-6@johnny-lubuntu', + 'method' => 'REQUEST', + 'sender' => 'mailto:martin@fruux.com', + 'senderName' => null, + 'recipient' => 'mailto:dominik@fruux.com', + 'recipientName' => null, + 'message' => $expectedICS, + ] + ]; + $this->parse(null, $ics, $expected, 'mailto:martin@fruux.com'); + + } + + /** + * This is an event originally from evolution, then parsed by sabredav and + * again mangled by iCal. This triggered a few bugs related to email + * address scheme casing. + */ + function testAttendeeModify() { + + $old = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject 3.3.1//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:/freeassociation.sourceforge.net/Tzfile/America/Toronto +X-LIC-LOCATION:America/Toronto +BEGIN:STANDARD +TZNAME:EST +DTSTART:19691026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19700426T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19701025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19710425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19711031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19720430T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19721029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19730429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19731028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19740428T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19741027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19750427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19751026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19760425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19761031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19770424T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19771030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19780430T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19781029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19790429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19791028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19800427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19801026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19810426T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19811025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19820425T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19821031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19830424T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19831030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19840429T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19841028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19850428T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19851027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19860427T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19861026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19870405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19871025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19880403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19881030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19890402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19891029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19900401T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19901028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19910407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19911027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19920405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19921025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19930404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19931031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19940403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19941030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19950402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19951029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19960407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19961027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19970406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19971026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19980405T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19981025T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:19990404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:19991031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20000402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20001029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20010401T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20011028T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20020407T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20021027T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20030406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20031026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20040404T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20041031T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20050403T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20051030T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20060402T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20061029T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20070311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20071104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20080309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20081102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20090308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20091101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20100314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20101107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20110313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20111106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20120311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20121104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20130310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20131103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20140309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20141102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20150308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20151101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20160313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20161106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20170312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20171105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20180311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20181104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20190310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20191103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20200308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20201101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20210314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20211107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20220313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20221106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20230312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20231105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20240310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20241103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20250309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20251102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20260308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20261101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20270314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20271107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20280312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20281105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20290311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20291104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20300310T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20301103T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20310309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20311102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20320314T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20321107T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20330313T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20331106T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20340312T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20341105T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20350311T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20351104T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20360309T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20361102T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:EDT +DTSTART:20370308T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZNAME:EST +DTSTART:20371101T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:20140813T212317Z-6646-1000-1221-23@evert-ubuntu +DTSTAMP:20140813T212221Z +DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:201408 + 13T180000 +DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Toronto:20140813 + T200000 +TRANSP:OPAQUE +SEQUENCE:4 +SUMMARY:Testing evolution +LOCATION:Online +CLASS:PUBLIC +ORGANIZER:MAILTO:o@example.org +CREATED:20140813T212510Z +LAST-MODIFIED:20140813T212541Z +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE;LANGUAGE=en:MAILTO:o@example.org +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;LANGUAGE=en:MAILTO:a1@example.org +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;LANGUAGE=en:MAILTO:a2@example.org +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;LANGUAGE=en:MAILTO:a3@example.org +STATUS:CANCELLED +END:VEVENT +END:VCALENDAR +ICS; + + $new = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Inc.//Mac OS X 10.9.4//EN +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:America/Toronto +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +DTSTART:20070311T020000 +TZNAME:EDT +TZOFFSETTO:-0400 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +DTSTART:20071104T020000 +TZNAME:EST +TZOFFSETTO:-0500 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +TRANSP:OPAQUE +DTEND;TZID=America/Toronto:20140813T200000 +ORGANIZER:MAILTO:o@example.org +UID:20140813T212317Z-6646-1000-1221-23@evert-ubuntu +DTSTAMP:20140813T212221Z +LOCATION:Online +STATUS:CANCELLED +SEQUENCE:4 +CLASS:PUBLIC +SUMMARY:Testing evolution +LAST-MODIFIED:20140813T212541Z +DTSTART;TZID=America/Toronto:20140813T180000 +CREATED:20140813T212510Z +ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:a2@example.org +ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:o@example.org +ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:a1@example.org +ATTENDEE;CUTYPE=INDIVIDUAL;LANGUAGE=en;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:a3@example.org +END:VEVENT +END:VCALENDAR +ICS; + + $this->parse($old, $new, [], 'mailto:a1@example.org'); + + + } + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/MessageTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/MessageTest.php new file mode 100644 index 00000000000..0fed7eb4a76 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ITip/MessageTest.php @@ -0,0 +1,32 @@ +<?php + +namespace Sabre\VObject\ITip; + +class MessageTest extends \PHPUnit_Framework_TestCase { + + function testNoScheduleStatus() { + + $message = new Message(); + $this->assertFalse($message->getScheduleStatus()); + + } + + function testScheduleStatus() { + + $message = new Message(); + $message->scheduleStatus = '1.2;Delivered'; + + $this->assertEquals('1.2', $message->getScheduleStatus()); + + } + + function testUnexpectedScheduleStatus() { + + $message = new Message(); + $message->scheduleStatus = '9.9.9'; + + $this->assertEquals('9.9.9', $message->getScheduleStatus()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue153Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue153Test.php new file mode 100644 index 00000000000..fca07fe9f39 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue153Test.php @@ -0,0 +1,14 @@ +<?php + +namespace Sabre\VObject; + +class Issue153Test extends \PHPUnit_Framework_TestCase { + + function testRead() { + + $obj = Reader::read(file_get_contents(dirname(__FILE__) . '/issue153.vcf')); + $this->assertEquals('Test Benutzer', (string)$obj->FN); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue259Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue259Test.php new file mode 100644 index 00000000000..4a73be560c2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue259Test.php @@ -0,0 +1,21 @@ +<?php + +namespace Sabre\VObject; + +class Issue259Test extends \PHPUnit_Framework_TestCase { + + function testParsingJcalWithUntil() { + $jcalWithUntil = '["vcalendar",[],[["vevent",[["uid",{},"text","dd1f7d29"],["organizer",{"cn":"robert"},"cal-address","mailto:robert@robert.com"],["dtstart",{"tzid":"Europe/Berlin"},"date-time","2015-10-21T12:00:00"],["dtend",{"tzid":"Europe/Berlin"},"date-time","2015-10-21T13:00:00"],["transp",{},"text","OPAQUE"],["rrule",{},"recur",{"freq":"MONTHLY","until":"2016-01-01T22:00:00Z"}]],[]]]]'; + $parser = new Parser\Json(); + $parser->setInput($jcalWithUntil); + + $vcalendar = $parser->parse(); + $eventAsArray = $vcalendar->select('VEVENT'); + $event = reset($eventAsArray); + $rruleAsArray = $event->select('RRULE'); + $rrule = reset($rruleAsArray); + $this->assertNotNull($rrule); + $this->assertEquals($rrule->getValue(), 'FREQ=MONTHLY;UNTIL=20160101T220000Z'); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue36WorkAroundTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue36WorkAroundTest.php new file mode 100644 index 00000000000..e2b9caf1d9f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue36WorkAroundTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\VObject; + +class Issue36WorkAroundTest extends \PHPUnit_Framework_TestCase { + + function testWorkaround() { + + // See https://github.com/fruux/sabre-vobject/issues/36 + $event = <<<ICS +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +SUMMARY:Titel +SEQUENCE:1 +TRANSP:TRANSPARENT +RRULE:FREQ=YEARLY +LAST-MODIFIED:20130323T225737Z +DTSTAMP:20130323T225737Z +UID:1833bd44-188b-405c-9f85-1a12105318aa +CATEGORIES:Jubiläum +X-MOZ-GENERATION:3 +RECURRENCE-ID;RANGE=THISANDFUTURE;VALUE=DATE:20131013 +DTSTART;VALUE=DATE:20131013 +CREATED:20100721T121914Z +DURATION:P1D +END:VEVENT +END:VCALENDAR +ICS; + + $obj = Reader::read($event); + + // If this does not throw an exception, it's all good. + $it = new Recur\EventIterator($obj, '1833bd44-188b-405c-9f85-1a12105318aa'); + $this->assertInstanceOf('Sabre\\VObject\\Recur\\EventIterator', $it); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue40Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue40Test.php new file mode 100644 index 00000000000..401cc19a05a --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue40Test.php @@ -0,0 +1,32 @@ +<?php + +namespace Sabre\VObject; + +/** + * This test is created to handle the issues brought forward by issue 40. + * + * https://github.com/fruux/sabre-vobject/issues/40 + */ +class Issue40Test extends \PHPUnit_Framework_TestCase { + + function testEncode() { + + $card = new Component\VCard(); + $card->add('N', ['van der Harten', ['Rene', 'J.'], "", 'Sir', 'R.D.O.N.'], ['SORT-AS' => ['Harten', 'Rene']]); + + unset($card->UID); + + $expected = implode("\r\n", [ + "BEGIN:VCARD", + "VERSION:4.0", + "PRODID:-//Sabre//Sabre VObject " . Version::VERSION . '//EN', + "N;SORT-AS=Harten,Rene:van der Harten;Rene,J.;;Sir;R.D.O.N.", + "END:VCARD", + "" + ]); + + $this->assertEquals($expected, $card->serialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue64Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue64Test.php new file mode 100644 index 00000000000..986a2481361 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue64Test.php @@ -0,0 +1,19 @@ +<?php + +namespace Sabre\VObject; + +class Issue64Test extends \PHPUnit_Framework_TestCase { + + function testRead() { + + $vcard = Reader::read(file_get_contents(dirname(__FILE__) . '/issue64.vcf')); + $vcard = $vcard->convert(\Sabre\VObject\Document::VCARD30); + $vcard = $vcard->serialize(); + + $converted = Reader::read($vcard); + + $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $converted); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue96Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue96Test.php new file mode 100644 index 00000000000..f0ed54804f1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Issue96Test.php @@ -0,0 +1,24 @@ +<?php + +namespace Sabre\VObject; + +class Issue96Test extends \PHPUnit_Framework_TestCase { + + function testRead() { + + $input = <<<VCF +BEGIN:VCARD +VERSION:2.1 +SOURCE:Yahoo Contacts (http://contacts.yahoo.com) +URL;CHARSET=utf-8;ENCODING=QUOTED-PRINTABLE:= +http://www.example.org +END:VCARD +VCF; + + $vcard = Reader::read($input, Reader::OPTION_FORGIVING); + $this->assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard); + $this->assertEquals("http://www.example.org", $vcard->URL->getValue()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/IssueUndefinedIndexTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/IssueUndefinedIndexTest.php new file mode 100644 index 00000000000..6f8afe60cd7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/IssueUndefinedIndexTest.php @@ -0,0 +1,29 @@ +<?php + +namespace Sabre\VObject; + +class IssueUndefinedIndexTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \Sabre\VObject\ParseException + */ + function testRead() { + + $input = <<<VCF +BEGIN:VCARD +VERSION:3.0 +PRODID:foo +N:Holmes;Sherlock;;; +FN:Sherlock Holmes +ORG:Acme Inc; +ADR;type=WORK;type=pref:;;, +\\n221B,Baker Street;London;;12345;United Kingdom +UID:foo +END:VCARD +VCF; + + $vcard = Reader::read($input, Reader::OPTION_FORGIVING); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/JCalTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/JCalTest.php new file mode 100644 index 00000000000..8c437162dde --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/JCalTest.php @@ -0,0 +1,149 @@ +<?php + +namespace Sabre\VObject; + +class JCalTest extends \PHPUnit_Framework_TestCase { + + function testToJCal() { + + $cal = new Component\VCalendar(); + + $event = $cal->add('VEVENT', [ + "UID" => "foo", + "DTSTART" => new \DateTime("2013-05-26 18:10:00Z"), + "DURATION" => "P1D", + "CATEGORIES" => ['home', 'testing'], + "CREATED" => new \DateTime("2013-05-26 18:10:00Z"), + + "ATTENDEE" => "mailto:armin@example.org", + "GEO" => [51.96668, 7.61876], + "SEQUENCE" => 5, + "FREEBUSY" => ["20130526T210213Z/PT1H", "20130626T120000Z/20130626T130000Z"], + "URL" => "http://example.org/", + "TZOFFSETFROM" => "+0500", + "RRULE" => ['FREQ' => 'WEEKLY', 'BYDAY' => ['MO', 'TU']], + ], false); + + // Modifying DTSTART to be a date-only. + $event->dtstart['VALUE'] = 'DATE'; + $event->add("X-BOOL", true, ['VALUE' => 'BOOLEAN']); + $event->add("X-TIME", "08:00:00", ['VALUE' => 'TIME']); + $event->add("ATTACH", "attachment", ['VALUE' => 'BINARY']); + $event->add("ATTENDEE", "mailto:dominik@example.org", ["CN" => "Dominik", "PARTSTAT" => "DECLINED"]); + + $event->add('REQUEST-STATUS', ["2.0", "Success"]); + $event->add('REQUEST-STATUS', ["3.7", "Invalid Calendar User", "ATTENDEE:mailto:jsmith@example.org"]); + + $event->add('DTEND', '20150108T133000'); + + $expected = [ + "vcalendar", + [ + [ + "version", + new \StdClass(), + "text", + "2.0" + ], + [ + "prodid", + new \StdClass(), + "text", + "-//Sabre//Sabre VObject " . Version::VERSION . "//EN", + ], + [ + "calscale", + new \StdClass(), + "text", + "GREGORIAN" + ], + ], + [ + ["vevent", + [ + [ + "uid", new \StdClass(), "text", "foo", + ], + [ + "dtstart", new \StdClass(), "date", "2013-05-26", + ], + [ + "duration", new \StdClass(), "duration", "P1D", + ], + [ + "categories", new \StdClass(), "text", "home", "testing", + ], + [ + "created", new \StdClass(), "date-time", "2013-05-26T18:10:00Z", + ], + [ + "attendee", new \StdClass(), "cal-address", "mailto:armin@example.org", + ], + [ + "attendee", + (object)[ + "cn" => "Dominik", + "partstat" => "DECLINED", + ], + "cal-address", + "mailto:dominik@example.org" + ], + [ + "geo", new \StdClass(), "float", [51.96668, 7.61876], + ], + [ + "sequence", new \StdClass(), "integer", 5 + ], + [ + "freebusy", new \StdClass(), "period", ["2013-05-26T21:02:13", "PT1H"], ["2013-06-26T12:00:00", "2013-06-26T13:00:00"], + ], + [ + "url", new \StdClass(), "uri", "http://example.org/", + ], + [ + "tzoffsetfrom", new \StdClass(), "utc-offset", "+05:00", + ], + [ + "rrule", new \StdClass(), "recur", [ + 'freq' => 'WEEKLY', + 'byday' => ['MO', 'TU'], + ], + ], + [ + "x-bool", new \StdClass(), "boolean", true + ], + [ + "x-time", new \StdClass(), "time", "08:00:00", + ], + [ + "attach", new \StdClass(), "binary", base64_encode('attachment') + ], + [ + "request-status", + new \StdClass(), + "text", + ["2.0", "Success"], + ], + [ + "request-status", + new \StdClass(), + "text", + ["3.7", "Invalid Calendar User", "ATTENDEE:mailto:jsmith@example.org"], + ], + [ + 'dtend', + new \StdClass(), + "date-time", + "2015-01-08T13:30:00", + ], + ], + [], + ] + ], + ]; + + $this->assertEquals($expected, $cal->jsonSerialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/JCardTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/JCardTest.php new file mode 100644 index 00000000000..87ff2fff885 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/JCardTest.php @@ -0,0 +1,195 @@ +<?php + +namespace Sabre\VObject; + +class JCardTest extends \PHPUnit_Framework_TestCase { + + function testToJCard() { + + $card = new Component\VCard([ + "VERSION" => "4.0", + "UID" => "foo", + "BDAY" => "19850407", + "REV" => "19951031T222710Z", + "LANG" => "nl", + "N" => ["Last", "First", "Middle", "", ""], + "item1.TEL" => "+1 555 123456", + "item1.X-AB-LABEL" => "Walkie Talkie", + "ADR" => [ + "", + "", + ["My Street", "Left Side", "Second Shack"], + "Hometown", + "PA", + "18252", + "U.S.A", + ], + ]); + + $card->add('BDAY', '1979-12-25', ['VALUE' => 'DATE', 'X-PARAM' => [1, 2]]); + $card->add('BDAY', '1979-12-25T02:00:00', ['VALUE' => 'DATE-TIME']); + + + $card->add('X-TRUNCATED', '--1225', ['VALUE' => 'DATE']); + $card->add('X-TIME-LOCAL', '123000', ['VALUE' => 'TIME']); + $card->add('X-TIME-UTC', '12:30:00Z', ['VALUE' => 'TIME']); + $card->add('X-TIME-OFFSET', '12:30:00-08:00', ['VALUE' => 'TIME']); + $card->add('X-TIME-REDUCED', '23', ['VALUE' => 'TIME']); + $card->add('X-TIME-TRUNCATED', '--30', ['VALUE' => 'TIME']); + + $card->add('X-KARMA-POINTS', '42', ['VALUE' => 'INTEGER']); + $card->add('X-GRADE', '1.3', ['VALUE' => 'FLOAT']); + + $card->add('TZ', '-0500', ['VALUE' => 'UTC-OFFSET']); + + $expected = [ + "vcard", + [ + [ + "version", + new \StdClass(), + "text", + "4.0" + ], + [ + "prodid", + new \StdClass(), + "text", + "-//Sabre//Sabre VObject " . Version::VERSION . "//EN", + ], + [ + "uid", + new \StdClass(), + "text", + "foo", + ], + [ + "bday", + new \StdClass(), + "date-and-or-time", + "1985-04-07", + ], + [ + "bday", + (object)[ + 'x-param' => [1,2], + ], + "date", + "1979-12-25", + ], + [ + "bday", + new \StdClass(), + "date-time", + "1979-12-25T02:00:00", + ], + [ + "rev", + new \StdClass(), + "timestamp", + "1995-10-31T22:27:10Z", + ], + [ + "lang", + new \StdClass(), + "language-tag", + "nl", + ], + [ + "n", + new \StdClass(), + "text", + ["Last", "First", "Middle", "", ""], + ], + [ + "tel", + (object)[ + "group" => "item1", + ], + "text", + "+1 555 123456", + ], + [ + "x-ab-label", + (object)[ + "group" => "item1", + ], + "unknown", + "Walkie Talkie", + ], + [ + "adr", + new \StdClass(), + "text", + [ + "", + "", + ["My Street", "Left Side", "Second Shack"], + "Hometown", + "PA", + "18252", + "U.S.A", + ], + ], + [ + "x-truncated", + new \StdClass(), + "date", + "--12-25", + ], + [ + "x-time-local", + new \StdClass(), + "time", + "12:30:00" + ], + [ + "x-time-utc", + new \StdClass(), + "time", + "12:30:00Z" + ], + [ + "x-time-offset", + new \StdClass(), + "time", + "12:30:00-08:00" + ], + [ + "x-time-reduced", + new \StdClass(), + "time", + "23" + ], + [ + "x-time-truncated", + new \StdClass(), + "time", + "--30" + ], + [ + "x-karma-points", + new \StdClass(), + "integer", + 42 + ], + [ + "x-grade", + new \StdClass(), + "float", + 1.3 + ], + [ + "tz", + new \StdClass(), + "utc-offset", + "-05:00", + ], + ], + ]; + + $this->assertEquals($expected, $card->jsonSerialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/LineFoldingIssueTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/LineFoldingIssueTest.php new file mode 100644 index 00000000000..47fef1c11e7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/LineFoldingIssueTest.php @@ -0,0 +1,23 @@ +<?php + +namespace Sabre\VObject; + +class LineFoldingIssueTest extends \PHPUnit_Framework_TestCase { + + function testRead() { + + $event = <<<ICS +BEGIN:VCALENDAR\r +BEGIN:VEVENT\r +DESCRIPTION:TEST\\n\\n \\n\\nTEST\\n\\n \\n\\nTEST\\n\\n \\n\\nTEST\\n\\nTEST\\nTEST, TEST\r +END:VEVENT\r +END:VCALENDAR\r + +ICS; + + $obj = Reader::read($event); + $this->assertEquals($event, $obj->serialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ParameterTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ParameterTest.php new file mode 100644 index 00000000000..e4f973115fb --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ParameterTest.php @@ -0,0 +1,135 @@ +<?php + +namespace Sabre\VObject; + +class ParameterTest extends \PHPUnit_Framework_TestCase { + + function testSetup() { + + $cal = new Component\VCalendar(); + + $param = new Parameter($cal, 'name', 'value'); + $this->assertEquals('NAME', $param->name); + $this->assertEquals('value', $param->getValue()); + + } + + function testSetupNameLess() { + + $card = new Component\VCard(); + + $param = new Parameter($card, null, 'URL'); + $this->assertEquals('VALUE', $param->name); + $this->assertEquals('URL', $param->getValue()); + $this->assertTrue($param->noName); + + } + + function testModify() { + + $cal = new Component\VCalendar(); + + $param = new Parameter($cal, 'name', null); + $param->addValue(1); + $this->assertEquals([1], $param->getParts()); + + $param->setParts([1, 2]); + $this->assertEquals([1, 2], $param->getParts()); + + $param->addValue(3); + $this->assertEquals([1, 2, 3], $param->getParts()); + + $param->setValue(4); + $param->addValue(5); + $this->assertEquals([4, 5], $param->getParts()); + + } + + function testCastToString() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', 'value'); + $this->assertEquals('value', $param->__toString()); + $this->assertEquals('value', (string)$param); + + } + + function testCastNullToString() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', null); + $this->assertEquals('', $param->__toString()); + $this->assertEquals('', (string)$param); + + } + + function testSerialize() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', 'value'); + $this->assertEquals('NAME=value', $param->serialize()); + + } + + function testSerializeEmpty() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', null); + $this->assertEquals('NAME=', $param->serialize()); + + } + + function testSerializeComplex() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', ["val1", "val2;", "val3^", "val4\n", "val5\""]); + $this->assertEquals('NAME=val1,"val2;","val3^^","val4^n","val5^\'"', $param->serialize()); + + } + + /** + * iCal 7.0 (OSX 10.9) has major issues with the EMAIL property, when the + * value contains a plus sign, and it's not quoted. + * + * So we specifically added support for that. + */ + function testSerializePlusSign() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'EMAIL', "user+something@example.org"); + $this->assertEquals('EMAIL="user+something@example.org"', $param->serialize()); + + } + + function testIterate() { + + $cal = new Component\VCalendar(); + + $param = new Parameter($cal, 'name', [1, 2, 3, 4]); + $result = []; + + foreach ($param as $value) { + $result[] = $value; + } + + $this->assertEquals([1, 2, 3, 4], $result); + + } + + function testSerializeColon() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', 'va:lue'); + $this->assertEquals('NAME="va:lue"', $param->serialize()); + + } + + function testSerializeSemiColon() { + + $cal = new Component\VCalendar(); + $param = new Parameter($cal, 'name', 'va;lue'); + $this->assertEquals('NAME="va;lue"', $param->serialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/JsonTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/JsonTest.php new file mode 100644 index 00000000000..c8725dfd06a --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/JsonTest.php @@ -0,0 +1,395 @@ +<?php + +namespace Sabre\VObject\Parser; + +use + Sabre\VObject; + +class JsonTest extends \PHPUnit_Framework_TestCase { + + function testRoundTripJCard() { + + $input = [ + "vcard", + [ + [ + "version", + new \StdClass(), + "text", + "4.0" + ], + [ + "prodid", + new \StdClass(), + "text", + "-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN", + ], + [ + "uid", + new \StdClass(), + "text", + "foo", + ], + [ + "bday", + new \StdClass(), + "date-and-or-time", + "1985-04-07", + ], + [ + "bday", + (object)[ + 'x-param' => [1,2], + ], + "date", + "1979-12-25", + ], + [ + "bday", + new \StdClass(), + "date-time", + "1979-12-25T02:00:00", + ], + [ + "rev", + new \StdClass(), + "timestamp", + "1995-10-31T22:27:10Z", + ], + [ + "lang", + new \StdClass(), + "language-tag", + "nl", + ], + [ + "n", + new \StdClass(), + "text", + ["Last", "First", "Middle", "", ""], + ], + [ + "tel", + (object)[ + "group" => "item1", + ], + "text", + "+1 555 123456", + ], + [ + "x-ab-label", + (object)[ + "group" => "item1", + ], + "unknown", + "Walkie Talkie", + ], + [ + "adr", + new \StdClass(), + "text", + [ + "", + "", + ["My Street", "Left Side", "Second Shack"], + "Hometown", + "PA", + "18252", + "U.S.A", + ], + ], + + [ + "x-truncated", + new \StdClass(), + "date", + "--12-25", + ], + [ + "x-time-local", + new \StdClass(), + "time", + "12:30:00" + ], + [ + "x-time-utc", + new \StdClass(), + "time", + "12:30:00Z" + ], + [ + "x-time-offset", + new \StdClass(), + "time", + "12:30:00-08:00" + ], + [ + "x-time-reduced", + new \StdClass(), + "time", + "23" + ], + [ + "x-time-truncated", + new \StdClass(), + "time", + "--30" + ], + [ + "x-karma-points", + new \StdClass(), + "integer", + 42 + ], + [ + "x-grade", + new \StdClass(), + "float", + 1.3 + ], + [ + "tz", + new \StdClass(), + "utc-offset", + "-05:00", + ], + ], + ]; + + $parser = new Json(json_encode($input)); + $vobj = $parser->parse(); + + $version = VObject\Version::VERSION; + + $result = $vobj->serialize(); + $expected = <<<VCF +BEGIN:VCARD +VERSION:4.0 +PRODID:-//Sabre//Sabre VObject $version//EN +UID:foo +BDAY:1985-04-07 +BDAY;X-PARAM=1,2;VALUE=DATE:1979-12-25 +BDAY;VALUE=DATE-TIME:1979-12-25T02:00:00 +REV:1995-10-31T22:27:10Z +LANG:nl +N:Last;First;Middle;; +item1.TEL:+1 555 123456 +item1.X-AB-LABEL:Walkie Talkie +ADR:;;My Street,Left Side,Second Shack;Hometown;PA;18252;U.S.A +X-TRUNCATED;VALUE=DATE:--12-25 +X-TIME-LOCAL;VALUE=TIME:123000 +X-TIME-UTC;VALUE=TIME:123000Z +X-TIME-OFFSET;VALUE=TIME:123000-0800 +X-TIME-REDUCED;VALUE=TIME:23 +X-TIME-TRUNCATED;VALUE=TIME:--30 +X-KARMA-POINTS;VALUE=INTEGER:42 +X-GRADE;VALUE=FLOAT:1.3 +TZ;VALUE=UTC-OFFSET:-0500 +END:VCARD + +VCF; + $this->assertEquals($expected, str_replace("\r", "", $result)); + + $this->assertEquals( + $input, + $vobj->jsonSerialize() + ); + + } + + function testRoundTripJCal() { + + $input = [ + "vcalendar", + [ + [ + "version", + new \StdClass(), + "text", + "2.0" + ], + [ + "prodid", + new \StdClass(), + "text", + "-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN", + ], + [ + "calscale", + new \StdClass(), + "text", + "GREGORIAN" + ], + ], + [ + ["vevent", + [ + [ + "uid", new \StdClass(), "text", "foo", + ], + [ + "dtstart", new \StdClass(), "date", "2013-05-26", + ], + [ + "duration", new \StdClass(), "duration", "P1D", + ], + [ + "categories", new \StdClass(), "text", "home", "testing", + ], + [ + "created", new \StdClass(), "date-time", "2013-05-26T18:10:00Z", + ], + [ + "attach", new \StdClass(), "binary", base64_encode('attachment') + ], + [ + "attendee", new \StdClass(), "cal-address", "mailto:armin@example.org", + ], + [ + "attendee", + (object)[ + "cn" => "Dominik", + "partstat" => "DECLINED", + ], + "cal-address", + "mailto:dominik@example.org" + ], + [ + "geo", new \StdClass(), "float", [51.96668, 7.61876], + ], + [ + "sequence", new \StdClass(), "integer", 5 + ], + [ + "freebusy", new \StdClass(), "period", ["2013-05-26T21:02:13", "PT1H"], ["2013-06-26T12:00:00", "2013-06-26T13:00:00"], + ], + [ + "url", new \StdClass(), "uri", "http://example.org/", + ], + [ + "tzoffsetfrom", new \StdClass(), "utc-offset", "+05:00", + ], + [ + "rrule", new \StdClass(), "recur", [ + 'freq' => 'WEEKLY', + 'byday' => ['MO', 'TU'], + ], + ], + [ + "x-bool", new \StdClass(), "boolean", true + ], + [ + "x-time", new \StdClass(), "time", "08:00:00", + ], + [ + "request-status", + new \StdClass(), + "text", + ["2.0", "Success"], + ], + [ + "request-status", + new \StdClass(), + "text", + ["3.7", "Invalid Calendar User", "ATTENDEE:mailto:jsmith@example.org"], + ], + ], + [ + ["valarm", + [ + [ + "action", new \StdClass(), "text", "DISPLAY", + ], + ], + [], + ], + ], + ] + ], + ]; + + $parser = new Json(json_encode($input)); + $vobj = $parser->parse(); + $result = $vobj->serialize(); + + $version = VObject\Version::VERSION; + + $expected = <<<VCF +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Sabre//Sabre VObject $version//EN +CALSCALE:GREGORIAN +BEGIN:VEVENT +UID:foo +DTSTART;VALUE=DATE:20130526 +DURATION:P1D +CATEGORIES:home,testing +CREATED:20130526T181000Z +ATTACH;VALUE=BINARY:YXR0YWNobWVudA== +ATTENDEE:mailto:armin@example.org +ATTENDEE;CN=Dominik;PARTSTAT=DECLINED:mailto:dominik@example.org +GEO:51.96668;7.61876 +SEQUENCE:5 +FREEBUSY:20130526T210213/PT1H,20130626T120000/20130626T130000 +URL;VALUE=URI:http://example.org/ +TZOFFSETFROM:+0500 +RRULE:FREQ=WEEKLY;BYDAY=MO,TU +X-BOOL;VALUE=BOOLEAN:TRUE +X-TIME;VALUE=TIME:080000 +REQUEST-STATUS:2.0;Success +REQUEST-STATUS:3.7;Invalid Calendar User;ATTENDEE:mailto:jsmith@example.org +BEGIN:VALARM +ACTION:DISPLAY +END:VALARM +END:VEVENT +END:VCALENDAR + +VCF; + $this->assertEquals($expected, str_replace("\r", "", $result)); + + $this->assertEquals( + $input, + $vobj->jsonSerialize() + ); + + } + + function testParseStreamArg() { + + $input = [ + "vcard", + [ + [ + "FN", new \StdClass(), 'text', "foo", + ], + ], + ]; + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, json_encode($input)); + rewind($stream); + + $result = VObject\Reader::readJson($stream, 0); + $this->assertEquals('foo', $result->FN->getValue()); + + } + + /** + * @expectedException \Sabre\VObject\ParseException + */ + function testParseInvalidData() { + + $json = new Json(); + $input = [ + "vlist", + [ + [ + "FN", new \StdClass(), 'text', "foo", + ], + ], + ]; + + $json->parse(json_encode($input), 0); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/MimeDirTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/MimeDirTest.php new file mode 100644 index 00000000000..63219dac2b9 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/MimeDirTest.php @@ -0,0 +1,143 @@ +<?php + +namespace Sabre\VObject\Parser; + +/** + * Note that most MimeDir related tests can actually be found in the ReaderTest + * class one level up. + */ +class MimeDirTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \Sabre\VObject\ParseException + */ + function testParseError() { + + $mimeDir = new MimeDir(); + $mimeDir->parse(fopen(__FILE__, 'a')); + + } + + function testDecodeLatin1() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +FN:umlaut u - \xFC +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $mimeDir->setCharset('ISO-8859-1'); + $vcard = $mimeDir->parse($vcard); + $this->assertEquals("umlaut u - \xC3\xBC", $vcard->FN->getValue()); + + } + + function testDecodeInlineLatin1() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:2.1 +FN;CHARSET=ISO-8859-1:umlaut u - \xFC +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $vcard = $mimeDir->parse($vcard); + $this->assertEquals("umlaut u - \xC3\xBC", $vcard->FN->getValue()); + + } + + function testIgnoreCharsetVCard30() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +FN;CHARSET=unknown:foo-bar - \xFC +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $vcard = $mimeDir->parse($vcard); + $this->assertEquals("foo-bar - \xFC", $vcard->FN->getValue()); + + } + + function testDontDecodeLatin1() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:4.0 +FN:umlaut u - \xFC +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $vcard = $mimeDir->parse($vcard); + // This basically tests that we don't touch the input string if + // the encoding was set to UTF-8. The result is actually invalid + // and the validator should report this, but it tests effectively + // that we pass through the string byte-by-byte. + $this->assertEquals("umlaut u - \xFC", $vcard->FN->getValue()); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testDecodeUnsupportedCharset() { + + $mimeDir = new MimeDir(); + $mimeDir->setCharset('foobar'); + + } + + /** + * @expectedException \Sabre\VObject\ParseException + */ + function testDecodeUnsupportedInlineCharset() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:2.1 +FN;CHARSET=foobar:nothing +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $mimeDir->parse($vcard); + + } + + function testDecodeWindows1252() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:3.0 +FN:Euro \x80 +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $mimeDir->setCharset('Windows-1252'); + $vcard = $mimeDir->parse($vcard); + $this->assertEquals("Euro \xE2\x82\xAC", $vcard->FN->getValue()); + + } + + function testDecodeWindows1252Inline() { + + $vcard = <<<VCF +BEGIN:VCARD +VERSION:2.1 +FN;CHARSET=Windows-1252:Euro \x80 +END:VCARD\n +VCF; + + $mimeDir = new MimeDir(); + $vcard = $mimeDir->parse($vcard); + $this->assertEquals("Euro \xE2\x82\xAC", $vcard->FN->getValue()); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/QuotedPrintableTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/QuotedPrintableTest.php new file mode 100644 index 00000000000..f40d6a677f4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/QuotedPrintableTest.php @@ -0,0 +1,108 @@ +<?php + +namespace Sabre\VObject\Parser; + +use + Sabre\VObject\Reader; + +class QuotedPrintableTest extends \PHPUnit_Framework_TestCase { + + function testReadQuotedPrintableSimple() { + + $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aach=65n\r\nEND:VCARD"; + + $result = Reader::read($data); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCARD', $result->name); + $this->assertEquals(1, count($result->children())); + $this->assertEquals("Aachen", $this->getPropertyValue($result->LABEL)); + + } + + function testReadQuotedPrintableNewlineSoft() { + + $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aa=\r\n ch=\r\n en\r\nEND:VCARD"; + $result = Reader::read($data); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCARD', $result->name); + $this->assertEquals(1, count($result->children())); + $this->assertEquals("Aachen", $this->getPropertyValue($result->LABEL)); + + } + + function testReadQuotedPrintableNewlineHard() { + + $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aachen=0D=0A=\r\n Germany\r\nEND:VCARD"; + $result = Reader::read($data); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCARD', $result->name); + $this->assertEquals(1, count($result->children())); + $this->assertEquals("Aachen\r\nGermany", $this->getPropertyValue($result->LABEL)); + + + } + + function testReadQuotedPrintableCompatibilityMS() { + + $data = "BEGIN:VCARD\r\nLABEL;ENCODING=QUOTED-PRINTABLE:Aachen=0D=0A=\r\nDeutschland:okay\r\nEND:VCARD"; + $result = Reader::read($data, Reader::OPTION_FORGIVING); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCARD', $result->name); + $this->assertEquals(1, count($result->children())); + $this->assertEquals("Aachen\r\nDeutschland:okay", $this->getPropertyValue($result->LABEL)); + + } + + function testReadQuotesPrintableCompoundValues() { + + $data = <<<VCF +BEGIN:VCARD +VERSION:2.1 +N:Doe;John;;; +FN:John Doe +ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;M=C3=BCnster = +Str. 1;M=C3=BCnster;;48143;Deutschland +END:VCARD +VCF; + + $result = Reader::read($data, Reader::OPTION_FORGIVING); + $this->assertEquals([ + '', '', 'Münster Str. 1', 'Münster', '', '48143', 'Deutschland' + ], $result->ADR->getParts()); + + + } + + private function getPropertyValue(\Sabre\VObject\Property $property) { + + return (string)$property; + + /* + $param = $property['encoding']; + if ($param !== null) { + $encoding = strtoupper((string)$param); + if ($encoding === 'QUOTED-PRINTABLE') { + $value = quoted_printable_decode($value); + } else { + throw new Exception(); + } + } + + $param = $property['charset']; + if ($param !== null) { + $charset = strtoupper((string)$param); + if ($charset !== 'UTF-8') { + $value = mb_convert_encoding($value, 'UTF-8', $charset); + } + } else { + $value = StringUtil::convertToUTF8($value); + } + + return $value; + */ + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/XmlTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/XmlTest.php new file mode 100644 index 00000000000..b8ba67d1719 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Parser/XmlTest.php @@ -0,0 +1,2893 @@ +<?php + +namespace Sabre\VObject\Parser; + +use Sabre\VObject; + +class XmlTest extends \PHPUnit_Framework_TestCase { + + use VObject\PHPUnitAssertions; + + function testRFC6321Example1() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <calscale> + <text>GREGORIAN</text> + </calscale> + <prodid> + <text>-//Example Inc.//Example Calendar//EN</text> + </prodid> + <version> + <text>2.0</text> + </version> + </properties> + <components> + <vevent> + <properties> + <dtstamp> + <date-time>2008-02-05T19:12:24Z</date-time> + </dtstamp> + <dtstart> + <date>2008-10-06</date> + </dtstart> + <summary> + <text>Planning meeting</text> + </summary> + <uid> + <text>4088E990AD89CB3DBB484909</text> + </uid> + </properties> + </vevent> + </components> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + // VERSION comes first because this is required by vCard 4.0. + 'VERSION:2.0' . "\n" . + 'CALSCALE:GREGORIAN' . "\n" . + 'PRODID:-//Example Inc.//Example Calendar//EN' . "\n" . + 'BEGIN:VEVENT' . "\n" . + 'DTSTAMP:20080205T191224Z' . "\n" . + 'DTSTART;VALUE=DATE:20081006' . "\n" . + 'SUMMARY:Planning meeting' . "\n" . + 'UID:4088E990AD89CB3DBB484909' . "\n" . + 'END:VEVENT' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + function testRFC6321Example2() { + + $xml = <<<XML +<?xml version="1.0" encoding="UTF-8" ?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <prodid> + <text>-//Example Inc.//Example Client//EN</text> + </prodid> + <version> + <text>2.0</text> + </version> + </properties> + <components> + <vtimezone> + <properties> + <last-modified> + <date-time>2004-01-10T03:28:45Z</date-time> + </last-modified> + <tzid><text>US/Eastern</text></tzid> + </properties> + <components> + <daylight> + <properties> + <dtstart> + <date-time>2000-04-04T02:00:00</date-time> + </dtstart> + <rrule> + <recur> + <freq>YEARLY</freq> + <byday>1SU</byday> + <bymonth>4</bymonth> + </recur> + </rrule> + <tzname> + <text>EDT</text> + </tzname> + <tzoffsetfrom> + <utc-offset>-05:00</utc-offset> + </tzoffsetfrom> + <tzoffsetto> + <utc-offset>-04:00</utc-offset> + </tzoffsetto> + </properties> + </daylight> + <standard> + <properties> + <dtstart> + <date-time>2000-10-26T02:00:00</date-time> + </dtstart> + <rrule> + <recur> + <freq>YEARLY</freq> + <byday>-1SU</byday> + <bymonth>10</bymonth> + </recur> + </rrule> + <tzname> + <text>EST</text> + </tzname> + <tzoffsetfrom> + <utc-offset>-04:00</utc-offset> + </tzoffsetfrom> + <tzoffsetto> + <utc-offset>-05:00</utc-offset> + </tzoffsetto> + </properties> + </standard> + </components> + </vtimezone> + <vevent> + <properties> + <dtstamp> + <date-time>2006-02-06T00:11:21Z</date-time> + </dtstamp> + <dtstart> + <parameters> + <tzid><text>US/Eastern</text></tzid> + </parameters> + <date-time>2006-01-02T12:00:00</date-time> + </dtstart> + <duration> + <duration>PT1H</duration> + </duration> + <rrule> + <recur> + <freq>DAILY</freq> + <count>5</count> + </recur> + </rrule> + <rdate> + <parameters> + <tzid><text>US/Eastern</text></tzid> + </parameters> + <period> + <start>2006-01-02T15:00:00</start> + <duration>PT2H</duration> + </period> + </rdate> + <summary> + <text>Event #2</text> + </summary> + <description> + <text>We are having a meeting all this week at 12 +pm for one hour, with an additional meeting on the first day +2 hours long. Please bring your own lunch for the 12 pm +meetings.</text> + </description> + <uid> + <text>00959BC664CA650E933C892C@example.com</text> + </uid> + </properties> + </vevent> + <vevent> + <properties> + <dtstamp> + <date-time>2006-02-06T00:11:21Z</date-time> + </dtstamp> + <dtstart> + <parameters> + <tzid><text>US/Eastern</text></tzid> + </parameters> + <date-time>2006-01-04T14:00:00</date-time> + </dtstart> + <duration> + <duration>PT1H</duration> + </duration> + <recurrence-id> + <parameters> + <tzid><text>US/Eastern</text></tzid> + </parameters> + <date-time>2006-01-04T12:00:00</date-time> + </recurrence-id> + <summary> + <text>Event #2 bis</text> + </summary> + <uid> + <text>00959BC664CA650E933C892C@example.com</text> + </uid> + </properties> + </vevent> + </components> + </vcalendar> +</icalendar> +XML; + + $component = VObject\Reader::readXML($xml); + $this->assertVObjectEqualsVObject( + 'BEGIN:VCALENDAR' . "\n" . + 'VERSION:2.0' . "\n" . + 'PRODID:-//Example Inc.//Example Client//EN' . "\n" . + 'BEGIN:VTIMEZONE' . "\n" . + 'LAST-MODIFIED:20040110T032845Z' . "\n" . + 'TZID:US/Eastern' . "\n" . + 'BEGIN:DAYLIGHT' . "\n" . + 'DTSTART:20000404T020000' . "\n" . + 'RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4' . "\n" . + 'TZNAME:EDT' . "\n" . + 'TZOFFSETFROM:-0500' . "\n" . + 'TZOFFSETTO:-0400' . "\n" . + 'END:DAYLIGHT' . "\n" . + 'BEGIN:STANDARD' . "\n" . + 'DTSTART:20001026T020000' . "\n" . + 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10' . "\n" . + 'TZNAME:EST' . "\n" . + 'TZOFFSETFROM:-0400' . "\n" . + 'TZOFFSETTO:-0500' . "\n" . + 'END:STANDARD' . "\n" . + 'END:VTIMEZONE' . "\n" . + 'BEGIN:VEVENT' . "\n" . + 'DTSTAMP:20060206T001121Z' . "\n" . + 'DTSTART;TZID=US/Eastern:20060102T120000' . "\n" . + 'DURATION:PT1H' . "\n" . + 'RRULE:FREQ=DAILY;COUNT=5' . "\n" . + 'RDATE;TZID=US/Eastern;VALUE=PERIOD:20060102T150000/PT2H' . "\n" . + 'SUMMARY:Event #2' . "\n" . + 'DESCRIPTION:We are having a meeting all this week at 12\npm for one hour\, ' . "\n" . + ' with an additional meeting on the first day\n2 hours long.\nPlease bring y' . "\n" . + ' our own lunch for the 12 pm\nmeetings.' . "\n" . + 'UID:00959BC664CA650E933C892C@example.com' . "\n" . + 'END:VEVENT' . "\n" . + 'BEGIN:VEVENT' . "\n" . + 'DTSTAMP:20060206T001121Z' . "\n" . + 'DTSTART;TZID=US/Eastern:20060104T140000' . "\n" . + 'DURATION:PT1H' . "\n" . + 'RECURRENCE-ID;TZID=US/Eastern:20060104T120000' . "\n" . + 'SUMMARY:Event #2 bis' . "\n" . + 'UID:00959BC664CA650E933C892C@example.com' . "\n" . + 'END:VEVENT' . "\n" . + 'END:VCALENDAR' . "\n", + VObject\Writer::write($component) + ); + + } + + /** + * iCalendar Stream. + */ + function testRFC6321Section3_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar/> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'END:VCALENDAR' . "\n" + ); + } + + /** + * All components exist. + */ + function testRFC6321Section3_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <components> + <vtimezone/> + <vevent/> + <vtodo/> + <vjournal/> + <vfreebusy/> + <standard/> + <daylight/> + <valarm/> + </components> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'BEGIN:VTIMEZONE' . "\n" . + 'END:VTIMEZONE' . "\n" . + 'BEGIN:VEVENT' . "\n" . + 'END:VEVENT' . "\n" . + 'BEGIN:VTODO' . "\n" . + 'END:VTODO' . "\n" . + 'BEGIN:VJOURNAL' . "\n" . + 'END:VJOURNAL' . "\n" . + 'BEGIN:VFREEBUSY' . "\n" . + 'END:VFREEBUSY' . "\n" . + 'BEGIN:STANDARD' . "\n" . + 'END:STANDARD' . "\n" . + 'BEGIN:DAYLIGHT' . "\n" . + 'END:DAYLIGHT' . "\n" . + 'BEGIN:VALARM' . "\n" . + 'END:VALARM' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Properties, Special Cases, GEO. + */ + function testRFC6321Section3_4_1_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <geo> + <latitude>37.386013</latitude> + <longitude>-122.082932</longitude> + </geo> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'GEO:37.386013;-122.082932' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Properties, Special Cases, REQUEST-STATUS. + */ + function testRFC6321Section3_4_1_3() { + + // Example 1 of RFC5545, Section 3.8.8.3. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <request-status> + <code>2.0</code> + <description>Success</description> + </request-status> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'REQUEST-STATUS:2.0;Success' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + // Example 2 of RFC5545, Section 3.8.8.3. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <request-status> + <code>3.1</code> + <description>Invalid property value</description> + <data>DTSTART:96-Apr-01</data> + </request-status> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'REQUEST-STATUS:3.1;Invalid property value;DTSTART:96-Apr-01' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + // Example 3 of RFC5545, Section 3.8.8.3. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <request-status> + <code>2.8</code> + <description>Success, repeating event ignored. Scheduled as a single event.</description> + <data>RRULE:FREQ=WEEKLY;INTERVAL=2</data> + </request-status> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'REQUEST-STATUS:2.8;Success\, repeating event ignored. Scheduled as a single' . "\n" . + ' event.;RRULE:FREQ=WEEKLY\;INTERVAL=2' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + // Example 4 of RFC5545, Section 3.8.8.3. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <request-status> + <code>4.1</code> + <description>Event conflict. Date-time is busy.</description> + </request-status> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'REQUEST-STATUS:4.1;Event conflict. Date-time is busy.' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + // Example 5 of RFC5545, Section 3.8.8.3. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <request-status> + <code>3.7</code> + <description>Invalid calendar user</description> + <data>ATTENDEE:mailto:jsmith@example.com</data> + </request-status> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'REQUEST-STATUS:3.7;Invalid calendar user;ATTENDEE:mailto:jsmith@example.com' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Binary. + */ + function testRFC6321Section3_6_1() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <attach> + <binary>SGVsbG8gV29ybGQh</binary> + </attach> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'ATTACH:SGVsbG8gV29ybGQh' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + // In vCard 4, BINARY no longer exists and is replaced by URI. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <attach> + <uri>SGVsbG8gV29ybGQh</uri> + </attach> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'ATTACH:SGVsbG8gV29ybGQh' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Boolean. + */ + function testRFC6321Section3_6_2() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <attendee> + <parameters> + <rsvp><boolean>true</boolean></rsvp> + </parameters> + <cal-address>mailto:cyrus@example.com</cal-address> + </attendee> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'ATTENDEE;RSVP=true:mailto:cyrus@example.com' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Calendar User Address. + */ + function testRFC6321Section3_6_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <attendee> + <cal-address>mailto:cyrus@example.com</cal-address> + </attendee> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'ATTENDEE:mailto:cyrus@example.com' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Date. + */ + function testRFC6321Section3_6_4() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <dtstart> + <date>2011-05-17</date> + </dtstart> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'DTSTART;VALUE=DATE:20110517' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Date-Time. + */ + function testRFC6321Section3_6_5() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <dtstart> + <date-time>2011-05-17T12:00:00</date-time> + </dtstart> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'DTSTART:20110517T120000' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Duration. + */ + function testRFC6321Section3_6_6() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <duration> + <duration>P1D</duration> + </duration> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'DURATION:P1D' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Float. + */ + function testRFC6321Section3_6_7() { + + // GEO uses <float /> with a positive and a non-negative numbers. + $this->testRFC6321Section3_4_1_2(); + + } + + /** + * Values, Integer. + */ + function testRFC6321Section3_6_8() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <foo> + <integer>42</integer> + </foo> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'FOO:42' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <foo> + <integer>-42</integer> + </foo> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'FOO:-42' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Period of Time. + */ + function testRFC6321Section3_6_9() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <freebusy> + <period> + <start>2011-05-17T12:00:00</start> + <duration>P1H</duration> + </period> + </freebusy> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'FREEBUSY:20110517T120000/P1H' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <freebusy> + <period> + <start>2011-05-17T12:00:00</start> + <end>2012-05-17T12:00:00</end> + </period> + </freebusy> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'FREEBUSY:20110517T120000/20120517T120000' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Recurrence Rule. + */ + function testRFC6321Section3_6_10() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rrule> + <recur> + <freq>YEARLY</freq> + <count>5</count> + <byday>-1SU</byday> + <bymonth>10</bymonth> + </recur> + </rrule> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RRULE:FREQ=YEARLY;COUNT=5;BYDAY=-1SU;BYMONTH=10' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Text. + */ + function testRFC6321Section3_6_11() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <calscale> + <text>GREGORIAN</text> + </calscale> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'CALSCALE:GREGORIAN' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, Time. + */ + function testRFC6321Section3_6_12() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <foo> + <time>12:00:00</time> + </foo> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'FOO:120000' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, URI. + */ + function testRFC6321Section3_6_13() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <attach> + <uri>http://calendar.example.com</uri> + </attach> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'ATTACH:http://calendar.example.com' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Values, UTC Offset. + */ + function testRFC6321Section3_6_14() { + + // Example 1 of RFC5545, Section 3.3.14. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <tzoffsetfrom> + <utc-offset>-05:00</utc-offset> + </tzoffsetfrom> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'TZOFFSETFROM:-0500' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + // Example 2 of RFC5545, Section 3.3.14. + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <tzoffsetfrom> + <utc-offset>+01:00</utc-offset> + </tzoffsetfrom> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'TZOFFSETFROM:+0100' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Handling Unrecognized Properties or Parameters. + */ + function testRFC6321Section5() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <x-property> + <unknown>20110512T120000Z</unknown> + </x-property> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'X-PROPERTY:20110512T120000Z' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <dtstart> + <parameters> + <x-param> + <text>PT30M</text> + </x-param> + </parameters> + <date-time>2011-05-12T13:00:00Z</date-time> + </dtstart> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'DTSTART;X-PARAM=PT30M:20110512T130000Z' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + function testRDateWithDateTime() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rdate> + <date-time>2008-02-05T19:12:24Z</date-time> + </rdate> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RDATE:20080205T191224Z' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rdate> + <date-time>2008-02-05T19:12:24Z</date-time> + <date-time>2009-02-05T19:12:24Z</date-time> + </rdate> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RDATE:20080205T191224Z,20090205T191224Z' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + function testRDateWithDate() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rdate> + <date>2008-10-06</date> + </rdate> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RDATE:20081006' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rdate> + <date>2008-10-06</date> + <date>2009-10-06</date> + <date>2010-10-06</date> + </rdate> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RDATE:20081006,20091006,20101006' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + function testRDateWithPeriod() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rdate> + <parameters> + <tzid> + <text>US/Eastern</text> + </tzid> + </parameters> + <period> + <start>2006-01-02T15:00:00</start> + <duration>PT2H</duration> + </period> + </rdate> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RDATE;TZID=US/Eastern;VALUE=PERIOD:20060102T150000/PT2H' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"> + <vcalendar> + <properties> + <rdate> + <parameters> + <tzid> + <text>US/Eastern</text> + </tzid> + </parameters> + <period> + <start>2006-01-02T15:00:00</start> + <duration>PT2H</duration> + </period> + <period> + <start>2008-01-02T15:00:00</start> + <duration>PT1H</duration> + </period> + </rdate> + </properties> + </vcalendar> +</icalendar> +XML +, + 'BEGIN:VCALENDAR' . "\n" . + 'RDATE;TZID=US/Eastern;VALUE=PERIOD:20060102T150000/PT2H,20080102T150000/PT1' . "\n" . + ' H' . "\n" . + 'END:VCALENDAR' . "\n" + ); + + } + + /** + * Basic example. + */ + function testRFC6351Basic() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <fn> + <text>J. Doe</text> + </fn> + <n> + <surname>Doe</surname> + <given>J.</given> + <additional/> + <prefix/> + <suffix/> + </n> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'FN:J. Doe' . "\n" . + 'N:Doe;J.;;;' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Example 1. + */ + function testRFC6351Example1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <fn> + <text>J. Doe</text> + </fn> + <n> + <surname>Doe</surname> + <given>J.</given> + <additional/> + <prefix/> + <suffix/> + </n> + <x-file> + <parameters> + <mediatype> + <text>image/jpeg</text> + </mediatype> + </parameters> + <unknown>alien.jpg</unknown> + </x-file> + <x1:a href="http://www.example.com" xmlns:x1="http://www.w3.org/1999/xhtml">My web page!</x1:a> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'FN:J. Doe' . "\n" . + 'N:Doe;J.;;;' . "\n" . + 'X-FILE;MEDIATYPE=image/jpeg:alien.jpg' . "\n" . + 'XML:<a xmlns="http://www.w3.org/1999/xhtml" href="http://www.example.com">M' . "\n" . + ' y web page!</a>' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Design Considerations. + */ + function testRFC6351Section5() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <tel> + <parameters> + <type> + <text>voice</text> + <text>video</text> + </type> + </parameters> + <uri>tel:+1-555-555-555</uri> + </tel> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TEL;TYPE="voice,video":tel:+1-555-555-555' . "\n" . + 'END:VCARD' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <tel> + <parameters> + <type> + <text>voice</text> + <text>video</text> + </type> + </parameters> + <text>tel:+1-555-555-555</text> + </tel> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TEL;TYPE="voice,video":tel:+1-555-555-555' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Design Considerations. + */ + function testRFC6351Section5Group() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <tel> + <text>tel:+1-555-555-556</text> + </tel> + <group name="contact"> + <tel> + <text>tel:+1-555-555-555</text> + </tel> + <fn> + <text>Gordon</text> + </fn> + </group> + <group name="media"> + <fn> + <text>Gordon</text> + </fn> + </group> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TEL:tel:+1-555-555-556' . "\n" . + 'contact.TEL:tel:+1-555-555-555' . "\n" . + 'contact.FN:Gordon' . "\n" . + 'media.FN:Gordon' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Extensibility. + */ + function testRFC6351Section5_1_NoNamespace() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <x-my-prop> + <parameters> + <pref> + <integer>1</integer> + </pref> + </parameters> + <text>value goes here</text> + </x-my-prop> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'X-MY-PROP;PREF=1:value goes here' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.1 of Relax NG Schema: value-date. + */ + function testRFC6351ValueDateWithYearMonthDay() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>20150128</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:20150128' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.1 of Relax NG Schema: value-date. + */ + function testRFC6351ValueDateWithYearMonth() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>2015-01</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:2015-01' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.1 of Relax NG Schema: value-date. + */ + function testRFC6351ValueDateWithMonth() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>--01</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:--01' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.1 of Relax NG Schema: value-date. + */ + function testRFC6351ValueDateWithMonthDay() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>--0128</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:--0128' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.1 of Relax NG Schema: value-date. + */ + function testRFC6351ValueDateWithDay() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>---28</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:---28' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithHour() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>13</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:13' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithHourMinute() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>1353</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:1353' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithHourMinuteSecond() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>135301</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:135301' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithMinute() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>-53</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:-53' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithMinuteSecond() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>-5301</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:-5301' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithSecond() { + + $this->assertTrue(true); + + /* + * According to the Relax NG Schema, there is a conflict between + * value-date and value-time. The --01 syntax can only match a + * value-date because of the higher priority set in + * value-date-and-or-time. So we basically skip this test. + * + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>--01</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:--01' . "\n" . + 'END:VCARD' . "\n" + ); + */ + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithSecondZ() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>--01Z</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:--01Z' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.2 of Relax NG Schema: value-time. + */ + function testRFC6351ValueTimeWithSecondTZ() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>--01+1234</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:--01+1234' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithYearMonthDayHour() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>20150128T13</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:20150128T13' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithMonthDayHour() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>--0128T13</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:--0128T13' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithDayHour() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>---28T13</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:---28T13' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithDayHourMinute() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>---28T1353</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:---28T1353' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithDayHourMinuteSecond() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>---28T135301</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:---28T135301' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithDayHourZ() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>---28T13Z</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:---28T13Z' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Section 4.3.3 of Relax NG Schema: value-date-time. + */ + function testRFC6351ValueDateTimeWithDayHourTZ() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>---28T13+1234</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:---28T13+1234' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: SOURCE. + */ + function testRFC6350Section6_1_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <source> + <uri>ldap://ldap.example.com/cn=Babs%20Jensen,%20o=Babsco,%20c=US</uri> + </source> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'SOURCE:ldap://ldap.example.com/cn=Babs%20Jensen\,%20o=Babsco\,%20c=US' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: KIND. + */ + function testRFC6350Section6_1_4() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <kind> + <text>individual</text> + </kind> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'KIND:individual' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: FN. + */ + function testRFC6350Section6_2_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <fn> + <text>Mr. John Q. Public, Esq.</text> + </fn> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'FN:Mr. John Q. Public\, Esq.' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: N. + */ + function testRFC6350Section6_2_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <n> + <surname>Stevenson</surname> + <given>John</given> + <additional>Philip,Paul</additional> + <prefix>Dr.</prefix> + <suffix>Jr.,M.D.,A.C.P.</suffix> + </n> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'N:Stevenson;John;Philip\,Paul;Dr.;Jr.\,M.D.\,A.C.P.' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: NICKNAME. + */ + function testRFC6350Section6_2_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <nickname> + <text>Jim</text> + <text>Jimmie</text> + </nickname> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'NICKNAME:Jim,Jimmie' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: PHOTO. + */ + function testRFC6350Section6_2_4() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <photo> + <uri>http://www.example.com/pub/photos/jqpublic.gif</uri> + </photo> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'PHOTO:http://www.example.com/pub/photos/jqpublic.gif' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + function testRFC6350Section6_2_5() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <bday> + <date-and-or-time>19531015T231000Z</date-and-or-time> + </bday> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'BDAY:19531015T231000Z' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + function testRFC6350Section6_2_6() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <anniversary> + <date-and-or-time>19960415</date-and-or-time> + </anniversary> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'ANNIVERSARY:19960415' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: GENDER. + */ + function testRFC6350Section6_2_7() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <gender> + <sex>Jim</sex> + <text>Jimmie</text> + </gender> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'GENDER:Jim;Jimmie' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: ADR. + */ + function testRFC6350Section6_3_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <adr> + <pobox/> + <ext/> + <street>123 Main Street</street> + <locality>Any Town</locality> + <region>CA</region> + <code>91921-1234</code> + <country>U.S.A.</country> + </adr> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'ADR:;;123 Main Street;Any Town;CA;91921-1234;U.S.A.' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: TEL. + */ + function testRFC6350Section6_4_1() { + + /** + * Quoting RFC: + * > Value type: By default, it is a single free-form text value (for + * > backward compatibility with vCard 3), but it SHOULD be reset to a + * > URI value. It is expected that the URI scheme will be "tel", as + * > specified in [RFC3966], but other schemes MAY be used. + * + * So first, we test xCard/URI to vCard/URI. + * Then, we test xCard/TEXT to vCard/TEXT to xCard/TEXT. + */ + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <tel> + <parameters> + <type> + <text>home</text> + </type> + </parameters> + <uri>tel:+33-01-23-45-67</uri> + </tel> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TEL;TYPE=home:tel:+33-01-23-45-67' . "\n" . + 'END:VCARD' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <tel> + <parameters> + <type> + <text>home</text> + </type> + </parameters> + <text>tel:+33-01-23-45-67</text> + </tel> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TEL;TYPE=home:tel:+33-01-23-45-67' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: EMAIL. + */ + function testRFC6350Section6_4_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <email> + <parameters> + <type> + <text>work</text> + </type> + </parameters> + <text>jqpublic@xyz.example.com</text> + </email> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'EMAIL;TYPE=work:jqpublic@xyz.example.com' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: IMPP. + */ + function testRFC6350Section6_4_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <impp> + <parameters> + <pref> + <text>1</text> + </pref> + </parameters> + <uri>xmpp:alice@example.com</uri> + </impp> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'IMPP;PREF=1:xmpp:alice@example.com' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: LANG. + */ + function testRFC6350Section6_4_4() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <lang> + <parameters> + <type> + <text>work</text> + </type> + <pref> + <text>2</text> + </pref> + </parameters> + <language-tag>en</language-tag> + </lang> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'LANG;TYPE=work;PREF=2:en' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: TZ. + */ + function testRFC6350Section6_5_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <tz> + <text>Raleigh/North America</text> + </tz> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TZ:Raleigh/North America' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: GEO. + */ + function testRFC6350Section6_5_2() { + + $this->assertXMLEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <geo> + <uri>geo:37.386013,-122.082932</uri> + </geo> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'GEO:geo:37.386013\,-122.082932' . "\n" . + 'END:VCARD' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <geo> + <text>geo:37.386013,-122.082932</text> + </geo> + </vcard> +</vcards> +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'GEO:geo:37.386013\,-122.082932' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: TITLE. + */ + function testRFC6350Section6_6_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<<<XML +<?xml version="1.0" encoding="UTF-8"?> +<vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0"> + <vcard> + <title> + <text>Research Scientist</text> + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'TITLE:Research Scientist' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: ROLE. + */ + function testRFC6350Section6_6_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + Project Leader + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'ROLE:Project Leader' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: LOGO. + */ + function testRFC6350Section6_6_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + http://www.example.com/pub/logos/abccorp.jpg + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'LOGO:http://www.example.com/pub/logos/abccorp.jpg' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: ORG. + */ + function testRFC6350Section6_6_4() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + ABC, Inc. + North American Division + Marketing + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'ORG:ABC\, Inc.;North American Division;Marketing' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: MEMBER. + */ + function testRFC6350Section6_6_5() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'MEMBER:urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af' . "\n" . + 'END:VCARD' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + mailto:subscriber1@example.com + + + xmpp:subscriber2@example.com + + + sip:subscriber3@example.com + + + tel:+1-418-555-5555 + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'MEMBER:mailto:subscriber1@example.com' . "\n" . + 'MEMBER:xmpp:subscriber2@example.com' . "\n" . + 'MEMBER:sip:subscriber3@example.com' . "\n" . + 'MEMBER:tel:+1-418-555-5555' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: RELATED. + */ + function testRFC6350Section6_6_6() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + + + friend + + + urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'RELATED;TYPE=friend:urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: CATEGORIES. + */ + function testRFC6350Section6_7_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + INTERNET + IETF + INDUSTRY + INFORMATION TECHNOLOGY + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: NOTE. + */ + function testRFC6350Section6_7_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + Foo, bar + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'NOTE:Foo\, bar' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: PRODID. + */ + function testRFC6350Section6_7_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + -//ONLINE DIRECTORY//NONSGML Version 1//EN + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + function testRFC6350Section6_7_4() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + 19951031T222710Z + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'REV:19951031T222710Z' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: SOUND. + */ + function testRFC6350Section6_7_5() { + + $this->assertXMLEqualsToMimeDir( +<< + + + + CID:JOHNQPUBLIC.part8.19960229T080000.xyzMail@example.com + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'SOUND:CID:JOHNQPUBLIC.part8.19960229T080000.xyzMail@example.com' . "\n" . + 'END:VCARD' . "\n" + ); + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + CID:JOHNQPUBLIC.part8.19960229T080000.xyzMail@example.com + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'SOUND:CID:JOHNQPUBLIC.part8.19960229T080000.xyzMail@example.com' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: UID. + */ + function testRFC6350Section6_7_6() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'UID:urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: CLIENTPIDMAP. + */ + function testRFC6350Section6_7_7() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + 1 + urn:uuid:3df403f4-5924-4bb7-b077-3c711d9eb34b + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'CLIENTPIDMAP:1;urn:uuid:3df403f4-5924-4bb7-b077-3c711d9eb34b' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: URL. + */ + function testRFC6350Section6_7_8() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + http://example.org/restaurant.french/~chezchic.html + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'URL:http://example.org/restaurant.french/~chezchic.html' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: VERSION. + */ + function testRFC6350Section6_7_9() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: KEY. + */ + function testRFC6350Section6_8_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + + + application/pgp-keys + + + ftp://example.com/keys/jdoe + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'KEY;MEDIATYPE=application/pgp-keys:ftp://example.com/keys/jdoe' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: FBURL. + */ + function testRFC6350Section6_9_1() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + + + 1 + + + http://www.example.com/busy/janedoe + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'FBURL;PREF=1:http://www.example.com/busy/janedoe' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: CALADRURI. + */ + function testRFC6350Section6_9_2() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + http://example.com/calendar/jdoe + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'CALADRURI:http://example.com/calendar/jdoe' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: CALURI. + */ + function testRFC6350Section6_9_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + + + 1 + + + http://cal.example.com/calA + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'CALURI;PREF=1:http://cal.example.com/calA' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Property: CAPURI. + */ + function testRFC6350SectionA_3() { + + $this->assertXMLReflexivelyEqualsToMimeDir( +<< + + + + http://cap.example.com/capA + + + +XML +, + 'BEGIN:VCARD' . "\n" . + 'VERSION:4.0' . "\n" . + 'CAPURI:http://cap.example.com/capA' . "\n" . + 'END:VCARD' . "\n" + ); + + } + + /** + * Check this equality: + * XML -> object model -> MIME Dir. + */ + protected function assertXMLEqualsToMimeDir($xml, $mimedir) { + + $component = VObject\Reader::readXML($xml); + $this->assertVObjectEqualsVObject($mimedir, $component); + + } + + /** + * Check this (reflexive) equality: + * XML -> object model -> MIME Dir -> object model -> XML. + */ + protected function assertXMLReflexivelyEqualsToMimeDir($xml, $mimedir) { + + $this->assertXMLEqualsToMimeDir($xml, $mimedir); + + $component = VObject\Reader::read($mimedir); + $this->assertXmlStringEqualsXmlString($xml, VObject\Writer::writeXML($component)); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/BinaryTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/BinaryTest.php new file mode 100644 index 00000000000..3356b1bd11e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/BinaryTest.php @@ -0,0 +1,19 @@ + '3.0']); + $vcard->add('PHOTO', ['a', 'b']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/BooleanTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/BooleanTest.php new file mode 100644 index 00000000000..0c885d37bbe --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/BooleanTest.php @@ -0,0 +1,22 @@ +assertTrue($vcard->{'X-AWESOME'}->getValue()); + $this->assertFalse($vcard->{'X-SUCKS'}->getValue()); + + $this->assertEquals('BOOLEAN', $vcard->{'X-AWESOME'}->getValueType()); + $this->assertEquals($input, $vcard->serialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/CompoundTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/CompoundTest.php new file mode 100644 index 00000000000..ced0d7a4c70 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/CompoundTest.php @@ -0,0 +1,50 @@ +createProperty('ORG'); + $elem->setParts($arr); + + $this->assertEquals('ABC\, Inc.;North American Division;Marketing\;Sales', $elem->getValue()); + $this->assertEquals(3, count($elem->getParts())); + $parts = $elem->getParts(); + $this->assertEquals('Marketing;Sales', $parts[2]); + + } + + function testGetParts() { + + $str = 'ABC\, Inc.;North American Division;Marketing\;Sales'; + + $vcard = new VCard(); + $elem = $vcard->createProperty('ORG'); + $elem->setRawMimeDirValue($str); + + $this->assertEquals(3, count($elem->getParts())); + $parts = $elem->getParts(); + $this->assertEquals('Marketing;Sales', $parts[2]); + } + + function testGetPartsNull() { + + $vcard = new VCard(); + $elem = $vcard->createProperty('ORG', null); + + $this->assertEquals(0, count($elem->getParts())); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/FloatTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/FloatTest.php new file mode 100644 index 00000000000..f4636518d83 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/FloatTest.php @@ -0,0 +1,30 @@ +parse($input); + + $this->assertInstanceOf('Sabre\VObject\Property\FloatValue', $result->{'X-FLOAT'}); + + $this->assertEquals([ + 0.234, + 1.245, + ], $result->{'X-FLOAT'}->getParts()); + + $this->assertEquals( + $input, + $result->serialize() + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/CalAddressTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/CalAddressTest.php new file mode 100644 index 00000000000..fe2a550bffe --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/CalAddressTest.php @@ -0,0 +1,32 @@ +add('ATTENDEE', $input); + + $this->assertEquals( + $expected, + $property->getNormalizedValue() + ); + + } + + function values() { + + return [ + ['mailto:a@b.com', 'mailto:a@b.com'], + ['mailto:a@b.com', 'MAILTO:a@b.com'], + ['/foo/bar', '/foo/bar'], + ]; + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/DateTimeTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/DateTimeTest.php new file mode 100644 index 00000000000..c89cfe69bca --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/DateTimeTest.php @@ -0,0 +1,371 @@ +vcal = new VCalendar(); + + } + + function testSetDateTime() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt = new \DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals('19850704T013000', (string)$elem); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertNull($elem['VALUE']); + + $this->assertTrue($elem->hasTime()); + + } + + function testSetDateTimeLOCAL() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt = new \DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setDateTime($dt, $isFloating = true); + + $this->assertEquals('19850704T013000', (string)$elem); + $this->assertNull($elem['TZID']); + + $this->assertTrue($elem->hasTime()); + } + + function testSetDateTimeUTC() { + + $tz = new \DateTimeZone('GMT'); + $dt = new \DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals('19850704T013000Z', (string)$elem); + $this->assertNull($elem['TZID']); + + $this->assertTrue($elem->hasTime()); + } + + function testSetDateTimeFromUnixTimestamp() { + + // When initialized from a Unix timestamp, the timezone is set to "+00:00". + $dt = new \DateTime('@489288600'); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals('19850704T013000Z', (string)$elem); + $this->assertNull($elem['TZID']); + + $this->assertTrue($elem->hasTime()); + } + + function testSetDateTimeLOCALTZ() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt = new \DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals('19850704T013000', (string)$elem); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + + $this->assertTrue($elem->hasTime()); + } + + function testSetDateTimeDATE() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt = new \DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem['VALUE'] = 'DATE'; + $elem->setDateTime($dt); + + $this->assertEquals('19850704', (string)$elem); + $this->assertNull($elem['TZID']); + $this->assertEquals('DATE', (string)$elem['VALUE']); + + $this->assertFalse($elem->hasTime()); + } + + function testSetValue() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt = new \DateTime('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setValue($dt); + + $this->assertEquals('19850704T013000', (string)$elem); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertNull($elem['VALUE']); + + $this->assertTrue($elem->hasTime()); + + } + + function testSetValueArray() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); + $dt2 = new \DateTime('1985-07-04 02:30:00', $tz); + $dt1->setTimeZone($tz); + $dt2->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setValue([$dt1, $dt2]); + + $this->assertEquals('19850704T013000,19850704T023000', (string)$elem); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertNull($elem['VALUE']); + + $this->assertTrue($elem->hasTime()); + + } + + function testSetParts() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt1 = new \DateTime('1985-07-04 01:30:00', $tz); + $dt2 = new \DateTime('1985-07-04 02:30:00', $tz); + $dt1->setTimeZone($tz); + $dt2->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setParts([$dt1, $dt2]); + + $this->assertEquals('19850704T013000,19850704T023000', (string)$elem); + $this->assertEquals('Europe/Amsterdam', (string)$elem['TZID']); + $this->assertNull($elem['VALUE']); + + $this->assertTrue($elem->hasTime()); + + } + function testSetPartsStrings() { + + $dt1 = '19850704T013000Z'; + $dt2 = '19850704T023000Z'; + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setParts([$dt1, $dt2]); + + $this->assertEquals('19850704T013000Z,19850704T023000Z', (string)$elem); + $this->assertNull($elem['VALUE']); + + $this->assertTrue($elem->hasTime()); + + } + + + function testGetDateTimeCached() { + + $tz = new \DateTimeZone('Europe/Amsterdam'); + $dt = new \DateTimeImmutable('1985-07-04 01:30:00', $tz); + $dt->setTimeZone($tz); + + $elem = $this->vcal->createProperty('DTSTART'); + $elem->setDateTime($dt); + + $this->assertEquals($elem->getDateTime(), $dt); + + } + + function testGetDateTimeDateNULL() { + + $elem = $this->vcal->createProperty('DTSTART'); + $dt = $elem->getDateTime(); + + $this->assertNull($dt); + + } + + function testGetDateTimeDateDATE() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 00:00:00', $dt->format('Y-m-d H:i:s')); + + } + + function testGetDateTimeDateDATEReferenceTimeZone() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704'); + + $tz = new \DateTimeZone('America/Toronto'); + $dt = $elem->getDateTime($tz); + $dt = $dt->setTimeZone(new \DateTimeZone('UTC')); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 04:00:00', $dt->format('Y-m-d H:i:s')); + + } + + function testGetDateTimeDateFloating() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704T013000'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + + } + + function testGetDateTimeDateFloatingReferenceTimeZone() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704T013000'); + + $tz = new \DateTimeZone('America/Toronto'); + $dt = $elem->getDateTime($tz); + $dt = $dt->setTimeZone(new \DateTimeZone('UTC')); + + $this->assertInstanceOf('DateTimeInterface', $dt); + $this->assertEquals('1985-07-04 05:30:00', $dt->format('Y-m-d H:i:s')); + + } + + function testGetDateTimeDateUTC() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704T013000Z'); + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('UTC', $dt->getTimeZone()->getName()); + + } + + function testGetDateTimeDateLOCALTZ() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704T013000'); + $elem['TZID'] = 'Europe/Amsterdam'; + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testGetDateTimeDateInvalid() { + + $elem = $this->vcal->createProperty('DTSTART', 'bla'); + $dt = $elem->getDateTime(); + + } + + function testGetDateTimeWeirdTZ() { + + $elem = $this->vcal->createProperty('DTSTART', '19850704T013000'); + $elem['TZID'] = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam'; + + + $event = $this->vcal->createComponent('VEVENT'); + $event->add($elem); + + $timezone = $this->vcal->createComponent('VTIMEZONE'); + $timezone->TZID = '/freeassociation.sourceforge.net/Tzfile/Europe/Amsterdam'; + $timezone->{'X-LIC-LOCATION'} = 'Europe/Amsterdam'; + + $this->vcal->add($event); + $this->vcal->add($timezone); + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Europe/Amsterdam', $dt->getTimeZone()->getName()); + + } + + function testGetDateTimeBadTimeZone() { + + $default = date_default_timezone_get(); + date_default_timezone_set('Canada/Eastern'); + + $elem = $this->vcal->createProperty('DTSTART', '19850704T013000'); + $elem['TZID'] = 'Moon'; + + + $event = $this->vcal->createComponent('VEVENT'); + $event->add($elem); + + $timezone = $this->vcal->createComponent('VTIMEZONE'); + $timezone->TZID = 'Moon'; + $timezone->{'X-LIC-LOCATION'} = 'Moon'; + + + $this->vcal->add($event); + $this->vcal->add($timezone); + + $dt = $elem->getDateTime(); + + $this->assertInstanceOf('DateTimeImmutable', $dt); + $this->assertEquals('1985-07-04 01:30:00', $dt->format('Y-m-d H:i:s')); + $this->assertEquals('Canada/Eastern', $dt->getTimeZone()->getName()); + date_default_timezone_set($default); + + } + + function testUpdateValueParameter() { + + $dtStart = $this->vcal->createProperty('DTSTART', new \DateTime('2013-06-07 15:05:00')); + $dtStart['VALUE'] = 'DATE'; + + $this->assertEquals("DTSTART;VALUE=DATE:20130607\r\n", $dtStart->serialize()); + + } + + function testValidate() { + + $exDate = $this->vcal->createProperty('EXDATE', '-00011130T143000Z'); + $messages = $exDate->validate(); + $this->assertEquals(1, count($messages)); + $this->assertEquals(3, $messages[0]['level']); + + } + + /** + * This issue was discovered on the sabredav mailing list. + */ + function testCreateDatePropertyThroughAdd() { + + $vcal = new VCalendar(); + $vevent = $vcal->add('VEVENT'); + + $dtstart = $vevent->add( + 'DTSTART', + new \DateTime('2014-03-07'), + ['VALUE' => 'DATE'] + ); + + $this->assertEquals("DTSTART;VALUE=DATE:20140307\r\n", $dtstart->serialize()); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/DurationTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/DurationTest.php new file mode 100644 index 00000000000..9aaaebce0a0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/DurationTest.php @@ -0,0 +1,20 @@ +add('VEVENT', ['DURATION' => ['PT1H']]); + + $this->assertEquals( + new \DateInterval('PT1H'), + $event->{'DURATION'}->getDateInterval() + ); + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/RecurTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/RecurTest.php new file mode 100644 index 00000000000..df95e3bc849 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/ICalendar/RecurTest.php @@ -0,0 +1,453 @@ +add('RRULE', 'FREQ=Daily'); + + $this->assertInstanceOf('Sabre\VObject\Property\ICalendar\Recur', $recur); + + $this->assertEquals(['FREQ' => 'DAILY'], $recur->getParts()); + $recur->setParts(['freq' => 'MONTHLY']); + + $this->assertEquals(['FREQ' => 'MONTHLY'], $recur->getParts()); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testSetValueBadVal() { + + $vcal = new VCalendar(); + $recur = $vcal->add('RRULE', 'FREQ=Daily'); + $recur->setValue(new \Exception()); + + } + + function testSetValueWithCount() { + $vcal = new VCalendar(); + $recur = $vcal->add('RRULE', 'FREQ=Daily'); + $recur->setValue(['COUNT' => 3]); + $this->assertEquals($recur->getParts()['COUNT'], 3); + } + + function testGetJSONWithCount() { + $input = 'BEGIN:VCALENDAR +BEGIN:VEVENT +UID:908d53c0-e1a3-4883-b69f-530954d6bd62 +TRANSP:OPAQUE +DTSTART;TZID=Europe/Berlin:20160301T150000 +DTEND;TZID=Europe/Berlin:20160301T170000 +SUMMARY:test +RRULE:FREQ=DAILY;COUNT=3 +ORGANIZER;CN=robert pipo:mailto:robert@example.org +END:VEVENT +END:VCALENDAR +'; + + $vcal = Reader::read($input); + $rrule = $vcal->VEVENT->RRULE; + $count = $rrule->getJsonValue()[0]['count']; + $this->assertTrue(is_int($count)); + $this->assertEquals(3, $count); + } + + function testSetSubParts() { + + $vcal = new VCalendar(); + $recur = $vcal->add('RRULE', ['FREQ' => 'DAILY', 'BYDAY' => 'mo,tu', 'BYMONTH' => [0, 1]]); + + $this->assertEquals([ + 'FREQ' => 'DAILY', + 'BYDAY' => ['MO', 'TU'], + 'BYMONTH' => [0, 1], + ], $recur->getParts()); + + } + + function testGetJSONWithUntil() { + $input = 'BEGIN:VCALENDAR +BEGIN:VEVENT +UID:908d53c0-e1a3-4883-b69f-530954d6bd62 +TRANSP:OPAQUE +DTSTART;TZID=Europe/Berlin:20160301T150000 +DTEND;TZID=Europe/Berlin:20160301T170000 +SUMMARY:test +RRULE:FREQ=DAILY;UNTIL=20160305T230000Z +ORGANIZER;CN=robert pipo:mailto:robert@example.org +END:VEVENT +END:VCALENDAR +'; + + $vcal = Reader::read($input); + $rrule = $vcal->VEVENT->RRULE; + $untilJsonString = $rrule->getJsonValue()[0]['until']; + $this->assertEquals('2016-03-05T23:00:00Z', $untilJsonString); + } + + + function testValidateStripEmpties() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foobar +BEGIN:VEVENT +UID:908d53c0-e1a3-4883-b69f-530954d6bd62 +TRANSP:OPAQUE +DTSTART;TZID=Europe/Berlin:20160301T150000 +DTEND;TZID=Europe/Berlin:20160301T170000 +SUMMARY:test +RRULE:FREQ=DAILY;BYMONTH=;UNTIL=20160305T230000Z +ORGANIZER;CN=robert pipo:mailto:robert@example.org +DTSTAMP:20160312T183800Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = Reader::read($input); + $this->assertEquals( + 1, + count($vcal->validate()) + ); + $this->assertEquals( + 1, + count($vcal->validate($vcal::REPAIR)) + ); + + $expected = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foobar +BEGIN:VEVENT +UID:908d53c0-e1a3-4883-b69f-530954d6bd62 +TRANSP:OPAQUE +DTSTART;TZID=Europe/Berlin:20160301T150000 +DTEND;TZID=Europe/Berlin:20160301T170000 +SUMMARY:test +RRULE:FREQ=DAILY;UNTIL=20160305T230000Z +ORGANIZER;CN=robert pipo:mailto:robert@example.org +DTSTAMP:20160312T183800Z +END:VEVENT +END:VCALENDAR +'; + + $this->assertVObjectEqualsVObject( + $expected, + $vcal + ); + + } + + function testValidateStripNoFreq() { + + $input = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foobar +BEGIN:VEVENT +UID:908d53c0-e1a3-4883-b69f-530954d6bd62 +TRANSP:OPAQUE +DTSTART;TZID=Europe/Berlin:20160301T150000 +DTEND;TZID=Europe/Berlin:20160301T170000 +SUMMARY:test +RRULE:UNTIL=20160305T230000Z +ORGANIZER;CN=robert pipo:mailto:robert@example.org +DTSTAMP:20160312T183800Z +END:VEVENT +END:VCALENDAR +'; + + $vcal = Reader::read($input); + $this->assertEquals( + 1, + count($vcal->validate()) + ); + $this->assertEquals( + 1, + count($vcal->validate($vcal::REPAIR)) + ); + + $expected = 'BEGIN:VCALENDAR +VERSION:2.0 +PRODID:foobar +BEGIN:VEVENT +UID:908d53c0-e1a3-4883-b69f-530954d6bd62 +TRANSP:OPAQUE +DTSTART;TZID=Europe/Berlin:20160301T150000 +DTEND;TZID=Europe/Berlin:20160301T170000 +SUMMARY:test +ORGANIZER;CN=robert pipo:mailto:robert@example.org +DTSTAMP:20160312T183800Z +END:VEVENT +END:VCALENDAR +'; + + $this->assertVObjectEqualsVObject( + $expected, + $vcal + ); + + } + + function testValidateInvalidByMonthRruleWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(1, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24', $property->getValue()); + + } + + function testValidateInvalidByMonthRruleWithoutRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0'); + $result = $property->validate(); + + $this->assertCount(1, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0', $property->getValue()); + + } + + function testValidateInvalidByMonthRruleWithRepair2() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=bla'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(1, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24', $property->getValue()); + + } + + function testValidateInvalidByMonthRruleWithoutRepair2() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=bla'); + $result = $property->validate(); + + $this->assertCount(1, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + // Without repair the invalid BYMONTH is still there, but the value is changed to uppercase + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=BLA', $property->getValue()); + + } + + function testValidateInvalidByMonthRruleValue14WithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=14'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(1, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24', $property->getValue()); + + } + + function testValidateInvalidByMonthRruleMultipleWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0,1,2,3,4,14'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(2, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[1]['message']); + $this->assertEquals(1, $result[1]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=1,2,3,4', $property->getValue()); + + } + + function testValidateOneOfManyInvalidByMonthRruleWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=bla,3,foo'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(2, $result); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('BYMONTH in RRULE must have value(s) between 1 and 12!', $result[1]['message']); + $this->assertEquals(1, $result[1]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=3', $property->getValue()); + + } + + function testValidateValidByMonthRrule() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=2,3'); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=2,3', $property->getValue()); + + } + + /** + * test for issue #336 + */ + function testValidateRruleBySecondZero() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000Z'); + $result = $property->validate(Node::REPAIR); + + // There should be 0 warnings and the value should be unchanged + $this->assertEmpty($result); + $this->assertEquals('FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000Z', $property->getValue()); + + } + + function testValidateValidByWeekNoWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYWEEKNO=11'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(0, $result); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYWEEKNO=11', $property->getValue()); + + } + + function testValidateInvalidByWeekNoWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYWEEKNO=55;BYDAY=WE'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(1, $result); + $this->assertEquals('BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYDAY=WE', $property->getValue()); + + } + + function testValidateMultipleInvalidByWeekNoWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYWEEKNO=55,2,-80;BYDAY=WE'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(2, $result); + $this->assertEquals('BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', $result[1]['message']); + $this->assertEquals(1, $result[1]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYWEEKNO=2;BYDAY=WE', $property->getValue()); + + } + + function testValidateAllInvalidByWeekNoWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYWEEKNO=55,-80;BYDAY=WE'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(2, $result); + $this->assertEquals('BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', $result[1]['message']); + $this->assertEquals(1, $result[1]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYDAY=WE', $property->getValue()); + + } + + function testValidateInvalidByWeekNoWithoutRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYWEEKNO=55;BYDAY=WE'); + $result = $property->validate(); + + $this->assertCount(1, $result); + $this->assertEquals('BYWEEKNO in RRULE must have value(s) from -53 to -1, or 1 to 53!', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYWEEKNO=55;BYDAY=WE', $property->getValue()); + + } + + function testValidateValidByYearDayWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYYEARDAY=119'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(0, $result); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYYEARDAY=119', $property->getValue()); + + } + + function testValidateInvalidByYearDayWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYYEARDAY=367;BYDAY=WE'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(1, $result); + $this->assertEquals('BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYDAY=WE', $property->getValue()); + + } + + function testValidateMultipleInvalidByYearDayWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYYEARDAY=380,2,-390;BYDAY=WE'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(2, $result); + $this->assertEquals('BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', $result[1]['message']); + $this->assertEquals(1, $result[1]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYYEARDAY=2;BYDAY=WE', $property->getValue()); + + } + + function testValidateAllInvalidByYearDayWithRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYYEARDAY=455,-480;BYDAY=WE'); + $result = $property->validate(Node::REPAIR); + + $this->assertCount(2, $result); + $this->assertEquals('BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', $result[0]['message']); + $this->assertEquals(1, $result[0]['level']); + $this->assertEquals('BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', $result[1]['message']); + $this->assertEquals(1, $result[1]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYDAY=WE', $property->getValue()); + + } + + function testValidateInvalidByYearDayWithoutRepair() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('RRULE', 'FREQ=YEARLY;COUNT=6;BYYEARDAY=380;BYDAY=WE'); + $result = $property->validate(); + + $this->assertCount(1, $result); + $this->assertEquals('BYYEARDAY in RRULE must have value(s) from -366 to -1, or 1 to 366!', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + $this->assertEquals('FREQ=YEARLY;COUNT=6;BYYEARDAY=380;BYDAY=WE', $property->getValue()); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/TextTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/TextTest.php new file mode 100644 index 00000000000..69ac8aaf181 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/TextTest.php @@ -0,0 +1,96 @@ + '2.1', + 'PROP' => $propValue + ], false); + + // Adding quoted-printable, because we're testing if it gets removed + // automatically. + $doc->PROP['ENCODING'] = 'QUOTED-PRINTABLE'; + $doc->PROP['P1'] = 'V1'; + + + $output = $doc->serialize(); + + + $this->assertEquals("BEGIN:VCARD\r\nVERSION:2.1\r\n$expected\r\nEND:VCARD\r\n", $output); + + } + + function testSerializeVCard21() { + + $this->assertVCard21Serialization( + 'f;oo', + 'PROP;P1=V1:f;oo' + ); + + } + + function testSerializeVCard21Array() { + + $this->assertVCard21Serialization( + ['f;oo', 'bar'], + 'PROP;P1=V1:f\;oo;bar' + ); + + } + function testSerializeVCard21Fold() { + + $this->assertVCard21Serialization( + str_repeat('x', 80), + 'PROP;P1=V1:' . str_repeat('x', 64) . "\r\n " . str_repeat('x', 16) + ); + + } + + + + function testSerializeQuotedPrintable() { + + $this->assertVCard21Serialization( + "foo\r\nbar", + 'PROP;P1=V1;ENCODING=QUOTED-PRINTABLE:foo=0D=0Abar' + ); + } + + function testSerializeQuotedPrintableFold() { + + $this->assertVCard21Serialization( + "foo\r\nbarxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "PROP;P1=V1;ENCODING=QUOTED-PRINTABLE:foo=0D=0Abarxxxxxxxxxxxxxxxxxxxxxxxxxx=\r\n xxx" + ); + + } + + function testValidateMinimumPropValue() { + + $vcard = <<assertEquals(1, count($vcard->validate())); + + $this->assertEquals(1, count($vcard->N->getParts())); + + $vcard->validate(\Sabre\VObject\Node::REPAIR); + + $this->assertEquals(5, count($vcard->N->getParts())); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/UriTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/UriTest.php new file mode 100644 index 00000000000..2c44d8888a4 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/UriTest.php @@ -0,0 +1,27 @@ +serialize(); + $this->assertContains('URL;VALUE=URI:http://example.org/', $output); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/VCard/DateAndOrTimeTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/VCard/DateAndOrTimeTest.php new file mode 100644 index 00000000000..7bc2c67a9e8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/VCard/DateAndOrTimeTest.php @@ -0,0 +1,269 @@ +createProperty('BDAY', $input); + + $this->assertEquals([$output], $prop->getJsonValue()); + + } + + function dates() { + + return [ + [ + "19961022T140000", + "1996-10-22T14:00:00", + ], + [ + "--1022T1400", + "--10-22T14:00", + ], + [ + "---22T14", + "---22T14", + ], + [ + "19850412", + "1985-04-12", + ], + [ + "1985-04", + "1985-04", + ], + [ + "1985", + "1985", + ], + [ + "--0412", + "--04-12", + ], + [ + "T102200", + "T10:22:00", + ], + [ + "T1022", + "T10:22", + ], + [ + "T10", + "T10", + ], + [ + "T-2200", + "T-22:00", + ], + [ + "T102200Z", + "T10:22:00Z", + ], + [ + "T102200-0800", + "T10:22:00-0800", + ], + [ + "T--00", + "T--00", + ], + ]; + + } + + function testSetParts() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setParts([ + new \DateTime('2014-04-02 18:37:00') + ]); + + $this->assertEquals('20140402T183700Z', $prop->getValue()); + + } + + function testSetPartsDateTimeImmutable() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setParts([ + new \DateTimeImmutable('2014-04-02 18:37:00') + ]); + + $this->assertEquals('20140402T183700Z', $prop->getValue()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testSetPartsTooMany() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setParts([ + 1, + 2 + ]); + + } + + function testSetPartsString() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setParts([ + "20140402T183700Z" + ]); + + $this->assertEquals('20140402T183700Z', $prop->getValue()); + + } + + function testSetValueDateTime() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setValue( + new \DateTime('2014-04-02 18:37:00') + ); + + $this->assertEquals('20140402T183700Z', $prop->getValue()); + + } + + function testSetValueDateTimeImmutable() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setValue( + new \DateTimeImmutable('2014-04-02 18:37:00') + ); + + $this->assertEquals('20140402T183700Z', $prop->getValue()); + + } + + function testSetDateTimeOffset() { + + $vcard = new VObject\Component\VCard(); + + $prop = $vcard->createProperty('BDAY'); + $prop->setValue( + new \DateTime('2014-04-02 18:37:00', new \DateTimeZone('America/Toronto')) + ); + + $this->assertEquals('20140402T183700-0400', $prop->getValue()); + + } + + function testGetDateTime() { + + $datetime = new \DateTime('2014-04-02 18:37:00', new \DateTimeZone('America/Toronto')); + + $vcard = new VObject\Component\VCard(); + $prop = $vcard->createProperty('BDAY', $datetime); + + $dt = $prop->getDateTime(); + $this->assertEquals('2014-04-02T18:37:00-04:00', $dt->format('c'), "For some reason this one failed. Current default timezone is: " . date_default_timezone_get()); + + } + + function testGetDate() { + + $datetime = new \DateTime('2014-04-02'); + + $vcard = new VObject\Component\VCard(); + $prop = $vcard->createProperty('BDAY', $datetime, null, 'DATE'); + + $this->assertEquals('DATE', $prop->getValueType()); + $this->assertEquals('BDAY:20140402', rtrim($prop->serialize())); + + } + + function testGetDateIncomplete() { + + $datetime = '--0407'; + + $vcard = new VObject\Component\VCard(); + $prop = $vcard->add('BDAY', $datetime); + + $dt = $prop->getDateTime(); + // Note: if the year changes between the last line and the next line of + // code, this test may fail. + // + // If that happens, head outside and have a drink. + $current = new \DateTime('now'); + $year = $current->format('Y'); + + $this->assertEquals($year . '0407', $dt->format('Ymd')); + + } + + function testGetDateIncompleteFromVCard() { + + $vcard = <<BDAY; + + $dt = $prop->getDateTime(); + // Note: if the year changes between the last line and the next line of + // code, this test may fail. + // + // If that happens, head outside and have a drink. + $current = new \DateTime('now'); + $year = $current->format('Y'); + + $this->assertEquals($year . '0407', $dt->format('Ymd')); + + } + + function testValidate() { + + $datetime = '--0407'; + + $vcard = new VObject\Component\VCard(); + $prop = $vcard->add('BDAY', $datetime); + + $this->assertEquals([], $prop->validate()); + + } + + function testValidateBroken() { + + $datetime = '123'; + + $vcard = new VObject\Component\VCard(); + $prop = $vcard->add('BDAY', $datetime); + + $this->assertEquals([[ + 'level' => 3, + 'message' => 'The supplied value (123) is not a correct DATE-AND-OR-TIME property', + 'node' => $prop, + ]], $prop->validate()); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/VCard/LanguageTagTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/VCard/LanguageTagTest.php new file mode 100644 index 00000000000..c38b6f26438 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Property/VCard/LanguageTagTest.php @@ -0,0 +1,48 @@ +parse($input); + + $this->assertInstanceOf('Sabre\VObject\Property\VCard\LanguageTag', $result->LANG); + + $this->assertEquals('nl', $result->LANG->getValue()); + + $this->assertEquals( + $input, + $result->serialize() + ); + + } + + function testChangeAndSerialize() { + + $input = "BEGIN:VCARD\r\nVERSION:4.0\r\nLANG:nl\r\nEND:VCARD\r\n"; + $mimeDir = new VObject\Parser\MimeDir($input); + + $result = $mimeDir->parse($input); + + $this->assertInstanceOf('Sabre\VObject\Property\VCard\LanguageTag', $result->LANG); + // This replicates what the vcard converter does and triggered a bug in + // the past. + $result->LANG->setValue(['de']); + + $this->assertEquals('de', $result->LANG->getValue()); + + $expected = "BEGIN:VCARD\r\nVERSION:4.0\r\nLANG:de\r\nEND:VCARD\r\n"; + $this->assertEquals( + $expected, + $result->serialize() + ); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/PropertyTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/PropertyTest.php new file mode 100644 index 00000000000..b6241ce8c02 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/PropertyTest.php @@ -0,0 +1,410 @@ +createProperty('propname', 'propvalue'); + $this->assertEquals('PROPNAME', $property->name); + $this->assertEquals('propvalue', $property->__toString()); + $this->assertEquals('propvalue', (string)$property); + $this->assertEquals('propvalue', $property->getValue()); + + } + + function testCreate() { + + $cal = new VCalendar(); + + $params = [ + 'param1' => 'value1', + 'param2' => 'value2', + ]; + + $property = $cal->createProperty('propname', 'propvalue', $params); + + $this->assertEquals('value1', $property['param1']->getValue()); + $this->assertEquals('value2', $property['param2']->getValue()); + + } + + function testSetValue() { + + $cal = new VCalendar(); + + $property = $cal->createProperty('propname', 'propvalue'); + $property->setValue('value2'); + + $this->assertEquals('PROPNAME', $property->name); + $this->assertEquals('value2', $property->__toString()); + + } + + function testParameterExists() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $property['paramname'] = 'paramvalue'; + + $this->assertTrue(isset($property['PARAMNAME'])); + $this->assertTrue(isset($property['paramname'])); + $this->assertFalse(isset($property['foo'])); + + } + + function testParameterGet() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $property['paramname'] = 'paramvalue'; + + $this->assertInstanceOf('Sabre\\VObject\\Parameter', $property['paramname']); + + } + + function testParameterNotExists() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $property['paramname'] = 'paramvalue'; + + $this->assertInternalType('null', $property['foo']); + + } + + function testParameterMultiple() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $property['paramname'] = 'paramvalue'; + $property->add('paramname', 'paramvalue'); + + $this->assertInstanceOf('Sabre\\VObject\\Parameter', $property['paramname']); + $this->assertEquals(2, count($property['paramname']->getParts())); + + } + + function testSetParameterAsString() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $property['paramname'] = 'paramvalue'; + + $this->assertEquals(1, count($property->parameters())); + $this->assertInstanceOf('Sabre\\VObject\\Parameter', $property->parameters['PARAMNAME']); + $this->assertEquals('PARAMNAME', $property->parameters['PARAMNAME']->name); + $this->assertEquals('paramvalue', $property->parameters['PARAMNAME']->getValue()); + + } + + function testUnsetParameter() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $property['paramname'] = 'paramvalue'; + + unset($property['PARAMNAME']); + $this->assertEquals(0, count($property->parameters())); + + } + + function testSerialize() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + + $this->assertEquals("PROPNAME:propvalue\r\n", $property->serialize()); + + } + + function testSerializeParam() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue', [ + 'paramname' => 'paramvalue', + 'paramname2' => 'paramvalue2', + ]); + + $this->assertEquals("PROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propvalue\r\n", $property->serialize()); + + } + + function testSerializeNewLine() { + + $cal = new VCalendar(); + $property = $cal->createProperty('SUMMARY', "line1\nline2"); + + $this->assertEquals("SUMMARY:line1\\nline2\r\n", $property->serialize()); + + } + + function testSerializeLongLine() { + + $cal = new VCalendar(); + $value = str_repeat('!', 200); + $property = $cal->createProperty('propname', $value); + + $expected = "PROPNAME:" . str_repeat('!', 66) . "\r\n " . str_repeat('!', 74) . "\r\n " . str_repeat('!', 60) . "\r\n"; + + $this->assertEquals($expected, $property->serialize()); + + } + + function testSerializeUTF8LineFold() { + + $cal = new VCalendar(); + $value = str_repeat('!', 65) . "\xc3\xa4bla"; // inserted umlaut-a + $property = $cal->createProperty('propname', $value); + $expected = "PROPNAME:" . str_repeat('!', 65) . "\r\n \xc3\xa4bla\r\n"; + $this->assertEquals($expected, $property->serialize()); + + } + + function testGetIterator() { + + $cal = new VCalendar(); + $it = new ElementList([]); + $property = $cal->createProperty('propname', 'propvalue'); + $property->setIterator($it); + $this->assertEquals($it, $property->getIterator()); + + } + + + function testGetIteratorDefault() { + + $cal = new VCalendar(); + $property = $cal->createProperty('propname', 'propvalue'); + $it = $property->getIterator(); + $this->assertTrue($it instanceof ElementList); + $this->assertEquals(1, count($it)); + + } + + function testAddScalar() { + + $cal = new VCalendar(); + $property = $cal->createProperty('EMAIL'); + + $property->add('myparam', 'value'); + + $this->assertEquals(1, count($property->parameters())); + + $this->assertTrue($property->parameters['MYPARAM'] instanceof Parameter); + $this->assertEquals('MYPARAM', $property->parameters['MYPARAM']->name); + $this->assertEquals('value', $property->parameters['MYPARAM']->getValue()); + + } + + function testAddParameter() { + + $cal = new VCalendar(); + $prop = $cal->createProperty('EMAIL'); + + $prop->add('MYPARAM', 'value'); + + $this->assertEquals(1, count($prop->parameters())); + $this->assertEquals('MYPARAM', $prop['myparam']->name); + + } + + function testAddParameterTwice() { + + $cal = new VCalendar(); + $prop = $cal->createProperty('EMAIL'); + + $prop->add('MYPARAM', 'value1'); + $prop->add('MYPARAM', 'value2'); + + $this->assertEquals(1, count($prop->parameters)); + $this->assertEquals(2, count($prop->parameters['MYPARAM']->getParts())); + + $this->assertEquals('MYPARAM', $prop['MYPARAM']->name); + + } + + + function testClone() { + + $cal = new VCalendar(); + $property = $cal->createProperty('EMAIL', 'value'); + $property['FOO'] = 'BAR'; + + $property2 = clone $property; + + $property['FOO'] = 'BAZ'; + $this->assertEquals('BAR', (string)$property2['FOO']); + + } + + function testCreateParams() { + + $cal = new VCalendar(); + $property = $cal->createProperty('X-PROP', 'value', [ + 'param1' => 'value1', + 'param2' => ['value2', 'value3'] + ]); + + $this->assertEquals(1, count($property['PARAM1']->getParts())); + $this->assertEquals(2, count($property['PARAM2']->getParts())); + + } + + function testValidateNonUTF8() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty('X-PROP', "Bla\x00"); + $result = $property->validate(Property::REPAIR); + + $this->assertEquals('Property contained a control character (0x00)', $result[0]['message']); + $this->assertEquals('Bla', $property->getValue()); + + } + + function testValidateControlChars() { + + $s = "chars["; + foreach ([ + 0x7F, 0x5E, 0x5C, 0x3B, 0x3A, 0x2C, 0x22, 0x20, + 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + ] as $c) { + $s .= sprintf('%02X(%c)', $c, $c); + } + $s .= "]end"; + + $calendar = new VCalendar(); + $property = $calendar->createProperty('X-PROP', $s); + $result = $property->validate(Property::REPAIR); + + $this->assertEquals('Property contained a control character (0x7f)', $result[0]['message']); + $this->assertEquals("chars[7F()5E(^)5C(\\\\)3B(\\;)3A(:)2C(\\,)22(\")20( )1F()1E()1D()1C()1B()1A()19()18()17()16()15()14()13()12()11()10()0F()0E()0D()0C()0B()0A(\\n)09(\t)08()07()06()05()04()03()02()01()00()]end", $property->getRawMimeDirValue()); + + } + + function testValidateBadPropertyName() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty("X_*&PROP*", "Bla"); + $result = $property->validate(Property::REPAIR); + + $this->assertEquals($result[0]['message'], 'The propertyname: X_*&PROP* contains invalid characters. Only A-Z, 0-9 and - are allowed'); + $this->assertEquals('X-PROP', $property->name); + + } + + function testGetValue() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty("SUMMARY", null); + $this->assertEquals([], $property->getParts()); + $this->assertNull($property->getValue()); + + $property->setValue([]); + $this->assertEquals([], $property->getParts()); + $this->assertNull($property->getValue()); + + $property->setValue([1]); + $this->assertEquals([1], $property->getParts()); + $this->assertEquals(1, $property->getValue()); + + $property->setValue([1, 2]); + $this->assertEquals([1, 2], $property->getParts()); + $this->assertEquals('1,2', $property->getValue()); + + $property->setValue('str'); + $this->assertEquals(['str'], $property->getParts()); + $this->assertEquals('str', $property->getValue()); + } + + /** + * ElementList should reject this. + * + * @expectedException \LogicException + */ + function testArrayAccessSetInt() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty("X-PROP", null); + + $calendar->add($property); + $calendar->{'X-PROP'}[0] = 'Something!'; + + } + + /** + * ElementList should reject this. + * + * @expectedException \LogicException + */ + function testArrayAccessUnsetInt() { + + $calendar = new VCalendar(); + $property = $calendar->createProperty("X-PROP", null); + + $calendar->add($property); + unset($calendar->{'X-PROP'}[0]); + + } + + function testValidateBadEncoding() { + + $document = new VCalendar(); + $property = $document->add('X-FOO', 'value'); + $property['ENCODING'] = 'invalid'; + + $result = $property->validate(); + + $this->assertEquals('ENCODING=INVALID is not valid for this document type.', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + + } + + function testValidateBadEncodingVCard4() { + + $document = new VCard(['VERSION' => '4.0']); + $property = $document->add('X-FOO', 'value'); + $property['ENCODING'] = 'BASE64'; + + $result = $property->validate(); + + $this->assertEquals('ENCODING parameter is not valid in vCard 4.', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + + } + + function testValidateBadEncodingVCard3() { + + $document = new VCard(['VERSION' => '3.0']); + $property = $document->add('X-FOO', 'value'); + $property['ENCODING'] = 'BASE64'; + + $result = $property->validate(); + + $this->assertEquals('ENCODING=BASE64 is not valid for this document type.', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + + } + + function testValidateBadEncodingVCard21() { + + $document = new VCard(['VERSION' => '2.1']); + $property = $document->add('X-FOO', 'value'); + $property['ENCODING'] = 'B'; + + $result = $property->validate(); + + $this->assertEquals('ENCODING=B is not valid for this document type.', $result[0]['message']); + $this->assertEquals(3, $result[0]['level']); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/ReaderTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ReaderTest.php new file mode 100644 index 00000000000..7c3217b7663 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/ReaderTest.php @@ -0,0 +1,491 @@ +assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + + function testReadStream() { + + $data = "BEGIN:VCALENDAR\r\nEND:VCALENDAR"; + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $data); + rewind($stream); + + $result = Reader::read($stream); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + + function testReadComponentUnixNewLine() { + + $data = "BEGIN:VCALENDAR\nEND:VCALENDAR"; + + $result = Reader::read($data); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + + function testReadComponentLineFold() { + + $data = "BEGIN:\r\n\tVCALENDAR\r\nE\r\n ND:VCALENDAR"; + + $result = Reader::read($data); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + + /** + * @expectedException Sabre\VObject\ParseException + */ + function testReadCorruptComponent() { + + $data = "BEGIN:VCALENDAR\r\nEND:FOO"; + + $result = Reader::read($data); + + } + + /** + * @expectedException Sabre\VObject\ParseException + */ + function testReadCorruptSubComponent() { + + $data = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:FOO\r\nEND:VCALENDAR"; + + $result = Reader::read($data); + + } + + function testReadProperty() { + + $data = "BEGIN:VCALENDAR\r\nSUMMARY:propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->SUMMARY; + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('SUMMARY', $result->name); + $this->assertEquals('propValue', $result->getValue()); + + } + + function testReadPropertyWithNewLine() { + + $data = "BEGIN:VCALENDAR\r\nSUMMARY:Line1\\nLine2\\NLine3\\\\Not the 4th line!\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->SUMMARY; + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('SUMMARY', $result->name); + $this->assertEquals("Line1\nLine2\nLine3\\Not the 4th line!", $result->getValue()); + + } + + function testReadMappedProperty() { + + $data = "BEGIN:VCALENDAR\r\nDTSTART:20110529\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->DTSTART; + $this->assertInstanceOf('Sabre\\VObject\\Property\\ICalendar\\DateTime', $result); + $this->assertEquals('DTSTART', $result->name); + $this->assertEquals('20110529', $result->getValue()); + + } + + function testReadMappedPropertyGrouped() { + + $data = "BEGIN:VCALENDAR\r\nfoo.DTSTART:20110529\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->DTSTART; + $this->assertInstanceOf('Sabre\\VObject\\Property\\ICalendar\\DateTime', $result); + $this->assertEquals('DTSTART', $result->name); + $this->assertEquals('20110529', $result->getValue()); + + } + + /** + * @expectedException Sabre\VObject\ParseException + */ + function testReadBrokenLine() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;propValue"; + $result = Reader::read($data); + + } + + function testReadPropertyInComponent() { + + $data = [ + "BEGIN:VCALENDAR", + "PROPNAME:propValue", + "END:VCALENDAR" + ]; + + $result = Reader::read(implode("\r\n", $data)); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(1, count($result->children())); + $this->assertInstanceOf('Sabre\\VObject\\Property', $result->children()[0]); + $this->assertEquals('PROPNAME', $result->children()[0]->name); + $this->assertEquals('propValue', $result->children()[0]->getValue()); + + } + + function testReadNestedComponent() { + + $data = [ + "BEGIN:VCALENDAR", + "BEGIN:VTIMEZONE", + "BEGIN:DAYLIGHT", + "END:DAYLIGHT", + "END:VTIMEZONE", + "END:VCALENDAR" + ]; + + $result = Reader::read(implode("\r\n", $data)); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(1, count($result->children())); + $this->assertInstanceOf('Sabre\\VObject\\Component', $result->children()[0]); + $this->assertEquals('VTIMEZONE', $result->children()[0]->name); + $this->assertEquals(1, count($result->children()[0]->children())); + $this->assertInstanceOf('Sabre\\VObject\\Component', $result->children()[0]->children()[0]); + $this->assertEquals('DAYLIGHT', $result->children()[0]->children()[0]->name); + + + } + + function testReadPropertyParameter() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue:propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name); + $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue()); + + } + + function testReadPropertyRepeatingParameter() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;N=1;N=2;N=3,4;N=\"5\",6;N=\"7,8\";N=9,10;N=^'11^':propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('N', $result->parameters['N']->name); + $this->assertEquals('1,2,3,4,5,6,7,8,9,10,"11"', $result->parameters['N']->getValue()); + $this->assertEquals([1, 2, 3, 4, 5, 6, "7,8", 9, 10, '"11"'], $result->parameters['N']->getParts()); + + } + + function testReadPropertyRepeatingNamelessGuessedParameter() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;WORK;VOICE;PREF:propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('TYPE', $result->parameters['TYPE']->name); + $this->assertEquals('WORK,VOICE,PREF', $result->parameters['TYPE']->getValue()); + $this->assertEquals(['WORK', 'VOICE', 'PREF'], $result->parameters['TYPE']->getParts()); + + } + + function testReadPropertyNoName() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PRODIGY:propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('TYPE', $result->parameters['TYPE']->name); + $this->assertTrue($result->parameters['TYPE']->noName); + $this->assertEquals('PRODIGY', $result->parameters['TYPE']); + + } + + function testReadPropertyParameterExtraColon() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue:propValue:anotherrandomstring\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue:anotherrandomstring', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name); + $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue()); + + } + + function testReadProperty2Parameters() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue;PARAMNAME2=paramvalue2:propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(2, count($result->parameters())); + $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name); + $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue()); + $this->assertEquals('PARAMNAME2', $result->parameters['PARAMNAME2']->name); + $this->assertEquals('paramvalue2', $result->parameters['PARAMNAME2']->getValue()); + + } + + function testReadPropertyParameterQuoted() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=\"paramvalue\":propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name); + $this->assertEquals('paramvalue', $result->parameters['PARAMNAME']->getValue()); + + } + + function testReadPropertyParameterNewLines() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=paramvalue1^nvalue2^^nvalue3:propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name); + $this->assertEquals("paramvalue1\nvalue2^nvalue3", $result->parameters['PARAMNAME']->getValue()); + + } + + function testReadPropertyParameterQuotedColon() { + + $data = "BEGIN:VCALENDAR\r\nPROPNAME;PARAMNAME=\"param:value\":propValue\r\nEND:VCALENDAR"; + $result = Reader::read($data); + $result = $result->PROPNAME; + + $this->assertInstanceOf('Sabre\\VObject\\Property', $result); + $this->assertEquals('PROPNAME', $result->name); + $this->assertEquals('propValue', $result->getValue()); + $this->assertEquals(1, count($result->parameters())); + $this->assertEquals('PARAMNAME', $result->parameters['PARAMNAME']->name); + $this->assertEquals('param:value', $result->parameters['PARAMNAME']->getValue()); + + } + + function testReadForgiving() { + + $data = [ + "BEGIN:VCALENDAR", + "X_PROP:propValue", + "END:VCALENDAR" + ]; + + $caught = false; + try { + $result = Reader::read(implode("\r\n", $data)); + } catch (ParseException $e) { + $caught = true; + } + + $this->assertEquals(true, $caught); + + $result = Reader::read(implode("\r\n", $data), Reader::OPTION_FORGIVING); + + $expected = implode("\r\n", [ + "BEGIN:VCALENDAR", + "X_PROP:propValue", + "END:VCALENDAR", + "" + ]); + + $this->assertEquals($expected, $result->serialize()); + + } + + function testReadWithInvalidLine() { + + $data = [ + "BEGIN:VCALENDAR", + "DESCRIPTION:propValue", + "Yes, we've actually seen a file with non-idented property values on multiple lines", + "END:VCALENDAR" + ]; + + $caught = false; + try { + $result = Reader::read(implode("\r\n", $data)); + } catch (ParseException $e) { + $caught = true; + } + + $this->assertEquals(true, $caught); + + $result = Reader::read(implode("\r\n", $data), Reader::OPTION_IGNORE_INVALID_LINES); + + $expected = implode("\r\n", [ + "BEGIN:VCALENDAR", + "DESCRIPTION:propValue", + "END:VCALENDAR", + "" + ]); + + $this->assertEquals($expected, $result->serialize()); + + } + + /** + * Reported as Issue 32. + * + * @expectedException \Sabre\VObject\ParseException + */ + function testReadIncompleteFile() { + + $input = <<assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + + function testReadXMLComponent() { + + $data = << + + + + +XML; + + $result = Reader::readXML($data); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + + function testReadXMLStream() { + + $data = << + + + + +XML; + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $data); + rewind($stream); + + $result = Reader::readXML($stream); + + $this->assertInstanceOf('Sabre\\VObject\\Component', $result); + $this->assertEquals('VCALENDAR', $result->name); + $this->assertEquals(0, count($result->children())); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/ByMonthInDailyTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/ByMonthInDailyTest.php new file mode 100644 index 00000000000..204dd36df0f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/ByMonthInDailyTest.php @@ -0,0 +1,58 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand(new DateTime('2013-09-28'), new DateTime('2014-09-11')); + + foreach ($vcal->VEVENT as $event) { + $dates[] = $event->DTSTART->getValue(); + } + + $expectedDates = [ + "20130929T160000Z", + "20131006T160000Z", + "20131013T160000Z", + "20131020T160000Z", + "20131027T160000Z", + "20140907T160000Z" + ]; + + $this->assertEquals($expectedDates, $dates, 'Recursed dates are restricted by month'); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/BySetPosHangTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/BySetPosHangTest.php new file mode 100644 index 00000000000..65e38f536e8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/BySetPosHangTest.php @@ -0,0 +1,60 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand(new DateTime('2015-01-01'), new DateTime('2016-01-01')); + + foreach ($vcal->VEVENT as $event) { + $dates[] = $event->DTSTART->getValue(); + } + + $expectedDates = [ + "20150101T160000Z", + "20150122T160000Z", + "20150219T160000Z", + "20150319T160000Z", + "20150423T150000Z", + "20150521T150000Z", + "20150618T150000Z", + "20150723T150000Z", + "20150820T150000Z", + "20150917T150000Z", + "20151022T150000Z", + "20151119T160000Z", + "20151224T160000Z", + ]; + + $this->assertEquals($expectedDates, $dates); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/ExpandFloatingTimesTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/ExpandFloatingTimesTest.php new file mode 100644 index 00000000000..3d7b6f19c82 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/ExpandFloatingTimesTest.php @@ -0,0 +1,122 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand(new DateTime('2015-01-01'), new DateTime('2015-01-31')); + $output = <<assertVObjectEqualsVObject($output, $vcal); + + } + + function testExpandWithReferenceTimezone() { + + $input = <<assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand( + new DateTime('2015-01-01'), + new DateTime('2015-01-31'), + new DateTimeZone('Europe/Berlin') + ); + + $output = <<assertVObjectEqualsVObject($output, $vcal); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/FifthTuesdayProblemTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/FifthTuesdayProblemTest.php new file mode 100644 index 00000000000..2029ec9c5c6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/FifthTuesdayProblemTest.php @@ -0,0 +1,54 @@ +VEVENT->UID); + + while ($it->valid()) { + $it->next(); + } + + // If we got here, it means we were successful. The bug that was in the + // system before would fail on the 5th tuesday of the month, if the 5th + // tuesday did not exist. + $this->assertTrue(true); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php new file mode 100644 index 00000000000..32dcf9330f7 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php @@ -0,0 +1,60 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand(new DateTime('2015-01-01'), new DateTime('2015-12-01')); + + $result = iterator_to_array($vcal->VEVENT); + + $this->assertEquals(5, count($result)); + + $utc = new DateTimeZone('UTC'); + $expected = [ + new DateTimeImmutable("2015-10-12", $utc), + new DateTimeImmutable("2015-10-15", $utc), + new DateTimeImmutable("2015-10-17", $utc), + new DateTimeImmutable("2015-10-18", $utc), + new DateTimeImmutable("2015-10-20", $utc), + ]; + + $result = array_map(function($ev) {return $ev->DTSTART->getDateTime();}, $result); + $this->assertEquals($expected, $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/IncorrectExpandTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/IncorrectExpandTest.php new file mode 100644 index 00000000000..82278293d61 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/IncorrectExpandTest.php @@ -0,0 +1,62 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand(new DateTime('2011-01-01'), new DateTime('2014-01-01')); + + $output = <<assertVObjectEqualsVObject($output, $vcal); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php new file mode 100644 index 00000000000..491b0e87970 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php @@ -0,0 +1,98 @@ +vcal = new VCalendar(); + + } + + /** + * This bug came from a Fruux customer. This would result in a never-ending + * request. + */ + function testFastForwardTooFar() { + + $ev = $this->vcal->createComponent('VEVENT'); + $ev->UID = 'foobar'; + $ev->DTSTART = '20090420T180000Z'; + $ev->RRULE = 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1'; + + $this->assertFalse($ev->isInTimeRange(new DateTimeImmutable('2012-01-01 12:00:00'), new DateTimeImmutable('3000-01-01 00:00:00'))); + + } + + /** + * Different bug, also likely an infinite loop. + */ + function testYearlyByMonthLoop() { + + $ev = $this->vcal->createComponent('VEVENT'); + $ev->UID = 'uuid'; + $ev->DTSTART = '20120101T154500'; + $ev->DTSTART['TZID'] = 'Europe/Berlin'; + $ev->RRULE = 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA'; + $ev->DTEND = '20120101T164500'; + $ev->DTEND['TZID'] = 'Europe/Berlin'; + + // This recurrence rule by itself is a yearly rule that should happen + // every february. + // + // The BYDAY part expands this to every day of the month, but the + // BYSETPOS limits this to only the 1st day of the month. Very crazy + // way to specify this, and could have certainly been a lot easier. + $this->vcal->add($ev); + + $it = new Recur\EventIterator($this->vcal, 'uuid'); + $it->fastForward(new DateTimeImmutable('2012-01-29 23:00:00', new DateTimeZone('UTC'))); + + $collect = []; + + while ($it->valid()) { + $collect[] = $it->getDtStart(); + if ($it->getDtStart() > new DateTimeImmutable('2013-02-05 22:59:59', new DateTimeZone('UTC'))) { + break; + } + $it->next(); + + } + + $this->assertEquals( + [new DateTimeImmutable('2012-02-01 15:45:00', new DateTimeZone('Europe/Berlin'))], + $collect + ); + + } + + /** + * Something, somewhere produced an ics with an interval set to 0. Because + * this means we increase the current day (or week, month) by 0, this also + * results in an infinite loop. + * + * @expectedException \Sabre\VObject\InvalidDataException + * @return void + */ + function testZeroInterval() { + + $ev = $this->vcal->createComponent('VEVENT'); + $ev->UID = 'uuid'; + $ev->DTSTART = '20120824T145700Z'; + $ev->RRULE = 'FREQ=YEARLY;INTERVAL=0'; + $this->vcal->add($ev); + + $it = new Recur\EventIterator($this->vcal, 'uuid'); + $it->fastForward(new DateTimeImmutable('2013-01-01 23:00:00', new DateTimeZone('UTC'))); + + // if we got this far.. it means we are no longer infinitely looping + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue26Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue26Test.php new file mode 100644 index 00000000000..df8619da579 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue26Test.php @@ -0,0 +1,34 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $it = new EventIterator($vcal, 'bae5d57a98'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue48Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue48Test.php new file mode 100644 index 00000000000..179da816dda --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue48Test.php @@ -0,0 +1,48 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $it = new Recur\EventIterator($vcal, 'foo'); + + $result = iterator_to_array($it); + + $tz = new DateTimeZone('Europe/Moscow'); + + $expected = [ + new DateTimeImmutable('2013-07-10 11:00:00', $tz), + new DateTimeImmutable('2013-07-12 11:00:00', $tz), + new DateTimeImmutable('2013-07-13 11:00:00', $tz), + ]; + + $this->assertEquals($expected, $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue50Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue50Test.php new file mode 100644 index 00000000000..193bdd878de --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/Issue50Test.php @@ -0,0 +1,127 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $it = new Recur\EventIterator($vcal, '1aef0b27-3d92-4581-829a-11999dd36724'); + + $result = []; + foreach ($it as $instance) { + + $result[] = $instance; + + } + + $tz = new DateTimeZone('Europe/Brussels'); + + $this->assertEquals([ + new DateTimeImmutable('2013-07-15 09:00:00', $tz), + new DateTimeImmutable('2013-07-16 07:00:00', $tz), + new DateTimeImmutable('2013-07-17 07:00:00', $tz), + new DateTimeImmutable('2013-07-18 09:00:00', $tz), + new DateTimeImmutable('2013-07-19 07:00:00', $tz), + ], $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MainTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MainTest.php new file mode 100644 index 00000000000..0d8c5b188c6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MainTest.php @@ -0,0 +1,1452 @@ +createComponent('VEVENT'); + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;BYHOUR=10;BYMINUTE=5;BYSECOND=16;BYWEEKNO=32;BYYEARDAY=100,200'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07')); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $this->assertTrue($it->isInfinite()); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + * @depends testValues + */ + function testInvalidFreq() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + $ev->RRULE = 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z'; + $ev->UID = 'foo'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testVCalendarNoUID() { + + $vcal = new VCalendar(); + $it = new EventIterator($vcal); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testVCalendarInvalidUID() { + + $vcal = new VCalendar(); + $it = new EventIterator($vcal, 'foo'); + + } + + /** + * @depends testValues + */ + function testHourly() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=HOURLY;INTERVAL=3;UNTIL=20111025T000000Z'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07 12:00:00', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + $vcal->add($ev); + + $it = new EventIterator($vcal, $ev->UID); + + // Max is to prevent overflow + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07 12:00:00', $tz), + new DateTimeImmutable('2011-10-07 15:00:00', $tz), + new DateTimeImmutable('2011-10-07 18:00:00', $tz), + new DateTimeImmutable('2011-10-07 21:00:00', $tz), + new DateTimeImmutable('2011-10-08 00:00:00', $tz), + new DateTimeImmutable('2011-10-08 03:00:00', $tz), + new DateTimeImmutable('2011-10-08 06:00:00', $tz), + new DateTimeImmutable('2011-10-08 09:00:00', $tz), + new DateTimeImmutable('2011-10-08 12:00:00', $tz), + new DateTimeImmutable('2011-10-08 15:00:00', $tz), + new DateTimeImmutable('2011-10-08 18:00:00', $tz), + new DateTimeImmutable('2011-10-08 21:00:00', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testDaily() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, $ev->UID); + + // Max is to prevent overflow + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07', $tz), + new DateTimeImmutable('2011-10-10', $tz), + new DateTimeImmutable('2011-10-13', $tz), + new DateTimeImmutable('2011-10-16', $tz), + new DateTimeImmutable('2011-10-19', $tz), + new DateTimeImmutable('2011-10-22', $tz), + new DateTimeImmutable('2011-10-25', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testNoRRULE() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, $ev->UID); + + // Max is to prevent overflow + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testDailyByDayByHour() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-08 06:00:00', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Grabbing the next 12 items + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-08 06:00:00', $tz), + new DateTimeImmutable('2011-10-08 07:00:00', $tz), + new DateTimeImmutable('2011-10-09 06:00:00', $tz), + new DateTimeImmutable('2011-10-09 07:00:00', $tz), + new DateTimeImmutable('2011-10-15 06:00:00', $tz), + new DateTimeImmutable('2011-10-15 07:00:00', $tz), + new DateTimeImmutable('2011-10-16 06:00:00', $tz), + new DateTimeImmutable('2011-10-16 07:00:00', $tz), + new DateTimeImmutable('2011-10-22 06:00:00', $tz), + new DateTimeImmutable('2011-10-22 07:00:00', $tz), + new DateTimeImmutable('2011-10-23 06:00:00', $tz), + new DateTimeImmutable('2011-10-23 07:00:00', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testDailyByHour() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2012-10-11 12:00:00', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Grabbing the next 12 items + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2012-10-11 12:00:00', $tz), + new DateTimeImmutable('2012-10-11 13:00:00', $tz), + new DateTimeImmutable('2012-10-11 14:00:00', $tz), + new DateTimeImmutable('2012-10-11 15:00:00', $tz), + new DateTimeImmutable('2012-10-13 10:00:00', $tz), + new DateTimeImmutable('2012-10-13 11:00:00', $tz), + new DateTimeImmutable('2012-10-13 12:00:00', $tz), + new DateTimeImmutable('2012-10-13 13:00:00', $tz), + new DateTimeImmutable('2012-10-13 14:00:00', $tz), + new DateTimeImmutable('2012-10-13 15:00:00', $tz), + new DateTimeImmutable('2012-10-15 10:00:00', $tz), + new DateTimeImmutable('2012-10-15 11:00:00', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testDailyByDay() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Grabbing the next 12 items + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07', $tz), + new DateTimeImmutable('2011-10-11', $tz), + new DateTimeImmutable('2011-10-19', $tz), + new DateTimeImmutable('2011-10-21', $tz), + new DateTimeImmutable('2011-10-25', $tz), + new DateTimeImmutable('2011-11-02', $tz), + new DateTimeImmutable('2011-11-04', $tz), + new DateTimeImmutable('2011-11-08', $tz), + new DateTimeImmutable('2011-11-16', $tz), + new DateTimeImmutable('2011-11-18', $tz), + new DateTimeImmutable('2011-11-22', $tz), + new DateTimeImmutable('2011-11-30', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testWeekly() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;COUNT=10'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Max is to prevent overflow + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07', $tz), + new DateTimeImmutable('2011-10-21', $tz), + new DateTimeImmutable('2011-11-04', $tz), + new DateTimeImmutable('2011-11-18', $tz), + new DateTimeImmutable('2011-12-02', $tz), + new DateTimeImmutable('2011-12-16', $tz), + new DateTimeImmutable('2011-12-30', $tz), + new DateTimeImmutable('2012-01-13', $tz), + new DateTimeImmutable('2012-01-27', $tz), + new DateTimeImmutable('2012-02-10', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testWeeklyByDayByHour() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07 08:00:00', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Grabbing the next 12 items + $max = 15; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07 08:00:00', $tz), + new DateTimeImmutable('2011-10-07 09:00:00', $tz), + new DateTimeImmutable('2011-10-07 10:00:00', $tz), + new DateTimeImmutable('2011-10-18 08:00:00', $tz), + new DateTimeImmutable('2011-10-18 09:00:00', $tz), + new DateTimeImmutable('2011-10-18 10:00:00', $tz), + new DateTimeImmutable('2011-10-19 08:00:00', $tz), + new DateTimeImmutable('2011-10-19 09:00:00', $tz), + new DateTimeImmutable('2011-10-19 10:00:00', $tz), + new DateTimeImmutable('2011-10-21 08:00:00', $tz), + new DateTimeImmutable('2011-10-21 09:00:00', $tz), + new DateTimeImmutable('2011-10-21 10:00:00', $tz), + new DateTimeImmutable('2011-11-01 08:00:00', $tz), + new DateTimeImmutable('2011-11-01 09:00:00', $tz), + new DateTimeImmutable('2011-11-01 10:00:00', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testWeeklyByDaySpecificHour() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07 18:00:00', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Grabbing the next 12 items + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07 18:00:00', $tz), + new DateTimeImmutable('2011-10-18 18:00:00', $tz), + new DateTimeImmutable('2011-10-19 18:00:00', $tz), + new DateTimeImmutable('2011-10-21 18:00:00', $tz), + new DateTimeImmutable('2011-11-01 18:00:00', $tz), + new DateTimeImmutable('2011-11-02 18:00:00', $tz), + new DateTimeImmutable('2011-11-04 18:00:00', $tz), + new DateTimeImmutable('2011-11-15 18:00:00', $tz), + new DateTimeImmutable('2011-11-16 18:00:00', $tz), + new DateTimeImmutable('2011-11-18 18:00:00', $tz), + new DateTimeImmutable('2011-11-29 18:00:00', $tz), + new DateTimeImmutable('2011-11-30 18:00:00', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testWeeklyByDay() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // Grabbing the next 12 items + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07', $tz), + new DateTimeImmutable('2011-10-18', $tz), + new DateTimeImmutable('2011-10-19', $tz), + new DateTimeImmutable('2011-10-21', $tz), + new DateTimeImmutable('2011-11-01', $tz), + new DateTimeImmutable('2011-11-02', $tz), + new DateTimeImmutable('2011-11-04', $tz), + new DateTimeImmutable('2011-11-15', $tz), + new DateTimeImmutable('2011-11-16', $tz), + new DateTimeImmutable('2011-11-18', $tz), + new DateTimeImmutable('2011-11-29', $tz), + new DateTimeImmutable('2011-11-30', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthly() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=3;COUNT=5'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-12-05', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 14; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-12-05', $tz), + new DateTimeImmutable('2012-03-05', $tz), + new DateTimeImmutable('2012-06-05', $tz), + new DateTimeImmutable('2012-09-05', $tz), + new DateTimeImmutable('2012-12-05', $tz), + ], + $result + ); + + + } + + /** + * @depends testValues + */ + function testMonthlyEndOfMonth() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=12'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-12-31', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 14; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-12-31', $tz), + new DateTimeImmutable('2012-08-31', $tz), + new DateTimeImmutable('2012-10-31', $tz), + new DateTimeImmutable('2012-12-31', $tz), + new DateTimeImmutable('2013-08-31', $tz), + new DateTimeImmutable('2013-10-31', $tz), + new DateTimeImmutable('2013-12-31', $tz), + new DateTimeImmutable('2014-08-31', $tz), + new DateTimeImmutable('2014-10-31', $tz), + new DateTimeImmutable('2014-12-31', $tz), + new DateTimeImmutable('2015-08-31', $tz), + new DateTimeImmutable('2015-10-31', $tz), + ], + $result + ); + + + } + + /** + * @depends testValues + */ + function testMonthlyByMonthDay() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-01-01', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 14; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-01-01', $tz), + new DateTimeImmutable('2011-01-25', $tz), + new DateTimeImmutable('2011-01-31', $tz), + new DateTimeImmutable('2011-06-01', $tz), + new DateTimeImmutable('2011-06-24', $tz), + new DateTimeImmutable('2011-11-01', $tz), + new DateTimeImmutable('2011-11-24', $tz), + new DateTimeImmutable('2012-04-01', $tz), + new DateTimeImmutable('2012-04-24', $tz), + ], + $result + ); + + } + + /** + * A pretty slow test. Had to be marked as 'medium' for phpunit to not die + * after 1 second. Would be good to optimize later. + * + * @depends testValues + * @medium + */ + function testMonthlyByDay() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-01-03', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-01-03', $tz), + new DateTimeImmutable('2011-01-05', $tz), + new DateTimeImmutable('2011-01-10', $tz), + new DateTimeImmutable('2011-01-17', $tz), + new DateTimeImmutable('2011-01-18', $tz), + new DateTimeImmutable('2011-01-20', $tz), + new DateTimeImmutable('2011-01-24', $tz), + new DateTimeImmutable('2011-01-31', $tz), + new DateTimeImmutable('2011-03-02', $tz), + new DateTimeImmutable('2011-03-07', $tz), + new DateTimeImmutable('2011-03-14', $tz), + new DateTimeImmutable('2011-03-17', $tz), + new DateTimeImmutable('2011-03-21', $tz), + new DateTimeImmutable('2011-03-22', $tz), + new DateTimeImmutable('2011-03-28', $tz), + new DateTimeImmutable('2011-05-02', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthlyByDayByMonthDay() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-08-01', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-08-01', $tz), + new DateTimeImmutable('2012-10-01', $tz), + new DateTimeImmutable('2013-04-01', $tz), + new DateTimeImmutable('2013-07-01', $tz), + new DateTimeImmutable('2014-09-01', $tz), + new DateTimeImmutable('2014-12-01', $tz), + new DateTimeImmutable('2015-06-01', $tz), + new DateTimeImmutable('2016-02-01', $tz), + new DateTimeImmutable('2016-08-01', $tz), + new DateTimeImmutable('2017-05-01', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testMonthlyByDayBySetPos() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-01-03', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-01-03', $tz), + new DateTimeImmutable('2011-01-31', $tz), + new DateTimeImmutable('2011-02-01', $tz), + new DateTimeImmutable('2011-02-28', $tz), + new DateTimeImmutable('2011-03-01', $tz), + new DateTimeImmutable('2011-03-31', $tz), + new DateTimeImmutable('2011-04-01', $tz), + new DateTimeImmutable('2011-04-29', $tz), + new DateTimeImmutable('2011-05-02', $tz), + new DateTimeImmutable('2011-05-31', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testYearly() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=10;INTERVAL=3'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-01-01', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-01-01', $tz), + new DateTimeImmutable('2014-01-01', $tz), + new DateTimeImmutable('2017-01-01', $tz), + new DateTimeImmutable('2020-01-01', $tz), + new DateTimeImmutable('2023-01-01', $tz), + new DateTimeImmutable('2026-01-01', $tz), + new DateTimeImmutable('2029-01-01', $tz), + new DateTimeImmutable('2032-01-01', $tz), + new DateTimeImmutable('2035-01-01', $tz), + new DateTimeImmutable('2038-01-01', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testYearlyLeapYear() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=3'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2012-02-29', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2012-02-29', $tz), + new DateTimeImmutable('2016-02-29', $tz), + new DateTimeImmutable('2020-02-29', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testYearlyByMonth() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-04-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-04-07', $tz), + new DateTimeImmutable('2011-10-07', $tz), + new DateTimeImmutable('2015-04-07', $tz), + new DateTimeImmutable('2015-10-07', $tz), + new DateTimeImmutable('2019-04-07', $tz), + new DateTimeImmutable('2019-10-07', $tz), + new DateTimeImmutable('2023-04-07', $tz), + new DateTimeImmutable('2023-10-07', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testYearlyByMonthByDay() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-04-04', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-04-04', $tz), + new DateTimeImmutable('2011-04-24', $tz), + new DateTimeImmutable('2011-10-03', $tz), + new DateTimeImmutable('2011-10-30', $tz), + new DateTimeImmutable('2016-04-04', $tz), + new DateTimeImmutable('2016-04-24', $tz), + new DateTimeImmutable('2016-10-03', $tz), + new DateTimeImmutable('2016-10-30', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testFastForward() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU'; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-04-04', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + // The idea is that we're fast-forwarding too far in the future, so + // there will be no results left. + $it->fastForward(new DateTimeImmutable('2020-05-05', new DateTimeZone('UTC'))); + + $max = 20; + $result = []; + while ($item = $it->current()) { + + $result[] = $item; + $max--; + + if (!$max) break; + $it->next(); + + } + + $this->assertEquals([], $result); + + } + + /** + * @depends testValues + */ + function testFastForwardAllDayEventThatStopAtTheStartTime() { + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=DAILY'; + + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-04-04', new DateTimeZone('UTC'))); + $ev->add($dtStart); + + $dtEnd = $vcal->createProperty('DTSTART'); + $dtEnd->setDateTime(new DateTimeImmutable('2011-04-05', new DateTimeZone('UTC'))); + $ev->add($dtEnd); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $it->fastForward(new DateTimeImmutable('2011-04-05T000000', new DateTimeZone('UTC'))); + + $this->assertEquals(new DateTimeImmutable('2011-04-06'), $it->getDTStart()); + } + + /** + * @depends testValues + */ + function testComplexExclusions() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RRULE = 'FREQ=YEARLY;COUNT=10'; + $dtStart = $vcal->createProperty('DTSTART'); + + $tz = new DateTimeZone('Canada/Eastern'); + $dtStart->setDateTime(new DateTimeImmutable('2011-01-01 13:50:20', $tz)); + + $exDate1 = $vcal->createProperty('EXDATE'); + $exDate1->setDateTimes([new DateTimeImmutable('2012-01-01 13:50:20', $tz), new DateTimeImmutable('2014-01-01 13:50:20', $tz)]); + $exDate2 = $vcal->createProperty('EXDATE'); + $exDate2->setDateTimes([new DateTimeImmutable('2016-01-01 13:50:20', $tz)]); + + $ev->add($dtStart); + $ev->add($exDate1); + $ev->add($exDate2); + + $vcal->add($ev); + + $it = new EventIterator($vcal, (string)$ev->UID); + + $max = 20; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $this->assertEquals( + [ + new DateTimeImmutable('2011-01-01 13:50:20', $tz), + new DateTimeImmutable('2013-01-01 13:50:20', $tz), + new DateTimeImmutable('2015-01-01 13:50:20', $tz), + new DateTimeImmutable('2017-01-01 13:50:20', $tz), + new DateTimeImmutable('2018-01-01 13:50:20', $tz), + new DateTimeImmutable('2019-01-01 13:50:20', $tz), + new DateTimeImmutable('2020-01-01 13:50:20', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + */ + function testOverridenEvent() { + + $vcal = new VCalendar(); + + $ev1 = $vcal->createComponent('VEVENT'); + $ev1->UID = 'overridden'; + $ev1->RRULE = 'FREQ=DAILY;COUNT=10'; + $ev1->DTSTART = '20120107T120000Z'; + $ev1->SUMMARY = 'baseEvent'; + + $vcal->add($ev1); + + // ev2 overrides an event, and puts it on 2pm instead. + $ev2 = $vcal->createComponent('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120110T120000Z'; + $ev2->DTSTART = '20120110T140000Z'; + $ev2->SUMMARY = 'Event 2'; + + $vcal->add($ev2); + + // ev3 overrides an event, and puts it 2 days and 2 hours later + $ev3 = $vcal->createComponent('VEVENT'); + $ev3->UID = 'overridden'; + $ev3->{'RECURRENCE-ID'} = '20120113T120000Z'; + $ev3->DTSTART = '20120115T140000Z'; + $ev3->SUMMARY = 'Event 3'; + + $vcal->add($ev3); + + $it = new EventIterator($vcal, 'overridden'); + + $dates = []; + $summaries = []; + while ($it->valid()) { + + $dates[] = $it->getDTStart(); + $summaries[] = (string)$it->getEventObject()->SUMMARY; + $it->next(); + + } + + $tz = new DateTimeZone('UTC'); + $this->assertEquals([ + new DateTimeImmutable('2012-01-07 12:00:00', $tz), + new DateTimeImmutable('2012-01-08 12:00:00', $tz), + new DateTimeImmutable('2012-01-09 12:00:00', $tz), + new DateTimeImmutable('2012-01-10 14:00:00', $tz), + new DateTimeImmutable('2012-01-11 12:00:00', $tz), + new DateTimeImmutable('2012-01-12 12:00:00', $tz), + new DateTimeImmutable('2012-01-14 12:00:00', $tz), + new DateTimeImmutable('2012-01-15 12:00:00', $tz), + new DateTimeImmutable('2012-01-15 14:00:00', $tz), + new DateTimeImmutable('2012-01-16 12:00:00', $tz), + ], $dates); + + $this->assertEquals([ + 'baseEvent', + 'baseEvent', + 'baseEvent', + 'Event 2', + 'baseEvent', + 'baseEvent', + 'baseEvent', + 'baseEvent', + 'Event 3', + 'baseEvent', + ], $summaries); + + } + + /** + * @depends testValues + */ + function testOverridenEvent2() { + + $vcal = new VCalendar(); + + $ev1 = $vcal->createComponent('VEVENT'); + $ev1->UID = 'overridden'; + $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3'; + $ev1->DTSTART = '20120112T120000Z'; + $ev1->SUMMARY = 'baseEvent'; + + $vcal->add($ev1); + + // ev2 overrides an event, and puts it 6 days earlier instead. + $ev2 = $vcal->createComponent('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120119T120000Z'; + $ev2->DTSTART = '20120113T120000Z'; + $ev2->SUMMARY = 'Override!'; + + $vcal->add($ev2); + + $it = new EventIterator($vcal, 'overridden'); + + $dates = []; + $summaries = []; + while ($it->valid()) { + + $dates[] = $it->getDTStart(); + $summaries[] = (string)$it->getEventObject()->SUMMARY; + $it->next(); + + } + + $tz = new DateTimeZone('UTC'); + $this->assertEquals([ + new DateTimeImmutable('2012-01-12 12:00:00', $tz), + new DateTimeImmutable('2012-01-13 12:00:00', $tz), + new DateTimeImmutable('2012-01-26 12:00:00', $tz), + + ], $dates); + + $this->assertEquals([ + 'baseEvent', + 'Override!', + 'baseEvent', + ], $summaries); + + } + + /** + * @depends testValues + */ + function testOverridenEventNoValuesExpected() { + + $vcal = new VCalendar(); + $ev1 = $vcal->createComponent('VEVENT'); + + $ev1->UID = 'overridden'; + $ev1->RRULE = 'FREQ=WEEKLY;COUNT=3'; + $ev1->DTSTART = '20120124T120000Z'; + $ev1->SUMMARY = 'baseEvent'; + + $vcal->add($ev1); + + // ev2 overrides an event, and puts it 6 days earlier instead. + $ev2 = $vcal->createComponent('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120131T120000Z'; + $ev2->DTSTART = '20120125T120000Z'; + $ev2->SUMMARY = 'Override!'; + + $vcal->add($ev2); + + $it = new EventIterator($vcal, 'overridden'); + + $dates = []; + $summaries = []; + + // The reported problem was specifically related to the VCALENDAR + // expansion. In this parcitular case, we had to forward to the 28th of + // january. + $it->fastForward(new DateTimeImmutable('2012-01-28 23:00:00')); + + // We stop the loop when it hits the 6th of februari. Normally this + // iterator would hit 24, 25 (overriden from 31) and 7 feb but because + // we 'filter' from the 28th till the 6th, we should get 0 results. + while ($it->valid() && $it->getDTStart() < new DateTimeImmutable('2012-02-06 23:00:00')) { + + $dates[] = $it->getDTStart(); + $summaries[] = (string)$it->getEventObject()->SUMMARY; + $it->next(); + + } + + $this->assertEquals([], $dates); + $this->assertEquals([], $summaries); + + } + + /** + * @depends testValues + */ + function testRDATE() { + + $vcal = new VCalendar(); + $ev = $vcal->createComponent('VEVENT'); + + $ev->UID = 'bla'; + $ev->RDATE = [ + new DateTimeImmutable('2014-08-07', new DateTimeZone('UTC')), + new DateTimeImmutable('2014-08-08', new DateTimeZone('UTC')), + ]; + $dtStart = $vcal->createProperty('DTSTART'); + $dtStart->setDateTime(new DateTimeImmutable('2011-10-07', new DateTimeZone('UTC'))); + + $ev->add($dtStart); + + $vcal->add($ev); + + $it = new EventIterator($vcal, $ev->UID); + + // Max is to prevent overflow + $max = 12; + $result = []; + foreach ($it as $item) { + + $result[] = $item; + $max--; + + if (!$max) break; + + } + + $tz = new DateTimeZone('UTC'); + + $this->assertEquals( + [ + new DateTimeImmutable('2011-10-07', $tz), + new DateTimeImmutable('2014-08-07', $tz), + new DateTimeImmutable('2014-08-08', $tz), + ], + $result + ); + + } + + /** + * @depends testValues + * @expectedException \InvalidArgumentException + */ + function testNoMasterBadUID() { + + $vcal = new VCalendar(); + // ev2 overrides an event, and puts it on 2pm instead. + $ev2 = $vcal->createComponent('VEVENT'); + $ev2->UID = 'overridden'; + $ev2->{'RECURRENCE-ID'} = '20120110T120000Z'; + $ev2->DTSTART = '20120110T140000Z'; + $ev2->SUMMARY = 'Event 2'; + + $vcal->add($ev2); + + // ev3 overrides an event, and puts it 2 days and 2 hours later + $ev3 = $vcal->createComponent('VEVENT'); + $ev3->UID = 'overridden'; + $ev3->{'RECURRENCE-ID'} = '20120113T120000Z'; + $ev3->DTSTART = '20120115T140000Z'; + $ev3->SUMMARY = 'Event 3'; + + $vcal->add($ev3); + + $it = new EventIterator($vcal, 'broken'); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MaxInstancesTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MaxInstancesTest.php new file mode 100644 index 00000000000..1e94032a308 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MaxInstancesTest.php @@ -0,0 +1,41 @@ +expand(new DateTime('2014-08-01'), new DateTime('2014-09-01')); + + } finally { + Settings::$maxRecurrences = $temp; + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MissingOverriddenTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MissingOverriddenTest.php new file mode 100644 index 00000000000..fc8c518dcda --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/MissingOverriddenTest.php @@ -0,0 +1,62 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal = $vcal->expand(new DateTime('2011-01-01'), new DateTime('2015-01-01')); + + $output = <<assertVObjectEqualsVObject($output, $vcal); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/NoInstancesTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/NoInstancesTest.php new file mode 100644 index 00000000000..84c7ec13e61 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/NoInstancesTest.php @@ -0,0 +1,40 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $it = new EventIterator($vcal, 'foo'); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/OverrideFirstEventTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/OverrideFirstEventTest.php new file mode 100644 index 00000000000..78c0782c877 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/OverrideFirstEventTest.php @@ -0,0 +1,121 @@ +expand(new DateTime('2014-08-01'), new DateTime('2014-09-01')); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $vcal + ); + + + } + + function testRemoveFirstEvent() { + + $input = <<expand(new DateTime('2014-08-01'), new DateTime('2014-08-19')); + + $expected = <<assertVObjectEqualsVObject( + $expected, + $vcal + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/SameDateForRecurringEventsTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/SameDateForRecurringEventsTest.php new file mode 100644 index 00000000000..b89f25aaa49 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/EventIterator/SameDateForRecurringEventsTest.php @@ -0,0 +1,55 @@ +getComponents()); + + $this->assertEquals(4, iterator_count($eventIterator), 'in ICS 4 events'); + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/RDateIteratorTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/RDateIteratorTest.php new file mode 100644 index 00000000000..e2852dc6986 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/RDateIteratorTest.php @@ -0,0 +1,78 @@ +assertEquals( + $expected, + iterator_to_array($it) + ); + + $this->assertFalse($it->isInfinite()); + + } + + function testTimezone() { + + $tz = new DateTimeZone('Europe/Berlin'); + $it = new RDateIterator('20140901T000000,20141001T000000', new DateTimeImmutable('2014-08-01 00:00:00', $tz)); + + $expected = [ + new DateTimeImmutable('2014-08-01 00:00:00', $tz), + new DateTimeImmutable('2014-09-01 00:00:00', $tz), + new DateTimeImmutable('2014-10-01 00:00:00', $tz), + ]; + + $this->assertEquals( + $expected, + iterator_to_array($it) + ); + + + $this->assertFalse($it->isInfinite()); + + } + + + function testFastForward() { + + $utc = new DateTimeZone('UTC'); + $it = new RDateIterator('20140901T000000Z,20141001T000000Z', new DateTimeImmutable('2014-08-01 00:00:00', $utc)); + + $it->fastForward(new DateTimeImmutable('2014-08-15 00:00:00')); + + $result = []; + while ($it->valid()) { + $result[] = $it->current(); + $it->next(); + } + + $expected = [ + new DateTimeImmutable('2014-09-01 00:00:00', $utc), + new DateTimeImmutable('2014-10-01 00:00:00', $utc), + ]; + + $this->assertEquals( + $expected, + $result + ); + + $this->assertFalse($it->isInfinite()); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/RRuleIteratorTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/RRuleIteratorTest.php new file mode 100644 index 00000000000..84649f41f37 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Recur/RRuleIteratorTest.php @@ -0,0 +1,995 @@ +parse( + 'FREQ=HOURLY;INTERVAL=3;COUNT=12', + '2011-10-07 12:00:00', + [ + '2011-10-07 12:00:00', + '2011-10-07 15:00:00', + '2011-10-07 18:00:00', + '2011-10-07 21:00:00', + '2011-10-08 00:00:00', + '2011-10-08 03:00:00', + '2011-10-08 06:00:00', + '2011-10-08 09:00:00', + '2011-10-08 12:00:00', + '2011-10-08 15:00:00', + '2011-10-08 18:00:00', + '2011-10-08 21:00:00', + ] + ); + + } + + function testDaily() { + + $this->parse( + 'FREQ=DAILY;INTERVAL=3;UNTIL=20111025T000000Z', + '2011-10-07', + [ + '2011-10-07 00:00:00', + '2011-10-10 00:00:00', + '2011-10-13 00:00:00', + '2011-10-16 00:00:00', + '2011-10-19 00:00:00', + '2011-10-22 00:00:00', + '2011-10-25 00:00:00', + ] + ); + + } + + function testDailyByDayByHour() { + + $this->parse( + 'FREQ=DAILY;BYDAY=SA,SU;BYHOUR=6,7', + '2011-10-08 06:00:00', + [ + '2011-10-08 06:00:00', + '2011-10-08 07:00:00', + '2011-10-09 06:00:00', + '2011-10-09 07:00:00', + '2011-10-15 06:00:00', + '2011-10-15 07:00:00', + '2011-10-16 06:00:00', + '2011-10-16 07:00:00', + '2011-10-22 06:00:00', + '2011-10-22 07:00:00', + '2011-10-23 06:00:00', + '2011-10-23 07:00:00', + ] + ); + + } + + function testDailyByHour() { + + $this->parse( + 'FREQ=DAILY;INTERVAL=2;BYHOUR=10,11,12,13,14,15', + '2012-10-11 12:00:00', + [ + '2012-10-11 12:00:00', + '2012-10-11 13:00:00', + '2012-10-11 14:00:00', + '2012-10-11 15:00:00', + '2012-10-13 10:00:00', + '2012-10-13 11:00:00', + '2012-10-13 12:00:00', + '2012-10-13 13:00:00', + '2012-10-13 14:00:00', + '2012-10-13 15:00:00', + '2012-10-15 10:00:00', + '2012-10-15 11:00:00', + ] + ); + + } + + function testDailyByDay() { + + $this->parse( + 'FREQ=DAILY;INTERVAL=2;BYDAY=TU,WE,FR', + '2011-10-07 12:00:00', + [ + '2011-10-07 12:00:00', + '2011-10-11 12:00:00', + '2011-10-19 12:00:00', + '2011-10-21 12:00:00', + '2011-10-25 12:00:00', + '2011-11-02 12:00:00', + '2011-11-04 12:00:00', + '2011-11-08 12:00:00', + '2011-11-16 12:00:00', + '2011-11-18 12:00:00', + '2011-11-22 12:00:00', + '2011-11-30 12:00:00', + ] + ); + + } + + function testDailyCount() { + + $this->parse( + 'FREQ=DAILY;COUNT=5', + '2014-08-01 18:03:00', + [ + '2014-08-01 18:03:00', + '2014-08-02 18:03:00', + '2014-08-03 18:03:00', + '2014-08-04 18:03:00', + '2014-08-05 18:03:00', + ] + ); + + } + + function testDailyByMonth() { + + $this->parse( + 'FREQ=DAILY;BYMONTH=9,10;BYDAY=SU', + '2007-10-04 16:00:00', + [ + '2013-09-29 16:00:00', + '2013-10-06 16:00:00', + '2013-10-13 16:00:00', + '2013-10-20 16:00:00', + '2013-10-27 16:00:00', + '2014-09-07 16:00:00' + ], + '2013-09-28' + ); + + } + + function testWeekly() { + + $this->parse( + 'FREQ=WEEKLY;INTERVAL=2;COUNT=10', + '2011-10-07 00:00:00', + [ + '2011-10-07 00:00:00', + '2011-10-21 00:00:00', + '2011-11-04 00:00:00', + '2011-11-18 00:00:00', + '2011-12-02 00:00:00', + '2011-12-16 00:00:00', + '2011-12-30 00:00:00', + '2012-01-13 00:00:00', + '2012-01-27 00:00:00', + '2012-02-10 00:00:00', + ] + ); + + } + + function testWeeklyByDay() { + + $this->parse( + 'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=MO;WKST=SA', + '2014-08-01 00:00:00', + [ + '2014-08-01 00:00:00', + '2014-08-04 00:00:00', + '2014-08-11 00:00:00', + '2014-08-18 00:00:00', + ] + ); + + } + + function testWeeklyByDay2() { + + $this->parse( + 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU', + '2011-10-07 00:00:00', + [ + '2011-10-07 00:00:00', + '2011-10-18 00:00:00', + '2011-10-19 00:00:00', + '2011-10-21 00:00:00', + '2011-11-01 00:00:00', + '2011-11-02 00:00:00', + '2011-11-04 00:00:00', + '2011-11-15 00:00:00', + '2011-11-16 00:00:00', + '2011-11-18 00:00:00', + '2011-11-29 00:00:00', + '2011-11-30 00:00:00', + ] + ); + + } + + function testWeeklyByDayByHour() { + + $this->parse( + 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=MO;BYHOUR=8,9,10', + '2011-10-07 08:00:00', + [ + '2011-10-07 08:00:00', + '2011-10-07 09:00:00', + '2011-10-07 10:00:00', + '2011-10-18 08:00:00', + '2011-10-18 09:00:00', + '2011-10-18 10:00:00', + '2011-10-19 08:00:00', + '2011-10-19 09:00:00', + '2011-10-19 10:00:00', + '2011-10-21 08:00:00', + '2011-10-21 09:00:00', + '2011-10-21 10:00:00', + '2011-11-01 08:00:00', + '2011-11-01 09:00:00', + '2011-11-01 10:00:00', + ] + ); + + } + + function testWeeklyByDaySpecificHour() { + + $this->parse( + 'FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,WE,FR;WKST=SU', + '2011-10-07 18:00:00', + [ + '2011-10-07 18:00:00', + '2011-10-18 18:00:00', + '2011-10-19 18:00:00', + '2011-10-21 18:00:00', + '2011-11-01 18:00:00', + '2011-11-02 18:00:00', + '2011-11-04 18:00:00', + '2011-11-15 18:00:00', + '2011-11-16 18:00:00', + '2011-11-18 18:00:00', + '2011-11-29 18:00:00', + '2011-11-30 18:00:00', + ] + ); + + } + + function testMonthly() { + + $this->parse( + 'FREQ=MONTHLY;INTERVAL=3;COUNT=5', + '2011-12-05 00:00:00', + [ + '2011-12-05 00:00:00', + '2012-03-05 00:00:00', + '2012-06-05 00:00:00', + '2012-09-05 00:00:00', + '2012-12-05 00:00:00', + ] + ); + + } + + function testMonlthyEndOfMonth() { + + $this->parse( + 'FREQ=MONTHLY;INTERVAL=2;COUNT=12', + '2011-12-31 00:00:00', + [ + '2011-12-31 00:00:00', + '2012-08-31 00:00:00', + '2012-10-31 00:00:00', + '2012-12-31 00:00:00', + '2013-08-31 00:00:00', + '2013-10-31 00:00:00', + '2013-12-31 00:00:00', + '2014-08-31 00:00:00', + '2014-10-31 00:00:00', + '2014-12-31 00:00:00', + '2015-08-31 00:00:00', + '2015-10-31 00:00:00', + ] + ); + + } + + function testMonthlyByMonthDay() { + + $this->parse( + 'FREQ=MONTHLY;INTERVAL=5;COUNT=9;BYMONTHDAY=1,31,-7', + '2011-01-01 00:00:00', + [ + '2011-01-01 00:00:00', + '2011-01-25 00:00:00', + '2011-01-31 00:00:00', + '2011-06-01 00:00:00', + '2011-06-24 00:00:00', + '2011-11-01 00:00:00', + '2011-11-24 00:00:00', + '2012-04-01 00:00:00', + '2012-04-24 00:00:00', + ] + ); + + } + + function testMonthlyByDay() { + + $this->parse( + 'FREQ=MONTHLY;INTERVAL=2;COUNT=16;BYDAY=MO,-2TU,+1WE,3TH', + '2011-01-03 00:00:00', + [ + '2011-01-03 00:00:00', + '2011-01-05 00:00:00', + '2011-01-10 00:00:00', + '2011-01-17 00:00:00', + '2011-01-18 00:00:00', + '2011-01-20 00:00:00', + '2011-01-24 00:00:00', + '2011-01-31 00:00:00', + '2011-03-02 00:00:00', + '2011-03-07 00:00:00', + '2011-03-14 00:00:00', + '2011-03-17 00:00:00', + '2011-03-21 00:00:00', + '2011-03-22 00:00:00', + '2011-03-28 00:00:00', + '2011-05-02 00:00:00', + ] + ); + + } + + function testMonthlyByDayByMonthDay() { + + $this->parse( + 'FREQ=MONTHLY;COUNT=10;BYDAY=MO;BYMONTHDAY=1', + '2011-08-01 00:00:00', + [ + '2011-08-01 00:00:00', + '2012-10-01 00:00:00', + '2013-04-01 00:00:00', + '2013-07-01 00:00:00', + '2014-09-01 00:00:00', + '2014-12-01 00:00:00', + '2015-06-01 00:00:00', + '2016-02-01 00:00:00', + '2016-08-01 00:00:00', + '2017-05-01 00:00:00', + ] + ); + + } + + function testMonthlyByDayBySetPos() { + + $this->parse( + 'FREQ=MONTHLY;COUNT=10;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1,-1', + '2011-01-03 00:00:00', + [ + '2011-01-03 00:00:00', + '2011-01-31 00:00:00', + '2011-02-01 00:00:00', + '2011-02-28 00:00:00', + '2011-03-01 00:00:00', + '2011-03-31 00:00:00', + '2011-04-01 00:00:00', + '2011-04-29 00:00:00', + '2011-05-02 00:00:00', + '2011-05-31 00:00:00', + ] + ); + + } + + function testYearly() { + + $this->parse( + 'FREQ=YEARLY;COUNT=10;INTERVAL=3', + '2011-01-01 00:00:00', + [ + '2011-01-01 00:00:00', + '2014-01-01 00:00:00', + '2017-01-01 00:00:00', + '2020-01-01 00:00:00', + '2023-01-01 00:00:00', + '2026-01-01 00:00:00', + '2029-01-01 00:00:00', + '2032-01-01 00:00:00', + '2035-01-01 00:00:00', + '2038-01-01 00:00:00', + ] + ); + } + + function testYearlyLeapYear() { + + $this->parse( + 'FREQ=YEARLY;COUNT=3', + '2012-02-29 00:00:00', + [ + '2012-02-29 00:00:00', + '2016-02-29 00:00:00', + '2020-02-29 00:00:00', + ] + ); + } + + function testYearlyByMonth() { + + $this->parse( + 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYMONTH=4,10', + '2011-04-07 00:00:00', + [ + '2011-04-07 00:00:00', + '2011-10-07 00:00:00', + '2015-04-07 00:00:00', + '2015-10-07 00:00:00', + '2019-04-07 00:00:00', + '2019-10-07 00:00:00', + '2023-04-07 00:00:00', + '2023-10-07 00:00:00', + ] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testYearlyByMonthInvalidValue1() { + + $this->parse( + 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0', + '2011-04-07 00:00:00', + [] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testYearlyByMonthInvalidValue2() { + + $this->parse( + 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=bla', + '2011-04-07 00:00:00', + [] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testYearlyByMonthManyInvalidValues() { + + $this->parse( + 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=0,bla', + '2011-04-07 00:00:00', + [] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testYearlyByMonthEmptyValue() { + + $this->parse( + 'FREQ=YEARLY;COUNT=6;BYMONTHDAY=24;BYMONTH=', + '2011-04-07 00:00:00', + [] + ); + + } + + function testYearlyByMonthByDay() { + + $this->parse( + 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU', + '2011-04-04 00:00:00', + [ + '2011-04-04 00:00:00', + '2011-04-24 00:00:00', + '2011-10-03 00:00:00', + '2011-10-30 00:00:00', + '2016-04-04 00:00:00', + '2016-04-24 00:00:00', + '2016-10-03 00:00:00', + '2016-10-30 00:00:00', + ] + ); + + } + + function testYearlyByYearDay() { + + $this->parse( + 'FREQ=YEARLY;COUNT=7;INTERVAL=2;BYYEARDAY=190', + '2011-07-10 03:07:00', + [ + '2011-07-10 03:07:00', + '2013-07-10 03:07:00', + '2015-07-10 03:07:00', + '2017-07-10 03:07:00', + '2019-07-10 03:07:00', + '2021-07-10 03:07:00', + '2023-07-10 03:07:00', + ] + ); + + } + + function testYearlyByYearDayMultiple() { + + $this->parse( + 'FREQ=YEARLY;COUNT=8;INTERVAL=3;BYYEARDAY=190,301', + '2011-07-10 14:53:11', + [ + '2011-07-10 14:53:11', + '2011-10-29 14:53:11', + '2014-07-10 14:53:11', + '2014-10-29 14:53:11', + '2017-07-10 14:53:11', + '2017-10-29 14:53:11', + '2020-07-09 14:53:11', + '2020-10-28 14:53:11', + ] + ); + + } + + function testYearlyByYearDayByDay() { + + $this->parse( + 'FREQ=YEARLY;COUNT=6;BYYEARDAY=97;BYDAY=SA', + '2001-04-07 14:53:11', + [ + '2001-04-07 14:53:11', + '2006-04-08 14:53:11', + '2012-04-07 14:53:11', + '2017-04-08 14:53:11', + '2023-04-08 14:53:11', + '2034-04-08 14:53:11', + ] + ); + + } + + function testYearlyByYearDayNegative() { + + $this->parse( + 'FREQ=YEARLY;COUNT=8;BYYEARDAY=-97,-5', + '2001-09-26 14:53:11', + [ + '2001-09-26 14:53:11', + '2001-12-27 14:53:11', + '2002-09-26 14:53:11', + '2002-12-27 14:53:11', + '2003-09-26 14:53:11', + '2003-12-27 14:53:11', + '2004-09-26 14:53:11', + '2004-12-27 14:53:11', + ] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testYearlyByYearDayInvalid390() { + + $this->parse( + 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYYEARDAY=390', + '2011-04-07 00:00:00', + [ + ] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testYearlyByYearDayInvalid0() { + + $this->parse( + 'FREQ=YEARLY;COUNT=8;INTERVAL=4;BYYEARDAY=0', + '2011-04-07 00:00:00', + [ + ] + ); + + } + + function testFastForward() { + + // The idea is that we're fast-forwarding too far in the future, so + // there will be no results left. + $this->parse( + 'FREQ=YEARLY;COUNT=8;INTERVAL=5;BYMONTH=4,10;BYDAY=1MO,-1SU', + '2011-04-04 00:00:00', + [], + '2020-05-05 00:00:00' + ); + + } + + /** + * The bug that was in the + * system before would fail on the 5th tuesday of the month, if the 5th + * tuesday did not exist. + * + * A pretty slow test. Had to be marked as 'medium' for phpunit to not die + * after 1 second. Would be good to optimize later. + * + * @medium + */ + function testFifthTuesdayProblem() { + + $this->parse( + 'FREQ=MONTHLY;INTERVAL=1;UNTIL=20071030T035959Z;BYDAY=5TU', + '2007-10-04 14:46:42', + [ + '2007-10-04 14:46:42', + ] + ); + + } + + /** + * This bug came from a Fruux customer. This would result in a never-ending + * request. + */ + function testFastFowardTooFar() { + + $this->parse( + 'FREQ=WEEKLY;BYDAY=MO;UNTIL=20090704T205959Z;INTERVAL=1', + '2009-04-20 18:00:00', + [ + '2009-04-20 18:00:00', + '2009-04-27 18:00:00', + '2009-05-04 18:00:00', + '2009-05-11 18:00:00', + '2009-05-18 18:00:00', + '2009-05-25 18:00:00', + '2009-06-01 18:00:00', + '2009-06-08 18:00:00', + '2009-06-15 18:00:00', + '2009-06-22 18:00:00', + '2009-06-29 18:00:00', + ] + ); + + } + + function testValidByWeekNo() { + + $this->parse( + 'FREQ=YEARLY;BYWEEKNO=20;BYDAY=TU', + '2011-02-07 00:00:00', + [ + '2011-02-07 00:00:00', + '2011-05-17 00:00:00', + '2012-05-15 00:00:00', + '2013-05-14 00:00:00', + '2014-05-13 00:00:00', + '2015-05-12 00:00:00', + '2016-05-17 00:00:00', + '2017-05-16 00:00:00', + '2018-05-15 00:00:00', + '2019-05-14 00:00:00', + '2020-05-12 00:00:00', + '2021-05-18 00:00:00', + ] + ); + + } + + function testNegativeValidByWeekNo() { + + $this->parse( + 'FREQ=YEARLY;BYWEEKNO=-20;BYDAY=TU,FR', + '2011-09-02 00:00:00', + [ + '2011-09-02 00:00:00', + '2012-08-07 00:00:00', + '2012-08-10 00:00:00', + '2013-08-06 00:00:00', + '2013-08-09 00:00:00', + '2014-08-05 00:00:00', + '2014-08-08 00:00:00', + '2015-08-11 00:00:00', + '2015-08-14 00:00:00', + '2016-08-09 00:00:00', + '2016-08-12 00:00:00', + '2017-08-08 00:00:00', + ] + ); + + } + + function testTwoValidByWeekNo() { + + $this->parse( + 'FREQ=YEARLY;BYWEEKNO=20;BYDAY=TU,FR', + '2011-09-07 09:00:00', + [ + '2011-09-07 09:00:00', + '2012-05-15 09:00:00', + '2012-05-18 09:00:00', + '2013-05-14 09:00:00', + '2013-05-17 09:00:00', + '2014-05-13 09:00:00', + '2014-05-16 09:00:00', + '2015-05-12 09:00:00', + '2015-05-15 09:00:00', + '2016-05-17 09:00:00', + '2016-05-20 09:00:00', + '2017-05-16 09:00:00', + ] + ); + + } + + function testValidByWeekNoByDayDefault() { + + $this->parse( + 'FREQ=YEARLY;BYWEEKNO=20', + '2011-05-16 00:00:00', + [ + '2011-05-16 00:00:00', + '2012-05-14 00:00:00', + '2013-05-13 00:00:00', + '2014-05-12 00:00:00', + '2015-05-11 00:00:00', + '2016-05-16 00:00:00', + '2017-05-15 00:00:00', + '2018-05-14 00:00:00', + '2019-05-13 00:00:00', + '2020-05-11 00:00:00', + '2021-05-17 00:00:00', + '2022-05-16 00:00:00', + ] + ); + + } + + function testMultipleValidByWeekNo() { + + $this->parse( + 'FREQ=YEARLY;BYWEEKNO=20,50;BYDAY=TU,FR', + '2011-01-16 00:00:00', + [ + '2011-01-16 00:00:00', + '2011-05-17 00:00:00', + '2011-05-20 00:00:00', + '2011-12-13 00:00:00', + '2011-12-16 00:00:00', + '2012-05-15 00:00:00', + '2012-05-18 00:00:00', + '2012-12-11 00:00:00', + '2012-12-14 00:00:00', + '2013-05-14 00:00:00', + '2013-05-17 00:00:00', + '2013-12-10 00:00:00', + ] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testInvalidByWeekNo() { + + $this->parse( + 'FREQ=YEARLY;BYWEEKNO=54', + '2011-05-16 00:00:00', + [ + ] + ); + + } + + /** + * This also at one point caused an infinite loop. We're keeping the test. + */ + function testYearlyByMonthLoop() { + + $this->parse( + 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA', + '2012-01-01 15:45:00', + [ + '2012-02-01 15:45:00', + ], + '2012-01-29 23:00:00' + ); + + + } + + /** + * Something, somewhere produced an ics with an interval set to 0. Because + * this means we increase the current day (or week, month) by 0, this also + * results in an infinite loop. + * + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testZeroInterval() { + + $this->parse( + 'FREQ=YEARLY;INTERVAL=0', + '2012-08-24 14:57:00', + [], + '2013-01-01 23:00:00' + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testInvalidFreq() { + + $this->parse( + 'FREQ=SMONTHLY;INTERVAL=3;UNTIL=20111025T000000Z', + '2011-10-07', + [] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testByDayBadOffset() { + + $this->parse( + 'FREQ=WEEKLY;INTERVAL=1;COUNT=4;BYDAY=0MO;WKST=SA', + '2014-08-01 00:00:00', + [] + ); + + } + + function testUntilBeginHasTimezone() { + + $this->parse( + 'FREQ=WEEKLY;UNTIL=20131118T183000', + '2013-09-23 18:30:00', + [ + '2013-09-23 18:30:00', + '2013-09-30 18:30:00', + '2013-10-07 18:30:00', + '2013-10-14 18:30:00', + '2013-10-21 18:30:00', + '2013-10-28 18:30:00', + '2013-11-04 18:30:00', + '2013-11-11 18:30:00', + '2013-11-18 18:30:00', + ], + null, + 'America/New_York' + ); + + } + + function testUntilBeforeDtStart() { + + $this->parse( + 'FREQ=DAILY;UNTIL=20140101T000000Z', + '2014-08-02 00:15:00', + [ + '2014-08-02 00:15:00', + ] + ); + + } + + function testIgnoredStuff() { + + $this->parse( + 'FREQ=DAILY;BYSECOND=1;BYMINUTE=1;BYYEARDAY=1;BYWEEKNO=1;COUNT=2', + '2014-08-02 00:15:00', + [ + '2014-08-02 00:15:00', + '2014-08-03 00:15:00', + ] + ); + + } + + function testMinusFifthThursday() { + + $this->parse( + 'FREQ=MONTHLY;BYDAY=-4TH,-5TH;COUNT=4', + '2015-01-01 00:15:00', + [ + '2015-01-01 00:15:00', + '2015-01-08 00:15:00', + '2015-02-05 00:15:00', + '2015-03-05 00:15:00' + ] + ); + + } + + /** + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testUnsupportedPart() { + + $this->parse( + 'FREQ=DAILY;BYWODAN=1', + '2014-08-02 00:15:00', + [] + ); + + } + + function testIteratorFunctions() { + + $parser = new RRuleIterator('FREQ=DAILY', new DateTime('2014-08-02 00:00:13')); + $parser->next(); + $this->assertEquals( + new DateTime('2014-08-03 00:00:13'), + $parser->current() + ); + $this->assertEquals( + 1, + $parser->key() + ); + + $parser->rewind(); + + $this->assertEquals( + new DateTime('2014-08-02 00:00:13'), + $parser->current() + ); + $this->assertEquals( + 0, + $parser->key() + ); + + } + + function parse($rule, $start, $expected, $fastForward = null, $tz = 'UTC') { + + $dt = new DateTime($start, new DateTimeZone($tz)); + $parser = new RRuleIterator($rule, $dt); + + if ($fastForward) { + $parser->fastForward(new DateTime($fastForward)); + } + + $result = []; + while ($parser->valid()) { + + $item = $parser->current(); + $result[] = $item->format('Y-m-d H:i:s'); + + if ($parser->isInfinite() && count($result) >= count($expected)) { + break; + } + $parser->next(); + + } + + $this->assertEquals( + $expected, + $result + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/RecurrenceIterator/UntilRespectsTimezoneTest.ics b/htdocs/includes/sabre/sabre/vobject/tests/VObject/RecurrenceIterator/UntilRespectsTimezoneTest.ics new file mode 100644 index 00000000000..1663c783d84 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/RecurrenceIterator/UntilRespectsTimezoneTest.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-TIMEZONE:America/New_York +PRODID:-//www.churchcommunitybuilder.com//Church Community Builder//EN +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Test Event +BEGIN:VTIMEZONE +TZID:America/New_York +X-LIC-LOCATION:America/New_York +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +UID:10621-1440@ccbchurch.com +DTSTART;TZID=America/New_York:20130923T183000 +DTEND;TZID=America/New_York:20130923T203000 +DTSTAMP:20131216T170211 +RRULE:FREQ=WEEKLY;UNTIL=20131118T183000 +CREATED:20130423T161111 +DESCRIPTION:Test Event ending November 11, 2013 +LAST-MODIFIED:20131126T163428 +SEQUENCE:1387231331 +SUMMARY:Test +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/SlashRTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/SlashRTest.php new file mode 100644 index 00000000000..8e9389de162 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/SlashRTest.php @@ -0,0 +1,20 @@ +add('test', "abc\r\ndef"); + $this->assertEquals("TEST:abc\\ndef\r\n", $prop->serialize()); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Splitter/ICalendarTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Splitter/ICalendarTest.php new file mode 100644 index 00000000000..ccbd5c88191 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Splitter/ICalendarTest.php @@ -0,0 +1,325 @@ +version = VObject\Version::VERSION; + } + + function createStream($data) { + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $data); + rewind($stream); + return $stream; + + } + + function testICalendarImportValidEvent() { + + $data = <<createStream($data); + + $objects = new ICalendar($tempFile); + + $return = ""; + while ($object = $objects->getNext()) { + $return .= $object->serialize(); + } + $this->assertEquals([], VObject\Reader::read($return)->validate()); + } + + /** + * @expectedException Sabre\VObject\ParseException + */ + function testICalendarImportWrongType() { + + $data = <<createStream($data); + + $objects = new ICalendar($tempFile); + + } + + function testICalendarImportEndOfData() { + $data = <<createStream($data); + + $objects = new ICalendar($tempFile); + + $return = ""; + while ($object = $objects->getNext()) { + $return .= $object->serialize(); + } + $this->assertNull($object = $objects->getNext()); + } + + /** + * @expectedException Sabre\VObject\ParseException + */ + function testICalendarImportInvalidEvent() { + $data = <<createStream($data); + $objects = new ICalendar($tempFile); + + } + + function testICalendarImportMultipleValidEvents() { + + $event[] = <<createStream($data); + + $objects = new ICalendar($tempFile); + + $return = ""; + $i = 0; + while ($object = $objects->getNext()) { + + $expected = <<version//EN +CALSCALE:GREGORIAN +$event[$i] +END:VCALENDAR + +EOT; + + $return .= $object->serialize(); + $expected = str_replace("\n", "\r\n", $expected); + $this->assertEquals($expected, $object->serialize()); + $i++; + } + $this->assertEquals([], VObject\Reader::read($return)->validate()); + } + + function testICalendarImportEventWithoutUID() { + + $data = <<version//EN +CALSCALE:GREGORIAN +BEGIN:VEVENT +DTSTART:20140101T040000Z +DTSTAMP:20140122T233226Z +END:VEVENT +END:VCALENDAR + +EOT; + $tempFile = $this->createStream($data); + + $objects = new ICalendar($tempFile); + + $return = ""; + while ($object = $objects->getNext()) { + $return .= $object->serialize(); + } + + $messages = VObject\Reader::read($return)->validate(); + + if ($messages) { + $messages = array_map( + function($item) { return $item['message']; }, + $messages + ); + $this->fail('Validation errors: ' . implode("\n", $messages)); + } else { + $this->assertEquals([], $messages); + } + } + + function testICalendarImportMultipleVTIMEZONESAndMultipleValidEvents() { + + $timezones = <<createStream($data); + + $objects = new ICalendar($tempFile); + + $return = ""; + $i = 0; + while ($object = $objects->getNext()) { + + $expected = <<version//EN +CALSCALE:GREGORIAN +$timezones +$event[$i] +END:VCALENDAR + +EOT; + $expected = str_replace("\n", "\r\n", $expected); + + $this->assertEquals($expected, $object->serialize()); + $return .= $object->serialize(); + $i++; + + } + + $this->assertEquals([], VObject\Reader::read($return)->validate()); + } + + function testICalendarImportWithOutVTIMEZONES() { + + $data = <<createStream($data); + + $objects = new ICalendar($tempFile); + + $return = ""; + while ($object = $objects->getNext()) { + $return .= $object->serialize(); + } + + $messages = VObject\Reader::read($return)->validate(); + $this->assertEquals([], $messages); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/Splitter/VCardTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Splitter/VCardTest.php new file mode 100644 index 00000000000..e19e2d8205e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/Splitter/VCardTest.php @@ -0,0 +1,193 @@ +createStream($data); + + $objects = new VCard($tempFile); + + $count = 0; + while ($objects->getNext()) { + $count++; + } + $this->assertEquals(1, $count); + + } + + /** + * @expectedException Sabre\VObject\ParseException + */ + function testVCardImportWrongType() { + $event[] = <<createStream($data); + + $splitter = new VCard($tempFile); + + while ($object = $splitter->getNext()) { + } + + } + + function testVCardImportValidVCardsWithCategories() { + $data = <<createStream($data); + + $splitter = new VCard($tempFile); + + $count = 0; + while ($object = $splitter->getNext()) { + $count++; + } + $this->assertEquals(4, $count); + + } + + function testVCardImportEndOfData() { + $data = <<createStream($data); + + $objects = new VCard($tempFile); + $object = $objects->getNext(); + + $this->assertNull($objects->getNext()); + + + } + + /** + * @expectedException \Sabre\VObject\ParseException + */ + function testVCardImportCheckInvalidArgumentException() { + $data = <<createStream($data); + + $objects = new VCard($tempFile); + while ($objects->getNext()) { } + + } + + function testVCardImportMultipleValidVCards() { + $data = <<createStream($data); + + $objects = new VCard($tempFile); + + $count = 0; + while ($objects->getNext()) { + $count++; + } + $this->assertEquals(2, $count); + + } + + function testImportMultipleSeparatedWithNewLines() { + $data = <<createStream($data); + $objects = new VCard($tempFile); + + $count = 0; + while ($objects->getNext()) { + $count++; + } + $this->assertEquals(2, $count); + } + + function testVCardImportVCardWithoutUID() { + $data = <<createStream($data); + + $objects = new VCard($tempFile); + + $count = 0; + while ($objects->getNext()) { + $count++; + } + + $this->assertEquals(1, $count); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/StringUtilTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/StringUtilTest.php new file mode 100644 index 00000000000..8e0bc483d15 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/StringUtilTest.php @@ -0,0 +1,55 @@ +assertEquals(false, $string); + + } + + function testIsUTF8() { + + $string = StringUtil::isUTF8('I 💚 SabreDAV'); + + $this->assertEquals(true, $string); + + } + + function testUTF8ControlChar() { + + $string = StringUtil::isUTF8(chr(0x00)); + + $this->assertEquals(false, $string); + + } + + function testConvertToUTF8nonUTF8() { + + $string = StringUtil::convertToUTF8(chr(0xbf)); + + $this->assertEquals(utf8_encode(chr(0xbf)), $string); + + } + + function testConvertToUTF8IsUTF8() { + + $string = StringUtil::convertToUTF8('I 💚 SabreDAV'); + + $this->assertEquals('I 💚 SabreDAV', $string); + + } + + function testConvertToUTF8ControlChar() { + + $string = StringUtil::convertToUTF8(chr(0x00)); + + $this->assertEquals('', $string); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/TimeZoneUtilTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/TimeZoneUtilTest.php new file mode 100644 index 00000000000..8d8357dc7c2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/TimeZoneUtilTest.php @@ -0,0 +1,377 @@ +assertInstanceOf('DateTimeZone', $tz); + } catch (\Exception $e) { + if (strpos($e->getMessage(), "Unknown or bad timezone") !== false) { + $this->markTestSkipped($timezoneName . ' is not (yet) supported in this PHP version. Update pecl/timezonedb'); + } else { + throw $e; + } + + } + + } + + function getMapping() { + + TimeZoneUtil::loadTzMaps(); + + // PHPUNit requires an array of arrays + return array_map( + function($value) { + return [$value]; + }, + TimeZoneUtil::$map + ); + + } + + function testExchangeMap() { + + $vobj = <<assertEquals($ex->getName(), $tz->getName()); + + } + + function testWetherMicrosoftIsStillInsane() { + + $vobj = <<assertEquals($ex->getName(), $tz->getName()); + + } + + function testUnknownExchangeId() { + + $vobj = <<assertEquals($ex->getName(), $tz->getName()); + + } + + function testWindowsTimeZone() { + + $tz = TimeZoneUtil::getTimeZone('Eastern Standard Time'); + $ex = new \DateTimeZone('America/New_York'); + $this->assertEquals($ex->getName(), $tz->getName()); + + } + + /** + * @dataProvider getPHPTimeZoneIdentifiers + */ + function testTimeZoneIdentifiers($tzid) { + + $tz = TimeZoneUtil::getTimeZone($tzid); + $ex = new \DateTimeZone($tzid); + + $this->assertEquals($ex->getName(), $tz->getName()); + + } + + /** + * @dataProvider getPHPTimeZoneBCIdentifiers + */ + function testTimeZoneBCIdentifiers($tzid) { + + $tz = TimeZoneUtil::getTimeZone($tzid); + $ex = new \DateTimeZone($tzid); + + $this->assertEquals($ex->getName(), $tz->getName()); + + } + + function getPHPTimeZoneIdentifiers() { + + // PHPUNit requires an array of arrays + return array_map( + function($value) { + return [$value]; + }, + \DateTimeZone::listIdentifiers() + ); + + } + + function getPHPTimeZoneBCIdentifiers() { + + // PHPUNit requires an array of arrays + return array_map( + function($value) { + return [$value]; + }, + TimeZoneUtil::getIdentifiersBC() + ); + + } + + function testTimezoneOffset() { + + $tz = TimeZoneUtil::getTimeZone('GMT-0400', null, true); + + if (version_compare(PHP_VERSION, '5.5.10', '>=') && !defined('HHVM_VERSION')) { + $ex = new \DateTimeZone('-04:00'); + } else { + $ex = new \DateTimeZone('Etc/GMT-4'); + } + $this->assertEquals($ex->getName(), $tz->getName()); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testTimezoneFail() { + + $tz = TimeZoneUtil::getTimeZone('FooBar', null, true); + + } + + function testFallBack() { + + $vobj = <<assertEquals($ex->getName(), $tz->getName()); + + } + + function testLjubljanaBug() { + + $vobj = <<assertEquals($ex->getName(), $tz->getName()); + + } + + function testWeirdSystemVLICs() { + +$vobj = <<assertEquals($ex->getName(), $tz->getName()); + + } + + + function testPrefixedOffsetExchangeIdentifier() + { + $tz = TimeZoneUtil::getTimeZone('(UTC-05:00) Eastern Time (US & Canada)'); + $ex = new \DateTimeZone('America/New_York'); + $this->assertEquals($ex->getName(), $tz->getName()); + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/UUIDUtilTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/UUIDUtilTest.php new file mode 100644 index 00000000000..d33a8794607 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/UUIDUtilTest.php @@ -0,0 +1,37 @@ +assertTrue( + UUIDUtil::validateUUID('11111111-2222-3333-4444-555555555555') + ); + $this->assertFalse( + UUIDUtil::validateUUID(' 11111111-2222-3333-4444-555555555555') + ); + $this->assertTrue( + UUIDUtil::validateUUID('ffffffff-2222-3333-4444-555555555555') + ); + $this->assertFalse( + UUIDUtil::validateUUID('fffffffg-2222-3333-4444-555555555555') + ); + + } + + /** + * @depends testValidateUUID + */ + function testGetUUID() { + + $this->assertTrue( + UUIDUtil::validateUUID( + UUIDUtil::getUUID() + ) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/VCard21Test.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/VCard21Test.php new file mode 100644 index 00000000000..cede1eac59f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/VCard21Test.php @@ -0,0 +1,52 @@ +serialize(); + + $this->assertEquals($input, $output); + + } + + function testPropertyPadValueCount() { + + $input = <<serialize(); + + $expected = <<assertEquals($expected, $output); + + } +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/VCardConverterTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/VCardConverterTest.php new file mode 100644 index 00000000000..77fc37d700f --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/VCardConverterTest.php @@ -0,0 +1,533 @@ +convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testConvert40to40() { + + $input = <<convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testConvert21to40() { + + $input = <<convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testConvert30to30() { + + $input = <<convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testConvert40to30() { + + $input = <<convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testConvertGroupCard() { + + $input = <<convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + $input = $output; + $output = <<convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testBDAYConversion() { + + $input = <<convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + $input = $output; + $output = <<convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testUnknownSourceVCardVersion() { + + $input = <<convert(Document::VCARD40); + + } + + /** + * @expectedException InvalidArgumentException + */ + function testUnknownTargetVCardVersion() { + + $input = <<convert(Document::VCARD21); + + } + + function testConvertIndividualCard() { + + $input = <<convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + $input = $output; + $output = <<convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testAnniversary() { + + $input = <<!$_ +ITEM1.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20081210 +END:VCARD + +OUT; + + $vcard = Reader::read($input); + $vcard = $vcard->convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + // Swapping input and output + list( + $input, + $output + ) = [ + $output, + $input + ]; + + $vcard = Reader::read($input); + $vcard = $vcard->convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testMultipleAnniversaries() { + + $input = <<!$_ +ITEM1.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20081210 +ITEM2.X-ABDATE;VALUE=DATE-AND-OR-TIME:20091210 +ITEM2.X-ABLABEL:_$!!$_ +ITEM2.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20091210 +ITEM3.X-ABDATE;VALUE=DATE-AND-OR-TIME:20101210 +ITEM3.X-ABLABEL:_$!!$_ +ITEM3.X-ANNIVERSARY;VALUE=DATE-AND-OR-TIME:20101210 +END:VCARD + +OUT; + + $vcard = Reader::read($input); + $vcard = $vcard->convert(Document::VCARD30); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + // Swapping input and output + list( + $input, + $output + ) = [ + $output, + $input + ]; + + $vcard = Reader::read($input); + $vcard = $vcard->convert(Document::VCARD40); + + $this->assertVObjectEqualsVObject( + $output, + $vcard + ); + + } + + function testNoLabel() { + + $input = <<assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard); + $vcard = $vcard->convert(Document::VCARD40); + $vcard = $vcard->serialize(); + + $converted = Reader::read($vcard); + $converted->validate(); + + $version = Version::VERSION; + + $expected = <<assertEquals($expected, str_replace("\r", "", $vcard)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/VersionTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/VersionTest.php new file mode 100644 index 00000000000..956479bf2e6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/VersionTest.php @@ -0,0 +1,14 @@ +assertEquals(-1, version_compare('2.0.0', $v)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/WriterTest.php b/htdocs/includes/sabre/sabre/vobject/tests/VObject/WriterTest.php new file mode 100644 index 00000000000..800e13dd054 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/WriterTest.php @@ -0,0 +1,41 @@ +getComponent()); + $this->assertEquals("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n", $result); + + } + + function testWriteToJson() { + + $result = Writer::writeJson($this->getComponent()); + $this->assertEquals('["vcalendar",[],[]]', $result); + + } + + function testWriteToXml() { + + $result = Writer::writeXml($this->getComponent()); + $this->assertEquals( + '' . "\n" . + '' . "\n" . + ' ' . "\n" . + '' . "\n", + $result + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/issue153.vcf b/htdocs/includes/sabre/sabre/vobject/tests/VObject/issue153.vcf new file mode 100644 index 00000000000..180949c5eb1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/issue153.vcf @@ -0,0 +1,352 @@ +BEGIN:VCARD +VERSION:3.0 +N:Benutzer;Test;;; +FN:Test Benutzer +PHOTO;BASE64: + /9j/4AAQSkZJRgABAQAAAQABAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA + AAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABQKADAAQAAAABAAABQAAAAAD/2wBD + AAIBAQIBAQICAQICAgICAwUDAwMDAwYEBAMFBwYHBwcGBgYHCAsJBwgKCAYGCQ0JCgsLDAwMBwkN + Dg0MDgsMDAv/2wBDAQICAgMCAwUDAwULCAYICwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL + CwsLCwsLCwsLCwsLCwsLCwsLCwv/wAARCAFAAUADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA + AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKB + kaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZn + aGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT + 1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI + CQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV + YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6 + goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk + 5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8J7JbO8tYo1tIFCDLOVG5qfdaVZRwmSOFWzyA + F4H1rLt5WViMhdp6HgmtKK8O3B+4Rhx6fSgBI9FtjaNN5aErwRjilSys7lFAt41xyTtqc2yJCVlY + 7eqgGqv2jyLcebjZnGPWncdzT0+w0u5eQXtrGiBcIyoPmNMXwpb/AGMTSRRbH6YAyPwqK21GKdfL + BAVfu+1SQX4jnjKFsp03dPypCKN9oEaKSkC7R0bGKpnSlSPdHErZOORXV3Ouy337sCLB6kpx+FY0 + t+VfyrgcbuCB1oAfoMemrcImq2sZX+I7ATXS618PdK1DRlvvDEaMq5LoV2nisx4LVrUfu5BOePau + m8EQS6PY3HmFXjljKhTzjOf1oA4mz8OxvMrLbW5RD8wbByKg1LRrRriRYY408w/KAMba1pRaWt/H + a6a7CVm2u7N8lUPEujzaRekzSK6tgqVNAGNBZJauY5Yon92GTRJp0ROY0Un0A4q3c2odkaYOMjii + KL7NIDGcj1NDAZBplmmWv1xnoFHStfS/DFpewqYoYm3DutZ8lv8AapdyOqk8EVteEbSe3KBSrDrQ + BT8S+HbawiiWGCAPjsuMnPesqHS4JSFlSMP7DitbXbvfrkkM2eGw3p+FMfTh5X+hr8w7t3oAhOhW + u8MkMZUY3fL0Heo9UsrN5FFrbxKmMBgoG41fWFra0Acjpzg9aoXjtgRoo29vagCoun27kbY059qn + bwykskYjRArdTT7GEl2UqMr2q/JtVU27iR15NADdK8DC/wBPle2iicxNg5ALH6Umm6FZ/a3ttQt4 + g2Cqnb0PbJ+tamn3j6ZCW0nILfeBORWVfO4dhLw7fMW7560AZuqeHf7MuTFcRpv6qVGVx70q2Eci + QwyW0SsPvOqjJrUtb6S9tHQKGeMZYuM8VUs7gRxbrncy9mWgB1x4QtTHvsQWkHJVhhax3tkhugHh + UkfeAXIFdPZ3v2uxkQ9G4jI6/j+tYun3r2Fy6yxeb2Py5IoAqXenJ5xaGNNvXH/1qcLSGeBdkSg9 + CcdaswC3be0pfexOMnpn2qaS1KQkQASKoydvLCgDNi09RKTNCuO2BxVjSobc6gqXMERQHkleDUsc + u9VADbG6qOWAp11bLbptkjlCkZRsde9AFi5sbO3kKfZYTnkHaOlVbuO2F5thtYcADjaKXUpHj8ku + Co2VDFL5wLeg696YFwQ2z7Qtlb8HJO0c1Zsr7T7a9kL6XazZ4CmMFRWfHdkEgjGRjPpU9raP5LSP + j5h2pAWdQ0+z1KdG+y21qvcRqBn8qXSvC+iTu63ssqyE/IAuR+NQwSrGm1g+c8E9qiSQW9wPNYYP + OR2oAW68GNa28k3lwGNHwvzDJGfSqM9nHBgm3j59QMVdmma4zIjsUBHy5OKp6o8s2BJjZjjAoAro + /nysbgYY9zWmLPCR+WQQwyaz4k2F/Pbft/GtKxvUeFN+B2x+NAEptsWpZSdo9etZe8su2X7pPFdU + LeOazKqVwevNYt7pw5EA5HIxQBQA8tAIeGz1NWIJvJlhW5OQBzjrUMR/eN9pwoXjB4qQ3ERJeYcy + 9P8AZoA0jf8AmybVxsHAFS6jp63ixmwjIwOfrWfaou12GcDpmt/w5qJhXc6hh2GM0AZkHiRpblVl + G0RjGMdxXQ+H/E0Rm+bjdw1crqEHm3EksY4Y9PTmq0cskc42qUOfpmgDovHOhLBOZ9O+aEnIUdRW + QZft1sgum/1Ywua3fDfiFDL5WoEPEwxzzirPizwTFPZC60kYUjcAp4NAHPSq91EoRS3061DHD9nb + 94Mkfw020v57GbcCRt4IIqzNcedIH2jc3JyOaAIYrRZmJxtNdB4fkGn2hluBgBR+NZ2n2X9ozAQD + 5qvaxGbKIRXkuFU4C96AMDxBKZdQkuEUkStuUegpNM1eWScAkqpHTHNPlwbjMzExZ4Pal1PS/s6+ + dY/6vuwPSgC9G8c0A+1xEknrnpUVxaeXNm2dVUfjVazvEZAEkMrccZzV1YYyBIhJP8SZ6fhQBSmV + 4JfMVT+96UJdSQdcMO4A6fjVmTUoJiqTOMJ/q+elRyQs0TtaxF0PVhzmgCzpd55r7YI2HHPTmrV0 + sDTF7gnJXGO4OKyNKgn80NbFhjoBzWjqdg6SISPmIBOaAKVnI1leyhsMJOD7CqOqRtZqotjiFulW + rhsSMshKH1ogsZbmF475TKifdf0oApabevHIAhCYOdxp0t59luS0I+995uxqpdRyWsrqmXGeCR/K + rVlZfaogqv8AvD/CaAIY42kV3K5zzn1p9jNLp6u/A80YPNWWsJNPAVpC4JAZT2HfFWJoVmVVjhVk + HTPrQBPoi2wsoo4APtBHL+tP1mS5uVEFxgJGNqH15plp5WmyBriMRsowM8UybXTNdbrpd6A/KKAD + xbJAGs44FIPlnd9c/wD16ynt/LiDW2SR2qa5vP7RnMs6BNuQMd6jhkAUb2K8+tADYp0fhj8w6itC + yQ3CFYeAOoqi8Uew+UMuf4u9T2NwIW+UgMetO4FmS6RJ1ik6HqxHAqC+gimUiA8DvjrU0kcE8ieY + itu+8c0+bShaWxksSZoM4b0SkBTgha0cq33Cuc1SvrrLFV6jpWqbuGe1HnnDdAKy7i3WSY7OT2NN + AMulWSV8ZDNzxV7SlbaFjClx69Kpww7W3ct7jpUtnNJHd5UjZnt1NIDdt7h7NQ7qGfpt7VR1XVEh + dhEpP94/4VpafexTy7ZlbBGDVHxFbQh1j04HaOTkdKAM5ZVlYso3E+tVp4w8gx0Bqd7QxNu+6D6V + DIoVySxAx2NAFyNmli2pjYBz61paW3lWrFS3BwP8/hWJbTBFJy2D6HgfWtiTWPsqxraBHyOeBg0A + RSoLSTdIepzz0606exTWyQGMXljORTNT1B7+ECZR5fHzDqapfbHjbFkTsIwSTQA43ptyyS44Paun + 8N64Z7Bre4YlZBtU5+7XLTQbjwN4Pb+IfWn2lw9uyrIw2Z5HpQBv3GirHc7LxWVZOVI71FNp7WDg + QYlIIGD6VvaPdi+tljb5yeAzcn8DT9YtbPSpVhDM87jJ3Htjnn6UAUIrJreD7Si7MDoKhv8AUxqt + pGt5GqIOr9zRfLM8ZFgZGtex2nGe4zWKN8rsDhYx2JpJ3Atx+HxcRSzWcpcL/CRwaj0zW1sQy3cS + nsFPSoYJpbIl7dm8tT8wzV7+0hqEO1Y4lQ9cqMn9KoCp9kW7kaaxU+Yx+5j5etWrb/RGxfr5bkdu + lW7KFILpfspDbVyc1fjNnrLtHqOYWP8AFjGfxpAc/e6Ql/GzW4AfqBWfpupS6Xer5vPlHmMjg10V + 5pp0u4JhYNGvAYHrUn2WLWrVo41AvSMRZAC/8CPr1oAvafdWOuNG+lqDekY+zg8MPXPX/wDXWZrF + tcWNw0erKElB4Rf4R6c1BpqyaBdbrnEcwyAc4x06H0rQS9a9jUTgOXPzMwycexoAw7u1jYb3zkU3 + Srtgdk54PFamv2C2pDQbWjcfKCeSa56aJld23YA6ZOKFqBrXGjjULuOKxKuZOTn+H/OKwr/ztOvs + uCrg7RgVLYapPbXAEW4EkHJNdBNBH4gtgyhFmXuw60AVpbT7VpiPJ94jLetQWsDRSIYz8mec1c0+ + 1nexdrw7GjJXk/epsFtDPG0bOdw+b5SaAKWsXA+14Y71FQi5S4RvlAC8A0y5hHmHarhvQ9BVGSQx + sUXPHX3oAmDCJ8rzgHg96gQ+ZGWbg9vahNRG7EnalkkF6hEXyD270MCWF3aEhdue1OsmNnMAih/r + VaBgAUY8561PaubdnMxJXseuKANhIY5Assp2v12itZtAgubEi2nb5xuKYHWubstQaO6SVzujTqpP + X8K2rXWLRF8xZJPMfjAzgUAcxcNiaRSpUocc96sW+yNgZCMVF4lvJdRvTOYkj52jbgZ98D6VWmlY + 2qCUnJOKaVwCzviibANwYc8Utkdl7tbKhjxmpUspvm8tgn16ipigSEG4G4pxu9TSA27GeFbRlGGm + P3cdhUN8GEP2hV3JjafrWfpU/wBmuAcZLA4/Sr1trkarJHcRmSEZO3uTQBmrcbZCLoDZ2x1qOHSi + yebJIAPQipp4kmbzI1EQJ6GtCxsoHP8Ap91GB2yDQBlSWO+M/ZsBHHzZ71XkfMIWNgGU9vSt3U9N + t9m21uonz0Iz/hVCfRkjg82FhtHDGgCuZ8EMjDZjBzSZ8pAwU7XbGT0pWtEjjAZgV4PFOml2QKqk + OoOcU1qBNYRSrdkrhw3BIrah8KwXoV/m3PyVzyDWNp999kccgZq/ea7PFAGgZlJ6EUgN23thpdi4 + V1Eucr7ev9K53V/ER1a/MkuWdBtG04zioLrXJ5wDK2XAxmqVqmZ2YPtHJ/GgDsvC3i0ppr2d2ish + yFAHIz706bRLNdOPnErKw4y3NcvZ3pjA8o4kB61o3OpSX9nbx3QIkU/MwoAj/sGaPzFjlWSJjk46 + ioYYwqssjIHHAHpWm4ESN9nYDIFZV+I7uVI1wrY5b1oAtafcvb3W4MM9Nx6U/VZpNRys54ToU4zW + KXaDKrJuC8cVdtpi1gzs43HNAD9N195bdYtRIUR4wD1NX2KuA9uThuSQelcsZwzq9xyzfezV/SdX + e3m8pXJhkPKkUAdYZk8RywjVVJES7U2cE/WtA+HDHohuY3Uxg7RF/GeaPBlxaawMW6rHKnAU9SOO + lX/FFv8A2bpzTQk+cpAAz93nrQBx+r4c5CODEOA3Y+wrKu5V1C1GFKznkk9K6Wzv49fs8Xf7y7DY + MhGNgrmtX0s2t66WknnKvUp0/WgCnbrJFdot0NwJxkDFdDYp86oMjjIArJivxbR7LuMyEjKitS21 + MW8auuW44H93/PFAG15aXdr5Uv7uULkA/wCFc+Yvstw0at8+eoq/p+rm6vRJMNwIx9KranYySXSy + WEZZHOCw7UARXFyj5STAk7ntWVf2gALLyfUVoataLbfLO2SO/Ws2c+VwhLK3QDpQBmz2xAyCG56d + 6uWPlnCkFcjoTzUBkMc/3cZpwn8oZkDFs8HsKALN1apDIHOeaiLkRkMOtSXE6yxAsRUcdxldswIJ + HANMCuJW8xQgOP51oacWPPGAeRUUOIZQzDhecd6mbIcbPusM0gLmq6bHPohlhDeZuH4c1zzF1+Rs + HByDXTae0s0IhjjZg3GPWqOs+HpLCTbNGyb+cHrQBZitjPEzW/LL97vinw2v2m2aORec9AKXQbsw + ygBBiX72TWxfaS8kiGFQAwz8vWkncDlbqNraT5cjb/n+lMGckx8kjOa1tU2TxkPkMpxyKyrhJ4Wa + KIDbTAkgvIp7URzgBwe/BpZYrd4vmZWNZ81x5cgBXDdzVlIvtUOGIBHpQA2aEROpR8DsB2q3bvG9 + iySzEsTkLnrVMqViCZzt7nrT7GBVuQRnODQA6Q+Sx80A4HApEJB3BAR9K19EmhkvCJ0ZsKe3tUc8 + Mc1yy7cpn6YoAzoUiclnYYY8AHpUl8zRxqpPy9qtC2tULgSMAvQ460lzIl9b7YiDt4GaAKMMQlJ5 + z9Kj8gIW5yKnS3Crlzhh6d6k0mbyZT565Q5z60ANtrRpPmhzWhbwy7DJcDhhwMdKlt7aK+gb+z33 + yKdxVuMCqaz5cqGYfWgB6yu8rBB8o6Gs/UpjGQXBGPTvVmSfyImyepqrqjbIw3WgCDz1ib9yOTg4 + NbVlNBJYvlVBHt1rBaPzQWU4IHSn2FwRJslJxQA6e3M0O4oAzdB6VXR2iKGQENGOK0ms1eAkFjF/ + BjrVGaAo371smgC7pety2kwl06Vo5AOWXmuwm+Itv4g8Ota30aWlySAJQfmkP/1zXIeG4Y5SVBB3 + evamXGly2tydwG0nKkHpQBZ86fRbpBLI252y4PGRWhO8Ml1IbJhHn+BTnNU9O1oRwvDqqhB2lHJP + 4U6awb+z4JdKbzdh5ZurDHtQBat5LaRHiaOP7QejEZKD/Oauy+FI7W3Bsroyhxkq3QH8q5a7ujM8 + nWOQnBqTR9burCT98xdR60AbbaHc6ZG3ymJsZC/3hVnw/fNIXt7hygHzZp2oeIBqCxzqfmCgEe3+ + RVdrmLVAEtf3bxfOW/ve36UAV7+7DXMu5Q4/Os2e3eRWkiAGOijtWrPodxfQmeNVAPOPWsppJIpi + JxsKcY9aAMwRyTSbpflx68VOYvOXb97OKtXAiZdzkqT0AGc037BIIRLHjsR60AVprZrZwGj4qTY0 + xyRj3PUVMJDduFfqvFRzxJCzrCzEr60ALEu+YI53c4qeGB7lGCnBU4FUopTBLvfk1at9R2sAMjNA + GtaXsnhy2FzPHvC46jgnNQ33imTXrkz3oVFAwo9Kfrtq03hAzEfJ5gyc81hWM5hhKrhgT0NPcByS + P5g2uVI98Vp6X4uuNGlyzCQIQR0bI7/1rNQxqW+05J7Y4qK5ZYUP2ZCW9TSA7SR9M8V30X9nMFZw + WfcNi5qPWPDtjo0pE7O03U/Mf055rmtFmN9E0DEox+atPWbiW7lSO8Ja4jQbcDC4A9PXFADYtM0+ + 6nc3u7aOm3IP6Vnak9tYt/xL/M445zTIbieOdmWNsE46cip42EkyC4hYx469KAFsrT7XEJgFPOT6 + 1s+H9PD3XlzxnL/MDtqn9pghgb7GjL/eJORWqfEnmrA9oFRoxjJ5BoAp6NqDW2pzRXtuyIAw3FMf + rVS4iF08pydmeCDxWvqeuC+Ro9qglcMw71mwReXD5aAlFJPPU0AZ0cEsbkSZKH15FD2xJJiJVj6c + VfnzLGEXAA71PFpDPaebE6/KOh60AYVws8TBgrFe57CmHUG25RVJA7AVozzSLbNvX5T1AHNY/m/Z + nPlqwDetAEtvqzJNu3FZBwQBjI96vPqkd3mRtokH31UYx+VZqWruxaFl+frkZxT1tvs1ujJgEH5m + PR/pQAXl2S371XAHI+Wkaf7VD8hGR2arKySylRccQ98DmiS0jifdsdgeODQBQd9x3IBx1xTYlBm3 + En86sXUAwPswKg9QeaBErIEj6nrQC0NHRtUjt0K3AHzDABGcVW1fTzJL51jyOpz0NVooispebBI4 + wK2YFEthk8qR07igDAgJil+TKtnnHFaP2h5yI3ZsgdSfaqd2P3im3BGM9aktsjmRgCOaAJZrMwR7 + 3A5PT0pdMvZtOning+byzuVDyh/A8VHczSzDPy7RwOKgiuHEewjKeoFAzp7TUNM8XXEw8RhYNQmP + 7ny18uNeOM7cCtMfDiS8uY0tDEYghyynjPbn864htP8ANhLIehzWzovxDvtFsDB9+PI4I/rQI0r3 + wNc6DO0N2VaQqW2q24YxmqFhYRgE/vkkDfMGBBP4GrSeJ7tZd6SxvIfmK4yQP84p0XiyC71gS65G + 00zAKGX5Qv4UAbFpd28WnIsBLsDzmub1+AXt1LJEoQqfu4xu+lbWsWgs4/NsCXjPIbqK5+5kklmE + rDD54BFAGb5cjybCrAnnB6ipEvXil2sM4GMVpFY7m4UNmNyOWJ4qteaM0BISVZe+RQBFHC2/zISg + B69KlIVhIHA3HuR70lqotlBulY5P4Vcls44k3u6N5oyoHb60wM6O1SRir5LemOKv2vhuW4iLg7VA + 6k4FTR2ax4aaVIwR3HWqGua5PcQm1WRBH6jqaQFzWbE2nhzynuIi+8HaHyKweJSEQEN6jpVcKyOw + cMVznOeKmtZvOPDKuOKAJbi0JYFf4eue9IW8sncfvdqnlvVFyFyu09abI0bysMZx0oArC4eCTcgb + juK2dNvE1N1M0ohljGQzc5A7cfSs6aweWAk7kTuapQysIT9mOSvG49aAOkvzLMxk06QNuG1l7j3r + PlnnJAuGJij+nNQ6XqT7wEYqyn5v9utLULaW7j321uiEjLqMkKKAIotbghb/AI8hKGPIBHNXLG6t + 7uzk3RLbKG/iP+Fc+8f2d1eFztzyD2q5p2oCFWRoxOX52nPFAGgLyC2lyZFKdB70r69buxRJBHjr + nvWVdeXLE7xE8fwnoPpVKZUnQPkBhwRmgDq7a9tLyARWiiWYngL1qG4gurJ28+NowO2a5a3v3smD + aa5WUd1HNbC6zI0KSX13JO7D5lbHFAE4V7pi0b5x1GazdUtXSM7v4iPw5rQ0/XrcXX75FgUdxzuq + /qFrp+sWRe3uDkc4BFAHLRDY42ycd6uPOXiiV+RGPlWnXOg3IQvEmIB/Ft6/jUUEZmMcgydvzECg + C1G2+Ly3YAvyM9qY88kaFcmmp807uwPJ4FS3do+Fzn5ulAFVrjbgS8Z4yah2C03SMffNWZdPknVA + iluQOnHWmX9pILvyY13HHK46UAVre7LSyOCTmtjSiy7VijLeZ0IqO08OzPIUiTI74Ga6bRP7O01F + h1KYJOv3V4BoA4zU1lExMrkbOAvpVcSifhjgrzmtjxPp7pO7SggOcqfUViy25hG5fSgC8rrLAojb + d7d6SexlEgwpRfTNV7e5LFBbKAwPNWHeX7TguxI7GmBPBExhaNVIJ6egqOVknO1fkx1J61aj1gLC + UEKlk4LVWvozC67kCFxkD1pAQ24e3uDLC3z9CR3H/wCqrczJdOGiOxvYc5/CocMYhtUBj3xU8Qjk + XbKPIZOjqclvzoAu2HiO60xPKvd7wY/1fGBWnJo8WuW6y6XIPMYZEAzuH9KxISonAuzuRzgk9qtR + 79KmMuhTt5cRyxznFADLzS2tMw6pAY5OoDEZ/Sm20TQQ74YwVQckGtMatB4kUpqreVIRw5+8aqXF + jc6bAsbD9yThWz94UAOmmjvrRCMJjOQRVS0sD9pLyABM5Of6Vdtrdn+RUGcZqO6uRBG0MuFI79KA + MfV7r7ZqDI7kohAVT6U2eJNimJQOuTnpSXFussrMvBz1pJov3YUsR9O9ABblRncQ3bAqY2EUwIiA + Vqr20ojfYqZx3q9bSKAGcYJPIoAoq7OCEQBffrRDGEcleM8nNPjuGkhHmbB74ApvmxltsuTnuDQA + +SFEjDwu5buD0qpLL5vMg2kEdOlXECMAyZGOMMePyprQRI5N0rt3BXO326UAV4b0Wt0pC5HrXS2W + qq9zE7jcO+OhFc81kbg7iMqeAFHSpLa8eymaNOUIwD6UAavjPQYYybq1bBmXcF9O39Kw4iXdDKcE + DAxW3q7NdWELISdiYIz71kz6ZNZNHI0cjqQfujIFAEtzAtu/7vODzmqlyzNyAo9vWp7uWSWJd+AM + jjGGqOWCSWRVVW2+uKAKskpWU5TP0p8c+ExsPPNTmCVD+5U/QrzRJHJGymeOQc45HFAFczh497KR + jirWlEsAudvII9znitEeBp7yAPZvEVPJUsP5ZqCO3j0yYDUNwliI6dOPpQBt/wDCR3Wj6eHFujvI + do3DIX9KoHXoL6J11CJYZAONlaWueIYtY8Nwx6ZHu2MdxVeTXKG0eaXKRuCeuBQB0mn+HRe2Yeze + MqRkFmwfyra0rwsIrRmvZICcgDLVw7xXFuFd2uEQfeAJAxUkkjSxh4J7gjPAErf40Abvjq1i0y4S + KByCdrfL+FUI7SR4Wc+WzMOCW5qhf3Mt9cCV2ZiihRk5qpdTSBgRI+R2DnFAFw2k6AqJZMjuD1qn + cxzyyAkPuiP3ieT/AJzV+01R7a2RpMZPVmGQ1WVuTqLDCptcfMBwRQBEkst/YMCSTH8vJqtJaoYQ + JPv1o+ZDZKAo+UnBpmrCBpRNp4/0crgZ9f8A9dAzCdGgkOynxSus2xjkj+L1qW5/fxYj+8D+NRWz + R4fzCd2O9Ai0lzI6mPaMOcZqW4uI7rbtJ3IMc1XScKqncQT0olPlKWfBz6UATKjSDcmdoFWtPCyR + kzckHiqUV0623lKVIPzHHWp7Ic/vSRz0zQBcCqdyT4J7YqC3uZdKv1a2UupO7B6H2NMglMUsmcnd + 0Lc4q3BmaMBiDjr60AWJRBfyb9P2RueWJ6KfQVLHqMdtcEysxJXayN0x0yKyWihWQBdwTOSdxHNb + zWEF5ErXhX7QQAMNge2f0oAnhs4rq2kksHwirkg9SfauXnJnmL3AbL9jXSRWh0N28x1cEfMqtnA/ + Cs+70+O9/fWRIb+76fhSTuBimbyyyKDgnipLk7AML1pZbCWO7Hnjn26U6ZykRL+veqAryuvm/Jwf + Sk3mo2AyHyCT6Ux5pLU5Gwg88gGkBPNAILUO3KmooyjL8ueegzTvPMsRjG4qBwKrW1sxJZzsIPGa + AJbmfp5q7MZx71NZawEi8qZSyHg4NRGLzCPtB3eme1R3Nutocodyd8UAaVtqEUDlI8/N3PaqV2Ht + X2x4lIOSwHFSWkEFyo+cD1BpbmNbNdkh20AMh1UiJ1c9RzWj/wAJa1vYiK1RmRvvetY5gDENxgnp + UlhN5TiI4O4845oAmu51lXzFDGQ8jnpTra4uJkBAOQavXvhG8tIhPawvJAfmY9gKE1COwgIiAZiO + 3rQBV866T52Qsw6YrXguZNTs0WSJ8IPnHr9KwZNamNumZSpPU4pbPxBeRy/uJjtXqfWgDodMtnXK + QjYeo3VnalpiXjMzXMKS9O9VV1ydCXkmLY/SorWwTVJTmQEt81AHTeCY49Mik+0SJKmOg71W1bxH + HLdgaXaSRNnjdzWapGlBBG2ec4GKtQ6yZD5hjLMvbIzQBfutWC2ajV4ywwN2OM/Sql/JY2kKGzU/ + McnBBqlf3Lam5e8lKMv3Yz2FU4VjgzsGQ3WgDa0ya0u7kxzgqCCcn1q43hizkEjRkOoXcAOua5Ka + 6Mc3ygEVb0nW57ac/ZC4Xuo5zQBBeZjcwuMxRn5fUUmnySx6kv2cgg98deK1LjT31pTLpymSVuWi + Xqv17U2GzFgFBUCVOo7igCTT7cnTp/ty5ZnyCvGOKz2uwimOY7geQB0FWY7tzu8xiqk8A96qOvmy + MSowOc0AVpkkgk3uAiP39KkjtonYtnO4cKOP1q1Z3K+X5V2N6OeM8gfWiewaxiKhDsAyJB2oAk0u + 1juAwniYshwoB61FLZfaJDv/AHWexpulXRNwpjkP7s8nu1Wd4uC7zfezxQBTjxZTHzlMigbdy8Up + YXEv7nPvk1aNqbhDhgARnFZMCvbzuWZgc/nQBo2l6qs63AJA6VIsiG4DI4jXP8XeqcbrK5JH3xkH + 0pWhWVR52CF6UAa8kUd7H8rD5f1p5txHAfNPasWRCjgh8D0BrV0a+DgCdfM3DaB9RigCml/JFPyB + 159xV+C/wfNHAbtUN9orxO3k5dhycfw1XmT7JarIjb1k6U2BcuNSVGDSAPu6be1QTXcO0CVSwbPA + 7VRtpftEmxW2Mx6HvUv2V1J2jkdaQBFJB5jBVYemetRyW6SqTKCfTFNllCHBX5vWkLBPvk4NADTG + 0ePKB5qdLN5NjycqvNQIpZAFVj71LsaJQBuGaAH3aCVwycKODUMsZgJjxv8AXIzUs0DpHhmBycjm + gOd37wdRjNAFETeTcARAbSeTViApfrhjufHXNJNCsUu18Z61Xit3Q5JxQBdW0MYKyn5hSf2BPIjS + 24I29T6f5xUMMrs5HOF71ooVmtMyu3ynAAzQBqeCfG7aaPsmuYkiYFG3HseKq67YQW2rSNpLCS0l + GQ5GSh74xWZc2SyxK4OZl5x7d/0rV0K+j+xPFOu4Pwpx0oAo3OnFreM7AR9Kp/2eYpxtyCx6VoXd + g2nSlQzMh6UxJdjqSpKgfN6mgCOLSZGkKyYw/wCn+c1YltRodoWA+Y8Z+taPhWz866DQqxLdmq34 + x0ZbS23yY3NgkUAcZcSyrjcc7zw3YU62meOeTazdOhrZ07TYLkYvSFVfmqveQWkDj7CW9zg0AZs9 + 8wbO3L8ZpvmGRsyZQDsO9WLu0EwZojwMc1DJCrsA5we1AFmGVZLc7Y1bA6nvU1gIyNzgxtnoKr7I + NgHO8dx0pJ3AYG3UnHegDRS+NpL5lsxh3dQverj38OtL/pKCKSPhWU/f+tYEt98xMnC9qgludrrJ + GzFl7DvQBq6pYNGdzHGO3aqS33kEBhlSME0+01z7OcXGXRupJ5H0q5fafFqNuJLLnofmGDRsBmJe + DzMEZGevpW7o8sN/bzLqTBML8oB71k/2YYh83FQRqbdtr7sDv60AX7jSo4ZsiVo067hj9anuNHey + jVizMj8gkdaqQyi+UxjO7O0A96tXDz6rEFucp5HygUANGEQKjDJGaqzWbzgyn5QOPY1p2xZtOaGN + VMo5BPoKqxa1NHHtmij+Q4xkUAUraZFiYScMOgNMf76CIZHf2q5KRq8arEjK4OTsGaki0oKwAEhP + uDmgCohEsqq/O6rrMNMj3AEdgfQmn3tqUgEcaYz1JFMtLdn0wpFGxYHhjQBa026M0XM2WQ/NnHzU + 6Yw6tCPt6rbpH0CdvzrPtrZ45ceU4cHk9qtzW6XLOjqwY9+1AEa+HWun8zR28xU5LAZx+VLaGSV9 + jrkr145amvEY4hGkjKMg5XoPY/571vaHFDr95HHqDMkoU4C9G+uKAOevoo5iSBjBxVYwLdRkL1Xt + XSeK/CdzpkjRMqyJ95SjbsD3rmJbUwoeuGOCfSgC9eWc9rcbbdA0KHPmhcq39Ka8e9DkBS5zk1X0 + /wAR3dvEtuTm3AwVzW/D4w0xIEivbOaSTAVWBAH40AYMu6CZDkFcHcTz6UrtkYlwVHIwOtb91olr + qtuRZSL5h5EX8VY97pc1jKAqZ2jB/wA/nQBRJhubjE4YOOnNMC+S+DzmrMkIA819wPTbjmqwfzcM + 4w3vQA9mbYwgIz/ENvSm2t+6jZsYKeTkVYjn/eqwGAOp9aeW+2sdkgVf5UAQLKY5MHGferNv+6IM + XT07CmyaeZIS1vtmkUdQKbZ+akOZoyqMe45oAvRzjUJPLLgSds8/zqyPDzwETagy4U8YwARWMbcw + NuDDePenPrbXEfkTn5hwrdqAO709LPSbbzlZdvqD0Ncnr/iufX793uWQrGdmFGBjpmstdQeFRHKx + 2Nn5f73+f61E7iLCxDnrjvQBaubtNypAxyRzg0q263DMsJIzzyc1mwyDeSD82e9XIGUIrSyBNw+X + 2+tAD3tSpcFvufrVZbdL2XbnDdjnGKnhs2nkYtcIEJ6461HMiJIApBVe5HWgB8mmtpzDzSrrkZYU + 65mRGYoBgirEkCStiJlC7c5IqjLNsYhtu0d6AKkshbAZcAdc81Gdwb5SD6cVZjYy5WXBVu/pWppn + h63urfdLdxR47MDk0AYjnhehxntVq11OVANuTj8q2/8AhBZ7mwkm00CYKQBtHXrWe+kTWS7J4zE+ + OQ1ACQX/ANrkC3DD0wODV280KQwM0jxheueKdZWcCrvkjYYHUHvRe6jFLapHtLKeDjg0AVrDQ5xd + xuhIUEMHx8pH1roZtH+2W+dPIbHDMOcms+81YNoqWltlFKhQD1HNP0e5udHsHFkcyMRkDoaALUPh + aa1n8yUgqRgjPOO/eq+reDkvHzoQYIB85JzzW5HBLqWmCSWQJM3UEdB3/Sk0S3uNPmIkBlgJyXAw + o/Ci4EHh3QYfDsfm3mHklGGLdFqS91HSYpvMw0jjkhTx/KqXjLUg8hihYiMn746H6Vg+QYxuV9vH + 1oA3xrem38TNe28rqp+VUyD+gpbTU7O6ylvEYoEBPzjDAjp2HeuUk1aeyfNqMH+8BTrvVhqEAMuP + O7n1oA3X1Q3U0klp5S7OGHFZt7rj4DwxlTJ6riqMTiDZsHTn6/WpbfU5EP8AxMVMqdFIOMfWgCZb + lpEO/GDgn9K6bwZpktjcC7lUsAMYPvj/AArBi0lrpc2sqbZsHbjkV20SvDp8UUZBcDp60AY+ueIZ + dIu3Frh0lbD+YNxAPXBPSqLrpuunyNPBSSM7mZyQpJ/KtWQ2uqvNDcjypQjAFjnJx0rhNYhntbvy + 7jcucgIe9AEUMOy5ImYgg4xViVVa4UFSoToc9a6DxZoEdqv2rTsHzDlx/dFcujFpG27vlPGe9AEi + anPpV359o7b143jqo/yP0rWs/FSavF9l1JltlB3tOerd+axl3XGfMXC9896iu7UbtyYIxg0AdTc2 + Vrqe3+zZxIF4Uj+I1S1Hwpexu0kts8aL7Vg2t9JZ8REjJ+UD+Guh0TxjeaW3/EwAuFAxh260AY8y + ujfLkBOCOuabHcqgCxYAbrz0rsbSysfHdzks1rO33Y0AwTWd4h+D2r6M5mmt0ER5D85P1oAxLfWZ + LSYrbnAb5eKnudVnyELFkHOcCqUmjzRzBWyD9K6W38JtLo6TtkLzmgDHtryGZiZUDZqDU1Vl3wp8 + g+9jsf8AOKmGnw2cpE8jFR1I7VdGjRXMQa0kdoSPmHrQBn6bYnWz5NydjgZVgORWeztBK8ZBJQld + x6nFdZ4ZtoNI1QPI7O+OB7VX8faO9rdC7ESrC4BJHqaAOcgUTtuORiraW0M9yiXLAIeoPc+1RWar + u6Haxq7e6ekEZkBGzGVz1ptgVprUw3ku3iJDgDPUYFEzAwZRN2CDgUw3JEkezD7+xolvytwn2pVV + RkADv060gLVlMk4aLIDHp7+1Vbu1+yzgThiHOOelElyIZl8v5CDkVtxWkGtaYs0bMblCcr/KgDCe + 3LzsN20L2HepUQJnHI9KsX+gT29pHKCd79qWw0u4aPcwU4796AL+meIr2G1aDSbiWHOMhR1qxZXz + xXBl1n/iYBBlg/FR6VZW1nciS9mdJADgYGO1Q3pIOOu5hz60AO1vxLDqluP7Pt47eJSQ2KzvtiSg + eWuPpU89gsfzH5cc+1ZaSpbXRZT8tAGjjz237gNuPwrc0O48uUPOM4GBXORXC3HmJD1bB/QVZivZ + fLwp+71oA6fVfEiwXC+UBGjfKTj14qZbi7gtJWjkY2zx5C9s4rnbCRdZiaOUkFQTke3P9KbYa1c6 + XcBARLEWxhzwBU2AotqzH5Ls5YdFPOKmiu1KgxfvCOqHrXTL4EXxLbl9MO6bGRkYzXPal4TuNLu2 + ju/3csfUD9KoDO19yChhO3OcqO1VoZEUbHVckZL9x3q09s8a5uDkZxUDWX2i4OzgHvQBLCwkwyEF + c4z6VNDZm7utkROCfwqCzAhuGRhhV/WtR5okjjkQ7ST2oAlSRtMdUjHzR1p2OuOI2Ly4kHQViS3K + iYBMsW5zSNF9klEjPnPSgC1dzm4uVKSMZd4JP41oeJPD8+r6ZHLbwmW5H3yCMqvr/Os6xu/tDfvU + CqSOfWuj0yf7OxLO2CAG9x6UAZs6vcIqSiVw3GQMisR7RVvpFkGFU46e1dN4c1hYmCXm0quDIO9c + 54quVl16drdDHGzZX6UAV5bTzWIi4Ws6/DQEoQSpI5q9BfywxkS7WU9OOlMa3F8hG7bj5sn86AKc + ErggKVA96lFwLcYHX3NQPAHnYD5e26pAnluA/JoAu6JevFqsEqs4YN0HQV39p8aL+CJVnWKWOP5c + OAf6VwCzrbxAIMMefpT48zEFD9RQB6hZ+PNE8YqsfiJFt5GOC0abcH6ioPF+i2/hiGK50xmuLOQ4 + AjO9s/T8a8wlzLIdxKkHIwcc1s6R43vdJi2xurxsdriQbto9RnpQBal1C1urtzcIVjfqu3FRMNM8 + zbpplViehyAKnuU0/X4N+ixtFdR/67e2fN+g4xzWPcWzWFyDL8gP3Qw+9+NAGhqulSWzpJHt/wBn + Bzj2NejeHLG28f8Ahox6/HsmA2DHBGO9eTrrksUTKSOD0Par+n/EnVdMRVsZYgpHIK9u9KwEvjn4 + eTeF9UY2Jie3HI+bJFc6b6eMkt909j2rsrTxpYa7bGHWYpXlc8Ord/yrOu/B8gEjQul3Ao6RjLL9 + cGhaAcu0skr7mK8HtTjEAcMMk881Zm0l7JXxg7uQBywqqzysygDBPr1qgHSWqzANL6UunXjWBOxW + KsaZcggbu4HSlindrf5ANxNIDqblPteiWrESNC2fujJ7Vd0bRY7KLfZswWYZYSdT2/pWJ4Q8ST21 + 1b2krIYj8pBFdd4k024ht0nsdpjA4AHNAHO6npkSs2SwPase6ieJcSYdenB+atGbWykgF9G2cHvi + qGqMxiWW0GFyCSRnFAFeSN4yGiLE9we1QXYEhzMo+bnAqaC9YzbpSGY8CoL/ACwDQ80AV1mxdJwQ + q9h1qd71WHU/QdqgDO0gJAyevFE4WI8dW60AafhzUHt5v3ZAzxVzXNFku/38Odg9KwbK4ELA4z+N + ddourgQKJsMv92gCr4Y8Qy6VGUmkdLcDjn5/8a6vS5tM8SWTG3kkaZeP3xIyfxrmPEuk/ZXF9akG + CY/LHj7tZy38tvcxSwnYw7DpQB0viLwrIigwhcHqAeKxDpbmcgJtKjOfStXRPHgjlEeuAzZ6bf4e + lajX+navE4gZIyQcFmxQBxd5ZPG+9iuDxmqitHGR5oO09M+tdDqmjNsDl90YPBHSsJ4N7uH7dOOt + MByxj+EkE/d5qwYGkUNu+VetUgxVz6gVNAryx7Y84J5PpSAeZWjG8A/Lg1sabqn2hF8wnniqPkK6 + qk/z/TilaEWo/cgqKANPSbRba8zM6MXGDzVPxHYPPOzOOVPy471R03XmSRXlQEHv6VstqaakgJKh + h0X1oA5jBjYrP8uTkA9TQ0qoxLHqPyrQ1+z6TMu104x65/8A1ViSsVc5GdwoAseWbkDyQWC01QVv + S+5WGcbe9OguTFZqIjhxnPHWnWTCO6LyKjPnpQBDfs4n3sMc8Y7VPBKWT922498U7X0RCjRnJmAL + KP4aq2rtA/ycBu5HXFAGkYg0GT8rY5J5qIw5jyMORxU28zwAou5jxj1pnktAzCUlT1xQBHFP/Z8w + dpNsg6ccj8a6jQPFNjqdqbfxJbvPM/yxTE/LF9c1zsNsJ1U3EYIP8VPe1iicCORsnnHTBoAtat4Z + mS92Wn79WBK7aw0ia3uXW4jdChxkjvW/Z+KLjTZFd4hKwyAc44qy+nwazpxEOPNdvMdx1UdTQBzb + AbSNyqGPf+lWvDPiW58IXDtZzOIpRiVVON4qS/0ePcG04/aYV4Z8YwaoPGJrgq2AqnAPY0AdVdww + eJLX7XoxSKfbnyRwzn61zGooyMzsreYpwQTyn+P/ANap9NvX0S4DQtzu7dhW/rel2viWzWfRiPtC + L88a/wAfuaAOQEvyDepIOOamtbFJZWKzrH7Gpk02QRBLgYYHkDtSTaf5LBgM7u1AEVxbS2aiSNfm + xw3St7RfiTLFZi2vUe4VRt44xWJDczTzoLoFgvO096bMomlkaJfI5ztFAG7Jqdlrcm2WNYHA+82C + KidbiCAoVLWzfKoHOawo1dyGO4bQcc9frWppOvSwQLDcDzQSOvbmgCjcWBQsqDYwOTmo44BdAZfG + OeuK1NYdZLjzCdu8dAKzpLYQt+6OKAK88ciXREQ3AY5/Ckmt3dlMoznPSrMU2zJxgD2zSSRmX5kY + gdiO9AFWO3KSDgqMjrXQ6fYuUAjG3HO7rWRawNeSDLYKnHPeunVG0bR4ruTnc20g96AHxn7ZbNA7 + qzgcVzup2s2mzOl0CAT8jYzvrb1TxpZ3tgr6fBFFL/EUqpp+pJqpxeqJAPulucfSgDDfcjgxAqSP + mB60xXXlZFBPXpV2+tms5W2oTnpk1nht0uZCAfTFAG9oOvCJBb6jueJj8qj+Grer6XFCqvHMvHTA + zmuajlMUmWHznoKvQ6tLDEPtKeZnsT0oAkaBVLGX7x54qOG6NvkEEA/rV2dYLi08y3fMhH3e4rMR + mkDLOMkHg9KALcN7vXI4Iq9ZyG5jw7An1rFuWMWMAopxTzqMkIxZAuOpINAD7ZAcg9F6VqaXdRFg + pX5h92sPzRbfKQdvr61c0+4MjDyxsYHkkUAdA2lvdQ+ZcDIPGOuawNY0wWNywjwVbocdK2E1ubTF + +T5gw5yM1Lc2kOqaX5kXMxG4nPT8KAOSUSKu5VGM03aZmRo22k9Tird26Fgp+6hwcVAZfNmCnBVu + mKAJp7N71FDcuOI8d6pJlLlt+d44PoK0dTZLKCI2HmCZQCd33c+1R6iqXKpJBu34+bPQGmBNpzND + bgH7zHjPapLiXMhEvzMRwarQXG+ILcfMP7w7VZjdHj+QgMOmaQCRF7AsVBZO2am2G5t2kIAJ9O1V + 2vzM21l+UU9Cjj5M8eh4NAAIXjUeRl8/pUa6k1hGFtWyG6n+lWYX25Y8dsUs9t5tkVkK7Tz7+tAE + 9l4hAj8q/RUf+Db0P1qZ/DUWrTO0paK9cfLGg+Qn61zc0SeYc53DgVr+HNfk0u623LgwSDaxHLY9 + QaYFa80a60G58vU1VmbqF5AFWdC1k6PqaTW6qyEbSD+FdRJd2s8IikZJbO46MTmRB7nr2/WsrxD4 + QjtohLo+9kHXPb0pAd6uh6Lrekm6hkkQSRgNtQfK/p+dc1f/AAsuGUnSWSVScgynbisHQfGFxpki + RKw8tRyD0z/nNWPFHji/1lFihkCxKMAocUAaNt8NNSt3bzYrYsnT5xTLvwZYQTIuqzlLh/vqigqP + xrk/7QuIwRHcXG4jnMpP9ary3kzhvtUkrSH7p3E0AdXqPgvT1vI47K4kfcCcYAx0/wAar2ngu2uW + ZIJX3pnjHFc3DqUikfPIGHU5PFb2ka3PDe7dPZGGzGW7/wCc0AX7LRLSzcxb3eXrhhxVG78JeVcA + bvvcVfEgudqaoyrOrbiV9Pwpmo311pMnmWmySH3w1AGRrXh6TRfLMq8yfcHGPxqxZ6fpmnmNddml + jlk5+RQRx/8ArqO51ptT3vMwWU9iOF/CsOZHnkIkYu3YnmgDo7qPTtPszcWTu5LcAr1ycVl6p4hk + 1BRbsCEXkCqEGqz20wEWGEZGAeRxVy+vRqV2JpUVJiACQMAUAZ0+mvaNuuz88hwAOmaktbt7C4Ub + c8jvW5rGkp/YUEsRM0nLSf7PFYogSWEF/lJ6CgDWcjXyuMhwOAO9Y09hLbSyKy9+pqzpM9xo90Jr + co2OMMM5ropr2PxBYGK7VVXBbIXG4jnrQByUI8xSADs6HPWpPLIjGxssvr3pxQmcqx+VGwFHenJI + gOF5oAW0jZB5nQnnH6Usnzjrg0rW2/8AeISD1x2pWR5VySNo60AQBX2EzHIXpSQJ5kjOOFpLgrtI + iLFvWi2Y3CFYuoNAEt4myTBBQ46Gq6OyHKjGTzSyyyXUm+/cnHc0+PY42RtuDcDigDS03UzdQlHG + WHFSw3/2CX99lo+hA64NUorOeyG9FJA68VJFaLqNu0hkIlXkgelAF3VtEjvNMF1pKOctyPTFc/bw + tGVeMfMRzW54f119M8yJ2IjlGzk9B/k1p6f4fsmi2xXsUmeP88U7gYV5Et3aQlWCsox+NR2eUnWG + 7bdvrZ1TRY7FXjuQsatzHJ7VkyeXbxnz38xl6NmkBFfiXR3MDKQjHI9xUMV0ijMnNdBZWbeJbUcC + SZU+U454rFu/DF7byNJcW0qxqeeOtAE0EcbI+4nax49qnKNY7CCG46Vjw3DRHO1gtaNrqPnBRKu1 + R0Y80AXYDHPAzlPmzzTWG2Evn8KafMMWIsFfamKxcAyjAHbNAFSeRJpOBg0xrXykVjyp6VLqFv5b + AqwTI6dal02ZZ5VjuMNGentQBJZxXFtFuUZDcitDSPFrwOYrkFkfj6Vl30l7p87RpKRDn92eoIqG + 31gRxk3qMzqRnmgC/wCJtIa2uzLYfMjgEj2rNs70woyIMjPLHtW7Y3y38gkUnGBke1R6p4dS/mNx + obeZgfvIVH3Pf3oAz7W3EmGzgrSSRqszF13+4/hqOOLdGSrk5HO0d6WCUxYaUMYhw4HegCM6TLcy + Ztkd0wckd6jtZZbPiI+aqnlem2tTStXNvcbYZyiSA4QcdMf41Y8Taf8A2dZieGMR7sAkc7s8H+dA + GVJqTT3AKtjIxtrStNVy/kyLuUj1rAlhG4NtKqOc/wB+l+2SpP8AcKMn3s07gdJdeHPtLRS2zpCr + csD171laro72bGSFWZRwzHpQdUe8hTDEMg5xU0N7Pcx7GVpIf4lzSAwlk2yAoevUDpWpa2hvYeTg + 0mo2UM8w8lPs4HUDvRpsFz9oYW6NKB07U0BbjvptGhkgJDRMu01VLRyyIYQSgA3HstVdVMiSlZyx + bPKiksbyS1hdWUmKQ5K0gJpt8UgAw69iKn0/UyJdrdOmKIPIvW/cyLEqj7p4zUEUIEr+blHXJBx1 + oAk1O28q6VoSFVhk1GbZQ25TzUlvcfakIucKAcAnqaWK1cyFkQlB70AJvJdNq5I4+tBcbCnCjv71 + LIVcAowVhxj0qO2t9zkXHKt0bsKAIpbPIHlKWUjk06wgaNiqIBzViF/kKKwBHA9aguI5oX3REk9j + TQErWypGPOGc/pTLTy47gMFyob5fetB7EmcG3G6N8hSTjNWRpgsws/y7ouWB70gKd5dGSRcfKnIP + HFXrHSYL61e4kfyVVcYA61lC7OrxurAKxbIHtUtxfC2sTDA/A49KAEazRmkEw+TqG9as+H7YSTeX + bvu7ccYrIt7qRdobPLc59K6jw9pf2KUXcJBVjuI/z9aALF88MsJh1AiRoPl54Iqt5GmXUG3ABx1x + 0/WneMbGfTryO8VB5d2N6qfTJHP5VBoNtFqUb/b28uU/d2d6AJLPV4dGtP8AQyokHGKgu/Fwu9wl + PXgj0pmpaSmnOxmYEdu5rOht2knZ4FX3oAimiju3AtlAznrVWSAW7OC2HQ/d7VdNjLaMjurbSeMC + s+4WS41BjyEB5zQBcgnk2ARnJbqKZcydmZt3fFVxB+9DRkjHfNWLh/KKGTp/6FQBGLg3C5PzFeBT + LeT5yEzlB0p1zb7wGtzt9RTNhWVQOHPWgDc0iUajbPbTgM5GE9aydTtPKk8sKcDrk9adZX5+0FLc + FZM/K1dPpmgReJLR2nOyZDhQT1z60AYWgXYtrvy5cFXBXA9+OtGpLceH9YIsZ3BwGI4+YHsaNR09 + 9C1ERTFTMjBgE6YyO9S+IoDqHlag5++RGPfGKALelpb+IbtA+Ldk+ZkXofxqHxFpn2Vpv7OXdGOW + 56Vk3GpCBQB8pB429a0bHXN8kX2gKY1ILju1AGakfmFfJXLN0/z+VdZYQG503yda5xyPp/8AqqXw + 2LKJJvsqbjIdwDL936Viarq8u9nhA8sNg88/TFAGrdeFbeWBHscSL/AM9DWRqnhObyS7KUYdfetH + wkx1Gdnm3rECAB6Vu674psYbIRxeZuHBJHWgDzZw2nybQMluDVnT9T2PsJK56Ve1OS1vJ/OhOfXj + pWVdWctu/mJhgTxQBeYrOS0xAxTojJHKHspCQ3GPSqaXCTuqpnf+lTQIJ5XRXwy0AaN7YxzWzT3I + /fSHp6VnS2LI8Yt13kj5ucAU17me4hYbvkHXJ5qvJfDMYDNlevqeaAJTAVJGBuHPFSWuoMN32iNW + UgjOelVo5vNUvg8HGKVollOIG4HNAGhb6dHewhrVy8gPK4qaFTZZRssT1GKzLWd7C5zDlS1a9rq5 + vU2uFAIznuaAK93po2GSIEjqefu1C8QZApc+uBxWnbQpeyCG1OB1cnjmi5sUuTlxgpTQFBAYCWEQ + bjrmmsHvDypH0qYqYGPlk56DPSnWFuz3BN2MCkB0niGK10bw/ExCyMxwhVskH8K5O98SPfWixqPm + AxkjBNEkkz2iQSzgqn3U54rPm4RkY4YEfhQBd0gPBMGnwc8fSpvElpFBIGU5Y4Ix0qjcanIkKBG5 + 7VGzPdIHvF3P9aAHpGtymc4Ira0fU5YYUG7KA5P0rAEgjOFjfHtVqzndD8ilFkGKAPTri4h1fRrW + DVAojmjwjdwPY/XNcJK6aTfubdjhDgc9a19PnbUYLW2upsRJ8o61S8WeH1sryKJ2AeRSUb1oApTX + TXpaQMWJGcdal8PSf6UTcj5WOKz5YW0zgTKZG44Bq4THLpSqj7LhWJdsdfSgDo9e16OGFba0ji3p + wZCBzXOoYZp2N2u0Mecd6Zp12cIbkfIBzTbwRG53W4wp5oAbeWVmgY2ZYeuTVC4SWFAzjdGO5qws + HmK28jaTVi1vhaR+XfRGeJhtVR69jz6dfwpgZEcrPcAp92pl2IzMxLuRwamfSJZCXtnRhnLgcFR6 + VWc7J9mNpbtikAW9w0MheQj5ea3NG1Y2sPmWhCvjuf5Vk7UadY48RseW960rDS11C3b7EMzL3oAt + 6hpn9pZu4GzGq7djH5g2PzpPDsMV/Y3Fveg/uVZl+vNJYRy2KhXfcB972q5aRw310/2eZLbcuCWH + X8qaA4yTeT845B4qaEqjZlVtzflV+80qY31z/Z8T3ENqMs8ZAAGcd6zoZMncEwH6H0pAdDpusLZQ + 7Rjc3ApkFoZJHmY4iAPXpms8R7oh/Gc5HtXQaALbUtGMN6ApPHrzQA/TvEdsdOWD92rRk8gcmud8 + QXkl1cZzlfapr3QP7NujGjfKTlSKzr2Jmdgx/wBX096AIkn8ucBQQjdat/bWMLZKOOnOOKzdjL0P + BoiXe2Cu7vQBpxC0KAyK2488Hiql3LskbaDtbpjrV+3tlubYC2TExGBVe+tJNOAF4PmHNAFO0meG + R1bI9jU0iK23zcbsdagWYO+xOH7mrkMWYcNgkUAQwKGA4JC5pzyFmPlEADt61asYIgSJWA3dOKv6 + zosFpdxPaBGVlG445BwKAMwuWADAbqs6eI/3hl++Pu1cj8NFyrRncAdxb0psElpY37NMhljD4YKe + poAsWmm/aIjKknlsvUnoalhtHLcbiueucA1Uu9UMs8wt4SsOfkUnkCrOmXcotj9rkV0HSLnmgDoD + 4JSXSzPNNFJhdwCkZX9a5+K9gD+XPgDdjNTpez6ZZywwPskcZbk/KK5qZ2llPmvvYnrQATr8zE5D + N1zxRbou7951anhZNYuUVFw7dvSp59IltXdZ1IZKAGvpLNGfLAfufaqDCSKUEkgdMkVd07VWs7oG + XLL0x60+7ePUjyCpByMUAV3bBGxsk1ZikV4gAMkHOKpzW5SUmN849qjjnlil3KODxj0oA6KykW7t + yJW8pk4BFdxrGhwax4TS5JWWaEBEY9QDn/CvNrPUfJmBcZDHLV0s2vsfDMwt2ZYy4z7cGgDHv9NK + yjfD+8bgYFUNRtTps4S6HlkjIBPU/wCcVeN86xKZmJlyMc5p/ifU5L/RYVmto9wJUyZ5oAy01Dfb + qZV2xnoKbfX6NEv2ZcHHWmPLFJYQx2ZLTL1U1EIJA+2bAJ6Y5oAIboyDb0PU1c8xLkBJLna4Hy44 + 5x06VAbZbdcyZ3elNBXeCRjnOaAG2808N5syYmJ7fx+5q7tW5QCZQso/iqsULT7rXLr6k4xVi0dX + +9kmgBlxpbI7SxqZAoGWz0p+i3txZ3AezJAHXjrWlZ26mFyzEnPC+vStzTLO3vZ1M8Yjwp6Hr0oA + 5/xFqyrIggQKrLlsdc96xpQZ5wySbu2DVnVYQ9/MJCSitxVOQFW4G1aAOm+H3iGPSbie1upBDBqC + CKRugwOfwrI8VWsenazNHZtvs0fEb/3h6j171Elg02N65x6Gt200i18VwwwXcjQ3Fou2NQMiTvye + 3WgDn4riKEhkfKf3h6+9aFlGLeyS8eT5DIMoDnv3FXZ9I0iwhJFxJLMpwY2ACg1TvvISzMs77S5w + EUcUAW9dH9qW6y6ZKBgcgdawoNOu7iWMmNiWOMDtT4Jxb5e1bKuMEHsfWpNM1ZrG4WWFmct0BHSg + CprWivp0u193mMeR6VHa2jmQbVH0zV3WNRkv5mkn5YnjFRJGBMjRMScdKANvR7OO1u4pS+SGGV68 + d61/GnhSHUYReQyqsZXiPI64rK0S5hRNzfePXvWr5w1KIwwucAccUAefW1q8kqiT+WK0RpdzFFuE + bFT0bHBqxrFj/Z87LjDZ/Km2ctw7Kgk3KO3SgDPQPuHmqNynv2rRs7hrhjDIcDqD6VPeafDfWbbC + UnUjav8AeHfn8qsaL4bl2pLcYWJT85PYdzQBq6dfjRtKX7QnmC4JQH07f1rIl0SztbsSrcoQnJQH + qaseJ7mBVT7PIXtDwrYwQ3esOO4RrxvLZmjI+90P5UAXrm881T9lHOeAOareXPH+8BKOB19Kb9rF + pcq0ILDPc8mp7m+S6k3fdKj7vWgB8Gtj7Oq3AZ3fCs7DmorqxQTbl+oAqJJlu4gJMKwIxT3kNq+H + G5/7o7D1zTA7Pwpd6NBrk5vQwMv3Pl+7UnjAwwXX7tFe3l5UjBbHvXP3GnCOxhuo2IL1G+qPcFYX + cknoT/n2pbgVZtGFxZvNbH5VOBk+vt+FZ8lrPakrcqyHGcEYzWidWS3lCxAlVPUdDWxf6pa6nLH/ + AGlH99QoI4wTwKbA45pHEirjk1asbxYZCsoDYH1rV17wyumSKVbeGG4Y6gVk/wBn7UdgCpPc0gLw + aEwtLKMDtWhoNykVwHdd8JGCjDIrDkSW1g2zOhVhkVLo+puSVlKlccYoA6Dxf4PbSLRb21wto7DG + W7ntj61mpKdXtxaOQvlfMCSBuJrqLfWIfEvhg2muKzQoN4CnBJHT9cVyU5hEjNbB0CHABPNAGTPa + fZriQONjqcZ6flUtqqB1SRmMr/dJzWlDaLrEUh1Qbnx+628ZNZE1s9nfctxEccjpQBO9tLcy7Zjw + vfNQ31q9oee3A75qe2Yyzby5OKiutRMsjKQDg4FG4EVvEyfM5xnsD1q5bbzKHBAB9KrCJN4YMd3p + V+wt8szRZUCnYDXsWSGPz7jGI+SMVVuvErXKEWuRk9QMYqXVyLXTUyRmRcmsSC4EAO8D2pAXxbma + IMR8w7+tVdRtkUAT9ew71as7wsF2nFGsKodDOMzHo/YU0rgULe7j098qW545Gaki1FIbwzeYyzfw + EdvyqkyGSfaw+bvRcQLayqyEnAyaQHR6gi6/pXnBER0IGFHzN15rnmlXyTGRuQHByeQau2GrS20G + 9OhO3H1//VWhf6RprXbXmnrMtuYsOjNk78DkfiDQBi2rpHIVQjb1otHPnBZAMAdRVUQiW6Bgyis2 + Buq29q2nXJjn/eDsycUAOLCG8yg9zkcVCzeVIZY+cenekN0LqYRSHAHA9aLMCOTy5BlTyPegCxa6 + ltkL2+ORzxjFWbTXpLSV3Y84+XFVJvLilKjgVFMpAyBxQBq6prEF7bQSzA+ZJ97jpVRGjDbUJAB+ + U+tUywlJUdE6VteHLK3kuoDqQZ0zyAcYFAG3feVo+io90u2d13R/LyR35rm77VZNSmzC5SEj5hnH + 14/Otu+hv/FN3gTWywW4KRqQM4/OsUeFZp5miaVAc9R0oAaXWa0EUWCIjuA9PeqEMbCYM3G77oAr + bi8Gz2YDmeLc3ygev61X1CxnnuTE8TvPb9fKXigDMuIJFlBdtzHnAPSrEF0IwDCm5hw2VNRzxTWt + 0BeKVMnTIxj8KZ/ahtgY49uT7UAX7VH1K63oERVOTxiuu0ex0nS7L7chJkm+R1kwwyPQZrh4JJDw + zbVbk4/OrNpefLsnyyg5UUAf/9k= +END:VCARD diff --git a/htdocs/includes/sabre/sabre/vobject/tests/VObject/issue64.vcf b/htdocs/includes/sabre/sabre/vobject/tests/VObject/issue64.vcf new file mode 100644 index 00000000000..611052907a2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/VObject/issue64.vcf @@ -0,0 +1,351 @@ +BEGIN:VCARD +VERSION:2.1 +PHOTO;ENCODING=BASE64;JPEG: + /9j/4AAQSkZJRgABAQAAAQABAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA + AAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABQKADAAQAAAABAAABQAAAAAD/2wBD + AAIBAQIBAQICAQICAgICAwUDAwMDAwYEBAMFBwYHBwcGBgYHCAsJBwgKCAYGCQ0JCgsLDAwMBwkN + Dg0MDgsMDAv/2wBDAQICAgMCAwUDAwULCAYICwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL + CwsLCwsLCwsLCwsLCwsLCwsLCwv/wAARCAFAAUADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA + AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKB + kaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZn + aGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT + 1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI + CQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV + YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6 + goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk + 5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8J7JbO8tYo1tIFCDLOVG5qfdaVZRwmSOFWzyA + F4H1rLt5WViMhdp6HgmtKK8O3B+4Rhx6fSgBI9FtjaNN5aErwRjilSys7lFAt41xyTtqc2yJCVlY + 7eqgGqv2jyLcebjZnGPWncdzT0+w0u5eQXtrGiBcIyoPmNMXwpb/AGMTSRRbH6YAyPwqK21GKdfL + BAVfu+1SQX4jnjKFsp03dPypCKN9oEaKSkC7R0bGKpnSlSPdHErZOORXV3Ouy337sCLB6kpx+FY0 + t+VfyrgcbuCB1oAfoMemrcImq2sZX+I7ATXS618PdK1DRlvvDEaMq5LoV2nisx4LVrUfu5BOePau + m8EQS6PY3HmFXjljKhTzjOf1oA4mz8OxvMrLbW5RD8wbByKg1LRrRriRYY408w/KAMba1pRaWt/H + a6a7CVm2u7N8lUPEujzaRekzSK6tgqVNAGNBZJauY5Yon92GTRJp0ROY0Un0A4q3c2odkaYOMjii + KL7NIDGcj1NDAZBplmmWv1xnoFHStfS/DFpewqYoYm3DutZ8lv8AapdyOqk8EVteEbSe3KBSrDrQ + BT8S+HbawiiWGCAPjsuMnPesqHS4JSFlSMP7DitbXbvfrkkM2eGw3p+FMfTh5X+hr8w7t3oAhOhW + u8MkMZUY3fL0Heo9UsrN5FFrbxKmMBgoG41fWFra0Acjpzg9aoXjtgRoo29vagCoun27kbY059qn + bwykskYjRArdTT7GEl2UqMr2q/JtVU27iR15NADdK8DC/wBPle2iicxNg5ALH6Umm6FZ/a3ttQt4 + g2Cqnb0PbJ+tamn3j6ZCW0nILfeBORWVfO4dhLw7fMW7560AZuqeHf7MuTFcRpv6qVGVx70q2Eci + QwyW0SsPvOqjJrUtb6S9tHQKGeMZYuM8VUs7gRxbrncy9mWgB1x4QtTHvsQWkHJVhhax3tkhugHh + UkfeAXIFdPZ3v2uxkQ9G4jI6/j+tYun3r2Fy6yxeb2Py5IoAqXenJ5xaGNNvXH/1qcLSGeBdkSg9 + CcdaswC3be0pfexOMnpn2qaS1KQkQASKoydvLCgDNi09RKTNCuO2BxVjSobc6gqXMERQHkleDUsc + u9VADbG6qOWAp11bLbptkjlCkZRsde9AFi5sbO3kKfZYTnkHaOlVbuO2F5thtYcADjaKXUpHj8ku + Co2VDFL5wLeg696YFwQ2z7Qtlb8HJO0c1Zsr7T7a9kL6XazZ4CmMFRWfHdkEgjGRjPpU9raP5LSP + j5h2pAWdQ0+z1KdG+y21qvcRqBn8qXSvC+iTu63ssqyE/IAuR+NQwSrGm1g+c8E9qiSQW9wPNYYP + OR2oAW68GNa28k3lwGNHwvzDJGfSqM9nHBgm3j59QMVdmma4zIjsUBHy5OKp6o8s2BJjZjjAoAro + /nysbgYY9zWmLPCR+WQQwyaz4k2F/Pbft/GtKxvUeFN+B2x+NAEptsWpZSdo9etZe8su2X7pPFdU + LeOazKqVwevNYt7pw5EA5HIxQBQA8tAIeGz1NWIJvJlhW5OQBzjrUMR/eN9pwoXjB4qQ3ERJeYcy + 9P8AZoA0jf8AmybVxsHAFS6jp63ixmwjIwOfrWfaou12GcDpmt/w5qJhXc6hh2GM0AZkHiRpblVl + G0RjGMdxXQ+H/E0Rm+bjdw1crqEHm3EksY4Y9PTmq0cskc42qUOfpmgDovHOhLBOZ9O+aEnIUdRW + QZft1sgum/1Ywua3fDfiFDL5WoEPEwxzzirPizwTFPZC60kYUjcAp4NAHPSq91EoRS3061DHD9nb + 94Mkfw020v57GbcCRt4IIqzNcedIH2jc3JyOaAIYrRZmJxtNdB4fkGn2hluBgBR+NZ2n2X9ozAQD + 5qvaxGbKIRXkuFU4C96AMDxBKZdQkuEUkStuUegpNM1eWScAkqpHTHNPlwbjMzExZ4Pal1PS/s6+ + dY/6vuwPSgC9G8c0A+1xEknrnpUVxaeXNm2dVUfjVazvEZAEkMrccZzV1YYyBIhJP8SZ6fhQBSmV + 4JfMVT+96UJdSQdcMO4A6fjVmTUoJiqTOMJ/q+elRyQs0TtaxF0PVhzmgCzpd55r7YI2HHPTmrV0 + sDTF7gnJXGO4OKyNKgn80NbFhjoBzWjqdg6SISPmIBOaAKVnI1leyhsMJOD7CqOqRtZqotjiFulW + rhsSMshKH1ogsZbmF475TKifdf0oApabevHIAhCYOdxp0t59luS0I+995uxqpdRyWsrqmXGeCR/K + rVlZfaogqv8AvD/CaAIY42kV3K5zzn1p9jNLp6u/A80YPNWWsJNPAVpC4JAZT2HfFWJoVmVVjhVk + HTPrQBPoi2wsoo4APtBHL+tP1mS5uVEFxgJGNqH15plp5WmyBriMRsowM8UybXTNdbrpd6A/KKAD + xbJAGs44FIPlnd9c/wD16ynt/LiDW2SR2qa5vP7RnMs6BNuQMd6jhkAUb2K8+tADYp0fhj8w6itC + yQ3CFYeAOoqi8Uew+UMuf4u9T2NwIW+UgMetO4FmS6RJ1ik6HqxHAqC+gimUiA8DvjrU0kcE8ieY + itu+8c0+bShaWxksSZoM4b0SkBTgha0cq33Cuc1SvrrLFV6jpWqbuGe1HnnDdAKy7i3WSY7OT2NN + AMulWSV8ZDNzxV7SlbaFjClx69Kpww7W3ct7jpUtnNJHd5UjZnt1NIDdt7h7NQ7qGfpt7VR1XVEh + dhEpP94/4VpafexTy7ZlbBGDVHxFbQh1j04HaOTkdKAM5ZVlYso3E+tVp4w8gx0Bqd7QxNu+6D6V + DIoVySxAx2NAFyNmli2pjYBz61paW3lWrFS3BwP8/hWJbTBFJy2D6HgfWtiTWPsqxraBHyOeBg0A + RSoLSTdIepzz0606exTWyQGMXljORTNT1B7+ECZR5fHzDqapfbHjbFkTsIwSTQA43ptyyS44Paun + 8N64Z7Bre4YlZBtU5+7XLTQbjwN4Pb+IfWn2lw9uyrIw2Z5HpQBv3GirHc7LxWVZOVI71FNp7WDg + QYlIIGD6VvaPdi+tljb5yeAzcn8DT9YtbPSpVhDM87jJ3Htjnn6UAUIrJreD7Si7MDoKhv8AUxqt + pGt5GqIOr9zRfLM8ZFgZGtex2nGe4zWKN8rsDhYx2JpJ3Atx+HxcRSzWcpcL/CRwaj0zW1sQy3cS + nsFPSoYJpbIl7dm8tT8wzV7+0hqEO1Y4lQ9cqMn9KoCp9kW7kaaxU+Yx+5j5etWrb/RGxfr5bkdu + lW7KFILpfspDbVyc1fjNnrLtHqOYWP8AFjGfxpAc/e6Ql/GzW4AfqBWfpupS6Xer5vPlHmMjg10V + 5pp0u4JhYNGvAYHrUn2WLWrVo41AvSMRZAC/8CPr1oAvafdWOuNG+lqDekY+zg8MPXPX/wDXWZrF + tcWNw0erKElB4Rf4R6c1BpqyaBdbrnEcwyAc4x06H0rQS9a9jUTgOXPzMwycexoAw7u1jYb3zkU3 + Srtgdk54PFamv2C2pDQbWjcfKCeSa56aJld23YA6ZOKFqBrXGjjULuOKxKuZOTn+H/OKwr/ztOvs + uCrg7RgVLYapPbXAEW4EkHJNdBNBH4gtgyhFmXuw60AVpbT7VpiPJ94jLetQWsDRSIYz8mec1c0+ + 1nexdrw7GjJXk/epsFtDPG0bOdw+b5SaAKWsXA+14Y71FQi5S4RvlAC8A0y5hHmHarhvQ9BVGSQx + sUXPHX3oAmDCJ8rzgHg96gQ+ZGWbg9vahNRG7EnalkkF6hEXyD270MCWF3aEhdue1OsmNnMAih/r + VaBgAUY8561PaubdnMxJXseuKANhIY5Assp2v12itZtAgubEi2nb5xuKYHWubstQaO6SVzujTqpP + X8K2rXWLRF8xZJPMfjAzgUAcxcNiaRSpUocc96sW+yNgZCMVF4lvJdRvTOYkj52jbgZ98D6VWmlY + 2qCUnJOKaVwCzviibANwYc8Utkdl7tbKhjxmpUspvm8tgn16ipigSEG4G4pxu9TSA27GeFbRlGGm + P3cdhUN8GEP2hV3JjafrWfpU/wBmuAcZLA4/Sr1trkarJHcRmSEZO3uTQBmrcbZCLoDZ2x1qOHSi + yebJIAPQipp4kmbzI1EQJ6GtCxsoHP8Ap91GB2yDQBlSWO+M/ZsBHHzZ71XkfMIWNgGU9vSt3U9N + t9m21uonz0Iz/hVCfRkjg82FhtHDGgCuZ8EMjDZjBzSZ8pAwU7XbGT0pWtEjjAZgV4PFOml2QKqk + OoOcU1qBNYRSrdkrhw3BIrah8KwXoV/m3PyVzyDWNp999kccgZq/ea7PFAGgZlJ6EUgN23thpdi4 + V1Eucr7ev9K53V/ER1a/MkuWdBtG04zioLrXJ5wDK2XAxmqVqmZ2YPtHJ/GgDsvC3i0ppr2d2ish + yFAHIz706bRLNdOPnErKw4y3NcvZ3pjA8o4kB61o3OpSX9nbx3QIkU/MwoAj/sGaPzFjlWSJjk46 + ioYYwqssjIHHAHpWm4ESN9nYDIFZV+I7uVI1wrY5b1oAtafcvb3W4MM9Nx6U/VZpNRys54ToU4zW + KXaDKrJuC8cVdtpi1gzs43HNAD9N195bdYtRIUR4wD1NX2KuA9uThuSQelcsZwzq9xyzfezV/SdX + e3m8pXJhkPKkUAdYZk8RywjVVJES7U2cE/WtA+HDHohuY3Uxg7RF/GeaPBlxaawMW6rHKnAU9SOO + lX/FFv8A2bpzTQk+cpAAz93nrQBx+r4c5CODEOA3Y+wrKu5V1C1GFKznkk9K6Wzv49fs8Xf7y7DY + MhGNgrmtX0s2t66WknnKvUp0/WgCnbrJFdot0NwJxkDFdDYp86oMjjIArJivxbR7LuMyEjKitS21 + MW8auuW44H93/PFAG15aXdr5Uv7uULkA/wCFc+Yvstw0at8+eoq/p+rm6vRJMNwIx9KranYySXSy + WEZZHOCw7UARXFyj5STAk7ntWVf2gALLyfUVoataLbfLO2SO/Ws2c+VwhLK3QDpQBmz2xAyCG56d + 6uWPlnCkFcjoTzUBkMc/3cZpwn8oZkDFs8HsKALN1apDIHOeaiLkRkMOtSXE6yxAsRUcdxldswIJ + HANMCuJW8xQgOP51oacWPPGAeRUUOIZQzDhecd6mbIcbPusM0gLmq6bHPohlhDeZuH4c1zzF1+Rs + HByDXTae0s0IhjjZg3GPWqOs+HpLCTbNGyb+cHrQBZitjPEzW/LL97vinw2v2m2aORec9AKXQbsw + ygBBiX72TWxfaS8kiGFQAwz8vWkncDlbqNraT5cjb/n+lMGckx8kjOa1tU2TxkPkMpxyKyrhJ4Wa + KIDbTAkgvIp7URzgBwe/BpZYrd4vmZWNZ81x5cgBXDdzVlIvtUOGIBHpQA2aEROpR8DsB2q3bvG9 + iySzEsTkLnrVMqViCZzt7nrT7GBVuQRnODQA6Q+Sx80A4HApEJB3BAR9K19EmhkvCJ0ZsKe3tUc8 + Mc1yy7cpn6YoAzoUiclnYYY8AHpUl8zRxqpPy9qtC2tULgSMAvQ460lzIl9b7YiDt4GaAKMMQlJ5 + z9Kj8gIW5yKnS3Crlzhh6d6k0mbyZT565Q5z60ANtrRpPmhzWhbwy7DJcDhhwMdKlt7aK+gb+z33 + yKdxVuMCqaz5cqGYfWgB6yu8rBB8o6Gs/UpjGQXBGPTvVmSfyImyepqrqjbIw3WgCDz1ib9yOTg4 + NbVlNBJYvlVBHt1rBaPzQWU4IHSn2FwRJslJxQA6e3M0O4oAzdB6VXR2iKGQENGOK0ms1eAkFjF/ + BjrVGaAo371smgC7pety2kwl06Vo5AOWXmuwm+Itv4g8Ota30aWlySAJQfmkP/1zXIeG4Y5SVBB3 + evamXGly2tydwG0nKkHpQBZ86fRbpBLI252y4PGRWhO8Ml1IbJhHn+BTnNU9O1oRwvDqqhB2lHJP + 4U6awb+z4JdKbzdh5ZurDHtQBat5LaRHiaOP7QejEZKD/Oauy+FI7W3Bsroyhxkq3QH8q5a7ujM8 + nWOQnBqTR9burCT98xdR60AbbaHc6ZG3ymJsZC/3hVnw/fNIXt7hygHzZp2oeIBqCxzqfmCgEe3+ + RVdrmLVAEtf3bxfOW/ve36UAV7+7DXMu5Q4/Os2e3eRWkiAGOijtWrPodxfQmeNVAPOPWsppJIpi + JxsKcY9aAMwRyTSbpflx68VOYvOXb97OKtXAiZdzkqT0AGc037BIIRLHjsR60AVprZrZwGj4qTY0 + xyRj3PUVMJDduFfqvFRzxJCzrCzEr60ALEu+YI53c4qeGB7lGCnBU4FUopTBLvfk1at9R2sAMjNA + GtaXsnhy2FzPHvC46jgnNQ33imTXrkz3oVFAwo9Kfrtq03hAzEfJ5gyc81hWM5hhKrhgT0NPcByS + P5g2uVI98Vp6X4uuNGlyzCQIQR0bI7/1rNQxqW+05J7Y4qK5ZYUP2ZCW9TSA7SR9M8V30X9nMFZw + WfcNi5qPWPDtjo0pE7O03U/Mf055rmtFmN9E0DEox+atPWbiW7lSO8Ja4jQbcDC4A9PXFADYtM0+ + 6nc3u7aOm3IP6Vnak9tYt/xL/M445zTIbieOdmWNsE46cip42EkyC4hYx469KAFsrT7XEJgFPOT6 + 1s+H9PD3XlzxnL/MDtqn9pghgb7GjL/eJORWqfEnmrA9oFRoxjJ5BoAp6NqDW2pzRXtuyIAw3FMf + rVS4iF08pydmeCDxWvqeuC+Ro9qglcMw71mwReXD5aAlFJPPU0AZ0cEsbkSZKH15FD2xJJiJVj6c + VfnzLGEXAA71PFpDPaebE6/KOh60AYVws8TBgrFe57CmHUG25RVJA7AVozzSLbNvX5T1AHNY/m/Z + nPlqwDetAEtvqzJNu3FZBwQBjI96vPqkd3mRtokH31UYx+VZqWruxaFl+frkZxT1tvs1ujJgEH5m + PR/pQAXl2S371XAHI+Wkaf7VD8hGR2arKySylRccQ98DmiS0jifdsdgeODQBQd9x3IBx1xTYlBm3 + En86sXUAwPswKg9QeaBErIEj6nrQC0NHRtUjt0K3AHzDABGcVW1fTzJL51jyOpz0NVooispebBI4 + wK2YFEthk8qR07igDAgJil+TKtnnHFaP2h5yI3ZsgdSfaqd2P3im3BGM9aktsjmRgCOaAJZrMwR7 + 3A5PT0pdMvZtOning+byzuVDyh/A8VHczSzDPy7RwOKgiuHEewjKeoFAzp7TUNM8XXEw8RhYNQmP + 7ny18uNeOM7cCtMfDiS8uY0tDEYghyynjPbn864htP8ANhLIehzWzovxDvtFsDB9+PI4I/rQI0r3 + wNc6DO0N2VaQqW2q24YxmqFhYRgE/vkkDfMGBBP4GrSeJ7tZd6SxvIfmK4yQP84p0XiyC71gS65G + 00zAKGX5Qv4UAbFpd28WnIsBLsDzmub1+AXt1LJEoQqfu4xu+lbWsWgs4/NsCXjPIbqK5+5kklmE + rDD54BFAGb5cjybCrAnnB6ipEvXil2sM4GMVpFY7m4UNmNyOWJ4qteaM0BISVZe+RQBFHC2/zISg + B69KlIVhIHA3HuR70lqotlBulY5P4Vcls44k3u6N5oyoHb60wM6O1SRir5LemOKv2vhuW4iLg7VA + 6k4FTR2ax4aaVIwR3HWqGua5PcQm1WRBH6jqaQFzWbE2nhzynuIi+8HaHyKweJSEQEN6jpVcKyOw + cMVznOeKmtZvOPDKuOKAJbi0JYFf4eue9IW8sncfvdqnlvVFyFyu09abI0bysMZx0oArC4eCTcgb + juK2dNvE1N1M0ohljGQzc5A7cfSs6aweWAk7kTuapQysIT9mOSvG49aAOkvzLMxk06QNuG1l7j3r + PlnnJAuGJij+nNQ6XqT7wEYqyn5v9utLULaW7j321uiEjLqMkKKAIotbghb/AI8hKGPIBHNXLG6t + 7uzk3RLbKG/iP+Fc+8f2d1eFztzyD2q5p2oCFWRoxOX52nPFAGgLyC2lyZFKdB70r69buxRJBHjr + nvWVdeXLE7xE8fwnoPpVKZUnQPkBhwRmgDq7a9tLyARWiiWYngL1qG4gurJ28+NowO2a5a3v3smD + aa5WUd1HNbC6zI0KSX13JO7D5lbHFAE4V7pi0b5x1GazdUtXSM7v4iPw5rQ0/XrcXX75FgUdxzuq + /qFrp+sWRe3uDkc4BFAHLRDY42ycd6uPOXiiV+RGPlWnXOg3IQvEmIB/Ft6/jUUEZmMcgydvzECg + C1G2+Ly3YAvyM9qY88kaFcmmp807uwPJ4FS3do+Fzn5ulAFVrjbgS8Z4yah2C03SMffNWZdPknVA + iluQOnHWmX9pILvyY13HHK46UAVre7LSyOCTmtjSiy7VijLeZ0IqO08OzPIUiTI74Ga6bRP7O01F + h1KYJOv3V4BoA4zU1lExMrkbOAvpVcSifhjgrzmtjxPp7pO7SggOcqfUViy25hG5fSgC8rrLAojb + d7d6SexlEgwpRfTNV7e5LFBbKAwPNWHeX7TguxI7GmBPBExhaNVIJ6egqOVknO1fkx1J61aj1gLC + UEKlk4LVWvozC67kCFxkD1pAQ24e3uDLC3z9CR3H/wCqrczJdOGiOxvYc5/CocMYhtUBj3xU8Qjk + XbKPIZOjqclvzoAu2HiO60xPKvd7wY/1fGBWnJo8WuW6y6XIPMYZEAzuH9KxISonAuzuRzgk9qtR + 79KmMuhTt5cRyxznFADLzS2tMw6pAY5OoDEZ/Sm20TQQ74YwVQckGtMatB4kUpqreVIRw5+8aqXF + jc6bAsbD9yThWz94UAOmmjvrRCMJjOQRVS0sD9pLyABM5Of6Vdtrdn+RUGcZqO6uRBG0MuFI79KA + MfV7r7ZqDI7kohAVT6U2eJNimJQOuTnpSXFussrMvBz1pJov3YUsR9O9ABblRncQ3bAqY2EUwIiA + Vqr20ojfYqZx3q9bSKAGcYJPIoAoq7OCEQBffrRDGEcleM8nNPjuGkhHmbB74ApvmxltsuTnuDQA + +SFEjDwu5buD0qpLL5vMg2kEdOlXECMAyZGOMMePyprQRI5N0rt3BXO326UAV4b0Wt0pC5HrXS2W + qq9zE7jcO+OhFc81kbg7iMqeAFHSpLa8eymaNOUIwD6UAavjPQYYybq1bBmXcF9O39Kw4iXdDKcE + DAxW3q7NdWELISdiYIz71kz6ZNZNHI0cjqQfujIFAEtzAtu/7vODzmqlyzNyAo9vWp7uWSWJd+AM + jjGGqOWCSWRVVW2+uKAKskpWU5TP0p8c+ExsPPNTmCVD+5U/QrzRJHJGymeOQc45HFAFczh497KR + jirWlEsAudvII9znitEeBp7yAPZvEVPJUsP5ZqCO3j0yYDUNwliI6dOPpQBt/wDCR3Wj6eHFujvI + do3DIX9KoHXoL6J11CJYZAONlaWueIYtY8Nwx6ZHu2MdxVeTXKG0eaXKRuCeuBQB0mn+HRe2Yeze + MqRkFmwfyra0rwsIrRmvZICcgDLVw7xXFuFd2uEQfeAJAxUkkjSxh4J7gjPAErf40Abvjq1i0y4S + KByCdrfL+FUI7SR4Wc+WzMOCW5qhf3Mt9cCV2ZiihRk5qpdTSBgRI+R2DnFAFw2k6AqJZMjuD1qn + cxzyyAkPuiP3ieT/AJzV+01R7a2RpMZPVmGQ1WVuTqLDCptcfMBwRQBEkst/YMCSTH8vJqtJaoYQ + JPv1o+ZDZKAo+UnBpmrCBpRNp4/0crgZ9f8A9dAzCdGgkOynxSus2xjkj+L1qW5/fxYj+8D+NRWz + R4fzCd2O9Ai0lzI6mPaMOcZqW4uI7rbtJ3IMc1XScKqncQT0olPlKWfBz6UATKjSDcmdoFWtPCyR + kzckHiqUV0623lKVIPzHHWp7Ic/vSRz0zQBcCqdyT4J7YqC3uZdKv1a2UupO7B6H2NMglMUsmcnd + 0Lc4q3BmaMBiDjr60AWJRBfyb9P2RueWJ6KfQVLHqMdtcEysxJXayN0x0yKyWihWQBdwTOSdxHNb + zWEF5ErXhX7QQAMNge2f0oAnhs4rq2kksHwirkg9SfauXnJnmL3AbL9jXSRWh0N28x1cEfMqtnA/ + Cs+70+O9/fWRIb+76fhSTuBimbyyyKDgnipLk7AML1pZbCWO7Hnjn26U6ZykRL+veqAryuvm/Jwf + Sk3mo2AyHyCT6Ux5pLU5Gwg88gGkBPNAILUO3KmooyjL8ueegzTvPMsRjG4qBwKrW1sxJZzsIPGa + AJbmfp5q7MZx71NZawEi8qZSyHg4NRGLzCPtB3eme1R3Nutocodyd8UAaVtqEUDlI8/N3PaqV2Ht + X2x4lIOSwHFSWkEFyo+cD1BpbmNbNdkh20AMh1UiJ1c9RzWj/wAJa1vYiK1RmRvvetY5gDENxgnp + UlhN5TiI4O4845oAmu51lXzFDGQ8jnpTra4uJkBAOQavXvhG8tIhPawvJAfmY9gKE1COwgIiAZiO + 3rQBV866T52Qsw6YrXguZNTs0WSJ8IPnHr9KwZNamNumZSpPU4pbPxBeRy/uJjtXqfWgDodMtnXK + QjYeo3VnalpiXjMzXMKS9O9VV1ydCXkmLY/SorWwTVJTmQEt81AHTeCY49Mik+0SJKmOg71W1bxH + HLdgaXaSRNnjdzWapGlBBG2ec4GKtQ6yZD5hjLMvbIzQBfutWC2ajV4ywwN2OM/Sql/JY2kKGzU/ + McnBBqlf3Lam5e8lKMv3Yz2FU4VjgzsGQ3WgDa0ya0u7kxzgqCCcn1q43hizkEjRkOoXcAOua5Ka + 6Mc3ygEVb0nW57ac/ZC4Xuo5zQBBeZjcwuMxRn5fUUmnySx6kv2cgg98deK1LjT31pTLpymSVuWi + Xqv17U2GzFgFBUCVOo7igCTT7cnTp/ty5ZnyCvGOKz2uwimOY7geQB0FWY7tzu8xiqk8A96qOvmy + MSowOc0AVpkkgk3uAiP39KkjtonYtnO4cKOP1q1Z3K+X5V2N6OeM8gfWiewaxiKhDsAyJB2oAk0u + 1juAwniYshwoB61FLZfaJDv/AHWexpulXRNwpjkP7s8nu1Wd4uC7zfezxQBTjxZTHzlMigbdy8Up + YXEv7nPvk1aNqbhDhgARnFZMCvbzuWZgc/nQBo2l6qs63AJA6VIsiG4DI4jXP8XeqcbrK5JH3xkH + 0pWhWVR52CF6UAa8kUd7H8rD5f1p5txHAfNPasWRCjgh8D0BrV0a+DgCdfM3DaB9RigCml/JFPyB + 159xV+C/wfNHAbtUN9orxO3k5dhycfw1XmT7JarIjb1k6U2BcuNSVGDSAPu6be1QTXcO0CVSwbPA + 7VRtpftEmxW2Mx6HvUv2V1J2jkdaQBFJB5jBVYemetRyW6SqTKCfTFNllCHBX5vWkLBPvk4NADTG + 0ePKB5qdLN5NjycqvNQIpZAFVj71LsaJQBuGaAH3aCVwycKODUMsZgJjxv8AXIzUs0DpHhmBycjm + gOd37wdRjNAFETeTcARAbSeTViApfrhjufHXNJNCsUu18Z61Xit3Q5JxQBdW0MYKyn5hSf2BPIjS + 24I29T6f5xUMMrs5HOF71ooVmtMyu3ynAAzQBqeCfG7aaPsmuYkiYFG3HseKq67YQW2rSNpLCS0l + GQ5GSh74xWZc2SyxK4OZl5x7d/0rV0K+j+xPFOu4Pwpx0oAo3OnFreM7AR9Kp/2eYpxtyCx6VoXd + g2nSlQzMh6UxJdjqSpKgfN6mgCOLSZGkKyYw/wCn+c1YltRodoWA+Y8Z+taPhWz866DQqxLdmq34 + x0ZbS23yY3NgkUAcZcSyrjcc7zw3YU62meOeTazdOhrZ07TYLkYvSFVfmqveQWkDj7CW9zg0AZs9 + 8wbO3L8ZpvmGRsyZQDsO9WLu0EwZojwMc1DJCrsA5we1AFmGVZLc7Y1bA6nvU1gIyNzgxtnoKr7I + NgHO8dx0pJ3AYG3UnHegDRS+NpL5lsxh3dQverj38OtL/pKCKSPhWU/f+tYEt98xMnC9qgludrrJ + GzFl7DvQBq6pYNGdzHGO3aqS33kEBhlSME0+01z7OcXGXRupJ5H0q5fafFqNuJLLnofmGDRsBmJe + DzMEZGevpW7o8sN/bzLqTBML8oB71k/2YYh83FQRqbdtr7sDv60AX7jSo4ZsiVo067hj9anuNHey + jVizMj8gkdaqQyi+UxjO7O0A96tXDz6rEFucp5HygUANGEQKjDJGaqzWbzgyn5QOPY1p2xZtOaGN + VMo5BPoKqxa1NHHtmij+Q4xkUAUraZFiYScMOgNMf76CIZHf2q5KRq8arEjK4OTsGaki0oKwAEhP + uDmgCohEsqq/O6rrMNMj3AEdgfQmn3tqUgEcaYz1JFMtLdn0wpFGxYHhjQBa026M0XM2WQ/NnHzU + 6Yw6tCPt6rbpH0CdvzrPtrZ45ceU4cHk9qtzW6XLOjqwY9+1AEa+HWun8zR28xU5LAZx+VLaGSV9 + jrkr145amvEY4hGkjKMg5XoPY/571vaHFDr95HHqDMkoU4C9G+uKAOevoo5iSBjBxVYwLdRkL1Xt + XSeK/CdzpkjRMqyJ95SjbsD3rmJbUwoeuGOCfSgC9eWc9rcbbdA0KHPmhcq39Ka8e9DkBS5zk1X0 + /wAR3dvEtuTm3AwVzW/D4w0xIEivbOaSTAVWBAH40AYMu6CZDkFcHcTz6UrtkYlwVHIwOtb91olr + qtuRZSL5h5EX8VY97pc1jKAqZ2jB/wA/nQBRJhubjE4YOOnNMC+S+DzmrMkIA819wPTbjmqwfzcM + 4w3vQA9mbYwgIz/ENvSm2t+6jZsYKeTkVYjn/eqwGAOp9aeW+2sdkgVf5UAQLKY5MHGferNv+6IM + XT07CmyaeZIS1vtmkUdQKbZ+akOZoyqMe45oAvRzjUJPLLgSds8/zqyPDzwETagy4U8YwARWMbcw + NuDDePenPrbXEfkTn5hwrdqAO709LPSbbzlZdvqD0Ncnr/iufX793uWQrGdmFGBjpmstdQeFRHKx + 2Nn5f73+f61E7iLCxDnrjvQBaubtNypAxyRzg0q263DMsJIzzyc1mwyDeSD82e9XIGUIrSyBNw+X + 2+tAD3tSpcFvufrVZbdL2XbnDdjnGKnhs2nkYtcIEJ6461HMiJIApBVe5HWgB8mmtpzDzSrrkZYU + 65mRGYoBgirEkCStiJlC7c5IqjLNsYhtu0d6AKkshbAZcAdc81Gdwb5SD6cVZjYy5WXBVu/pWppn + h63urfdLdxR47MDk0AYjnhehxntVq11OVANuTj8q2/8AhBZ7mwkm00CYKQBtHXrWe+kTWS7J4zE+ + OQ1ACQX/ANrkC3DD0wODV280KQwM0jxheueKdZWcCrvkjYYHUHvRe6jFLapHtLKeDjg0AVrDQ5xd + xuhIUEMHx8pH1roZtH+2W+dPIbHDMOcms+81YNoqWltlFKhQD1HNP0e5udHsHFkcyMRkDoaALUPh + aa1n8yUgqRgjPOO/eq+reDkvHzoQYIB85JzzW5HBLqWmCSWQJM3UEdB3/Sk0S3uNPmIkBlgJyXAw + o/Ci4EHh3QYfDsfm3mHklGGLdFqS91HSYpvMw0jjkhTx/KqXjLUg8hihYiMn746H6Vg+QYxuV9vH + 1oA3xrem38TNe28rqp+VUyD+gpbTU7O6ylvEYoEBPzjDAjp2HeuUk1aeyfNqMH+8BTrvVhqEAMuP + O7n1oA3X1Q3U0klp5S7OGHFZt7rj4DwxlTJ6riqMTiDZsHTn6/WpbfU5EP8AxMVMqdFIOMfWgCZb + lpEO/GDgn9K6bwZpktjcC7lUsAMYPvj/AArBi0lrpc2sqbZsHbjkV20SvDp8UUZBcDp60AY+ueIZ + dIu3Frh0lbD+YNxAPXBPSqLrpuunyNPBSSM7mZyQpJ/KtWQ2uqvNDcjypQjAFjnJx0rhNYhntbvy + 7jcucgIe9AEUMOy5ImYgg4xViVVa4UFSoToc9a6DxZoEdqv2rTsHzDlx/dFcujFpG27vlPGe9AEi + anPpV359o7b143jqo/yP0rWs/FSavF9l1JltlB3tOerd+axl3XGfMXC9896iu7UbtyYIxg0AdTc2 + Vrqe3+zZxIF4Uj+I1S1Hwpexu0kts8aL7Vg2t9JZ8REjJ+UD+Guh0TxjeaW3/EwAuFAxh260AY8y + ujfLkBOCOuabHcqgCxYAbrz0rsbSysfHdzks1rO33Y0AwTWd4h+D2r6M5mmt0ER5D85P1oAxLfWZ + LSYrbnAb5eKnudVnyELFkHOcCqUmjzRzBWyD9K6W38JtLo6TtkLzmgDHtryGZiZUDZqDU1Vl3wp8 + g+9jsf8AOKmGnw2cpE8jFR1I7VdGjRXMQa0kdoSPmHrQBn6bYnWz5NydjgZVgORWeztBK8ZBJQld + x6nFdZ4ZtoNI1QPI7O+OB7VX8faO9rdC7ESrC4BJHqaAOcgUTtuORiraW0M9yiXLAIeoPc+1RWar + u6Haxq7e6ekEZkBGzGVz1ptgVprUw3ku3iJDgDPUYFEzAwZRN2CDgUw3JEkezD7+xolvytwn2pVV + RkADv060gLVlMk4aLIDHp7+1Vbu1+yzgThiHOOelElyIZl8v5CDkVtxWkGtaYs0bMblCcr/KgDCe + 3LzsN20L2HepUQJnHI9KsX+gT29pHKCd79qWw0u4aPcwU4796AL+meIr2G1aDSbiWHOMhR1qxZXz + xXBl1n/iYBBlg/FR6VZW1nciS9mdJADgYGO1Q3pIOOu5hz60AO1vxLDqluP7Pt47eJSQ2KzvtiSg + eWuPpU89gsfzH5cc+1ZaSpbXRZT8tAGjjz237gNuPwrc0O48uUPOM4GBXORXC3HmJD1bB/QVZivZ + fLwp+71oA6fVfEiwXC+UBGjfKTj14qZbi7gtJWjkY2zx5C9s4rnbCRdZiaOUkFQTke3P9KbYa1c6 + XcBARLEWxhzwBU2AotqzH5Ls5YdFPOKmiu1KgxfvCOqHrXTL4EXxLbl9MO6bGRkYzXPal4TuNLu2 + ju/3csfUD9KoDO19yChhO3OcqO1VoZEUbHVckZL9x3q09s8a5uDkZxUDWX2i4OzgHvQBLCwkwyEF + c4z6VNDZm7utkROCfwqCzAhuGRhhV/WtR5okjjkQ7ST2oAlSRtMdUjHzR1p2OuOI2Ly4kHQViS3K + iYBMsW5zSNF9klEjPnPSgC1dzm4uVKSMZd4JP41oeJPD8+r6ZHLbwmW5H3yCMqvr/Os6xu/tDfvU + CqSOfWuj0yf7OxLO2CAG9x6UAZs6vcIqSiVw3GQMisR7RVvpFkGFU46e1dN4c1hYmCXm0quDIO9c + 54quVl16drdDHGzZX6UAV5bTzWIi4Ws6/DQEoQSpI5q9BfywxkS7WU9OOlMa3F8hG7bj5sn86AKc + ErggKVA96lFwLcYHX3NQPAHnYD5e26pAnluA/JoAu6JevFqsEqs4YN0HQV39p8aL+CJVnWKWOP5c + OAf6VwCzrbxAIMMefpT48zEFD9RQB6hZ+PNE8YqsfiJFt5GOC0abcH6ioPF+i2/hiGK50xmuLOQ4 + AjO9s/T8a8wlzLIdxKkHIwcc1s6R43vdJi2xurxsdriQbto9RnpQBal1C1urtzcIVjfqu3FRMNM8 + zbpplViehyAKnuU0/X4N+ixtFdR/67e2fN+g4xzWPcWzWFyDL8gP3Qw+9+NAGhqulSWzpJHt/wBn + Bzj2NejeHLG28f8Ahox6/HsmA2DHBGO9eTrrksUTKSOD0Par+n/EnVdMRVsZYgpHIK9u9KwEvjn4 + eTeF9UY2Jie3HI+bJFc6b6eMkt909j2rsrTxpYa7bGHWYpXlc8Ord/yrOu/B8gEjQul3Ao6RjLL9 + cGhaAcu0skr7mK8HtTjEAcMMk881Zm0l7JXxg7uQBywqqzysygDBPr1qgHSWqzANL6UunXjWBOxW + KsaZcggbu4HSlindrf5ANxNIDqblPteiWrESNC2fujJ7Vd0bRY7KLfZswWYZYSdT2/pWJ4Q8ST21 + 1b2krIYj8pBFdd4k024ht0nsdpjA4AHNAHO6npkSs2SwPase6ieJcSYdenB+atGbWykgF9G2cHvi + qGqMxiWW0GFyCSRnFAFeSN4yGiLE9we1QXYEhzMo+bnAqaC9YzbpSGY8CoL/ACwDQ80AV1mxdJwQ + q9h1qd71WHU/QdqgDO0gJAyevFE4WI8dW60AafhzUHt5v3ZAzxVzXNFku/38Odg9KwbK4ELA4z+N + ddourgQKJsMv92gCr4Y8Qy6VGUmkdLcDjn5/8a6vS5tM8SWTG3kkaZeP3xIyfxrmPEuk/ZXF9akG + CY/LHj7tZy38tvcxSwnYw7DpQB0viLwrIigwhcHqAeKxDpbmcgJtKjOfStXRPHgjlEeuAzZ6bf4e + lajX+navE4gZIyQcFmxQBxd5ZPG+9iuDxmqitHGR5oO09M+tdDqmjNsDl90YPBHSsJ4N7uH7dOOt + MByxj+EkE/d5qwYGkUNu+VetUgxVz6gVNAryx7Y84J5PpSAeZWjG8A/Lg1sabqn2hF8wnniqPkK6 + qk/z/TilaEWo/cgqKANPSbRba8zM6MXGDzVPxHYPPOzOOVPy471R03XmSRXlQEHv6VstqaakgJKh + h0X1oA5jBjYrP8uTkA9TQ0qoxLHqPyrQ1+z6TMu104x65/8A1ViSsVc5GdwoAseWbkDyQWC01QVv + S+5WGcbe9OguTFZqIjhxnPHWnWTCO6LyKjPnpQBDfs4n3sMc8Y7VPBKWT922498U7X0RCjRnJmAL + KP4aq2rtA/ycBu5HXFAGkYg0GT8rY5J5qIw5jyMORxU28zwAou5jxj1pnktAzCUlT1xQBHFP/Z8w + dpNsg6ccj8a6jQPFNjqdqbfxJbvPM/yxTE/LF9c1zsNsJ1U3EYIP8VPe1iicCORsnnHTBoAtat4Z + mS92Wn79WBK7aw0ia3uXW4jdChxkjvW/Z+KLjTZFd4hKwyAc44qy+nwazpxEOPNdvMdx1UdTQBzb + AbSNyqGPf+lWvDPiW58IXDtZzOIpRiVVON4qS/0ePcG04/aYV4Z8YwaoPGJrgq2AqnAPY0AdVdww + eJLX7XoxSKfbnyRwzn61zGooyMzsreYpwQTyn+P/ANap9NvX0S4DQtzu7dhW/rel2viWzWfRiPtC + L88a/wAfuaAOQEvyDepIOOamtbFJZWKzrH7Gpk02QRBLgYYHkDtSTaf5LBgM7u1AEVxbS2aiSNfm + xw3St7RfiTLFZi2vUe4VRt44xWJDczTzoLoFgvO096bMomlkaJfI5ztFAG7Jqdlrcm2WNYHA+82C + KidbiCAoVLWzfKoHOawo1dyGO4bQcc9frWppOvSwQLDcDzQSOvbmgCjcWBQsqDYwOTmo44BdAZfG + OeuK1NYdZLjzCdu8dAKzpLYQt+6OKAK88ciXREQ3AY5/Ckmt3dlMoznPSrMU2zJxgD2zSSRmX5kY + gdiO9AFWO3KSDgqMjrXQ6fYuUAjG3HO7rWRawNeSDLYKnHPeunVG0bR4ruTnc20g96AHxn7ZbNA7 + qzgcVzup2s2mzOl0CAT8jYzvrb1TxpZ3tgr6fBFFL/EUqpp+pJqpxeqJAPulucfSgDDfcjgxAqSP + mB60xXXlZFBPXpV2+tms5W2oTnpk1nht0uZCAfTFAG9oOvCJBb6jueJj8qj+Grer6XFCqvHMvHTA + zmuajlMUmWHznoKvQ6tLDEPtKeZnsT0oAkaBVLGX7x54qOG6NvkEEA/rV2dYLi08y3fMhH3e4rMR + mkDLOMkHg9KALcN7vXI4Iq9ZyG5jw7An1rFuWMWMAopxTzqMkIxZAuOpINAD7ZAcg9F6VqaXdRFg + pX5h92sPzRbfKQdvr61c0+4MjDyxsYHkkUAdA2lvdQ+ZcDIPGOuawNY0wWNywjwVbocdK2E1ubTF + +T5gw5yM1Lc2kOqaX5kXMxG4nPT8KAOSUSKu5VGM03aZmRo22k9Tird26Fgp+6hwcVAZfNmCnBVu + mKAJp7N71FDcuOI8d6pJlLlt+d44PoK0dTZLKCI2HmCZQCd33c+1R6iqXKpJBu34+bPQGmBNpzND + bgH7zHjPapLiXMhEvzMRwarQXG+ILcfMP7w7VZjdHj+QgMOmaQCRF7AsVBZO2am2G5t2kIAJ9O1V + 2vzM21l+UU9Cjj5M8eh4NAAIXjUeRl8/pUa6k1hGFtWyG6n+lWYX25Y8dsUs9t5tkVkK7Tz7+tAE + 9l4hAj8q/RUf+Db0P1qZ/DUWrTO0paK9cfLGg+Qn61zc0SeYc53DgVr+HNfk0u623LgwSDaxHLY9 + QaYFa80a60G58vU1VmbqF5AFWdC1k6PqaTW6qyEbSD+FdRJd2s8IikZJbO46MTmRB7nr2/WsrxD4 + QjtohLo+9kHXPb0pAd6uh6Lrekm6hkkQSRgNtQfK/p+dc1f/AAsuGUnSWSVScgynbisHQfGFxpki + RKw8tRyD0z/nNWPFHji/1lFihkCxKMAocUAaNt8NNSt3bzYrYsnT5xTLvwZYQTIuqzlLh/vqigqP + xrk/7QuIwRHcXG4jnMpP9ary3kzhvtUkrSH7p3E0AdXqPgvT1vI47K4kfcCcYAx0/wAar2ngu2uW + ZIJX3pnjHFc3DqUikfPIGHU5PFb2ka3PDe7dPZGGzGW7/wCc0AX7LRLSzcxb3eXrhhxVG78JeVcA + bvvcVfEgudqaoyrOrbiV9Pwpmo311pMnmWmySH3w1AGRrXh6TRfLMq8yfcHGPxqxZ6fpmnmNddml + jlk5+RQRx/8ArqO51ptT3vMwWU9iOF/CsOZHnkIkYu3YnmgDo7qPTtPszcWTu5LcAr1ycVl6p4hk + 1BRbsCEXkCqEGqz20wEWGEZGAeRxVy+vRqV2JpUVJiACQMAUAZ0+mvaNuuz88hwAOmaktbt7C4Ub + c8jvW5rGkp/YUEsRM0nLSf7PFYogSWEF/lJ6CgDWcjXyuMhwOAO9Y09hLbSyKy9+pqzpM9xo90Jr + co2OMMM5ropr2PxBYGK7VVXBbIXG4jnrQByUI8xSADs6HPWpPLIjGxssvr3pxQmcqx+VGwFHenJI + gOF5oAW0jZB5nQnnH6Usnzjrg0rW2/8AeISD1x2pWR5VySNo60AQBX2EzHIXpSQJ5kjOOFpLgrtI + iLFvWi2Y3CFYuoNAEt4myTBBQ46Gq6OyHKjGTzSyyyXUm+/cnHc0+PY42RtuDcDigDS03UzdQlHG + WHFSw3/2CX99lo+hA64NUorOeyG9FJA68VJFaLqNu0hkIlXkgelAF3VtEjvNMF1pKOctyPTFc/bw + tGVeMfMRzW54f119M8yJ2IjlGzk9B/k1p6f4fsmi2xXsUmeP88U7gYV5Et3aQlWCsox+NR2eUnWG + 7bdvrZ1TRY7FXjuQsatzHJ7VkyeXbxnz38xl6NmkBFfiXR3MDKQjHI9xUMV0ijMnNdBZWbeJbUcC + SZU+U454rFu/DF7byNJcW0qxqeeOtAE0EcbI+4nax49qnKNY7CCG46Vjw3DRHO1gtaNrqPnBRKu1 + R0Y80AXYDHPAzlPmzzTWG2Evn8KafMMWIsFfamKxcAyjAHbNAFSeRJpOBg0xrXykVjyp6VLqFv5b + AqwTI6dal02ZZ5VjuMNGentQBJZxXFtFuUZDcitDSPFrwOYrkFkfj6Vl30l7p87RpKRDn92eoIqG + 31gRxk3qMzqRnmgC/wCJtIa2uzLYfMjgEj2rNs70woyIMjPLHtW7Y3y38gkUnGBke1R6p4dS/mNx + obeZgfvIVH3Pf3oAz7W3EmGzgrSSRqszF13+4/hqOOLdGSrk5HO0d6WCUxYaUMYhw4HegCM6TLcy + Ztkd0wckd6jtZZbPiI+aqnlem2tTStXNvcbYZyiSA4QcdMf41Y8Taf8A2dZieGMR7sAkc7s8H+dA + GVJqTT3AKtjIxtrStNVy/kyLuUj1rAlhG4NtKqOc/wB+l+2SpP8AcKMn3s07gdJdeHPtLRS2zpCr + csD171laro72bGSFWZRwzHpQdUe8hTDEMg5xU0N7Pcx7GVpIf4lzSAwlk2yAoevUDpWpa2hvYeTg + 0mo2UM8w8lPs4HUDvRpsFz9oYW6NKB07U0BbjvptGhkgJDRMu01VLRyyIYQSgA3HstVdVMiSlZyx + bPKiksbyS1hdWUmKQ5K0gJpt8UgAw69iKn0/UyJdrdOmKIPIvW/cyLEqj7p4zUEUIEr+blHXJBx1 + oAk1O28q6VoSFVhk1GbZQ25TzUlvcfakIucKAcAnqaWK1cyFkQlB70AJvJdNq5I4+tBcbCnCjv71 + LIVcAowVhxj0qO2t9zkXHKt0bsKAIpbPIHlKWUjk06wgaNiqIBzViF/kKKwBHA9aguI5oX3REk9j + TQErWypGPOGc/pTLTy47gMFyob5fetB7EmcG3G6N8hSTjNWRpgsws/y7ouWB70gKd5dGSRcfKnIP + HFXrHSYL61e4kfyVVcYA61lC7OrxurAKxbIHtUtxfC2sTDA/A49KAEazRmkEw+TqG9as+H7YSTeX + bvu7ccYrIt7qRdobPLc59K6jw9pf2KUXcJBVjuI/z9aALF88MsJh1AiRoPl54Iqt5GmXUG3ABx1x + 0/WneMbGfTryO8VB5d2N6qfTJHP5VBoNtFqUb/b28uU/d2d6AJLPV4dGtP8AQyokHGKgu/Fwu9wl + PXgj0pmpaSmnOxmYEdu5rOht2knZ4FX3oAimiju3AtlAznrVWSAW7OC2HQ/d7VdNjLaMjurbSeMC + s+4WS41BjyEB5zQBcgnk2ARnJbqKZcydmZt3fFVxB+9DRkjHfNWLh/KKGTp/6FQBGLg3C5PzFeBT + LeT5yEzlB0p1zb7wGtzt9RTNhWVQOHPWgDc0iUajbPbTgM5GE9aydTtPKk8sKcDrk9adZX5+0FLc + FZM/K1dPpmgReJLR2nOyZDhQT1z60AYWgXYtrvy5cFXBXA9+OtGpLceH9YIsZ3BwGI4+YHsaNR09 + 9C1ERTFTMjBgE6YyO9S+IoDqHlag5++RGPfGKALelpb+IbtA+Ldk+ZkXofxqHxFpn2Vpv7OXdGOW + 56Vk3GpCBQB8pB429a0bHXN8kX2gKY1ILju1AGakfmFfJXLN0/z+VdZYQG503yda5xyPp/8AqqXw + 2LKJJvsqbjIdwDL936Viarq8u9nhA8sNg88/TFAGrdeFbeWBHscSL/AM9DWRqnhObyS7KUYdfetH + wkx1Gdnm3rECAB6Vu674psYbIRxeZuHBJHWgDzZw2nybQMluDVnT9T2PsJK56Ve1OS1vJ/OhOfXj + pWVdWctu/mJhgTxQBeYrOS0xAxTojJHKHspCQ3GPSqaXCTuqpnf+lTQIJ5XRXwy0AaN7YxzWzT3I + /fSHp6VnS2LI8Yt13kj5ucAU17me4hYbvkHXJ5qvJfDMYDNlevqeaAJTAVJGBuHPFSWuoMN32iNW + UgjOelVo5vNUvg8HGKVollOIG4HNAGhb6dHewhrVy8gPK4qaFTZZRssT1GKzLWd7C5zDlS1a9rq5 + vU2uFAIznuaAK93po2GSIEjqefu1C8QZApc+uBxWnbQpeyCG1OB1cnjmi5sUuTlxgpTQFBAYCWEQ + bjrmmsHvDypH0qYqYGPlk56DPSnWFuz3BN2MCkB0niGK10bw/ExCyMxwhVskH8K5O98SPfWixqPm + AxkjBNEkkz2iQSzgqn3U54rPm4RkY4YEfhQBd0gPBMGnwc8fSpvElpFBIGU5Y4Ix0qjcanIkKBG5 + 7VGzPdIHvF3P9aAHpGtymc4Ira0fU5YYUG7KA5P0rAEgjOFjfHtVqzndD8ilFkGKAPTri4h1fRrW + DVAojmjwjdwPY/XNcJK6aTfubdjhDgc9a19PnbUYLW2upsRJ8o61S8WeH1sryKJ2AeRSUb1oApTX + TXpaQMWJGcdal8PSf6UTcj5WOKz5YW0zgTKZG44Bq4THLpSqj7LhWJdsdfSgDo9e16OGFba0ji3p + wZCBzXOoYZp2N2u0Mecd6Zp12cIbkfIBzTbwRG53W4wp5oAbeWVmgY2ZYeuTVC4SWFAzjdGO5qws + HmK28jaTVi1vhaR+XfRGeJhtVR69jz6dfwpgZEcrPcAp92pl2IzMxLuRwamfSJZCXtnRhnLgcFR6 + VWc7J9mNpbtikAW9w0MheQj5ea3NG1Y2sPmWhCvjuf5Vk7UadY48RseW960rDS11C3b7EMzL3oAt + 6hpn9pZu4GzGq7djH5g2PzpPDsMV/Y3Fveg/uVZl+vNJYRy2KhXfcB972q5aRw310/2eZLbcuCWH + X8qaA4yTeT845B4qaEqjZlVtzflV+80qY31z/Z8T3ENqMs8ZAAGcd6zoZMncEwH6H0pAdDpusLZQ + 7Rjc3ApkFoZJHmY4iAPXpms8R7oh/Gc5HtXQaALbUtGMN6ApPHrzQA/TvEdsdOWD92rRk8gcmud8 + QXkl1cZzlfapr3QP7NujGjfKTlSKzr2Jmdgx/wBX096AIkn8ucBQQjdat/bWMLZKOOnOOKzdjL0P + BoiXe2Cu7vQBpxC0KAyK2488Hiql3LskbaDtbpjrV+3tlubYC2TExGBVe+tJNOAF4PmHNAFO0meG + R1bI9jU0iK23zcbsdagWYO+xOH7mrkMWYcNgkUAQwKGA4JC5pzyFmPlEADt61asYIgSJWA3dOKv6 + zosFpdxPaBGVlG445BwKAMwuWADAbqs6eI/3hl++Pu1cj8NFyrRncAdxb0psElpY37NMhljD4YKe + poAsWmm/aIjKknlsvUnoalhtHLcbiueucA1Uu9UMs8wt4SsOfkUnkCrOmXcotj9rkV0HSLnmgDoD + 4JSXSzPNNFJhdwCkZX9a5+K9gD+XPgDdjNTpez6ZZywwPskcZbk/KK5qZ2llPmvvYnrQATr8zE5D + N1zxRbou7951anhZNYuUVFw7dvSp59IltXdZ1IZKAGvpLNGfLAfufaqDCSKUEkgdMkVd07VWs7oG + XLL0x60+7ePUjyCpByMUAV3bBGxsk1ZikV4gAMkHOKpzW5SUmN849qjjnlil3KODxj0oA6KykW7t + yJW8pk4BFdxrGhwax4TS5JWWaEBEY9QDn/CvNrPUfJmBcZDHLV0s2vsfDMwt2ZYy4z7cGgDHv9NK + yjfD+8bgYFUNRtTps4S6HlkjIBPU/wCcVeN86xKZmJlyMc5p/ifU5L/RYVmto9wJUyZ5oAy01Dfb + qZV2xnoKbfX6NEv2ZcHHWmPLFJYQx2ZLTL1U1EIJA+2bAJ6Y5oAIboyDb0PU1c8xLkBJLna4Hy44 + 5x06VAbZbdcyZ3elNBXeCRjnOaAG2808N5syYmJ7fx+5q7tW5QCZQso/iqsULT7rXLr6k4xVi0dX + +9kmgBlxpbI7SxqZAoGWz0p+i3txZ3AezJAHXjrWlZ26mFyzEnPC+vStzTLO3vZ1M8Yjwp6Hr0oA + 5/xFqyrIggQKrLlsdc96xpQZ5wySbu2DVnVYQ9/MJCSitxVOQFW4G1aAOm+H3iGPSbie1upBDBqC + CKRugwOfwrI8VWsenazNHZtvs0fEb/3h6j171Elg02N65x6Gt200i18VwwwXcjQ3Fou2NQMiTvye + 3WgDn4riKEhkfKf3h6+9aFlGLeyS8eT5DIMoDnv3FXZ9I0iwhJFxJLMpwY2ACg1TvvISzMs77S5w + EUcUAW9dH9qW6y6ZKBgcgdawoNOu7iWMmNiWOMDtT4Jxb5e1bKuMEHsfWpNM1ZrG4WWFmct0BHSg + CprWivp0u193mMeR6VHa2jmQbVH0zV3WNRkv5mkn5YnjFRJGBMjRMScdKANvR7OO1u4pS+SGGV68 + d61/GnhSHUYReQyqsZXiPI64rK0S5hRNzfePXvWr5w1KIwwucAccUAefW1q8kqiT+WK0RpdzFFuE + bFT0bHBqxrFj/Z87LjDZ/Km2ctw7Kgk3KO3SgDPQPuHmqNynv2rRs7hrhjDIcDqD6VPeafDfWbbC + UnUjav8AeHfn8qsaL4bl2pLcYWJT85PYdzQBq6dfjRtKX7QnmC4JQH07f1rIl0SztbsSrcoQnJQH + qaseJ7mBVT7PIXtDwrYwQ3esOO4RrxvLZmjI+90P5UAXrm881T9lHOeAOareXPH+8BKOB19Kb9rF + pcq0ILDPc8mp7m+S6k3fdKj7vWgB8Gtj7Oq3AZ3fCs7DmorqxQTbl+oAqJJlu4gJMKwIxT3kNq+H + G5/7o7D1zTA7Pwpd6NBrk5vQwMv3Pl+7UnjAwwXX7tFe3l5UjBbHvXP3GnCOxhuo2IL1G+qPcFYX + cknoT/n2pbgVZtGFxZvNbH5VOBk+vt+FZ8lrPakrcqyHGcEYzWidWS3lCxAlVPUdDWxf6pa6nLH/ + AGlH99QoI4wTwKbA45pHEirjk1asbxYZCsoDYH1rV17wyumSKVbeGG4Y6gVk/wBn7UdgCpPc0gLw + aEwtLKMDtWhoNykVwHdd8JGCjDIrDkSW1g2zOhVhkVLo+puSVlKlccYoA6Dxf4PbSLRb21wto7DG + W7ntj61mpKdXtxaOQvlfMCSBuJrqLfWIfEvhg2muKzQoN4CnBJHT9cVyU5hEjNbB0CHABPNAGTPa + fZriQONjqcZ6flUtqqB1SRmMr/dJzWlDaLrEUh1Qbnx+628ZNZE1s9nfctxEccjpQBO9tLcy7Zjw + vfNQ31q9oee3A75qe2Yyzby5OKiutRMsjKQDg4FG4EVvEyfM5xnsD1q5bbzKHBAB9KrCJN4YMd3p + V+wt8szRZUCnYDXsWSGPz7jGI+SMVVuvErXKEWuRk9QMYqXVyLXTUyRmRcmsSC4EAO8D2pAXxbma + IMR8w7+tVdRtkUAT9ew71as7wsF2nFGsKodDOMzHo/YU0rgULe7j098qW545Gaki1FIbwzeYyzfw + EdvyqkyGSfaw+bvRcQLayqyEnAyaQHR6gi6/pXnBER0IGFHzN15rnmlXyTGRuQHByeQau2GrS20G + 9OhO3H1//VWhf6RprXbXmnrMtuYsOjNk78DkfiDQBi2rpHIVQjb1otHPnBZAMAdRVUQiW6Bgyis2 + Buq29q2nXJjn/eDsycUAOLCG8yg9zkcVCzeVIZY+cenekN0LqYRSHAHA9aLMCOTy5BlTyPegCxa6 + ltkL2+ORzxjFWbTXpLSV3Y84+XFVJvLilKjgVFMpAyBxQBq6prEF7bQSzA+ZJ97jpVRGjDbUJAB+ + U+tUywlJUdE6VteHLK3kuoDqQZ0zyAcYFAG3feVo+io90u2d13R/LyR35rm77VZNSmzC5SEj5hnH + 14/Otu+hv/FN3gTWywW4KRqQM4/OsUeFZp5miaVAc9R0oAaXWa0EUWCIjuA9PeqEMbCYM3G77oAr + bi8Gz2YDmeLc3ygev61X1CxnnuTE8TvPb9fKXigDMuIJFlBdtzHnAPSrEF0IwDCm5hw2VNRzxTWt + 0BeKVMnTIxj8KZ/ahtgY49uT7UAX7VH1K63oERVOTxiuu0ex0nS7L7chJkm+R1kwwyPQZrh4JJDw + zbVbk4/OrNpefLsnyyg5UUAf/9k= + +END:VCARD diff --git a/htdocs/includes/sabre/sabre/vobject/tests/bootstrap.php b/htdocs/includes/sabre/sabre/vobject/tests/bootstrap.php new file mode 100644 index 00000000000..14281e2182e --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/bootstrap.php @@ -0,0 +1,25 @@ +addPsr4('Sabre\\VObject\\', __DIR__ . '/VObject'); + +if (!defined('SABRE_TEMPDIR')) { + define('SABRE_TEMPDIR', __DIR__ . '/temp/'); +} + +if (!file_exists(SABRE_TEMPDIR)) { + mkdir(SABRE_TEMPDIR); +} diff --git a/htdocs/includes/sabre/sabre/vobject/tests/phpunit.xml b/htdocs/includes/sabre/sabre/vobject/tests/phpunit.xml new file mode 100644 index 00000000000..46dad6a3d22 --- /dev/null +++ b/htdocs/includes/sabre/sabre/vobject/tests/phpunit.xml @@ -0,0 +1,23 @@ + + + VObject/ + + + + + ../lib/ + + ../lib/Sabre/VObject/includes.php + + + + diff --git a/htdocs/includes/sabre/sabre/xml/.gitignore b/htdocs/includes/sabre/sabre/xml/.gitignore new file mode 100644 index 00000000000..accb586c771 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/.gitignore @@ -0,0 +1,9 @@ +vendor +composer.lock +tests/cov +.*.swp + +# Composer binaries +bin/phpunit +bin/php-cs-fixer +bin/sabre-cs-fixer diff --git a/htdocs/includes/sabre/sabre/xml/.travis.yml b/htdocs/includes/sabre/sabre/xml/.travis.yml new file mode 100644 index 00000000000..96396564e76 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/.travis.yml @@ -0,0 +1,26 @@ +language: php +php: + - 5.5 + - 5.6 + - 7.0 + - 7.1 + +matrix: + fast_finish: true + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +before_install: + - phpenv config-rm xdebug.ini; true + +install: + - composer install + +script: + - ./bin/phpunit --configuration tests/phpunit.xml.dist + - ./bin/sabre-cs-fixer fix . --dry-run --diff + diff --git a/htdocs/includes/sabre/sabre/xml/CHANGELOG.md b/htdocs/includes/sabre/sabre/xml/CHANGELOG.md new file mode 100644 index 00000000000..39a39bffe9f --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/CHANGELOG.md @@ -0,0 +1,228 @@ +ChangeLog +========= + +1.5.0 (2016-10-09) +------------------ + +* Now requires PHP 5.5. +* Using `finally` to always roll back the context stack when serializing. +* #94: Fixed an infinite loop condition when reading some invalid XML + documents. + + +1.4.2 (2016-05-19) +------------------ + +* The `contextStack` in the Reader object is now correctly rolled back in + error conditions (@staabm). +* repeatingElements deserializer now still parses if a bare element name + without clark notation was given. +* `$elementMap` in the Reader now also supports bare element names. +* `Service::expect()` can now also work with bare element names. + + +1.4.1 (2016-03-12) +----------------- + +* Parsing clark-notation is now cached. This can speed up parsing large + documents with lots of repeating elements a fair bit. (@icewind1991). + + +1.4.0 (2016-02-14) +------------------ + +* Any array thrown into the serializer with numeric keys is now simply + traversed and each individual item is serialized. This fixes an issue + related to serializing value objects with array children. +* When serializing value objects, properties that have a null value or an + empty array are now skipped. We believe this to be the saner default, but + does constitute a BC break for those depending on this. +* Serializing array properties in value objects was broken. + + +1.3.0 (2015-12-29) +------------------ + +* The `Service` class adds a new `mapValueObject` method which provides basic + capabilities to map between ValueObjects and XML. +* #61: You can now specify serializers for specific classes, allowing you + separate the object you want to serialize from the serializer. This uses the + `$classMap` property which is defined on both the `Service` and `Writer`. +* It's now possible to pass an array of possible root elements to + `Sabre\Xml\Service::expect()`. +* Moved some parsing logic to `Reader::getDeserializerForElementName()`, + so people with more advanced use-cases can implement their own logic there. +* #63: When serializing elements using arrays, the `value` key in the array is + now optional. +* #62: Added a `keyValue` deserializer function. This can be used instead of + the `Element\KeyValue` class and is a lot more flexible. (@staabm) +* Also added an `enum` deserializer function to replace + `Element\Elements`. +* Using an empty string for a namespace prefix now has the same effect as + `null`. + + +1.2.0 (2015-08-30) +------------------ + +* #53: Added `parseGetElements`, a function like `parseInnerTree`, except + that it always returns an array of elements, or an empty array. + + +1.1.0 (2015-06-29) +------------------ + +* #44, #45: Catching broken and invalid XML better and throwing + `Sabre\Xml\LibXMLException` whenever we encounter errors. (@stefanmajoor, + @DaanBiesterbos) + + +1.0.0 (2015-05-25) +------------------ + +* No functional changes since 0.4.3. Marking it as 1.0.0 as a promise for + API stability. +* Using php-cs-fixer for automated CS enforcement. + + +0.4.3 (2015-04-01) +----------------- + +* Minor tweaks for the public release. + + +0.4.2 (2015-03-20) +------------------ + +* Removed `constants.php` again. They messed with PHPUnit and don't really + provide a great benefit. +* #41: Correctly handle self-closing xml elements. + + +0.4.1 (2015-03-19) +------------------ + +* #40: An element with an empty namespace (xmlns="") is not allowed to have a + prefix. This is now fixed. + + +0.4.0 (2015-03-18) +------------------ + +* Added `Sabre\Xml\Service`. This is intended as a simple way to centrally + configure xml applications and easily parse/write things from there. #35, #38. +* Renamed 'baseUri' to 'contextUri' everywhere. +* #36: Added a few convenience constants to `lib/constants.php`. +* `Sabre\Xml\Util::parseClarkNotation` is now in the `Sabre\Xml\Service` class. + + +0.3.1 (2015-02-08) +------------------ + +* Added `XmlDeserializable` to match `XmlSerializable`. + + +0.3.0 (2015-02-06) +------------------ + +* Added `$elementMap` argument to parseInnerTree, for quickly overriding + parsing rules within an element. + + +0.2.2 (2015-02-05) +------------------ + +* Now depends on sabre/uri 1.0. + + +0.2.1 (2014-12-17) +------------------ + +* LibXMLException now inherits from ParseException, so it's easy for users to + catch any exception thrown by the parser. + + +0.2.0 (2014-12-05) +------------------ + +* Major BC Break: method names for the Element interface have been renamed + from `serializeXml` and `deserializeXml` to `xmlSerialize` and + `xmlDeserialize`. This is so that it matches PHP's `JsonSerializable` + interface. +* #25: Added `XmlSerializable` to allow people to write serializers without + having to implement a deserializer in the same class. +* #26: Renamed the `Sabre\XML` namespace to `Sabre\Xml`. Due to composer magic + and the fact that PHP namespace are case-insensitive, this should not affect + anyone, unless you are doing exact string matches on class names. +* #23: It's not possible to automatically extract or serialize Xml fragments + from documents using `Sabre\Xml\Element\XmlFragment`. + + +0.1.0 (2014-11-24) +------------------ + +* #16: Added ability to override `elementMap`, `namespaceMap` and `baseUri` for + a fragment of a document during reading an writing using `pushContext` and + `popContext`. +* Removed: `Writer::$context` and `Reader::$context`. +* #15: Added `Reader::$baseUri` to match `Writer::$baseUri`. +* #20: Allow callbacks to be used instead of `Element` classes in the `Reader`. +* #25: Added `readText` to quickly grab all text from a node and advance the + reader to the next node. +* #15: Added `Sabre\XML\Element\Uri`. + + +0.0.6 (2014-09-26) +------------------ + +* Added: `CData` element. +* #13: Better support for xml with no namespaces. (@kalmas) +* Switched to PSR-4 directory structure. + + +0.0.5 (2013-03-27) +------------------ + +* Added: baseUri property to the Writer class. +* Added: The writeElement method can now write complex elements. +* Added: Throwing exception when invalid objects are written. + + +0.0.4 (2013-03-14) +------------------ + +* Fixed: The KeyValue parser was skipping over elements when there was no + whitespace between them. +* Fixed: Clearing libxml errors after parsing. +* Added: Support for CDATA. +* Added: Context properties. + + +0.0.3 (2013-02-22) +------------------ + +* Changed: Reader::parse returns an array with 1 level less depth. +* Added: A LibXMLException is now thrown if the XMLReader comes across an error. +* Fixed: Both the Elements and KeyValue parsers had severe issues with + nesting. +* Fixed: The reader now detects when the end of the document is hit before it + should (because we're still parsing an element). + + +0.0.2 (2013-02-17) +------------------ + +* Added: Elements parser. +* Added: KeyValue parser. +* Change: Reader::parseSubTree is now named parseInnerTree, and returns either + a string (in case of a text-node), or an array (in case there were child + elements). +* Added: Reader::parseCurrentElement is now public. + + +0.0.1 (2013-02-07) +------------------ + +* First alpha release + +Project started: 2012-11-13. First experiments in June 2009. diff --git a/htdocs/includes/sabre/sabre/xml/LICENSE b/htdocs/includes/sabre/sabre/xml/LICENSE new file mode 100644 index 00000000000..c9faf409b95 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name Sabre nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/htdocs/includes/sabre/sabre/xml/README.md b/htdocs/includes/sabre/sabre/xml/README.md new file mode 100644 index 00000000000..e6fc4db5f3d --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/README.md @@ -0,0 +1,25 @@ +sabre/xml +========= + +[![Build Status](https://secure.travis-ci.org/fruux/sabre-xml.svg?branch=master)](http://travis-ci.org/fruux/sabre-xml) + +The sabre/xml library is a specialized XML reader and writer. + +Documentation +------------- + +* [Introduction](http://sabre.io/xml/). +* [Installation](http://sabre.io/xml/install/). +* [Reading XML](http://sabre.io/xml/reading/). +* [Writing XML](http://sabre.io/xml/writing/). + + +Support +------- + +Head over to the [SabreDAV mailing list](http://groups.google.com/group/sabredav-discuss) for any questions. + +Made at fruux +------------- + +This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. diff --git a/htdocs/includes/sabre/sabre/xml/bin/.empty b/htdocs/includes/sabre/sabre/xml/bin/.empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/includes/sabre/sabre/xml/composer.json b/htdocs/includes/sabre/sabre/xml/composer.json new file mode 100644 index 00000000000..386f8213f5e --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/composer.json @@ -0,0 +1,53 @@ +{ + "name": "sabre/xml", + "description" : "sabre/xml is an XML library that you may not hate.", + "keywords" : [ "XML", "XMLReader", "XMLWriter", "DOM" ], + "homepage" : "https://sabre.io/xml/", + "license" : "BSD-3-Clause", + "require" : { + "php" : ">=5.5.5", + "ext-xmlwriter" : "*", + "ext-xmlreader" : "*", + "ext-dom" : "*", + "lib-libxml" : ">=2.6.20", + "sabre/uri" : ">=1.0,<3.0.0" + }, + "authors" : [ + { + "name" : "Evert Pot", + "email" : "me@evertpot.com", + "homepage" : "http://evertpot.com/", + "role" : "Developer" + }, + { + "name": "Markus Staab", + "email": "markus.staab@redaxo.de", + "role" : "Developer" + } + ], + "support" : { + "forum" : "https://groups.google.com/group/sabredav-discuss", + "source" : "https://github.com/fruux/sabre-xml" + }, + "autoload" : { + "psr-4" : { + "Sabre\\Xml\\" : "lib/" + }, + "files": [ + "lib/Deserializer/functions.php", + "lib/Serializer/functions.php" + ] + }, + "autoload-dev" : { + "psr-4" : { + "Sabre\\Xml\\" : "tests/Sabre/Xml/" + } + }, + "require-dev": { + "sabre/cs": "~1.0.0", + "phpunit/phpunit" : "*" + }, + "config" : { + "bin-dir" : "bin/" + } +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php b/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php new file mode 100644 index 00000000000..ee3a3baca5c --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php @@ -0,0 +1,123 @@ +contextStack[] = [ + $this->elementMap, + $this->contextUri, + $this->namespaceMap, + $this->classMap + ]; + + } + + /** + * Restore the previous "context". + * + * @return null + */ + function popContext() { + + list( + $this->elementMap, + $this->contextUri, + $this->namespaceMap, + $this->classMap + ) = array_pop($this->contextStack); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php b/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php new file mode 100644 index 00000000000..2e5d877e912 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php @@ -0,0 +1,258 @@ +value" array. + * + * For example, keyvalue will parse: + * + * + * + * value1 + * value2 + * + * + * + * Into: + * + * [ + * "{http://sabredav.org/ns}elem1" => "value1", + * "{http://sabredav.org/ns}elem2" => "value2", + * "{http://sabredav.org/ns}elem3" => null, + * ]; + * + * If you specify the 'namespace' argument, the deserializer will remove + * the namespaces of the keys that match that namespace. + * + * For example, if you call keyValue like this: + * + * keyValue($reader, 'http://sabredav.org/ns') + * + * it's output will instead be: + * + * [ + * "elem1" => "value1", + * "elem2" => "value2", + * "elem3" => null, + * ]; + * + * Attributes will be removed from the top-level elements. If elements with + * the same name appear twice in the list, only the last one will be kept. + * + * + * @param Reader $reader + * @param string $namespace + * @return array + */ +function keyValue(Reader $reader, $namespace = null) { + + // If there's no children, we don't do anything. + if ($reader->isEmptyElement) { + $reader->next(); + return []; + } + + $values = []; + + $reader->read(); + do { + + if ($reader->nodeType === Reader::ELEMENT) { + if ($namespace !== null && $reader->namespaceURI === $namespace) { + $values[$reader->localName] = $reader->parseCurrentElement()['value']; + } else { + $clark = $reader->getClark(); + $values[$clark] = $reader->parseCurrentElement()['value']; + } + } else { + $reader->read(); + } + } while ($reader->nodeType !== Reader::END_ELEMENT); + + $reader->read(); + + return $values; + +} + +/** + * The 'enum' deserializer parses elements into a simple list + * without values or attributes. + * + * For example, Elements will parse: + * + * + * + * + * + * + * content + * + * + * + * Into: + * + * [ + * "{http://sabredav.org/ns}elem1", + * "{http://sabredav.org/ns}elem2", + * "{http://sabredav.org/ns}elem3", + * "{http://sabredav.org/ns}elem4", + * "{http://sabredav.org/ns}elem5", + * ]; + * + * This is useful for 'enum'-like structures. + * + * If the $namespace argument is specified, it will strip the namespace + * for all elements that match that. + * + * For example, + * + * enum($reader, 'http://sabredav.org/ns') + * + * would return: + * + * [ + * "elem1", + * "elem2", + * "elem3", + * "elem4", + * "elem5", + * ]; + * + * @param Reader $reader + * @param string $namespace + * @return string[] + */ +function enum(Reader $reader, $namespace = null) { + + // If there's no children, we don't do anything. + if ($reader->isEmptyElement) { + $reader->next(); + return []; + } + $reader->read(); + $currentDepth = $reader->depth; + + $values = []; + do { + + if ($reader->nodeType !== Reader::ELEMENT) { + continue; + } + if (!is_null($namespace) && $namespace === $reader->namespaceURI) { + $values[] = $reader->localName; + } else { + $values[] = $reader->getClark(); + } + + } while ($reader->depth >= $currentDepth && $reader->next()); + + $reader->next(); + return $values; + +} + +/** + * The valueObject deserializer turns an xml element into a PHP object of + * a specific class. + * + * This is primarily used by the mapValueObject function from the Service + * class, but it can also easily be used for more specific situations. + * + * @param Reader $reader + * @param string $className + * @param string $namespace + * @return object + */ +function valueObject(Reader $reader, $className, $namespace) { + + $valueObject = new $className(); + if ($reader->isEmptyElement) { + $reader->next(); + return $valueObject; + } + + $defaultProperties = get_class_vars($className); + + $reader->read(); + do { + + if ($reader->nodeType === Reader::ELEMENT && $reader->namespaceURI == $namespace) { + + if (property_exists($valueObject, $reader->localName)) { + if (is_array($defaultProperties[$reader->localName])) { + $valueObject->{$reader->localName}[] = $reader->parseCurrentElement()['value']; + } else { + $valueObject->{$reader->localName} = $reader->parseCurrentElement()['value']; + } + } else { + // Ignore property + $reader->next(); + } + } else { + $reader->read(); + } + } while ($reader->nodeType !== Reader::END_ELEMENT); + + $reader->read(); + return $valueObject; + +} + +/** + * This deserializer helps you deserialize xml structures that look like + * this: + * + * + * ... + * ... + * ... + * + * + * Many XML documents use patterns like that, and this deserializer + * allow you to get all the 'items' as an array. + * + * In that previous example, you would register the deserializer as such: + * + * $reader->elementMap['{}collection'] = function($reader) { + * return repeatingElements($reader, '{}item'); + * } + * + * The repeatingElements deserializer simply returns everything as an array. + * + * @param Reader $reader + * @param string $childElementName Element name in clark-notation + * @return array + */ +function repeatingElements(Reader $reader, $childElementName) { + + if ($childElementName[0] !== '{') { + $childElementName = '{}' . $childElementName; + } + $result = []; + + foreach ($reader->parseGetElements() as $element) { + + if ($element['name'] === $childElementName) { + $result[] = $element['value']; + } + + } + + return $result; + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element.php b/htdocs/includes/sabre/sabre/xml/lib/Element.php new file mode 100644 index 00000000000..dd89c58882c --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Element.php @@ -0,0 +1,20 @@ +value = $value; + + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializable should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $writer->write($this->value); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statictly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Xml\Reader $reader + * @return mixed + */ + static function xmlDeserialize(Xml\Reader $reader) { + + $subTree = $reader->parseInnerTree(); + return $subTree; + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php b/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php new file mode 100644 index 00000000000..5f42c4c6e8e --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php @@ -0,0 +1,64 @@ +value = $value; + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $writer->writeCData($this->value); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php b/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php new file mode 100644 index 00000000000..9eefd1bf884 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php @@ -0,0 +1,108 @@ + + * + * + * + * + * content + * + * + * + * Into: + * + * [ + * "{http://sabredav.org/ns}elem1", + * "{http://sabredav.org/ns}elem2", + * "{http://sabredav.org/ns}elem3", + * "{http://sabredav.org/ns}elem4", + * "{http://sabredav.org/ns}elem5", + * ]; + * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Elements implements Xml\Element { + + /** + * Value to serialize + * + * @var array + */ + protected $value; + + /** + * Constructor + * + * @param array $value + */ + function __construct(array $value = []) { + + $this->value = $value; + + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + Serializer\enum($writer, $this->value); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statictly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseSubTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Xml\Reader $reader + * @return mixed + */ + static function xmlDeserialize(Xml\Reader $reader) { + + return Deserializer\enum($reader); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php b/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php new file mode 100644 index 00000000000..7ce53bf4c6d --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php @@ -0,0 +1,108 @@ +value struct. + * + * Attributes will be removed, and duplicate child elements are discarded. + * Complex values within the elements will be parsed by the 'standard' parser. + * + * For example, KeyValue will parse: + * + * + * + * value1 + * value2 + * + * + * + * Into: + * + * [ + * "{http://sabredav.org/ns}elem1" => "value1", + * "{http://sabredav.org/ns}elem2" => "value2", + * "{http://sabredav.org/ns}elem3" => null, + * ]; + * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class KeyValue implements Xml\Element { + + /** + * Value to serialize + * + * @var array + */ + protected $value; + + /** + * Constructor + * + * @param array $value + */ + function __construct(array $value = []) { + + $this->value = $value; + + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $writer->write($this->value); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called staticly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Xml\Reader $reader + * @return mixed + */ + static function xmlDeserialize(Xml\Reader $reader) { + + return Deserializer\keyValue($reader); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php b/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php new file mode 100644 index 00000000000..8f45c0027eb --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php @@ -0,0 +1,104 @@ +/foo/bar + * http://example.org/hi + * + * If the uri is relative, it will be automatically expanded to an absolute + * url during writing and reading, if the contextUri property is set on the + * reader and/or writer. + * + * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Uri implements Xml\Element { + + /** + * Uri element value. + * + * @var string + */ + protected $value; + + /** + * Constructor + * + * @param string $value + */ + function __construct($value) + { + $this->value = $value; + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Xml\Writer $writer) { + + $writer->text( + \Sabre\Uri\resolve( + $writer->contextUri, + $this->value + ) + ); + + } + + /** + * This method is called during xml parsing. + * + * This method is called statically, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseSubTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Xml\Reader $reader + * @return mixed + */ + static function xmlDeserialize(Xml\Reader $reader) { + + return new self( + \Sabre\Uri\resolve( + $reader->contextUri, + $reader->readText() + ) + ); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php b/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php new file mode 100644 index 00000000000..642241ca485 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php @@ -0,0 +1,147 @@ +xml = $xml; + + } + + function getXml() { + + return $this->xml; + + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + $reader = new Reader(); + + // Wrapping the xml in a container, so root-less values can still be + // parsed. + $xml = << +{$this->getXml()} +XML; + + $reader->xml($xml); + + while ($reader->read()) { + + if ($reader->depth < 1) { + // Skipping the root node. + continue; + } + + switch ($reader->nodeType) { + + case Reader::ELEMENT : + $writer->startElement( + $reader->getClark() + ); + $empty = $reader->isEmptyElement; + while ($reader->moveToNextAttribute()) { + switch ($reader->namespaceURI) { + case '' : + $writer->writeAttribute($reader->localName, $reader->value); + break; + case 'http://www.w3.org/2000/xmlns/' : + // Skip namespace declarations + break; + default : + $writer->writeAttribute($reader->getClark(), $reader->value); + break; + } + } + if ($empty) { + $writer->endElement(); + } + break; + case Reader::CDATA : + case Reader::TEXT : + $writer->text( + $reader->value + ); + break; + case Reader::END_ELEMENT : + $writer->endElement(); + break; + + } + + } + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statictly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + + $result = new self($reader->readInnerXml()); + $reader->next(); + return $result; + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php b/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php new file mode 100644 index 00000000000..f0190eb51ea --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php @@ -0,0 +1,53 @@ +errors = $errors; + parent::__construct($errors[0]->message . ' on line ' . $errors[0]->line . ', column ' . $errors[0]->column, $code, $previousException); + + } + + /** + * Returns the LibXML errors + * + * @return void + */ + function getErrors() { + + return $this->errors; + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/ParseException.php b/htdocs/includes/sabre/sabre/xml/lib/ParseException.php new file mode 100644 index 00000000000..3a6883b2ff8 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/ParseException.php @@ -0,0 +1,17 @@ +localName) { + return null; + } + + return '{' . $this->namespaceURI . '}' . $this->localName; + + } + + /** + * Reads the entire document. + * + * This function returns an array with the following three elements: + * * name - The root element name. + * * value - The value for the root element. + * * attributes - An array of attributes. + * + * This function will also disable the standard libxml error handler (which + * usually just results in PHP errors), and throw exceptions instead. + * + * @return array + */ + function parse() { + + $previousEntityState = libxml_disable_entity_loader(true); + $previousSetting = libxml_use_internal_errors(true); + + try { + + // Really sorry about the silence operator, seems like I have no + // choice. See: + // + // https://bugs.php.net/bug.php?id=64230 + while ($this->nodeType !== self::ELEMENT && @$this->read()) { + // noop + } + $result = $this->parseCurrentElement(); + + $errors = libxml_get_errors(); + libxml_clear_errors(); + if ($errors) { + throw new LibXMLException($errors); + } + + } finally { + libxml_use_internal_errors($previousSetting); + libxml_disable_entity_loader($previousEntityState); + } + + return $result; + } + + + + /** + * parseGetElements parses everything in the current sub-tree, + * and returns a an array of elements. + * + * Each element has a 'name', 'value' and 'attributes' key. + * + * If the the element didn't contain sub-elements, an empty array is always + * returned. If there was any text inside the element, it will be + * discarded. + * + * If the $elementMap argument is specified, the existing elementMap will + * be overridden while parsing the tree, and restored after this process. + * + * @param array $elementMap + * @return array + */ + function parseGetElements(array $elementMap = null) { + + $result = $this->parseInnerTree($elementMap); + if (!is_array($result)) { + return []; + } + return $result; + + } + + /** + * Parses all elements below the current element. + * + * This method will return a string if this was a text-node, or an array if + * there were sub-elements. + * + * If there's both text and sub-elements, the text will be discarded. + * + * If the $elementMap argument is specified, the existing elementMap will + * be overridden while parsing the tree, and restored after this process. + * + * @param array $elementMap + * @return array|string + */ + function parseInnerTree(array $elementMap = null) { + + $text = null; + $elements = []; + + if ($this->nodeType === self::ELEMENT && $this->isEmptyElement) { + // Easy! + $this->next(); + return null; + } + + if (!is_null($elementMap)) { + $this->pushContext(); + $this->elementMap = $elementMap; + } + + try { + + // Really sorry about the silence operator, seems like I have no + // choice. See: + // + // https://bugs.php.net/bug.php?id=64230 + if (!@$this->read()) { + $errors = libxml_get_errors(); + libxml_clear_errors(); + if ($errors) { + throw new LibXMLException($errors); + } + throw new ParseException('This should never happen (famous last words)'); + } + + while (true) { + + if (!$this->isValid()) { + + $errors = libxml_get_errors(); + + if ($errors) { + libxml_clear_errors(); + throw new LibXMLException($errors); + } + } + + switch ($this->nodeType) { + case self::ELEMENT : + $elements[] = $this->parseCurrentElement(); + break; + case self::TEXT : + case self::CDATA : + $text .= $this->value; + $this->read(); + break; + case self::END_ELEMENT : + // Ensuring we are moving the cursor after the end element. + $this->read(); + break 2; + case self::NONE : + throw new ParseException('We hit the end of the document prematurely. This likely means that some parser "eats" too many elements. Do not attempt to continue parsing.'); + default : + // Advance to the next element + $this->read(); + break; + } + + } + + } finally { + + if (!is_null($elementMap)) { + $this->popContext(); + } + + } + return ($elements ? $elements : $text); + + } + + /** + * Reads all text below the current element, and returns this as a string. + * + * @return string + */ + function readText() { + + $result = ''; + $previousDepth = $this->depth; + + while ($this->read() && $this->depth != $previousDepth) { + if (in_array($this->nodeType, [XMLReader::TEXT, XMLReader::CDATA, XMLReader::WHITESPACE])) { + $result .= $this->value; + } + } + return $result; + + } + + /** + * Parses the current XML element. + * + * This method returns arn array with 3 properties: + * * name - A clark-notation XML element name. + * * value - The parsed value. + * * attributes - A key-value list of attributes. + * + * @return array + */ + function parseCurrentElement() { + + $name = $this->getClark(); + + $attributes = []; + + if ($this->hasAttributes) { + $attributes = $this->parseAttributes(); + } + + $value = call_user_func( + $this->getDeserializerForElementName($name), + $this + ); + + return [ + 'name' => $name, + 'value' => $value, + 'attributes' => $attributes, + ]; + } + + + /** + * Grabs all the attributes from the current element, and returns them as a + * key-value array. + * + * If the attributes are part of the same namespace, they will simply be + * short keys. If they are defined on a different namespace, the attribute + * name will be retured in clark-notation. + * + * @return array + */ + function parseAttributes() { + + $attributes = []; + + while ($this->moveToNextAttribute()) { + if ($this->namespaceURI) { + + // Ignoring 'xmlns', it doesn't make any sense. + if ($this->namespaceURI === 'http://www.w3.org/2000/xmlns/') { + continue; + } + + $name = $this->getClark(); + $attributes[$name] = $this->value; + + } else { + $attributes[$this->localName] = $this->value; + } + } + $this->moveToElement(); + + return $attributes; + + } + + /** + * Returns the function that should be used to parse the element identified + * by it's clark-notation name. + * + * @param string $name + * @return callable + */ + function getDeserializerForElementName($name) { + + + if (!array_key_exists($name, $this->elementMap)) { + if (substr($name, 0, 2) == '{}' && array_key_exists(substr($name, 2), $this->elementMap)) { + $name = substr($name, 2); + } else { + return ['Sabre\\Xml\\Element\\Base', 'xmlDeserialize']; + } + } + + $deserializer = $this->elementMap[$name]; + if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) { + return [$deserializer, 'xmlDeserialize']; + } + + if (is_callable($deserializer)) { + return $deserializer; + } + + $type = gettype($deserializer); + if ($type === 'string') { + $type .= ' (' . $deserializer . ')'; + } elseif ($type === 'object') { + $type .= ' (' . get_class($deserializer) . ')'; + } + throw new \LogicException('Could not use this type as a deserializer: ' . $type . ' for element: ' . $name); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php b/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php new file mode 100644 index 00000000000..21448017d94 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php @@ -0,0 +1,249 @@ + + * + * + * content + * + * + * @param Writer $writer + * @param string[] $values + * @return void + */ +function enum(Writer $writer, array $values) { + + foreach ($values as $value) { + $writer->writeElement($value); + } +} + +/** + * The valueObject serializer turns a simple PHP object into a classname. + * + * Every public property will be encoded as an xml element with the same + * name, in the XML namespace as specified. + * + * Values that are set to null or an empty array are not serialized. To + * serialize empty properties, you must specify them as an empty string. + * + * @param Writer $writer + * @param object $valueObject + * @param string $namespace + */ +function valueObject(Writer $writer, $valueObject, $namespace) { + foreach (get_object_vars($valueObject) as $key => $val) { + if (is_array($val)) { + // If $val is an array, it has a special meaning. We need to + // generate one child element for each item in $val + foreach ($val as $child) { + $writer->writeElement('{' . $namespace . '}' . $key, $child); + } + + } elseif ($val !== null) { + $writer->writeElement('{' . $namespace . '}' . $key, $val); + } + } +} + + +/** + * This serializer helps you serialize xml structures that look like + * this: + * + * + * ... + * ... + * ... + * + * + * In that previous example, this serializer just serializes the item element, + * and this could be called like this: + * + * repeatingElements($writer, $items, '{}item'); + * + * @param Writer $writer + * @param array $items A list of items sabre/xml can serialize. + * @param string $childElementName Element name in clark-notation + * @return void + */ +function repeatingElements(Writer $writer, array $items, $childElementName) { + + foreach ($items as $item) { + $writer->writeElement($childElementName, $item); + } + +} + +/** + * This function is the 'default' serializer that is able to serialize most + * things, and delegates to other serializers if needed. + * + * The standardSerializer supports a wide-array of values. + * + * $value may be a string or integer, it will just write out the string as text. + * $value may be an instance of XmlSerializable or Element, in which case it + * calls it's xmlSerialize() method. + * $value may be a PHP callback/function/closure, in case we call the callback + * and give it the Writer as an argument. + * $value may be a an object, and if it's in the classMap we automatically call + * the correct serializer for it. + * $value may be null, in which case we do nothing. + * + * If $value is an array, the array must look like this: + * + * [ + * [ + * 'name' => '{namespaceUri}element-name', + * 'value' => '...', + * 'attributes' => [ 'attName' => 'attValue' ] + * ] + * [, + * 'name' => '{namespaceUri}element-name2', + * 'value' => '...', + * ] + * ] + * + * This would result in xml like: + * + * + * ... + * + * + * ... + * + * + * The value property may be any value standardSerializer supports, so you can + * nest data-structures this way. Both value and attributes are optional. + * + * Alternatively, you can also specify the array using this syntax: + * + * [ + * [ + * '{namespaceUri}element-name' => '...', + * '{namespaceUri}element-name2' => '...', + * ] + * ] + * + * This is excellent for simple key->value structures, and here you can also + * specify anything for the value. + * + * You can even mix the two array syntaxes. + * + * @param Writer $writer + * @param string|int|float|bool|array|object + * @return void + */ +function standardSerializer(Writer $writer, $value) { + + if (is_scalar($value)) { + + // String, integer, float, boolean + $writer->text($value); + + } elseif ($value instanceof XmlSerializable) { + + // XmlSerializable classes or Element classes. + $value->xmlSerialize($writer); + + } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { + + // It's an object which class appears in the classmap. + $writer->classMap[get_class($value)]($writer, $value); + + } elseif (is_callable($value)) { + + // A callback + $value($writer); + + } elseif (is_null($value)) { + + // nothing! + + } elseif (is_array($value) && array_key_exists('name', $value)) { + + // if the array had a 'name' element, we assume that this array + // describes a 'name' and optionally 'attributes' and 'value'. + + $name = $value['name']; + $attributes = isset($value['attributes']) ? $value['attributes'] : []; + $value = isset($value['value']) ? $value['value'] : null; + + $writer->startElement($name); + $writer->writeAttributes($attributes); + $writer->write($value); + $writer->endElement(); + + } elseif (is_array($value)) { + + foreach ($value as $name => $item) { + + if (is_int($name)) { + + // This item has a numeric index. We just loop through the + // array and throw it back in the writer. + standardSerializer($writer, $item); + + } elseif (is_string($name) && is_array($item) && isset($item['attributes'])) { + + // The key is used for a name, but $item has 'attributes' and + // possibly 'value' + $writer->startElement($name); + $writer->writeAttributes($item['attributes']); + if (isset($item['value'])) { + $writer->write($item['value']); + } + $writer->endElement(); + + } elseif (is_string($name)) { + + // This was a plain key-value array. + $writer->startElement($name); + $writer->write($item); + $writer->endElement(); + + } else { + + throw new InvalidArgumentException('The writer does not know how to serialize arrays with keys of type: ' . gettype($name)); + + } + } + + } elseif (is_object($value)) { + + throw new InvalidArgumentException('The writer cannot serialize objects of class: ' . get_class($value)); + + } else { + + throw new InvalidArgumentException('The writer cannot serialize values of type: ' . gettype($value)); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Service.php b/htdocs/includes/sabre/sabre/xml/lib/Service.php new file mode 100644 index 00000000000..09ee341cf8f --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Service.php @@ -0,0 +1,297 @@ +elementMap = $this->elementMap; + return $r; + + } + + /** + * Returns a fresh xml writer + * + * @return Writer + */ + function getWriter() { + + $w = new Writer(); + $w->namespaceMap = $this->namespaceMap; + $w->classMap = $this->classMap; + return $w; + + } + + /** + * Parses a document in full. + * + * Input may be specified as a string or readable stream resource. + * The returned value is the value of the root document. + * + * Specifying the $contextUri allows the parser to figure out what the URI + * of the document was. This allows relative URIs within the document to be + * expanded easily. + * + * The $rootElementName is specified by reference and will be populated + * with the root element name of the document. + * + * @param string|resource $input + * @param string|null $contextUri + * @param string|null $rootElementName + * @throws ParseException + * @return array|object|string + */ + function parse($input, $contextUri = null, &$rootElementName = null) { + + if (is_resource($input)) { + // Unfortunately the XMLReader doesn't support streams. When it + // does, we can optimize this. + $input = stream_get_contents($input); + } + $r = $this->getReader(); + $r->contextUri = $contextUri; + $r->xml($input); + + $result = $r->parse(); + $rootElementName = $result['name']; + return $result['value']; + + } + + /** + * Parses a document in full, and specify what the expected root element + * name is. + * + * This function works similar to parse, but the difference is that the + * user can specify what the expected name of the root element should be, + * in clark notation. + * + * This is useful in cases where you expected a specific document to be + * passed, and reduces the amount of if statements. + * + * It's also possible to pass an array of expected rootElements if your + * code may expect more than one document type. + * + * @param string|string[] $rootElementName + * @param string|resource $input + * @param string|null $contextUri + * @return void + */ + function expect($rootElementName, $input, $contextUri = null) { + + if (is_resource($input)) { + // Unfortunately the XMLReader doesn't support streams. When it + // does, we can optimize this. + $input = stream_get_contents($input); + } + $r = $this->getReader(); + $r->contextUri = $contextUri; + $r->xml($input); + + $rootElementName = (array)$rootElementName; + + foreach ($rootElementName as &$rEl) { + if ($rEl[0] !== '{') $rEl = '{}' . $rEl; + } + + $result = $r->parse(); + if (!in_array($result['name'], $rootElementName, true)) { + throw new ParseException('Expected ' . implode(' or ', (array)$rootElementName) . ' but received ' . $result['name'] . ' as the root element'); + } + return $result['value']; + + } + + /** + * Generates an XML document in one go. + * + * The $rootElement must be specified in clark notation. + * The value must be a string, an array or an object implementing + * XmlSerializable. Basically, anything that's supported by the Writer + * object. + * + * $contextUri can be used to specify a sort of 'root' of the PHP application, + * in case the xml document is used as a http response. + * + * This allows an implementor to easily create URI's relative to the root + * of the domain. + * + * @param string $rootElementName + * @param string|array|XmlSerializable $value + * @param string|null $contextUri + */ + function write($rootElementName, $value, $contextUri = null) { + + $w = $this->getWriter(); + $w->openMemory(); + $w->contextUri = $contextUri; + $w->setIndent(true); + $w->startDocument(); + $w->writeElement($rootElementName, $value); + return $w->outputMemory(); + + } + + /** + * Map an xml element to a PHP class. + * + * Calling this function will automatically setup the Reader and Writer + * classes to turn a specific XML element to a PHP class. + * + * For example, given a class such as : + * + * class Author { + * public $firstName; + * public $lastName; + * } + * + * and an XML element such as: + * + * + * ... + * ... + * + * + * These can easily be mapped by calling: + * + * $service->mapValueObject('{http://example.org}author', 'Author'); + * + * @param string $elementName + * @param object $className + * @return void + */ + function mapValueObject($elementName, $className) { + list($namespace) = self::parseClarkNotation($elementName); + + $this->elementMap[$elementName] = function(Reader $reader) use ($className, $namespace) { + return \Sabre\Xml\Deserializer\valueObject($reader, $className, $namespace); + }; + $this->classMap[$className] = function(Writer $writer, $valueObject) use ($namespace) { + return \Sabre\Xml\Serializer\valueObject($writer, $valueObject, $namespace); + }; + $this->valueObjectMap[$className] = $elementName; + } + + /** + * Writes a value object. + * + * This function largely behaves similar to write(), except that it's + * intended specifically to serialize a Value Object into an XML document. + * + * The ValueObject must have been previously registered using + * mapValueObject(). + * + * @param object $object + * @param string $contextUri + * @return void + */ + function writeValueObject($object, $contextUri = null) { + + if (!isset($this->valueObjectMap[get_class($object)])) { + throw new \InvalidArgumentException('"' . get_class($object) . '" is not a registered value object class. Register your class with mapValueObject.'); + } + return $this->write( + $this->valueObjectMap[get_class($object)], + $object, + $contextUri + ); + + } + + /** + * Parses a clark-notation string, and returns the namespace and element + * name components. + * + * If the string was invalid, it will throw an InvalidArgumentException. + * + * @param string $str + * @throws InvalidArgumentException + * @return array + */ + static function parseClarkNotation($str) { + static $cache = []; + + if (!isset($cache[$str])) { + + if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { + throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); + } + + $cache[$str] = [ + $matches[1], + $matches[2] + ]; + } + + return $cache[$str]; + } + + /** + * A list of classes and which XML elements they map to. + */ + protected $valueObjectMap = []; + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/Version.php b/htdocs/includes/sabre/sabre/xml/lib/Version.php new file mode 100644 index 00000000000..7edb40d67ac --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/Version.php @@ -0,0 +1,19 @@ + "..", + * "{namespace}name2" => "..", + * ] + * + * One element will be created for each key in this array. The values of + * this array support any format this method supports (this method is + * called recursively). + * + * Array format 2: + * + * [ + * [ + * "name" => "{namespace}name1" + * "value" => "..", + * "attributes" => [ + * "attr" => "attribute value", + * ] + * ], + * [ + * "name" => "{namespace}name1" + * "value" => "..", + * "attributes" => [ + * "attr" => "attribute value", + * ] + * ] + * ] + * + * @param mixed $value + * @return void + */ + function write($value) { + + Serializer\standardSerializer($this, $value); + + } + + /** + * Opens a new element. + * + * You can either just use a local elementname, or you can use clark- + * notation to start a new element. + * + * Example: + * + * $writer->startElement('{http://www.w3.org/2005/Atom}entry'); + * + * Would result in something like: + * + * + * + * @param string $name + * @return bool + */ + function startElement($name) { + + if ($name[0] === '{') { + + list($namespace, $localName) = + Service::parseClarkNotation($name); + + if (array_key_exists($namespace, $this->namespaceMap)) { + $result = $this->startElementNS( + $this->namespaceMap[$namespace] === '' ? null : $this->namespaceMap[$namespace], + $localName, + null + ); + } else { + + // An empty namespace means it's the global namespace. This is + // allowed, but it mustn't get a prefix. + if ($namespace === "" || $namespace === null) { + $result = $this->startElement($localName); + $this->writeAttribute('xmlns', ''); + } else { + if (!isset($this->adhocNamespaces[$namespace])) { + $this->adhocNamespaces[$namespace] = 'x' . (count($this->adhocNamespaces) + 1); + } + $result = $this->startElementNS($this->adhocNamespaces[$namespace], $localName, $namespace); + } + } + + } else { + $result = parent::startElement($name); + } + + if (!$this->namespacesWritten) { + + foreach ($this->namespaceMap as $namespace => $prefix) { + $this->writeAttribute(($prefix ? 'xmlns:' . $prefix : 'xmlns'), $namespace); + } + $this->namespacesWritten = true; + + } + + return $result; + + } + + /** + * Write a full element tag and it's contents. + * + * This method automatically closes the element as well. + * + * The element name may be specified in clark-notation. + * + * Examples: + * + * $writer->writeElement('{http://www.w3.org/2005/Atom}author',null); + * becomes: + * + * + * $writer->writeElement('{http://www.w3.org/2005/Atom}author', [ + * '{http://www.w3.org/2005/Atom}name' => 'Evert Pot', + * ]); + * becomes: + * Evert Pot + * + * @param string $name + * @param string $content + * @return bool + */ + function writeElement($name, $content = null) { + + $this->startElement($name); + if (!is_null($content)) { + $this->write($content); + } + $this->endElement(); + + } + + /** + * Writes a list of attributes. + * + * Attributes are specified as a key->value array. + * + * The key is an attribute name. If the key is a 'localName', the current + * xml namespace is assumed. If it's a 'clark notation key', this namespace + * will be used instead. + * + * @param array $attributes + * @return void + */ + function writeAttributes(array $attributes) { + + foreach ($attributes as $name => $value) { + $this->writeAttribute($name, $value); + } + + } + + /** + * Writes a new attribute. + * + * The name may be specified in clark-notation. + * + * Returns true when successful. + * + * @param string $name + * @param string $value + * @return bool + */ + function writeAttribute($name, $value) { + + if ($name[0] === '{') { + + list( + $namespace, + $localName + ) = Service::parseClarkNotation($name); + + if (array_key_exists($namespace, $this->namespaceMap)) { + // It's an attribute with a namespace we know + $this->writeAttribute( + $this->namespaceMap[$namespace] . ':' . $localName, + $value + ); + } else { + + // We don't know the namespace, we must add it in-line + if (!isset($this->adhocNamespaces[$namespace])) { + $this->adhocNamespaces[$namespace] = 'x' . (count($this->adhocNamespaces) + 1); + } + $this->writeAttributeNS( + $this->adhocNamespaces[$namespace], + $localName, + $namespace, + $value + ); + + } + + } else { + return parent::writeAttribute($name, $value); + } + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php b/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php new file mode 100644 index 00000000000..fa857e82c79 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php @@ -0,0 +1,38 @@ +next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader); + +} diff --git a/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php b/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php new file mode 100644 index 00000000000..3e2c528b945 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php @@ -0,0 +1,36 @@ +stack = $this->getMockForTrait('Sabre\\Xml\\ContextStackTrait'); + + } + + function testPushAndPull() { + + $this->stack->contextUri = '/foo/bar'; + $this->stack->elementMap['{DAV:}foo'] = 'Bar'; + $this->stack->namespaceMap['DAV:'] = 'd'; + + $this->stack->pushContext(); + + $this->assertEquals('/foo/bar', $this->stack->contextUri); + $this->assertEquals('Bar', $this->stack->elementMap['{DAV:}foo']); + $this->assertEquals('d', $this->stack->namespaceMap['DAV:']); + + $this->stack->contextUri = '/gir/zim'; + $this->stack->elementMap['{DAV:}foo'] = 'newBar'; + $this->stack->namespaceMap['DAV:'] = 'dd'; + + $this->stack->popContext(); + + $this->assertEquals('/foo/bar', $this->stack->contextUri); + $this->assertEquals('Bar', $this->stack->elementMap['{DAV:}foo']); + $this->assertEquals('d', $this->stack->namespaceMap['DAV:']); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/EnumTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/EnumTest.php new file mode 100644 index 00000000000..2eea9bb5aaf --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/EnumTest.php @@ -0,0 +1,62 @@ +elementMap['{urn:test}root'] = 'Sabre\Xml\Deserializer\enum'; + + $xml = << + + + + +XML; + + $result = $service->parse($xml); + + $expected = [ + '{urn:test}foo1', + '{urn:test}foo2', + ]; + + + $this->assertEquals($expected, $result); + + + } + + function testDeserializeDefaultNamespace() { + + $service = new Service(); + $service->elementMap['{urn:test}root'] = function($reader) { + return enum($reader, 'urn:test'); + }; + + $xml = << + + + + +XML; + + $result = $service->parse($xml); + + $expected = [ + 'foo1', + 'foo2', + ]; + + + $this->assertEquals($expected, $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/KeyValueTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/KeyValueTest.php new file mode 100644 index 00000000000..a94ff4e01a1 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/KeyValueTest.php @@ -0,0 +1,112 @@ + + + + + hi + + foo + foo & bar + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}struct' => function(Reader $reader) { + return keyValue($reader, 'http://sabredav.org/ns'); + } + ]; + $reader->xml($input); + $output = $reader->parse(); + + $this->assertEquals([ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}struct', + 'value' => [ + 'elem1' => null, + 'elem2' => 'hi', + '{http://sabredav.org/another-ns}elem3' => [ + [ + 'name' => '{http://sabredav.org/another-ns}elem4', + 'value' => 'foo', + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/another-ns}elem5', + 'value' => 'foo & bar', + 'attributes' => [], + ], + ] + ], + 'attributes' => [], + ] + ], + 'attributes' => [], + ], $output); + } + + /** + * @expectedException \Sabre\Xml\LibXMLException + */ + function testKeyValueLoop() { + + /** + * This bug is a weird one, because it triggers an infinite loop, but + * only if the XML document is a certain size (in bytes). Removing one + * or two characters from the xml body here cause the infinite loop to + * *not* get triggered, so to properly test this bug (Issue #94), don't + * change the XML body. + */ + $invalid_xml = ' + + + NONE + ENVELOPE + 1 + DC + + NONE + ENVELOPE + 1 + DC/FleetType> + + '; + $reader = new Reader(); + + $reader->xml($invalid_xml); + $reader->elementMap = [ + + '{}Package' => function($reader) { + $recipient = []; + // Borrowing a parser from the KeyValue class. + $keyValue = keyValue($reader); + + if (isset($keyValue['{}WeightOz'])){ + $recipient['referenceId'] = $keyValue['{}WeightOz']; + } + + return $recipient; + }, + ]; + + $reader->parse(); + + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/RepeatingElementsTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/RepeatingElementsTest.php new file mode 100644 index 00000000000..025d997feb2 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/RepeatingElementsTest.php @@ -0,0 +1,35 @@ +elementMap['{urn:test}collection'] = function($reader) { + return repeatingElements($reader, '{urn:test}item'); + }; + + $xml = << + + foo + bar + +XML; + + $result = $service->parse($xml); + + $expected = [ + 'foo', + 'bar', + ]; + + $this->assertEquals($expected, $result); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/ValueObjectTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/ValueObjectTest.php new file mode 100644 index 00000000000..2d6ce98ce4f --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Deserializer/ValueObjectTest.php @@ -0,0 +1,169 @@ + + + Harry + Turtle + +XML; + + $reader = new Reader(); + $reader->xml($input); + $reader->elementMap = [ + '{urn:foo}foo' => function(Reader $reader) { + return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); + } + ]; + + $output = $reader->parse(); + + $vo = new TestVo(); + $vo->firstName = 'Harry'; + $vo->lastName = 'Turtle'; + + $expected = [ + 'name' => '{urn:foo}foo', + 'value' => $vo, + 'attributes' => [] + ]; + + $this->assertEquals( + $expected, + $output + ); + + } + + function testDeserializeValueObjectIgnoredElement() { + + $input = << + + Harry + Turtle + harry@example.org + +XML; + + $reader = new Reader(); + $reader->xml($input); + $reader->elementMap = [ + '{urn:foo}foo' => function(Reader $reader) { + return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); + } + ]; + + $output = $reader->parse(); + + $vo = new TestVo(); + $vo->firstName = 'Harry'; + $vo->lastName = 'Turtle'; + + $expected = [ + 'name' => '{urn:foo}foo', + 'value' => $vo, + 'attributes' => [] + ]; + + $this->assertEquals( + $expected, + $output + ); + + } + + function testDeserializeValueObjectAutoArray() { + + $input = << + + Harry + Turtle + http://example.org/ + http://example.net/ + +XML; + + $reader = new Reader(); + $reader->xml($input); + $reader->elementMap = [ + '{urn:foo}foo' => function(Reader $reader) { + return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); + } + ]; + + $output = $reader->parse(); + + $vo = new TestVo(); + $vo->firstName = 'Harry'; + $vo->lastName = 'Turtle'; + $vo->link = [ + 'http://example.org/', + 'http://example.net/', + ]; + + + $expected = [ + 'name' => '{urn:foo}foo', + 'value' => $vo, + 'attributes' => [] + ]; + + $this->assertEquals( + $expected, + $output + ); + + } + function testDeserializeValueObjectEmpty() { + + $input = << + +XML; + + $reader = new Reader(); + $reader->xml($input); + $reader->elementMap = [ + '{urn:foo}foo' => function(Reader $reader) { + return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); + } + ]; + + $output = $reader->parse(); + + $vo = new TestVo(); + + $expected = [ + 'name' => '{urn:foo}foo', + 'value' => $vo, + 'attributes' => [] + ]; + + $this->assertEquals( + $expected, + $output + ); + + } + +} + +class TestVo { + + public $firstName; + public $lastName; + + public $link = []; + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/CDataTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/CDataTest.php new file mode 100644 index 00000000000..2d12d7b2196 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/CDataTest.php @@ -0,0 +1,58 @@ + + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}blabla' => 'Sabre\\Xml\\Element\\Cdata', + ]; + $reader->xml($input); + + $output = $reader->parse(); + + } + + function testSerialize() { + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://sabredav.org/ns' => null + ]; + $writer->openMemory(); + $writer->startDocument('1.0'); + $writer->setIndent(true); + $writer->write([ + '{http://sabredav.org/ns}root' => new Cdata(''), + ]); + + $output = $writer->outputMemory(); + + $expected = << +]]> + +XML; + + $this->assertEquals($expected, $output); + + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/Eater.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/Eater.php new file mode 100644 index 00000000000..aaba2a01f2b --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/Eater.php @@ -0,0 +1,78 @@ +startElement('{http://sabredav.org/ns}elem1'); + $writer->write('hiiii!'); + $writer->endElement(); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statictly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseSubTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Xml\Reader $reader + * @return mixed + */ + static function xmlDeserialize(Xml\Reader $reader) { + + $reader->next(); + + $count = 1; + while ($count) { + + $reader->read(); + if ($reader->nodeType === $reader::END_ELEMENT) { + $count--; + } + + } + $reader->read(); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/ElementsTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/ElementsTest.php new file mode 100644 index 00000000000..f17f2094b2e --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/ElementsTest.php @@ -0,0 +1,129 @@ + + + + + + + + content + + + + + + + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}listThingy' => 'Sabre\\Xml\\Element\\Elements', + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $this->assertEquals([ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}listThingy', + 'value' => [ + '{http://sabredav.org/ns}elem1', + '{http://sabredav.org/ns}elem2', + '{http://sabredav.org/ns}elem3', + '{http://sabredav.org/ns}elem4', + '{http://sabredav.org/ns}elem5', + '{http://sabredav.org/ns}elem6', + ], + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}listThingy', + 'value' => [], + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}otherThing', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => null, + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}elem2', + 'value' => null, + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}elem3', + 'value' => null, + 'attributes' => [], + ], + ], + 'attributes' => [], + ], + ], + 'attributes' => [], + ], $output); + + } + + function testSerialize() { + + $value = [ + '{http://sabredav.org/ns}elem1', + '{http://sabredav.org/ns}elem2', + '{http://sabredav.org/ns}elem3', + '{http://sabredav.org/ns}elem4', + '{http://sabredav.org/ns}elem5', + '{http://sabredav.org/ns}elem6', + ]; + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://sabredav.org/ns' => null + ]; + $writer->openMemory(); + $writer->startDocument('1.0'); + $writer->setIndent(true); + $writer->write([ + '{http://sabredav.org/ns}root' => new Elements($value), + ]); + + $output = $writer->outputMemory(); + + $expected = << + + + + + + + + + +XML; + + $this->assertEquals($expected, $output); + + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/KeyValueTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/KeyValueTest.php new file mode 100644 index 00000000000..51c87b5203d --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/KeyValueTest.php @@ -0,0 +1,210 @@ + + + + + hi + + foo + foo & bar + + Hithere + + + + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}struct' => 'Sabre\\Xml\\Element\\KeyValue', + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $this->assertEquals([ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}struct', + 'value' => [ + '{http://sabredav.org/ns}elem1' => null, + '{http://sabredav.org/ns}elem2' => 'hi', + '{http://sabredav.org/ns}elem3' => [ + [ + 'name' => '{http://sabredav.org/ns}elem4', + 'value' => 'foo', + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}elem5', + 'value' => 'foo & bar', + 'attributes' => [], + ], + ], + '{http://sabredav.org/ns}elem6' => 'Hithere', + ], + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}struct', + 'value' => [], + 'attributes' => [], + ], + [ + 'name' => '{http://sabredav.org/ns}otherThing', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => null, + 'attributes' => [], + ], + ], + 'attributes' => [], + ], + ], + 'attributes' => [], + ], $output); + + } + + /** + * This test was added to find out why an element gets eaten by the + * SabreDAV MKCOL parser. + */ + function testElementEater() { + + $input = << + + + + + bla + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{DAV:}set' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}resourcetype' => 'Sabre\\Xml\\Element\\Elements', + ]; + $reader->xml($input); + + $expected = [ + 'name' => '{DAV:}mkcol', + 'value' => [ + [ + 'name' => '{DAV:}set', + 'value' => [ + '{DAV:}prop' => [ + '{DAV:}resourcetype' => [ + '{DAV:}collection', + ], + '{DAV:}displayname' => 'bla', + ], + ], + 'attributes' => [], + ], + ], + 'attributes' => [], + ]; + + $this->assertEquals($expected, $reader->parse()); + + } + + + function testSerialize() { + + $value = [ + '{http://sabredav.org/ns}elem1' => null, + '{http://sabredav.org/ns}elem2' => 'textValue', + '{http://sabredav.org/ns}elem3' => [ + '{http://sabredav.org/ns}elem4' => 'text2', + '{http://sabredav.org/ns}elem5' => null, + ], + '{http://sabredav.org/ns}elem6' => 'text3', + ]; + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://sabredav.org/ns' => null + ]; + $writer->openMemory(); + $writer->startDocument('1.0'); + $writer->setIndent(true); + $writer->write([ + '{http://sabredav.org/ns}root' => new KeyValue($value), + ]); + + $output = $writer->outputMemory(); + + $expected = << + + + textValue + + text2 + + + text3 + + +XML; + + $this->assertEquals($expected, $output); + + } + + /** + * I discovered that when there's no whitespace between elements, elements + * can get skipped. + */ + function testElementSkipProblem() { + + $input = << + +val3val4val5 +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}root' => 'Sabre\\Xml\\Element\\KeyValue', + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $this->assertEquals([ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + '{http://sabredav.org/ns}elem3' => 'val3', + '{http://sabredav.org/ns}elem4' => 'val4', + '{http://sabredav.org/ns}elem5' => 'val5', + ], + 'attributes' => [], + ], $output); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/Mock.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/Mock.php new file mode 100644 index 00000000000..f96684cb55a --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/Mock.php @@ -0,0 +1,60 @@ +startElement('{http://sabredav.org/ns}elem1'); + $writer->write('hiiii!'); + $writer->endElement(); + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statictly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * Important note 2: You are responsible for advancing the reader to the + * next element. Not doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseSubTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Xml\Reader $reader + * @return mixed + */ + static function xmlDeserialize(Xml\Reader $reader) { + + $reader->next(); + return 'foobar'; + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/UriTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/UriTest.php new file mode 100644 index 00000000000..53f89ed7aae --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/UriTest.php @@ -0,0 +1,76 @@ + + + /foo/bar + +BLA; + + $reader = new Reader(); + $reader->contextUri = 'http://example.org/'; + $reader->elementMap = [ + '{http://sabredav.org/ns}uri' => 'Sabre\\Xml\\Element\\Uri', + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $this->assertEquals( + [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}uri', + 'value' => new Uri('http://example.org/foo/bar'), + 'attributes' => [], + ] + ], + 'attributes' => [], + ], + $output + ); + + } + + function testSerialize() { + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://sabredav.org/ns' => null + ]; + $writer->openMemory(); + $writer->startDocument('1.0'); + $writer->setIndent(true); + $writer->contextUri = 'http://example.org/'; + $writer->write([ + '{http://sabredav.org/ns}root' => [ + '{http://sabredav.org/ns}uri' => new Uri('/foo/bar'), + ] + ]); + + $output = $writer->outputMemory(); + + $expected = << + + http://example.org/foo/bar + + +XML; + + $this->assertEquals($expected, $output); + + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/XmlFragmentTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/XmlFragmentTest.php new file mode 100644 index 00000000000..461cc155ca6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Element/XmlFragmentTest.php @@ -0,0 +1,143 @@ + + + $input + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}fragment' => 'Sabre\\Xml\\Element\\XmlFragment', + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $this->assertEquals([ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}fragment', + 'value' => new XmlFragment($expected), + 'attributes' => [], + ], + ], + 'attributes' => [], + ], $output); + + } + + /** + * Data provider for serialize and deserialize tests. + * + * Returns three items per test: + * + * 1. Input data for the reader. + * 2. Expected output for XmlFragment deserializer + * 3. Expected output after serializing that value again. + * + * If 3 is not set, use 1 for 3. + * + * @return void + */ + function xmlProvider() { + + return [ + [ + 'hello', + 'hello', + ], + [ + 'hello', + 'hello' + ], + [ + 'hello', + 'hello' + ], + [ + 'hello', + 'hello' + ], + [ + 'hello', + 'hello', + 'hello', + ], + [ + 'hello', + 'hello', + 'hello', + ], + [ + 'hello', + 'hello', + 'hello', + ], + [ + 'hello', + 'hello', + 'hello', + ], + [ + '', + '', + '', + ], + [ + '', + '', + '', + ], + ]; + + } + + /** + * @dataProvider xmlProvider + */ + function testSerialize($expectedFallback, $input, $expected = null) { + + if (is_null($expected)) { + $expected = $expectedFallback; + } + + $writer = new Writer(); + $writer->namespaceMap = [ + 'http://sabredav.org/ns' => null + ]; + $writer->openMemory(); + $writer->startDocument('1.0'); + //$writer->setIndent(true); + $writer->write([ + '{http://sabredav.org/ns}root' => [ + '{http://sabredav.org/ns}fragment' => new XmlFragment($input), + ], + ]); + + $output = $writer->outputMemory(); + + $expected = << +$expected +XML; + + $this->assertEquals($expected, $output); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/InfiteLoopTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/InfiteLoopTest.php new file mode 100644 index 00000000000..ec8a136d092 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/InfiteLoopTest.php @@ -0,0 +1,50 @@ + + + + +'; + + $reader = new Reader(); + $reader->elementMap = [ + '{DAV:}set' => 'Sabre\\Xml\\Element\\KeyValue', + ]; + $reader->xml($body); + + $output = $reader->parse(); + + $this->assertEquals([ + 'name' => '{DAV:}propertyupdate', + 'value' => [ + [ + 'name' => '{DAV:}set', + 'value' => [ + '{DAV:}prop' => null, + ], + 'attributes' => [], + ], + [ + 'name' => '{DAV:}set', + 'value' => [ + '{DAV:}prop' => null, + ], + 'attributes' => [], + ], + ], + 'attributes' => [], + ], $output); + + } + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/ReaderTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/ReaderTest.php new file mode 100644 index 00000000000..8da81d1202a --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/ReaderTest.php @@ -0,0 +1,585 @@ + + +BLA; + $reader = new Reader(); + $reader->xml($input); + + $reader->next(); + + $this->assertEquals('{http://sabredav.org/ns}root', $reader->getClark()); + + } + + function testGetClarkNoNS() { + + $input = << + +BLA; + $reader = new Reader(); + $reader->xml($input); + + $reader->next(); + + $this->assertEquals('{}root', $reader->getClark()); + + } + + function testGetClarkNotOnAnElement() { + + $input = << + +BLA; + $reader = new Reader(); + $reader->xml($input); + + $this->assertNull($reader->getClark()); + } + + function testSimple() { + + $input = << + + + + Hi! + + +BLA; + + $reader = new Reader(); + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => null, + 'attributes' => [ + 'attr' => 'val', + ], + ], + [ + 'name' => '{http://sabredav.org/ns}elem2', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem3', + 'value' => 'Hi!', + 'attributes' => [], + ], + ], + 'attributes' => [], + ], + + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + function testCDATA() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}foo', + 'value' => 'bar', + 'attributes' => [], + ], + + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + function testSimpleNamespacedAttribute() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => null, + 'attributes' => [ + '{urn:foo}attr' => 'val', + ], + ], + ], + 'attributes' => [], + ]; + + $this->assertEquals($expected, $output); + + } + + function testMappedElement() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => 'Sabre\\Xml\\Element\\Mock' + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'foobar', + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + /** + * @expectedException \LogicException + */ + function testMappedElementBadClass() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => new \StdClass() + ]; + $reader->xml($input); + + $reader->parse(); + } + + /** + * @depends testMappedElement + */ + function testMappedElementCallBack() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => function(Reader $reader) { + $reader->next(); + return 'foobar'; + } + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'foobar', + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + /** + * @depends testMappedElementCallBack + */ + function testMappedElementCallBackNoNamespace() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + 'elem1' => function(Reader $reader) { + $reader->next(); + return 'foobar'; + } + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{}root', + 'value' => [ + [ + 'name' => '{}elem1', + 'value' => 'foobar', + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + /** + * @depends testMappedElementCallBack + */ + function testReadText() { + + $input = << + + + hello + world + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => function(Reader $reader) { + return $reader->readText(); + } + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'hello world', + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + function testParseProblem() { + + $input = << + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => 'Sabre\\Xml\\Element\\Mock' + ]; + $reader->xml($input); + + try { + $output = $reader->parse(); + $this->fail('We expected a ParseException to be thrown'); + } catch (LibXMLException $e) { + + $this->assertInternalType('array', $e->getErrors()); + + } + + } + + /** + * @expectedException \Sabre\Xml\ParseException + */ + function testBrokenParserClass() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => 'Sabre\\Xml\\Element\\Eater' + ]; + $reader->xml($input); + $reader->parse(); + + + } + + /** + * Test was added for Issue #10. + * + * @expectedException Sabre\Xml\LibXMLException + */ + function testBrokenXml() { + + $input = << + + + +BLA; + + $reader = new Reader(); + $reader->xml($input); + $reader->parse(); + + } + + /** + * Test was added for Issue #45. + * + * @expectedException Sabre\Xml\LibXMLException + */ + function testBrokenXml2() { + + $input = << + + + + + + ""Administrative w"> + + + + xml($input); + $reader->parse(); + + } + + + /** + * @depends testMappedElement + */ + function testParseInnerTree() { + + $input = << + + + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => function(Reader $reader) { + + $innerTree = $reader->parseInnerTree(['{http://sabredav.org/ns}elem1' => function(Reader $reader) { + $reader->next(); + return "foobar"; + }]); + + return $innerTree; + } + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'foobar', + 'attributes' => [], + ] + ], + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + /** + * @depends testParseInnerTree + */ + function testParseGetElements() { + + $input = << + + + + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => function(Reader $reader) { + + $innerTree = $reader->parseGetElements(['{http://sabredav.org/ns}elem1' => function(Reader $reader) { + $reader->next(); + return "foobar"; + }]); + + return $innerTree; + } + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'foobar', + 'attributes' => [], + ] + ], + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + /** + * @depends testParseInnerTree + */ + function testParseGetElementsNoElements() { + + $input = << + + + hi + + +BLA; + + $reader = new Reader(); + $reader->elementMap = [ + '{http://sabredav.org/ns}elem1' => function(Reader $reader) { + + $innerTree = $reader->parseGetElements(['{http://sabredav.org/ns}elem1' => function(Reader $reader) { + $reader->next(); + return "foobar"; + }]); + + return $innerTree; + } + ]; + $reader->xml($input); + + $output = $reader->parse(); + + $expected = [ + 'name' => '{http://sabredav.org/ns}root', + 'value' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => [], + 'attributes' => [], + ], + ], + 'attributes' => [], + + ]; + + $this->assertEquals($expected, $output); + + } + + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Serializer/EnumTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Serializer/EnumTest.php new file mode 100644 index 00000000000..2d26e665a2a --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Serializer/EnumTest.php @@ -0,0 +1,36 @@ +namespaceMap['urn:test'] = null; + + $xml = $service->write('{urn:test}root', function($writer) { + enum($writer, [ + '{urn:test}foo1', + '{urn:test}foo2', + ]); + }); + + $expected = << + + + + +XML; + + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + + } + + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Serializer/RepeatingElementsTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Serializer/RepeatingElementsTest.php new file mode 100644 index 00000000000..dbca65a572a --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/Serializer/RepeatingElementsTest.php @@ -0,0 +1,35 @@ +namespaceMap['urn:test'] = null; + $xml = $service->write('{urn:test}collection', function($writer) { + repeatingElements($writer, [ + 'foo', + 'bar', + ], '{urn:test}item'); + }); + + $expected = << + + foo + bar + +XML; + + + $this->assertXmlStringEqualsXmlString($expected, $xml); + + + } + + +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/ServiceTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/ServiceTest.php new file mode 100644 index 00000000000..e6fcf149926 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/ServiceTest.php @@ -0,0 +1,328 @@ + 'Test!', + ]; + + $util = new Service(); + $util->elementMap = $elems; + + $reader = $util->getReader(); + $this->assertInstanceOf('Sabre\\Xml\\Reader', $reader); + $this->assertEquals($elems, $reader->elementMap); + + } + + function testGetWriter() { + + $ns = [ + 'http://sabre.io/ns' => 's', + ]; + + $util = new Service(); + $util->namespaceMap = $ns; + + $writer = $util->getWriter(); + $this->assertInstanceOf('Sabre\\Xml\\Writer', $writer); + $this->assertEquals($ns, $writer->namespaceMap); + + } + + /** + * @depends testGetReader + */ + function testParse() { + + $xml = << + value + +XML; + $util = new Service(); + $result = $util->parse($xml, null, $rootElement); + $this->assertEquals('{http://sabre.io/ns}root', $rootElement); + + $expected = [ + [ + 'name' => '{http://sabre.io/ns}child', + 'value' => 'value', + 'attributes' => [], + ] + ]; + + $this->assertEquals( + $expected, + $result + ); + + } + + /** + * @depends testGetReader + */ + function testParseStream() { + + $xml = << + value + +XML; + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $xml); + rewind($stream); + + $util = new Service(); + $result = $util->parse($stream, null, $rootElement); + $this->assertEquals('{http://sabre.io/ns}root', $rootElement); + + $expected = [ + [ + 'name' => '{http://sabre.io/ns}child', + 'value' => 'value', + 'attributes' => [], + ] + ]; + + $this->assertEquals( + $expected, + $result + ); + + } + + /** + * @depends testGetReader + */ + function testExpect() { + + $xml = << + value + +XML; + $util = new Service(); + $result = $util->expect('{http://sabre.io/ns}root', $xml); + + $expected = [ + [ + 'name' => '{http://sabre.io/ns}child', + 'value' => 'value', + 'attributes' => [], + ] + ]; + + $this->assertEquals( + $expected, + $result + ); + } + + /** + * @depends testGetReader + */ + function testExpectStream() { + + $xml = << + value + +XML; + + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $xml); + rewind($stream); + + $util = new Service(); + $result = $util->expect('{http://sabre.io/ns}root', $stream); + + $expected = [ + [ + 'name' => '{http://sabre.io/ns}child', + 'value' => 'value', + 'attributes' => [], + ] + ]; + + $this->assertEquals( + $expected, + $result + ); + } + + /** + * @depends testGetReader + * @expectedException \Sabre\Xml\ParseException + */ + function testExpectWrong() { + + $xml = << + value + +XML; + $util = new Service(); + $util->expect('{http://sabre.io/ns}error', $xml); + + } + + /** + * @depends testGetWriter + */ + function testWrite() { + + $util = new Service(); + $util->namespaceMap = [ + 'http://sabre.io/ns' => 's', + ]; + $result = $util->write('{http://sabre.io/ns}root', [ + '{http://sabre.io/ns}child' => 'value', + ]); + + $expected = << + + value + + +XML; + $this->assertEquals( + $expected, + $result + ); + + } + + function testMapValueObject() { + + $input = << + + 1234 + 99.99 + black friday deal + + 5 + + + + +XML; + + $ns = 'http://sabredav.org/ns'; + $orderService = new \Sabre\Xml\Service(); + $orderService->mapValueObject('{' . $ns . '}order', 'Sabre\Xml\Order'); + $orderService->mapValueObject('{' . $ns . '}status', 'Sabre\Xml\OrderStatus'); + $orderService->namespaceMap[$ns] = null; + + $order = $orderService->parse($input); + $expected = new Order(); + $expected->id = 1234; + $expected->amount = 99.99; + $expected->description = 'black friday deal'; + $expected->status = new OrderStatus(); + $expected->status->id = 5; + $expected->status->label = 'processed'; + + $this->assertEquals($expected, $order); + + $writtenXml = $orderService->writeValueObject($order); + $this->assertEquals($input, $writtenXml); + } + + function testMapValueObjectArrayProperty() { + + $input = << + + 1234 + 99.99 + black friday deal + + 5 + + + http://example.org/ + http://example.com/ + + +XML; + + $ns = 'http://sabredav.org/ns'; + $orderService = new \Sabre\Xml\Service(); + $orderService->mapValueObject('{' . $ns . '}order', 'Sabre\Xml\Order'); + $orderService->mapValueObject('{' . $ns . '}status', 'Sabre\Xml\OrderStatus'); + $orderService->namespaceMap[$ns] = null; + + $order = $orderService->parse($input); + $expected = new Order(); + $expected->id = 1234; + $expected->amount = 99.99; + $expected->description = 'black friday deal'; + $expected->status = new OrderStatus(); + $expected->status->id = 5; + $expected->status->label = 'processed'; + $expected->link = ['http://example.org/', 'http://example.com/']; + + $this->assertEquals($expected, $order); + + $writtenXml = $orderService->writeValueObject($order); + $this->assertEquals($input, $writtenXml); + } + + /** + * @expectedException \InvalidArgumentException + */ + function testWriteVoNotFound() { + + $service = new Service(); + $service->writeValueObject(new \StdClass()); + + } + + function testParseClarkNotation() { + + $this->assertEquals([ + 'http://sabredav.org/ns', + 'elem', + ], Service::parseClarkNotation('{http://sabredav.org/ns}elem')); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testParseClarkNotationFail() { + + Service::parseClarkNotation('http://sabredav.org/ns}elem'); + + } + +} + +/** + * asset for testMapValueObject() + * @internal + */ +class Order { + public $id; + public $amount; + public $description; + public $status; + public $empty; + public $link = []; +} + +/** + * asset for testMapValueObject() + * @internal + */ +class OrderStatus { + public $id; + public $label; +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/WriterTest.php b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/WriterTest.php new file mode 100644 index 00000000000..574d8023700 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/Sabre/Xml/WriterTest.php @@ -0,0 +1,439 @@ +writer = new Writer(); + $this->writer->namespaceMap = [ + 'http://sabredav.org/ns' => 's', + ]; + $this->writer->openMemory(); + $this->writer->setIndent(true); + $this->writer->startDocument(); + + } + + function compare($input, $output) { + + $this->writer->write($input); + $this->assertEquals($output, $this->writer->outputMemory()); + + } + + + function testSimple() { + + $this->compare([ + '{http://sabredav.org/ns}root' => 'text', + ], << +text + +HI + ); + + } + + /** + * @depends testSimple + */ + function testSimpleQuotes() { + + $this->compare([ + '{http://sabredav.org/ns}root' => '"text"', + ], << +"text" + +HI + ); + + } + + function testSimpleAttributes() { + + $this->compare([ + '{http://sabredav.org/ns}root' => [ + 'value' => 'text', + 'attributes' => [ + 'attr1' => 'attribute value', + ], + ], + ], << +text + +HI + ); + + } + function testMixedSyntax() { + $this->compare([ + '{http://sabredav.org/ns}root' => [ + '{http://sabredav.org/ns}single' => 'value', + '{http://sabredav.org/ns}multiple' => [ + [ + 'name' => '{http://sabredav.org/ns}foo', + 'value' => 'bar', + ], + [ + 'name' => '{http://sabredav.org/ns}foo', + 'value' => 'foobar', + ], + ], + [ + 'name' => '{http://sabredav.org/ns}attributes', + 'value' => null, + 'attributes' => [ + 'foo' => 'bar', + ], + ], + [ + 'name' => '{http://sabredav.org/ns}verbose', + 'value' => 'syntax', + 'attributes' => [ + 'foo' => 'bar', + ], + ], + ], + ], << + + value + + bar + foobar + + + syntax + + +HI + ); + } + + function testNull() { + + $this->compare([ + '{http://sabredav.org/ns}root' => null, + ], << + + +HI + ); + + } + + function testArrayFormat2() { + + $this->compare([ + '{http://sabredav.org/ns}root' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'text', + 'attributes' => [ + 'attr1' => 'attribute value', + ], + ], + ], + ], << + + text + + +HI + ); + + } + + function testArrayOfValues() { + + $this->compare([ + '{http://sabredav.org/ns}root' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => [ + 'foo', + 'bar', + 'baz', + ], + ], + ], + ], << + + foobarbaz + + +HI + ); + + } + + /** + * @depends testArrayFormat2 + */ + function testArrayFormat2NoValue() { + + $this->compare([ + '{http://sabredav.org/ns}root' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'attributes' => [ + 'attr1' => 'attribute value', + ], + ], + ], + ], << + + + + +HI + ); + + } + + function testCustomNamespace() { + + $this->compare([ + '{http://sabredav.org/ns}root' => [ + '{urn:foo}elem1' => 'bar', + ], + ], << + + bar + + +HI + ); + + } + + function testEmptyNamespace() { + + // Empty namespaces are allowed, so we should support this. + $this->compare([ + '{http://sabredav.org/ns}root' => [ + '{}elem1' => 'bar', + ], + ], << + + bar + + +HI + ); + + } + + function testAttributes() { + + $this->compare([ + '{http://sabredav.org/ns}root' => [ + [ + 'name' => '{http://sabredav.org/ns}elem1', + 'value' => 'text', + 'attributes' => [ + 'attr1' => 'val1', + '{http://sabredav.org/ns}attr2' => 'val2', + '{urn:foo}attr3' => 'val3', + ], + ], + ], + ], << + + text + + +HI + ); + + } + + function testBaseElement() { + + $this->compare([ + '{http://sabredav.org/ns}root' => new Element\Base('hello') + ], << +hello + +HI + ); + + } + + function testElementObj() { + + $this->compare([ + '{http://sabredav.org/ns}root' => new Element\Mock() + ], << + + hiiii! + + +HI + ); + + } + + function testEmptyNamespacePrefix() { + + $this->writer->namespaceMap['http://sabredav.org/ns'] = null; + $this->compare([ + '{http://sabredav.org/ns}root' => new Element\Mock() + ], << + + hiiii! + + +HI + ); + + } + + function testEmptyNamespacePrefixEmptyString() { + + $this->writer->namespaceMap['http://sabredav.org/ns'] = ''; + $this->compare([ + '{http://sabredav.org/ns}root' => new Element\Mock() + ], << + + hiiii! + + +HI + ); + + } + + function testWriteElement() { + + $this->writer->writeElement("{http://sabredav.org/ns}foo", 'content'); + + $output = << +content + +HI; + + $this->assertEquals($output, $this->writer->outputMemory()); + + + } + + function testWriteElementComplex() { + + $this->writer->writeElement("{http://sabredav.org/ns}foo", new Element\KeyValue(['{http://sabredav.org/ns}bar' => 'test'])); + + $output = << + + test + + +HI; + + $this->assertEquals($output, $this->writer->outputMemory()); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testWriteBadObject() { + + $this->writer->write(new \StdClass()); + + } + + function testStartElementSimple() { + + $this->writer->startElement("foo"); + $this->writer->endElement(); + + $output = << + + +HI; + + $this->assertEquals($output, $this->writer->outputMemory()); + + } + + function testCallback() { + + $this->compare([ + '{http://sabredav.org/ns}root' => function(Writer $writer) { + $writer->text('deferred writer'); + }, + ], << +deferred writer + +HI + ); + + } + + /** + * @expectedException \InvalidArgumentException + */ + function testResource() { + + $this->compare([ + '{http://sabredav.org/ns}root' => fopen('php://memory', 'r'), + ], << +deferred writer + +HI + ); + + } + + function testClassMap() { + + $obj = (object)[ + 'key1' => 'value1', + 'key2' => 'value2', + ]; + + $this->writer->classMap['stdClass'] = function(Writer $writer, $value) { + + foreach (get_object_vars($value) as $key => $val) { + $writer->writeElement('{http://sabredav.org/ns}' . $key, $val); + } + + }; + + $this->compare([ + '{http://sabredav.org/ns}root' => $obj + ], << + + value1 + value2 + + +HI + ); + + } +} diff --git a/htdocs/includes/sabre/sabre/xml/tests/phpcs/ruleset.xml b/htdocs/includes/sabre/sabre/xml/tests/phpcs/ruleset.xml new file mode 100644 index 00000000000..07acb89eea0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/phpcs/ruleset.xml @@ -0,0 +1,56 @@ + + + sabre.io codesniffer ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/htdocs/includes/sabre/sabre/xml/tests/phpunit.xml.dist b/htdocs/includes/sabre/sabre/xml/tests/phpunit.xml.dist new file mode 100644 index 00000000000..fe8b2359aa6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/xml/tests/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + Sabre/ + + + + + ../lib/ + + + diff --git a/htdocs/includes/stripe/.gitignore b/htdocs/includes/stripe/.gitignore index 93251c95077..17b94183baf 100644 --- a/htdocs/includes/stripe/.gitignore +++ b/htdocs/includes/stripe/.gitignore @@ -12,3 +12,6 @@ composer.lock # Ignore PHPUnit coverage file clover.xml + +# Ignore IDE's configuration files +.idea diff --git a/htdocs/includes/stripe/.travis.yml b/htdocs/includes/stripe/.travis.yml index 934b517bfea..d575ed51a7d 100644 --- a/htdocs/includes/stripe/.travis.yml +++ b/htdocs/includes/stripe/.travis.yml @@ -1,18 +1,40 @@ +sudo: false + language: php php: - - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 - 7.1 + - 7.2 - hhvm env: - - AUTOLOAD=1 - - AUTOLOAD=0 + global: + - STRIPE_MOCK_VERSION=0.8.0 + matrix: + - AUTOLOAD=1 + - AUTOLOAD=0 + +cache: + directories: + - $HOME/.composer/cache/files + - stripe-mock + +before_install: + # Unpack and start stripe-mock so that the test suite can talk to it + - | + if [ ! -d "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}" ]; then + mkdir -p stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/ + curl -L "https://github.com/stripe/stripe-mock/releases/download/v${STRIPE_MOCK_VERSION}/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -o "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" + tar -zxf "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -C "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/" + fi + - | + stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/stripe-mock > /dev/null & + STRIPE_MOCK_PID=$! script: ./build.php ${AUTOLOAD} + after_script: ./vendor/bin/coveralls -v -sudo: false diff --git a/htdocs/includes/stripe/CHANGELOG.md b/htdocs/includes/stripe/CHANGELOG.md index 3650c21d1c2..3f0fdfce153 100644 --- a/htdocs/includes/stripe/CHANGELOG.md +++ b/htdocs/includes/stripe/CHANGELOG.md @@ -1,237 +1,315 @@ -### 4.7.0 2017-04-10 +# Changelog +## 6.4.1 - 2018-03-02 +* [#455](https://github.com/stripe/stripe-php/pull/455) Fix namespaces in PHPDoc +* [#456](https://github.com/stripe/stripe-php/pull/456) Fix namespaces for some exceptions + +## 6.4.0 - 2018-02-28 +* [#453](https://github.com/stripe/stripe-php/pull/453) Add constants for `reason` (`REASON_*`) and `status` (`STATUS_*`) on `\Stripe\Dispute` + +## 6.3.2 - 2018-02-27 +* [#452](https://github.com/stripe/stripe-php/pull/452) Add PHPDoc for `amount_paid` and `amount_remaining` on `\Stripe\Invoice` + +## 6.3.1 - 2018-02-26 +* [#443](https://github.com/stripe/stripe-php/pull/443) Add event types as constants to `\Stripe\Event` class + +## 6.3.0 - 2018-02-23 +* [#450](https://github.com/stripe/stripe-php/pull/450) Add support for `code` attribute on all Stripe exceptions + +## 6.2.0 - 2018-02-21 +* [#440](https://github.com/stripe/stripe-php/pull/440) Add support for topups +* [#442](https://github.com/stripe/stripe-php/pull/442) Fix PHPDoc for `\Stripe\Error\SignatureVerification` + +## 6.1.0 - 2018-02-12 +* [#435](https://github.com/stripe/stripe-php/pull/435) Fix header persistence on `Collection` objects +* [#436](https://github.com/stripe/stripe-php/pull/436) Introduce new `Idempotency` error class + +## 6.0.0 - 2018-02-07 +Major version release. List of backwards incompatible changes to watch out for: ++ The minimum PHP version is now 5.4.0. If you're using PHP 5.3 or older, consider upgrading to a more recent version. +* `\Stripe\AttachedObject` no longer exists. Attributes that used to be instances of `\Stripe\AttachedObject` (such as `metadata`) are now instances of `\Stripe\StripeObject`. ++ Attributes that used to be PHP arrays (such as `legal_entity->additional_owners` on `\Stripe\Account` instances) are now instances of `\Stripe\StripeObject`, except when they are empty. `\Stripe\StripeObject` has array semantics so this should not be an issue unless you are actively checking types. +* `\Stripe\Collection` now derives from `\Stripe\StripeObject` rather than from `\Stripe\ApiResource`. + +Pull requests included in this release: +* [#410](https://github.com/stripe/stripe-php/pull/410) Drop support for PHP 5.3 +* [#411](https://github.com/stripe/stripe-php/pull/411) Use traits for common API operations +* [#414](https://github.com/stripe/stripe-php/pull/414) Use short array syntax +* [#404](https://github.com/stripe/stripe-php/pull/404) Fix serialization logic +* [#417](https://github.com/stripe/stripe-php/pull/417) Remove `ExternalAccount` class +* [#418](https://github.com/stripe/stripe-php/pull/418) Increase test coverage +* [#421](https://github.com/stripe/stripe-php/pull/421) Update CA bundle and add script for future updates +* [#422](https://github.com/stripe/stripe-php/pull/422) Use vendored CA bundle for all requests +* [#428](https://github.com/stripe/stripe-php/pull/428) Support for automatic request retries + +## 5.9.2 - 2018-02-07 +* [#431](https://github.com/stripe/stripe-php/pull/431) Update PHPDoc @property tags for latest API version + +## 5.9.1 - 2018-02-06 +* [#427](https://github.com/stripe/stripe-php/pull/427) Add and update PHPDoc @property tags on all API resources + +## 5.9.0 - 2018-01-17 +* [#421](https://github.com/stripe/stripe-php/pull/421) Updated bundled CA certificates +* [#423](https://github.com/stripe/stripe-php/pull/423) Escape unsanitized input in OAuth example + +## 5.8.0 - 2017-12-20 +* [#403](https://github.com/stripe/stripe-php/pull/403) Add `__debugInfo()` magic method to `StripeObject` + +## 5.7.0 - 2017-11-28 +* [#390](https://github.com/stripe/stripe-php/pull/390) Remove some unsupported API methods +* [#391](https://github.com/stripe/stripe-php/pull/391) Alphabetize the list of API resources in `Util::convertToStripeObject()` and add missing resources +* [#393](https://github.com/stripe/stripe-php/pull/393) Fix expiry date update for card sources + +## 5.6.0 - 2017-10-31 +* [#386](https://github.com/stripe/stripe-php/pull/386) Support for exchange rates APIs + +## 5.5.1 - 2017-10-30 +* [#387](https://github.com/stripe/stripe-php/pull/387) Allow `personal_address_kana` and `personal_address_kanji` to be updated on an account + +## 5.5.0 - 2017-10-27 +* [#385](https://github.com/stripe/stripe-php/pull/385) Support for listing source transactions + +## 5.4.0 - 2017-10-24 +* [#383](https://github.com/stripe/stripe-php/pull/383) Add static methods to manipulate resources from parent + * `Account` gains methods for external accounts and login links (e.g. `createExternalAccount`, `createLoginLink`) + * `ApplicationFee` gains methods for refunds + * `Customer` gains methods for sources + * `Transfer` gains methods for reversals + +## 5.3.0 - 2017-10-11 +* [#378](https://github.com/stripe/stripe-php/pull/378) Rename source `delete` to `detach` (and deprecate the former) + +## 5.2.3 - 2017-09-27 +* Add PHPDoc for `Card` + +## 5.2.2 - 2017-09-20 +* Fix deserialization mapping of `FileUpload` objects + +## 5.2.1 - 2017-09-14 +* Serialized `shipping` nested attribute + +## 5.2.0 - 2017-08-29 +* Add support for `InvalidClient` OAuth error + +## 5.1.3 - 2017-08-14 +* Allow `address_kana` and `address_kanji` to be updated for custom accounts + +## 5.1.2 - 2017-08-01 +* Fix documented return type of `autoPagingIterator()` (was missing namespace) + +## 5.1.1 - 2017-07-03 +* Fix order returns to use the right URL `/v1/order_returns` + +## 5.1.0 - 2017-06-30 +* Add support for OAuth + +## 5.0.0 - 2017-06-27 +* `pay` on invoice now takes params as well as opts + +## 4.13.0 - 2017-06-19 +* Add support for ephemeral keys + +## 4.12.0 - 2017-06-05 +* Clients can implement `getUserAgentInfo()` to add additional user agent information + +## 4.11.0 - 2017-06-05 +* Implement `Countable` for `AttachedObject` (`metadata` and `additional_owners`) + +## 4.10.0 - 2017-05-25 +* Add support for login links + +## 4.9.1 - 2017-05-10 +* Fix docs to include arrays on `$id` parameter for retrieve methods + +## 4.9.0 - 2017-04-28 +* Support for checking webhook signatures + +## 4.8.1 - 2017-04-24 +* Allow nested field `payout_schedule` to be updated + +## 4.8.0 - 2017-04-20 +* Add `\Stripe\Stripe::setLogger()` to support an external PSR-3 compatible logger + +## 4.7.0 - 2017-04-10 * Add support for payouts and recipient transfers -### 4.6.0 2017-04-06 - +## 4.6.0 - 2017-04-06 * Please see 4.7.0 instead (no-op release) -### 4.5.1 2017-03-22 - +## 4.5.1 - 2017-03-22 * Remove hard dependency on cURL -### 4.5.0 2017-03-20 - +## 4.5.0 - 2017-03-20 * Support for detaching sources from customers -### 4.4.2 2017-02-27 - +## 4.4.2 - 2017-02-27 * Correct handling of `owner` parameter when updating sources -### 4.4.1 2017-02-24 - +## 4.4.1 - 2017-02-24 * Correct the error check on a bad JSON decoding -### 4.4.0 2017-01-18 - +## 4.4.0 - 2017-01-18 * Add support for updating sources -### 4.3.0 2016-11-30 - +## 4.3.0 - 2016-11-30 * Add support for verifying sources -### 4.2.0 2016-11-21 - +## 4.2.0 - 2016-11-21 * Add retrieve method for 3-D Secure resources -### 4.1.1 2016-10-21 - +## 4.1.1 - 2016-10-21 * Add docblock with model properties for `Plan` -### 4.1.0 2016-10-18 - +## 4.1.0 - 2016-10-18 * Support for 403 status codes (permission denied) -### 4.0.1 2016-10-17 - +## 4.0.1 - 2016-10-17 * Fix transfer reversal materialization * Fixes for some property definitions in docblocks -### 4.0.0 2016-09-28 - +## 4.0.0 - 2016-09-28 * Support for subscription items * Drop attempt to force TLS 1.2: please note that this could be breaking if you're using old OS distributions or packages and upgraded recently (so please make sure to test your integration!) -### 3.23.0 2016-09-15 - +## 3.23.0 - 2016-09-15 * Add support for Apple Pay domains -### 3.22.0 2016-09-13 - +## 3.22.0 - 2016-09-13 * Add `Stripe::setAppInfo` to allow plugins to register user agent information -### 3.21.0 2016-08-25 - +## 3.21.0 - 2016-08-25 * Add `Source` model for generic payment sources -### 3.20.0 2016-08-08 - +## 3.20.0 - 2016-08-08 * Add `getDeclineCode` to card errors -### 3.19.0 2016-07-29 - +## 3.19.0 - 2016-07-29 * Opt requests directly into TLS 1.2 where OpenSSL >= 1.0.1 (see #277 for context) -### 3.18.0 2016-07-28 - +## 3.18.0 - 2016-07-28 * Add new `STATUS_` constants for subscriptions -### 3.17.1 2016-07-28 - +## 3.17.1 - 2016-07-28 * Fix auto-paging iterator so that it plays nicely with `iterator_to_array` -### 3.17.0 2016-07-14 - +## 3.17.0 - 2016-07-14 * Add field annotations to model classes for better editor hinting -### 3.16.0 2016-07-12 - +## 3.16.0 - 2016-07-12 * Add `ThreeDSecure` model for 3-D secure payments -### 3.15.0 2016-06-29 - +## 3.15.0 - 2016-06-29 * Add static `update` method to all resources that can be changed. -### 3.14.3 2016-06-20 - +## 3.14.3 - 2016-06-20 * Make sure that cURL never sends `Expects: 100-continue`, even on large request bodies -### 3.14.2 2016-06-03 - +## 3.14.2 - 2016-06-03 * Add `inventory` under `SKU` to list of keys that have nested data and can be updated -### 3.14.1 2016-05-27 - +## 3.14.1 - 2016-05-27 * Fix some inconsistencies in PHPDoc -### 3.14.0 2016-05-25 - +## 3.14.0 - 2016-05-25 * Add support for returning Relay orders -### 3.13.0 2016-05-04 - +## 3.13.0 - 2016-05-04 * Add `list`, `create`, `update`, `retrieve`, and `delete` methods to the Subscription class -### 3.12.1 2016-04-07 - +## 3.12.1 - 2016-04-07 * Additional check on value arrays for some extra safety -### 3.12.0 2016-03-31 - +## 3.12.0 - 2016-03-31 * Fix bug `refreshFrom` on `StripeObject` would not take an `$opts` array * Fix bug where `$opts` not passed to parent `save` method in `Account` * Fix bug where non-existent variable was referenced in `reverse` in `Transfer` * Update CA cert bundle for compatibility with OpenSSL versions below 1.0.1 -### 3.11.0 2016-03-22 - +## 3.11.0 - 2016-03-22 * Allow `CurlClient` to be initialized with default `CURLOPT_*` options -### 3.10.1 2016-03-22 - +## 3.10.1 - 2016-03-22 * Fix bug where request params and options were ignored in `ApplicationFee`'s `refund.` -### 3.10.0 2016-03-15 - +## 3.10.0 - 2016-03-15 * Add `reject` on `Account` to support the new API feature -### 3.9.2 2016-03-04 - +## 3.9.2 - 2016-03-04 * Fix error when an object's metadata is set more than once -### 3.9.1 2016-02-24 - +## 3.9.1 - 2016-02-24 * Fix encoding behavior of nested arrays for requests (see #227) -### 3.9.0 2016-02-09 - +## 3.9.0 - 2016-02-09 * Add automatic pagination mechanism with `autoPagingIterator()` * Allow global account ID to be set with `Stripe::setAccountId()` -### 3.8.0 2016-02-08 - +## 3.8.0 - 2016-02-08 * Add `CountrySpec` model for looking up country payment information -### 3.7.1 2016-02-01 - +## 3.7.1 - 2016-02-01 * Update bundled CA certs -### 3.7.0 2016-01-27 - +## 3.7.0 - 2016-01-27 * Support deleting Relay products and SKUs -### 3.6.0 2016-01-05 - +## 3.6.0 - 2016-01-05 * Allow configuration of HTTP client timeouts -### 3.5.0 2015-12-01 - +## 3.5.0 - 2015-12-01 * Add a verification routine for external accounts -### 3.4.0 2015-09-14 - +## 3.4.0 - 2015-09-14 * Products, SKUs, and Orders -- https://stripe.com/relay -### 3.3.0 2015-09-11 - +## 3.3.0 - 2015-09-11 * Add support for 429 Rate Limit response -### 3.2.0 2015-08-17 - +## 3.2.0 - 2015-08-17 * Add refund listing and retrieval without an associated charge -### 3.1.0 2015-08-03 - +## 3.1.0 - 2015-08-03 * Add dispute listing and retrieval * Add support for manage account deletion -### 3.0.0 2015-07-28 - +## 3.0.0 - 2015-07-28 * Rename `\Stripe\Object` to `\Stripe\StripeObject` (PHP 7 compatibility) * Rename `getCode` and `getParam` in exceptions to `getStripeCode` and `getStripeParam` * Add support for calling `json_encode` on Stripe objects in PHP 5.4+ * Start supporting/testing PHP 7 -### 2.3.0 2015-07-06 - +## 2.3.0 - 2015-07-06 * Add request ID to all Stripe exceptions -### 2.2.0 2015-06-01 - +## 2.2.0 - 2015-06-01 * Add support for Alipay accounts as sources * Add support for bank accounts as sources (private beta) * Add support for bank accounts and cards as external_accounts on Account objects -### 2.1.4 2015-05-13 - +## 2.1.4 - 2015-05-13 * Fix CA certificate file path (thanks @lphilps & @matthewarkin) -### 2.1.3 2015-05-12 - +## 2.1.3 - 2015-05-12 * Fix to account updating to permit `tos_acceptance` and `personal_address` to be set properly * Fix to Transfer reversal creation (thanks @neatness!) * Network requests are now done through a swappable class for easier mocking -### 2.1.2 2015-04-10 - +## 2.1.2 - 2015-04-10 * Remove SSL cert revokation checking (all pre-Heartbleed certs have expired) * Bug fixes to account updating -### 2.1.1 2015-02-27 +## 2.1.1 - 2015-02-27 * Support transfer reversals -### 2.1.0 2015-02-19 - +## 2.1.0 - 2015-02-19 * Support new API version (2015-02-18) * Added Bitcoin Receiever update and delete actions * Edited tests to prefer "source" over "card" as per new API version -### 2.0.1 2015-02-16 - +## 2.0.1 - 2015-02-16 * Fix to fetching endpoints that use a non-default baseUrl (`FileUpload`) -### 2.0.0 2015-02-14 - +## 2.0.0 - 2015-02-14 * Bumped minimum version to 5.3.3 * Switched to Stripe namespace instead of Stripe_ class name prefiexes (thanks @chadicus!) * Switched tests to PHPUnit (thanks @chadicus!) @@ -239,192 +317,134 @@ * Added $opts hash to the end of most methods: this permits passing 'idempotency_key', 'stripe_account', or 'stripe_version'. The last 2 will persist across multiple object loads. * Added support for retrieving Account by ID -### 1.18.0 2015-01-21 - +## 1.18.0 - 2015-01-21 * Support making bitcoin charges through BitcoinReceiver source object -### 1.17.5 2014-12-23 - +## 1.17.5 - 2014-12-23 * Adding support for creating file uploads. -### 1.17.4 2014-12-15 - +## 1.17.4 - 2014-12-15 * Saving objects fetched with a custom key now works (thanks @JustinHook & @jpasilan) * Added methods for reporting charges as safe or fraudulent and for specifying the reason for refunds -### 1.17.3 2014-11-06 - +## 1.17.3 - 2014-11-06 * Better handling of HHVM support for SSL certificate blacklist checking. -### 1.17.2 2014-09-23 - +## 1.17.2 - 2014-09-23 * Coupons now are backed by a `Stripe_Coupon` instead of `Stripe_Object`, and support updating metadata * Running operations (`create`, `retrieve`, `all`) on upcoming invoice items now works -### 1.17.1 2014-07-31 - +## 1.17.1 - 2014-07-31 * Requests now send Content-Type header -### 1.17.0 2014-07-29 - +## 1.17.0 - 2014-07-29 * Application Fee refunds now a list instead of array * HHVM now works * Small bug fixes (thanks @bencromwell & @fastest963) -* __toString now returns the name of the object in addition to its JSON representation - -### 1.16.0 2014-06-17 +* `__toString` now returns the name of the object in addition to its JSON representation +## 1.16.0 - 2014-06-17 * Add metadata for refunds and disputes -### 1.15.0 2014-05-28 - +## 1.15.0 - 2014-05-28 * Support canceling transfers -### 1.14.1 2014-05-21 - +## 1.14.1 - 2014-05-21 * Support cards for recipients. -### 1.13.1 2014-05-15 - +## 1.13.1 - 2014-05-15 * Fix bug in account resource where `id` wasn't in the result -### 1.13.0 2014-04-10 - +## 1.13.0 - 2014-04-10 * Add support for certificate blacklisting * Update ca bundle * Drop support for HHVM (Temporarily) -### 1.12.0 2014-04-01 - +## 1.12.0 - 2014-04-01 * Add Stripe_RateLimitError for catching rate limit errors. * Update to Zend coding style (thanks, @jpiasetz) -### 1.11.0 2014-01-29 - +## 1.11.0 - 2014-01-29 * Add support for multiple subscriptions per customer -### 1.10.1 2013-12-02 - +## 1.10.1 - 2013-12-02 * Add new ApplicationFee -### 1.9.1 2013-11-08 - +## 1.9.1 - 2013-11-08 * Fix a bug where a null nestable object causes warnings to fire. -### 1.9.0 2013-10-16 - +## 1.9.0 - 2013-10-16 * Add support for metadata API. -### 1.8.4 2013-09-18 - +## 1.8.4 - 2013-09-18 * Add support for closing disputes. -### 1.8.3 2013-08-13 - +## 1.8.3 - 2013-08-13 * Add new Balance and BalanceTransaction -### 1.8.2 2013-08-12 - -* Add support for unsetting attributes by updating to NULL. - Setting properties to a blank string is now an error. - -### 1.8.1 2013-07-12 +## 1.8.2 - 2013-08-12 +* Add support for unsetting attributes by updating to NULL. Setting properties to a blank string is now an error. +## 1.8.1 - 2013-07-12 * Add support for multiple cards API (Stripe API version 2013-07-12: https://stripe.com/docs/upgrades#2013-07-05) -### 1.8.0 2013-04-11 - +## 1.8.0 - 2013-04-11 * Allow Transfers to be creatable * Add new Recipient resource -### 1.7.15 2013-02-21 - +## 1.7.15 - 2013-02-21 * Add 'id' to the list of permanent object attributes -### 1.7.14 2013-02-20 +## 1.7.14 - 2013-02-20 -* Don't re-encode strings that are already encoded in UTF-8. If you - were previously using plan or coupon objects with UTF-8 IDs, they - may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a - 2nd time. You may now need to pass the IDs to utf8_encode before - passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve. -* Ensure that all input is encoded in UTF-8 before submitting it to - Stripe's servers. (github issue #27) +* Don't re-encode strings that are already encoded in UTF-8. If you were previously using plan or coupon objects with UTF-8 IDs, they may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a 2nd time. You may now need to pass the IDs to utf8_encode before passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve. +* Ensure that all input is encoded in UTF-8 before submitting it to Stripe's servers. (github issue #27) -### 1.7.13 2013-02-01 - -* Add support for passing options when retrieving Stripe objects - e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer"))) - Stripe_Charge::retrieve("foo") will continue to work - -### 1.7.12 2013-01-15 +## 1.7.13 - 2013-02-01 +* Add support for passing options when retrieving Stripe objects e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer"))); Stripe_Charge::retrieve("foo") will continue to work +## 1.7.12 - 2013-01-15 * Add support for setting a Stripe API version override -### 1.7.11 2012-12-30 - -* Version bump to cleanup constants and such (github issue #26) - -### 1.7.10 2012-11-08 +## 1.7.11 - 2012-12-30 +* Version bump to cleanup constants and such (fix issue #26) +## 1.7.10 - 2012-11-08 * Add support for updating charge disputes. * Fix bug preventing retrieval of null attributes -### 1.7.9 2012-11-08 +## 1.7.9 - 2012-11-08 +* Fix usage under autoloaders such as the one generated by composer (fix issue #22) -* Fix usage under autoloaders such as the one generated by composer - (github issue #22) - -### 1.7.8 2012-10-30 +## 1.7.8 - 2012-10-30 * Add support for creating invoices. * Add support for new invoice lines return format * Add support for new list objects -### 1.7.7 2012-09-14 - -* Get all of the various version numbers in the repo in sync (no other - changes) - -### 1.7.6 2012-08-31 +## 1.7.7 - 2012-09-14 +* Get all of the various version numbers in the repo in sync (no other changes) +## 1.7.6 - 2012-08-31 * Add update and pay methods to Invoice resource -### 1.7.5 2012-08-23 +## 1.7.5 - 2012-08-23 +* Change internal function names so that Stripe_SingletonApiRequest is E_STRICT-clean (github issue #16) -* Change internal function names so that Stripe_SingletonApiRequest is - E_STRICT-clean (github issue #16) - -### 1.7.4 2012-08-21 - -* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used - in API calls are transparently converted to their object IDs - -### 1.7.3 2012-08-15 +## 1.7.4 - 2012-08-21 +* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used in API calls are transparently converted to their object IDs +## 1.7.3 - 2012-08-15 * Add new Account resource -### 1.7.2 2012-06-26 +## 1.7.2 - 2012-06-26 +* Make clearer that you should be including lib/Stripe.php, not test/Stripe.php (github issue #14) -* Make clearer that you should be including lib/Stripe.php, not - test/Stripe.php (github issue #14) - -### 1.7.1 2012-05-24 - -* Add missing argument to Stripe_InvalidRequestError constructor in - Stripe_ApiResource::instanceUrl. Fixes a warning when - Stripe_ApiResource::instanceUrl is called on a resource with no ID - (github issue #12) - -### 1.7.0 2012-05-17 +## 1.7.1 - 2012-05-24 +* Add missing argument to Stripe_InvalidRequestError constructor in Stripe_ApiResource::instanceUrl. Fixes a warning when Stripe_ApiResource::instanceUrl is called on a resource with no ID (fix issue #12) +## 1.7.0 - 2012-05-17 * Support Composer and Packagist (github issue #9) - * Add new deleteDiscount method to Stripe_Customer - * Add new Transfer resource - -* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will - support Basic auth for the indefinite future, but recommends Bearer - auth when possible going forward) - +* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will support Basic auth for the indefinite future, but recommends Bearer auth when possible going forward) * Numerous test suite improvements diff --git a/htdocs/includes/stripe/README.md b/htdocs/includes/stripe/README.md index 16642900a1f..fa492c45cc4 100644 --- a/htdocs/includes/stripe/README.md +++ b/htdocs/includes/stripe/README.md @@ -10,7 +10,7 @@ You can sign up for a Stripe account at https://stripe.com. ## Requirements -PHP 5.3.3 and later. +PHP 5.4.0 and later. ## Composer @@ -20,7 +20,7 @@ You can install the bindings via [Composer](http://getcomposer.org/). Run the fo composer require stripe/stripe-php ``` -To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/00-intro.md#autoloading): +To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/01-basic-usage.md#autoloading): ```php require_once('vendor/autoload.php'); @@ -36,7 +36,7 @@ require_once('/path/to/stripe-php/init.php'); ## Dependencies -The bindings require the following extension in order to work properly: +The bindings require the following extensions in order to work properly: - [`curl`](https://secure.php.net/manual/en/book.curl.php), although you can use your own non-cURL client if you prefer - [`json`](https://secure.php.net/manual/en/book.json.php) @@ -50,7 +50,7 @@ Simple usage looks like: ```php \Stripe\Stripe::setApiKey('sk_test_BQokikJOvBiI2HlWgH4olfQ2'); -$charge = \Stripe\Charge::create(array('amount' => 2000, 'currency' => 'usd', 'source' => 'tok_189fqt2eZvKYlo2CTGBeg6Uq' )); +$charge = \Stripe\Charge::create(['amount' => 2000, 'currency' => 'usd', 'source' => 'tok_189fqt2eZvKYlo2CTGBeg6Uq']); echo $charge; ``` @@ -60,14 +60,19 @@ Please see https://stripe.com/docs/api for up-to-date documentation. ## Legacy Version Support +### PHP 5.3 + +If you are using PHP 5.3, you can download v5.8.0 ([zip](https://github.com/stripe/stripe-php/archive/v5.8.0.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.8.0.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses. + +### PHP 5.2 + If you are using PHP 5.2, you can download v1.18.0 ([zip](https://github.com/stripe/stripe-php/archive/v1.18.0.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v1.18.0.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses. This legacy version may be included via `require_once("/path/to/stripe-php/lib/Stripe.php");`, and used like: ```php Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249'); -$myCard = array('number' => '4242424242424242', 'exp_month' => 8, 'exp_year' => 2018); -$charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd')); +$charge = Stripe_Charge::create(array('source' => 'tok_XXXXXXXX', 'amount' => 2000, 'currency' => 'usd')); echo $charge; ``` @@ -98,13 +103,32 @@ Need to set a proxy for your requests? Pass in the requisite `CURLOPT_*` array t ```php // set up your tweaked Curl client -$curl = new \Stripe\HttpClient\CurlClient(array(CURLOPT_PROXY => 'proxy.local:80')); +$curl = new \Stripe\HttpClient\CurlClient([CURLOPT_PROXY => 'proxy.local:80']); // tell Stripe to use the tweaked client \Stripe\ApiRequestor::setHttpClient($curl); ``` Alternately, a callable can be passed to the CurlClient constructor that returns the above array based on request inputs. See `testDefaultOptions()` in `tests/CurlClientTest.php` for an example of this behavior. Note that the callable is called at the beginning of every API request, before the request is sent. +### Configuring a Logger + +The library does minimal logging, but it can be configured +with a [`PSR-3` compatible logger][psr3] so that messages +end up there instead of `error_log`: + +```php +\Stripe\Stripe::setLogger($logger); +``` + +### Accessing response data + +You can access the data from the last API response on any object via `getLastResponse()`. + +```php +$charge = \Stripe\Charge::create(['amount' => 2000, 'currency' => 'usd', 'source' => 'tok_visa']); +echo $charge->getLastResponse()->headers['Request-Id']; +``` + ### SSL / TLS compatibility issues Stripe's API now requires that [all connections use TLS 1.2](https://stripe.com/blog/upgrading-tls). Some systems (most notably some older CentOS and RHEL versions) are capable of using TLS 1.2 but will use TLS 1.0 or 1.1 by default. In this case, you'd get an `invalid_request_error` with the following error message: "Stripe no longer supports API requests made with TLS 1.0. Please initiate HTTPS connections with TLS 1.2 or later. You can learn more about this at [https://stripe.com/blog/upgrading-tls](https://stripe.com/blog/upgrading-tls).". @@ -112,10 +136,49 @@ Stripe's API now requires that [all connections use TLS 1.2](https://stripe.com/ The recommended course of action is to [upgrade your cURL and OpenSSL packages](https://support.stripe.com/questions/how-do-i-upgrade-my-stripe-integration-from-tls-1-0-to-tls-1-2#php) so that TLS 1.2 is used by default, but if that is not possible, you might be able to solve the issue by setting the `CURLOPT_SSLVERSION` option to either `CURL_SSLVERSION_TLSv1` or `CURL_SSLVERSION_TLSv1_2`: ```php -$curl = new \Stripe\HttpClient\CurlClient(array(CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1)); +$curl = new \Stripe\HttpClient\CurlClient([CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1]); \Stripe\ApiRequestor::setHttpClient($curl); ``` +### Per-request Configuration + +For apps that need to use multiple keys during the lifetime of a process, like +one that uses [Stripe Connect][connect], it's also possible to set a +per-request key and/or account: + +```php +\Stripe\Charge::all([], [ + 'api_key' => 'sk_test_...', + 'stripe_account' => 'acct_...' +]); + +\Stripe\Charge::retrieve("ch_18atAXCdGbJFKhCuBAa4532Z", [ + 'api_key' => 'sk_test_...', + 'stripe_account' => 'acct_...' +]); +``` + +### Configuring CA Bundles + +By default, the library will use its own internal bundle of known CA +certificates, but it's possible to configure your own: + +```php +\Stripe\Stripe::setCABundlePath("path/to/ca/bundle"); +``` + +### Configuring Automatic Retries + +The library can be configured to automatically retry requests that fail due to +an intermittent network problem: + +```php +\Stripe\Stripe::setMaxNetworkRetries(2); +``` + +[Idempotency keys][idempotency-keys] are added to requests to guarantee that +retries are safe. + ## Development Install dependencies: @@ -124,7 +187,12 @@ Install dependencies: composer install ``` -## Tests +The test suite depends on [stripe-mock], so make sure to fetch and run it from a +background terminal ([stripe-mock's README][stripe-mock] also contains +instructions for installing via Homebrew and other methods): + + go get -u github.com/stripe/stripe-mock + stripe-mock Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), then you can run the test suite: @@ -138,6 +206,12 @@ Or to run an individual test file: ./vendor/bin/phpunit tests/UtilTest.php ``` +Update bundled CA certificates from the [Mozilla cURL release][curl]: + +```bash +./update_certs.php +``` + ## Attention plugin developers Are you writing a plugin that integrates Stripe and embeds our library? Then please use the `setAppInfo` function to identify your plugin. For example: @@ -151,3 +225,9 @@ The method should be called once, before any request is sent to the API. The sec ### SSL / TLS configuration option See the "SSL / TLS compatibility issues" paragraph above for full context. If you want to ensure that your plugin can be used on all systems, you should add a configuration option to let your users choose between different values for `CURLOPT_SSLVERSION`: none (default), `CURL_SSLVERSION_TLSv1` and `CURL_SSLVERSION_TLSv1_2`. + +[connect]: https://stripe.com/connect +[curl]: http://curl.haxx.se/docs/caextract.html +[psr3]: http://www.php-fig.org/psr/psr-3/ +[idempotency-keys]: https://stripe.com/docs/api/php#idempotent_requests +[stripe-mock]: https://github.com/stripe/stripe-mock diff --git a/htdocs/includes/stripe/VERSION b/htdocs/includes/stripe/VERSION index f6cdf40983f..4c77920fd2c 100644 --- a/htdocs/includes/stripe/VERSION +++ b/htdocs/includes/stripe/VERSION @@ -1 +1 @@ -4.7.0 +6.4.1 diff --git a/htdocs/includes/stripe/build.php b/htdocs/includes/stripe/build.php old mode 100755 new mode 100644 index 4401bb4b81f..cd053e2baea --- a/htdocs/includes/stripe/build.php +++ b/htdocs/includes/stripe/build.php @@ -10,7 +10,7 @@ if (!$autoload) { $composer = json_decode(file_get_contents('composer.json'), true); unset($composer['autoload']); unset($composer['require-dev']['squizlabs/php_codesniffer']); - file_put_contents('composer.json', json_encode($composer)); + file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT)); } passthru('composer install', $returnStatus); diff --git a/htdocs/includes/stripe/composer.json b/htdocs/includes/stripe/composer.json index 8cfc38a048d..67473fca421 100644 --- a/htdocs/includes/stripe/composer.json +++ b/htdocs/includes/stripe/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": ">=5.3.3", + "php": ">=5.4.0", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*" diff --git a/htdocs/includes/stripe/data/ca-certificates.crt b/htdocs/includes/stripe/data/ca-certificates.crt index 1c4e5b22673..39ba33683c5 100644 --- a/htdocs/includes/stripe/data/ca-certificates.crt +++ b/htdocs/includes/stripe/data/ca-certificates.crt @@ -1,20 +1,20 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Fri Jan 22 20:39:57 2016 +## Certificate data from Mozilla as of: Wed Sep 20 03:12:05 2017 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates ## file (certdata.txt). This file can be found in the mozilla source tree: -## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt ## ## It contains the certificates in PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with ## an Apache+mod_ssl webserver for SSL client authentication. ## Just configure this file as the SSLCACertificateFile. ## -## Conversion done with mk-ca-bundle.pl version 1.25. -## SHA1: 0ab47e2f41518f8d223eab517cb799e5b071231e +## Conversion done with mk-ca-bundle.pl version 1.27. +## SHA256: 2b2dbe5244e0047e088c597998883a913f6c5fffd1cb5c0fe5a368c8466cb2ec ## @@ -130,30 +130,6 @@ Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- -AddTrust Low-Value Services Root -================================ ------BEGIN CERTIFICATE----- -MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU -cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw -CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO -ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 -54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr -oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 -Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui -GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w -HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD -AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT -RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw -HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt -ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph -iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY -eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr -mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj -ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= ------END CERTIFICATE----- - AddTrust External Root ====================== -----BEGIN CERTIFICATE----- @@ -178,54 +154,6 @@ e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= -----END CERTIFICATE----- -AddTrust Public Services Root -============================= ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU -cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ -BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l -dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu -nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i -d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG -Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw -HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G -A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G -A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 -JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL -+YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao -GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 -Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H -EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= ------END CERTIFICATE----- - -AddTrust Qualified Certificates Root -==================================== ------BEGIN CERTIFICATE----- -MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU -cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx -CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ -IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx -64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 -KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o -L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR -wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU -MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE -BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y -azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD -ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG -GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X -dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze -RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB -iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= ------END CERTIFICATE----- - Entrust Root Certification Authority ==================================== -----BEGIN CERTIFICATE----- @@ -252,27 +180,6 @@ W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- -RSA Security 2048 v3 -==================== ------BEGIN CERTIFICATE----- -MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK -ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy -MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb -BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 -Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb -WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH -KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP -+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ -MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E -FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY -v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj -0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj -VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 -nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA -pKnXwiJPZ9d37CAFYd4= ------END CERTIFICATE----- - GeoTrust Global CA ================== -----BEGIN CERTIFICATE----- @@ -294,27 +201,6 @@ XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm Mw== -----END CERTIFICATE----- -GeoTrust Global CA 2 -==================== ------BEGIN CERTIFICATE----- -MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN -R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw -MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j -LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ -NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k -LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA -Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b -HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH -K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 -srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh -ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL -OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC -x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF -H4z1Ir+rzoPz4iIprn2DQKi6bA== ------END CERTIFICATE----- - GeoTrust Universal CA ===================== -----BEGIN CERTIFICATE----- @@ -440,56 +326,6 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z 12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- -Comodo Secure Services root -=========================== ------BEGIN CERTIFICATE----- -MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS -R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg -TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw -MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu -Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi -BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP -9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc -rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC -oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V -p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E -FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w -gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj -YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm -aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm -4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj -Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL -DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw -pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H -RR3B7Hzs/Sk= ------END CERTIFICATE----- - -Comodo Trusted Services root -============================ ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS -R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg -TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw -MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h -bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw -IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 -3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y -/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 -juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS -ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud -DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp -ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl -cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw -uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 -pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA -BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l -R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O -9y5Xt5hwXsjEeLBi ------END CERTIFICATE----- - QuoVadis Root CA ================ -----BEGIN CERTIFICATE----- @@ -629,54 +465,6 @@ EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH llpwrN9M -----END CERTIFICATE----- -Staat der Nederlanden Root CA -============================= ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE -ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g -Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w -HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh -bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt -vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P -jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca -C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth -vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 -22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV -HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v -dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN -BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR -EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw -MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y -nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR -iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== ------END CERTIFICATE----- - -UTN USERFirst Hardware Root CA -============================== ------BEGIN CERTIFICATE----- -MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE -BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl -IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd -BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx -OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 -eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz -ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI -wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd -tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 -i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf -Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw -gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF -lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF -UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF -BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM -//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW -XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 -lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn -iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 -nfhmqA== ------END CERTIFICATE----- - Camerfirma Chambers of Commerce Root ==================================== -----BEGIN CERTIFICATE----- @@ -731,41 +519,6 @@ IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== -----END CERTIFICATE----- -NetLock Notary (Class A) Root -============================= ------BEGIN CERTIFICATE----- -MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI -EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 -dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j -ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX -DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH -EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD -VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz -cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM -D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ -z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC -/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 -tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 -4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG -A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC -Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv -bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu -IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn -LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 -ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz -IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh -IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu -b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh -bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg -Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp -bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 -ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP -ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB -CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr -KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM -8CgHrTwXZoi1/baI ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -909,38 +662,6 @@ CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy +fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS -----END CERTIFICATE----- -Swisscom Root CA 1 -================== ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG -EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy -dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4 -MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln -aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC -IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM -MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF -NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe -AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC -b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn -7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN -cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp -WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5 -haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY -MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw -HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j -BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9 -MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn -jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ -MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H -VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl -vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl -OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3 -1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq -nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy -x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW -NY6E0F/6MBr1mmz0DlP5OlvRHA== ------END CERTIFICATE----- - DigiCert Assured ID Root CA =========================== -----BEGIN CERTIFICATE----- @@ -1298,33 +1019,6 @@ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey -----END CERTIFICATE----- -WellsSecure Public Root Certificate Authority -============================================= ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM -F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw -NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN -MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl -bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD -VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 -iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 -i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 -bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB -K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB -AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu -cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm -lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB -i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww -GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg -Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI -K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 -bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj -qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es -E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ -tylv2G0xffX8oRAHh84vWdw+WNs= ------END CERTIFICATE----- - COMODO ECC Certification Authority ================================== -----BEGIN CERTIFICATE----- @@ -1342,30 +1036,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- -IGC/A -===== ------BEGIN CERTIFICATE----- -MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD -VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE -Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy -MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI -EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT -STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 -TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW -So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy -HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd -frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ -tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB -egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC -iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK -q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q -MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg -Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI -lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF -0mBWWg== ------END CERTIFICATE----- - Security Communication EV RootCA1 ================================= -----BEGIN CERTIFICATE----- @@ -1410,46 +1080,6 @@ hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= -----END CERTIFICATE----- -Microsec e-Szigno Root CA -========================= ------BEGIN CERTIFICATE----- -MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE -BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL -EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 -MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz -dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT -GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG -d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N -oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc -QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ -PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb -MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG -IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD -VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 -LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A -dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn -AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA -4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg -AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA -egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 -Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO -PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv -c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h -cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw -IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT -WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV -MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER -MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp -Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal -HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT -nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE -aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a -86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK -yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB -S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= ------END CERTIFICATE----- - Certigna ======== -----BEGIN CERTIFICATE----- @@ -1575,58 +1205,6 @@ LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= -----END CERTIFICATE----- -Buypass Class 2 CA 1 -==================== ------BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 -MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh -c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M -cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 -0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 -0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R -uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P -AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV -1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt -7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 -fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w -wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho ------END CERTIFICATE----- - -EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 -========================================================================== ------BEGIN CERTIFICATE----- -MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF -bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg -QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe -Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p -ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt -IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by -X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b -gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr -eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ -TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy -Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn -uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI -qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm -ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0 -Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB -/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW -Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t -FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm -zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k -XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT -bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU -RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK -1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt -2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ -Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9 -AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT ------END CERTIFICATE----- - certSIGN ROOT CA ================ -----BEGIN CERTIFICATE----- @@ -1647,49 +1225,6 @@ vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD -----END CERTIFICATE----- -CNNIC ROOT -========== ------BEGIN CERTIFICATE----- -MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE -ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw -OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD -o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz -VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT -VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or -czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK -y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC -wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S -lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 -Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM -O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 -BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 -G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m -mxE= ------END CERTIFICATE----- - -ApplicationCA - Japanese Government -=================================== ------BEGIN CERTIFICATE----- -MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT -SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw -MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl -cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 -fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN -wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE -jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu -nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU -WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV -BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD -vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs -o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g -/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD -io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW -dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL -rosot4LKGAfmt1t06SAZf7IbiVQ= ------END CERTIFICATE----- - GeoTrust Primary Certification Authority - G3 ============================================= -----BEGIN CERTIFICATE----- @@ -1821,7 +1356,7 @@ AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== -----END CERTIFICATE----- NetLock Arany (Class Gold) Főtanúsítvány -============================================ +======================================== -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 @@ -1876,58 +1411,6 @@ IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm 66+KAQ== -----END CERTIFICATE----- -CA Disig -======== ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK -QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw -MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz -bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm -GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD -Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo -hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt -ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w -gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P -AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz -aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff -ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa -BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t -WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 -mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ -CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K -ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA -4Z7CRneC9VkGjCFMhwnN5ag= ------END CERTIFICATE----- - -Juur-SK -======= ------BEGIN CERTIFICATE----- -MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA -c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw -DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG -SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy -aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf -TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC -+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw -UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa -Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF -MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD -HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh -AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA -cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr -AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw -cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE -FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G -A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo -ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL -abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 -IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh -Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 -yyqcjg== ------END CERTIFICATE----- - Hongkong Post Root CA 1 ======================= -----BEGIN CERTIFICATE----- @@ -2361,7 +1844,7 @@ Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -----END CERTIFICATE----- Certinomis - Autorité Racine -============================= +============================ -----BEGIN CERTIFICATE----- MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg @@ -2391,41 +1874,6 @@ wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ vgt2Fl43N+bYdJeimUV5 -----END CERTIFICATE----- -Root CA Generalitat Valenciana -============================== ------BEGIN CERTIFICATE----- -MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE -ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 -IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 -WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE -CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 -F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B -ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ -D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte -JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB -AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n -dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB -ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl -AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA -YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy -AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA -aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt -AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA -YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu -AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA -OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 -dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV -BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G -A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S -b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh -TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz -Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 -NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH -iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt -+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= ------END CERTIFICATE----- - TWCA Root Certification Authority ================================= -----BEGIN CERTIFICATE----- @@ -2871,93 +2319,6 @@ poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km -----END CERTIFICATE----- -China Internet Network Information Center EV Certificates Root -============================================================== ------BEGIN CERTIFICATE----- -MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV -BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D -aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg -Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG -A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM -PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl -cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y -jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV -98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H -klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 -KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC -7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD -glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 -0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM -7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws -ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 -5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= ------END CERTIFICATE----- - -Swisscom Root CA 2 -================== ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG -EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy -dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2 -MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln -aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC -IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM -LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo -ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ -wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH -Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a -SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS -NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab -mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY -Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3 -qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw -HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O -BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu -MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO -v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ -82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz -o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs -a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx -OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW -mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o -+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC -rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX -5OfNeOI5wSsSnqaeG8XmDtkx2Q== ------END CERTIFICATE----- - -Swisscom Root EV CA 2 -===================== ------BEGIN CERTIFICATE----- -MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE -BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl -cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN -MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT -HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg -Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz -o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy -Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti -GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li -qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH -Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG -alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa -m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox -bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi -xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/ -BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED -MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB -bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL -j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU -wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7 -XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH -59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/ -23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq -J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA -HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi -uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW -l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= ------END CERTIFICATE----- - CA Disig Root R1 ================ -----BEGIN CERTIFICATE----- @@ -3756,7 +3117,7 @@ ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su -----END CERTIFICATE----- TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 -========================================================= +==================================================== -----BEGIN CERTIFICATE----- MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp @@ -3779,30 +3140,6 @@ lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8 B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU= -----END CERTIFICATE----- -TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6 -========================================================= ------BEGIN CERTIFICATE----- -MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G -A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls -acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF -bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5 -MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL -BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf -aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm -aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a -2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED -wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb -HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV -+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT -9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG -9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R -fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy -o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW -hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1 -O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw== ------END CERTIFICATE----- - Certinomis - Root CA ==================== -----BEGIN CERTIFICATE----- @@ -3892,28 +3229,418 @@ Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu a/GRspBl9JrmkO5K -----END CERTIFICATE----- -================================================================ -C: US -O: GTE Corporation -OU: GTE CyberTrust Solutions, Inc. -CN: GTE CyberTrust Global Root --- -Not Before: 1998-08-13 -Not After: 2018-08-13 -Signature: md5WithRSAEncryption -Key: RSA:1024 +SZAFIR ROOT CA2 +=============== -----BEGIN CERTIFICATE----- -MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD -VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv -bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv -b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV -UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU -cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds -b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH -iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS -r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 -04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r -GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 -3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P -lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +Certplus Root CA G1 +=================== +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV +BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe +Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD +ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN +r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx +Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj +BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv +LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2 +z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc +4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd +4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj +jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+ +ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY +lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh +66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG +YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/ +2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F +6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX +CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe +tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC +VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/ ++mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+ +qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo= +-----END CERTIFICATE----- + +Certplus Root CA G2 +=================== +-----BEGIN CERTIFICATE----- +MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT +AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x +NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0 +cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN +Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud +IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV +HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl +vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw== +-----END CERTIFICATE----- + +OpenTrust Root CA G1 +==================== +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx +MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM +CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa +Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87 +ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO +YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9 +xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO +9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq +3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi +n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9 +URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr +TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px +N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E +PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv +uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK +n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh +X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80 +nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm +GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/ +bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o +4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA +OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx +-----END CERTIFICATE----- + +OpenTrust Root CA G2 +==================== +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy +MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM +CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+ +Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz +4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV +eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt +UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz +3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj +3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz +9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0 +0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT +y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59 +M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz +Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI +mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG +S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp +EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ +6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr +gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo +SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0 +YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm +u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK +-----END CERTIFICATE----- + +OpenTrust Root CA G3 +==================== +-----BEGIN CERTIFICATE----- +MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X +DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w +ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B +ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf +BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM +BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta +3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +LuxTrust Global Root 2 +====================== +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG +A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh +bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW +MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm +Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2 +xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC +wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm +1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm +FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF +wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/ +a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U +ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ +MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB +/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5 +Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ +FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN +H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW +7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu +ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA +VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR +TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt +/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc +7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I +iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= -----END CERTIFICATE----- diff --git a/htdocs/includes/stripe/init.php b/htdocs/includes/stripe/init.php index 3e7f93e4e85..79cf657a7eb 100644 --- a/htdocs/includes/stripe/init.php +++ b/htdocs/includes/stripe/init.php @@ -5,6 +5,9 @@ require(dirname(__FILE__) . '/lib/Stripe.php'); // Utilities require(dirname(__FILE__) . '/lib/Util/AutoPagingIterator.php'); +require(dirname(__FILE__) . '/lib/Util/LoggerInterface.php'); +require(dirname(__FILE__) . '/lib/Util/DefaultLogger.php'); +require(dirname(__FILE__) . '/lib/Util/RandomGenerator.php'); require(dirname(__FILE__) . '/lib/Util/RequestOptions.php'); require(dirname(__FILE__) . '/lib/Util/Set.php'); require(dirname(__FILE__) . '/lib/Util/Util.php'); @@ -19,19 +22,36 @@ require(dirname(__FILE__) . '/lib/Error/Api.php'); require(dirname(__FILE__) . '/lib/Error/ApiConnection.php'); require(dirname(__FILE__) . '/lib/Error/Authentication.php'); require(dirname(__FILE__) . '/lib/Error/Card.php'); +require(dirname(__FILE__) . '/lib/Error/Idempotency.php'); require(dirname(__FILE__) . '/lib/Error/InvalidRequest.php'); require(dirname(__FILE__) . '/lib/Error/Permission.php'); require(dirname(__FILE__) . '/lib/Error/RateLimit.php'); +require(dirname(__FILE__) . '/lib/Error/SignatureVerification.php'); + +// OAuth errors +require(dirname(__FILE__) . '/lib/Error/OAuth/OAuthBase.php'); +require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidClient.php'); +require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidGrant.php'); +require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidRequest.php'); +require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidScope.php'); +require(dirname(__FILE__) . '/lib/Error/OAuth/UnsupportedGrantType.php'); +require(dirname(__FILE__) . '/lib/Error/OAuth/UnsupportedResponseType.php'); + +// API operations +require(dirname(__FILE__) . '/lib/ApiOperations/All.php'); +require(dirname(__FILE__) . '/lib/ApiOperations/Create.php'); +require(dirname(__FILE__) . '/lib/ApiOperations/Delete.php'); +require(dirname(__FILE__) . '/lib/ApiOperations/NestedResource.php'); +require(dirname(__FILE__) . '/lib/ApiOperations/Request.php'); +require(dirname(__FILE__) . '/lib/ApiOperations/Retrieve.php'); +require(dirname(__FILE__) . '/lib/ApiOperations/Update.php'); // Plumbing require(dirname(__FILE__) . '/lib/ApiResponse.php'); -require(dirname(__FILE__) . '/lib/JsonSerializable.php'); require(dirname(__FILE__) . '/lib/StripeObject.php'); require(dirname(__FILE__) . '/lib/ApiRequestor.php'); require(dirname(__FILE__) . '/lib/ApiResource.php'); require(dirname(__FILE__) . '/lib/SingletonApiResource.php'); -require(dirname(__FILE__) . '/lib/AttachedObject.php'); -require(dirname(__FILE__) . '/lib/ExternalAccount.php'); // Stripe API Resources require(dirname(__FILE__) . '/lib/Account.php'); @@ -51,10 +71,13 @@ require(dirname(__FILE__) . '/lib/CountrySpec.php'); require(dirname(__FILE__) . '/lib/Coupon.php'); require(dirname(__FILE__) . '/lib/Customer.php'); require(dirname(__FILE__) . '/lib/Dispute.php'); +require(dirname(__FILE__) . '/lib/EphemeralKey.php'); require(dirname(__FILE__) . '/lib/Event.php'); +require(dirname(__FILE__) . '/lib/ExchangeRate.php'); require(dirname(__FILE__) . '/lib/FileUpload.php'); require(dirname(__FILE__) . '/lib/Invoice.php'); require(dirname(__FILE__) . '/lib/InvoiceItem.php'); +require(dirname(__FILE__) . '/lib/LoginLink.php'); require(dirname(__FILE__) . '/lib/Order.php'); require(dirname(__FILE__) . '/lib/OrderReturn.php'); require(dirname(__FILE__) . '/lib/Payout.php'); @@ -65,9 +88,18 @@ require(dirname(__FILE__) . '/lib/RecipientTransfer.php'); require(dirname(__FILE__) . '/lib/Refund.php'); require(dirname(__FILE__) . '/lib/SKU.php'); require(dirname(__FILE__) . '/lib/Source.php'); +require(dirname(__FILE__) . '/lib/SourceTransaction.php'); require(dirname(__FILE__) . '/lib/Subscription.php'); require(dirname(__FILE__) . '/lib/SubscriptionItem.php'); require(dirname(__FILE__) . '/lib/ThreeDSecure.php'); require(dirname(__FILE__) . '/lib/Token.php'); +require(dirname(__FILE__) . '/lib/Topup.php'); require(dirname(__FILE__) . '/lib/Transfer.php'); require(dirname(__FILE__) . '/lib/TransferReversal.php'); + +// OAuth +require(dirname(__FILE__) . '/lib/OAuth.php'); + +// Webhooks +require(dirname(__FILE__) . '/lib/Webhook.php'); +require(dirname(__FILE__) . '/lib/WebhookSignature.php'); diff --git a/htdocs/includes/stripe/lib/Account.php b/htdocs/includes/stripe/lib/Account.php index 3899b8233a6..d77d60dedbc 100644 --- a/htdocs/includes/stripe/lib/Account.php +++ b/htdocs/includes/stripe/lib/Account.php @@ -7,11 +7,13 @@ namespace Stripe; * * @property string $id * @property string $object - * @property mixed $business_logo + * @property string $business_logo * @property string $business_name - * @property mixed $business_url + * @property string $business_primary_color + * @property string $business_url * @property bool $charges_enabled * @property string $country + * @property int $created * @property bool $debit_negative_balances * @property mixed $decline_charge_on * @property string $default_currency @@ -20,23 +22,46 @@ namespace Stripe; * @property string $email * @property mixed $external_accounts * @property mixed $legal_entity - * @property bool $managed + * @property StripeObject $metadata * @property mixed $payout_schedule - * @property mixed $payout_statement_descriptor + * @property string $payout_statement_descriptor * @property bool $payouts_enabled - * @property mixed $product_description - * @property mixed $statement_descriptor - * @property mixed $support_email - * @property mixed $support_phone + * @property string $product_description + * @property string $statement_descriptor + * @property string $support_email + * @property string $support_phone * @property string $timezone * @property mixed $tos_acceptance * @property mixed $verification - * @property mixed $keys * * @package Stripe */ class Account extends ApiResource { + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\NestedResource; + use ApiOperations\Retrieve { + retrieve as protected _retrieve; + } + use ApiOperations\Update; + + public static function getSavedNestedResources() + { + static $savedNestedResources = null; + if ($savedNestedResources === null) { + $savedNestedResources = new Util\Set([ + 'external_account', + 'bank_account', + ]); + } + return $savedNestedResources; + } + + const PATH_EXTERNAL_ACCOUNTS = '/external_accounts'; + const PATH_LOGIN_LINKS = '/login_links'; + public function instanceUrl() { if ($this['id'] === null) { @@ -47,7 +72,8 @@ class Account extends ApiResource } /** - * @param string|null $id + * @param array|string|null $id The ID of the account to retrieve, or an + * options array containing an `id` key. * @param array|string|null $opts * * @return Account @@ -61,50 +87,6 @@ class Account extends ApiResource return self::_retrieve($id, $opts); } - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Account - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the account to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Account The updated account. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Account - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Account The deleted account. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } - /** * @param array|null $params * @param array|string|null $opts @@ -120,13 +102,133 @@ class Account extends ApiResource } /** + * @param array|null $clientId + * @param array|string|null $opts + * + * @return StripeObject Object containing the response from the API. + */ + public function deauthorize($clientId = null, $opts = null) + { + $params = [ + 'client_id' => $clientId, + 'stripe_user_id' => $this->id, + ]; + OAuth::deauthorize($params, $opts); + } + + /** + * @param array|null $id The ID of the account on which to create the external account. * @param array|null $params * @param array|string|null $opts * - * @return Collection of Accounts + * @return BankAccount|Card */ - public static function all($params = null, $opts = null) + public static function createExternalAccount($id, $params = null, $opts = null) { - return self::_all($params, $opts); + return self::_createNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts); + } + + /** + * @param array|null $id The ID of the account to which the external account belongs. + * @param array|null $externalAccountId The ID of the external account to retrieve. + * @param array|null $params + * @param array|string|null $opts + * + * @return BankAccount|Card + */ + public static function retrieveExternalAccount($id, $externalAccountId, $params = null, $opts = null) + { + return self::_retrieveNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts); + } + + /** + * @param array|null $id The ID of the account to which the external account belongs. + * @param array|null $externalAccountId The ID of the external account to update. + * @param array|null $params + * @param array|string|null $opts + * + * @return BankAccount|Card + */ + public static function updateExternalAccount($id, $externalAccountId, $params = null, $opts = null) + { + return self::_updateNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts); + } + + /** + * @param array|null $id The ID of the account to which the external account belongs. + * @param array|null $externalAccountId The ID of the external account to delete. + * @param array|null $params + * @param array|string|null $opts + * + * @return BankAccount|Card + */ + public static function deleteExternalAccount($id, $externalAccountId, $params = null, $opts = null) + { + return self::_deleteNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts); + } + + /** + * @param array|null $id The ID of the account on which to retrieve the external accounts. + * @param array|null $params + * @param array|string|null $opts + * + * @return BankAccount|Card + */ + public static function allExternalAccounts($id, $params = null, $opts = null) + { + return self::_allNestedResources($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts); + } + + /** + * @param array|null $id The ID of the account on which to create the login link. + * @param array|null $params + * @param array|string|null $opts + * + * @return LoginLink + */ + public static function createLoginLink($id, $params = null, $opts = null) + { + return self::_createNestedResource($id, static::PATH_LOGIN_LINKS, $params, $opts); + } + + public function serializeParameters($force = false) + { + $update = parent::serializeParameters($force); + if (isset($this->_values['legal_entity'])) { + $entity = $this['legal_entity']; + if (isset($entity->_values['additional_owners'])) { + $owners = $entity['additional_owners']; + $entityUpdate = isset($update['legal_entity']) ? $update['legal_entity'] : []; + $entityUpdate['additional_owners'] = $this->serializeAdditionalOwners($entity, $owners); + $update['legal_entity'] = $entityUpdate; + } + } + return $update; + } + + private function serializeAdditionalOwners($legalEntity, $additionalOwners) + { + if (isset($legalEntity->_originalValues['additional_owners'])) { + $originalValue = $legalEntity->_originalValues['additional_owners']; + } else { + $originalValue = []; + } + if (($originalValue) && (count($originalValue) > count($additionalOwners))) { + throw new \InvalidArgumentException( + "You cannot delete an item from an array, you must instead set a new array" + ); + } + + $updateArr = []; + foreach ($additionalOwners as $i => $v) { + $update = ($v instanceof StripeObject) ? $v->serializeParameters() : $v; + + if ($update !== []) { + if (!$originalValue || ($update != $legalEntity->serializeParamsValue($originalValue[$i], null, false, true))) { + $updateArr[$i] = $update; + } + } + } + return $updateArr; } } diff --git a/htdocs/includes/stripe/lib/AlipayAccount.php b/htdocs/includes/stripe/lib/AlipayAccount.php index 1ba34bfa646..fb7d5bb791e 100644 --- a/htdocs/includes/stripe/lib/AlipayAccount.php +++ b/htdocs/includes/stripe/lib/AlipayAccount.php @@ -6,8 +6,64 @@ namespace Stripe; * Class AlipayAccount * * @package Stripe + * + * @deprecated Alipay accounts are deprecated. Please use the sources API instead. + * @link https://stripe.com/docs/sources/alipay */ -class AlipayAccount extends ExternalAccount +class AlipayAccount extends ApiResource { + use ApiOperations\Delete; + use ApiOperations\Update; + /** + * @return string The instance URL for this resource. It needs to be special + * cased because it doesn't fit into the standard resource pattern. + */ + public function instanceUrl() + { + if ($this['customer']) { + $base = Customer::classUrl(); + $parent = $this['customer']; + $path = 'sources'; + } else { + $msg = "Alipay accounts cannot be accessed without a customer ID."; + throw new Error\InvalidRequest($msg, null); + } + $parentExtn = urlencode(Util\Util::utf8($parent)); + $extn = urlencode(Util\Util::utf8($this['id'])); + return "$base/$parentExtn/$path/$extn"; + } + + /** + * @param array|string $_id + * @param array|string|null $_opts + * + * @throws \Stripe\Error\InvalidRequest + * + * @deprecated Alipay accounts are deprecated. Please use the sources API instead. + * @link https://stripe.com/docs/sources/alipay + */ + public static function retrieve($_id, $_opts = null) + { + $msg = "Alipay accounts cannot be accessed without a customer ID. " . + "Retrieve an Alipay account using \$customer->sources->retrieve('alipay_account_id') instead."; + throw new Error\InvalidRequest($msg, null); + } + + /** + * @param string $_id + * @param array|null $_params + * @param array|string|null $_options + * + * @throws \Stripe\Error\InvalidRequest + * + * @deprecated Alipay accounts are deprecated. Please use the sources API instead. + * @link https://stripe.com/docs/sources/alipay + */ + public static function update($_id, $_params = null, $_options = null) + { + $msg = "Alipay accounts cannot be accessed without a customer ID. " . + "Call save() on \$customer->sources->retrieve('alipay_account_id') instead."; + throw new Error\InvalidRequest($msg, null); + } } diff --git a/htdocs/includes/stripe/lib/ApiOperations/All.php b/htdocs/includes/stripe/lib/ApiOperations/All.php new file mode 100644 index 00000000000..d1c6424816f --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/All.php @@ -0,0 +1,34 @@ +json, $opts); + if (!is_a($obj, 'Stripe\\Collection')) { + $class = get_class($obj); + $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead"; + throw new Error\Api($message); + } + $obj->setLastResponse($response); + $obj->setRequestParams($params); + return $obj; + } +} diff --git a/htdocs/includes/stripe/lib/ApiOperations/Create.php b/htdocs/includes/stripe/lib/ApiOperations/Create.php new file mode 100644 index 00000000000..0fb341d029b --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/Create.php @@ -0,0 +1,28 @@ +json, $opts); + $obj->setLastResponse($response); + return $obj; + } +} diff --git a/htdocs/includes/stripe/lib/ApiOperations/Delete.php b/htdocs/includes/stripe/lib/ApiOperations/Delete.php new file mode 100644 index 00000000000..7df6797919a --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/Delete.php @@ -0,0 +1,27 @@ +instanceUrl(); + list($response, $opts) = $this->_request('delete', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/htdocs/includes/stripe/lib/ApiOperations/NestedResource.php b/htdocs/includes/stripe/lib/ApiOperations/NestedResource.php new file mode 100644 index 00000000000..1c3a73f45de --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/NestedResource.php @@ -0,0 +1,115 @@ +json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param string $id + * @param string $nestedPath + * @param string|null $nestedId + * + * @return string + */ + protected static function _nestedResourceUrl($id, $nestedPath, $nestedId = null) + { + $url = static::resourceUrl($id) . $nestedPath; + if ($nestedId !== null) { + $url .= "/$nestedId"; + } + return $url; + } + + /** + * @param string $id + * @param string $nestedPath + * @param array|null $params + * @param array|string|null $options + * + * @return \Stripe\StripeObject + */ + protected static function _createNestedResource($id, $nestedPath, $params = null, $options = null) + { + $url = static::_nestedResourceUrl($id, $nestedPath); + return self::_nestedResourceOperation('post', $url, $params, $options); + } + + /** + * @param string $id + * @param string $nestedPath + * @param array|null $params + * @param array|string|null $options + * + * @return \Stripe\StripeObject + */ + protected static function _retrieveNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null) + { + $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId); + return self::_nestedResourceOperation('get', $url, $params, $options); + } + + /** + * @param string $id + * @param string $nestedPath + * @param array|null $params + * @param array|string|null $options + * + * @return \Stripe\StripeObject + */ + protected static function _updateNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null) + { + $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId); + return self::_nestedResourceOperation('post', $url, $params, $options); + } + + /** + * @param string $id + * @param string $nestedPath + * @param array|null $params + * @param array|string|null $options + * + * @return \Stripe\StripeObject + */ + protected static function _deleteNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null) + { + $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId); + return self::_nestedResourceOperation('delete', $url, $params, $options); + } + + /** + * @param string $id + * @param string $nestedPath + * @param array|null $params + * @param array|string|null $options + * + * @return \Stripe\StripeObject + */ + protected static function _allNestedResources($id, $nestedPath, $params = null, $options = null) + { + $url = static::_nestedResourceUrl($id, $nestedPath); + return self::_nestedResourceOperation('get', $url, $params, $options); + } +} diff --git a/htdocs/includes/stripe/lib/ApiOperations/Request.php b/htdocs/includes/stripe/lib/ApiOperations/Request.php new file mode 100644 index 00000000000..c6b06585ad2 --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/Request.php @@ -0,0 +1,60 @@ + 100, " + . "'currency' => 'usd', 'source' => 'tok_1234'])\")"; + throw new \Stripe\Error\Api($message); + } + } + + /** + * @param string $method HTTP method ('get', 'post', etc.) + * @param string $url URL for the request + * @param array $params list of parameters for the request + * @param array|string|null $options + * + * @return array tuple containing (the JSON response, $options) + */ + protected function _request($method, $url, $params = [], $options = null) + { + $opts = $this->_opts->merge($options); + list($resp, $options) = static::_staticRequest($method, $url, $params, $opts); + $this->setLastResponse($resp); + return [$resp->json, $options]; + } + + /** + * @param string $method HTTP method ('get', 'post', etc.) + * @param string $url URL for the request + * @param array $params list of parameters for the request + * @param array|string|null $options + * + * @return array tuple containing (the JSON response, $options) + */ + protected static function _staticRequest($method, $url, $params, $options) + { + $opts = \Stripe\Util\RequestOptions::parse($options); + $requestor = new \Stripe\ApiRequestor($opts->apiKey, static::baseUrl()); + list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers); + $opts->discardNonPersistentHeaders(); + return [$response, $opts]; + } +} diff --git a/htdocs/includes/stripe/lib/ApiOperations/Retrieve.php b/htdocs/includes/stripe/lib/ApiOperations/Retrieve.php new file mode 100644 index 00000000000..a037326b3e8 --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/Retrieve.php @@ -0,0 +1,27 @@ +refresh(); + return $instance; + } +} diff --git a/htdocs/includes/stripe/lib/ApiOperations/Update.php b/htdocs/includes/stripe/lib/ApiOperations/Update.php new file mode 100644 index 00000000000..0683e77af1a --- /dev/null +++ b/htdocs/includes/stripe/lib/ApiOperations/Update.php @@ -0,0 +1,46 @@ +json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param array|string|null $opts + * + * @return \Stripe\ApiResource The saved resource. + */ + public function save($opts = null) + { + $params = $this->serializeParameters(); + if (count($params) > 0) { + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + } + return $this; + } +} diff --git a/htdocs/includes/stripe/lib/ApiRequestor.php b/htdocs/includes/stripe/lib/ApiRequestor.php index 03c718f4e1e..334d84425b3 100644 --- a/htdocs/includes/stripe/lib/ApiRequestor.php +++ b/htdocs/includes/stripe/lib/ApiRequestor.php @@ -33,7 +33,7 @@ class ApiRequestor } elseif ($d === false) { return 'false'; } elseif (is_array($d)) { - $res = array(); + $res = []; foreach ($d as $k => $v) { $res[$k] = self::_encodeObjects($v); } @@ -54,17 +54,13 @@ class ApiRequestor */ public function request($method, $url, $params = null, $headers = null) { - if (!$params) { - $params = array(); - } - if (!$headers) { - $headers = array(); - } + $params = $params ?: []; + $headers = $headers ?: []; list($rbody, $rcode, $rheaders, $myApiKey) = $this->_requestRaw($method, $url, $params, $headers); $json = $this->_interpretResponse($rbody, $rcode, $rheaders); $resp = new ApiResponse($rbody, $rcode, $rheaders, $json); - return array($resp, $myApiKey); + return [$resp, $myApiKey]; } /** @@ -74,6 +70,7 @@ class ApiRequestor * @param array $resp * * @throws Error\InvalidRequest if the error is caused by the user. + * @throws Error\Idempotency if the error is caused by an idempotency key. * @throws Error\Authentication if the error is caused by a lack of * permissions. * @throws Error\Permission if the error is caused by insufficient @@ -84,7 +81,7 @@ class ApiRequestor * hitting the API. * @throws Error\Api otherwise. */ - public function handleApiError($rbody, $rcode, $rheaders, $resp) + public function handleErrorResponse($rbody, $rcode, $rheaders, $resp) { if (!is_array($resp) || !isset($resp['error'])) { $msg = "Invalid response object from API: $rbody " @@ -92,35 +89,75 @@ class ApiRequestor throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); } - $error = $resp['error']; - $msg = isset($error['message']) ? $error['message'] : null; - $param = isset($error['param']) ? $error['param'] : null; - $code = isset($error['code']) ? $error['code'] : null; + $errorData = $resp['error']; + + $error = null; + if (is_string($errorData)) { + $error = self::_specificOAuthError($rbody, $rcode, $rheaders, $resp, $errorData); + } + if (!$error) { + $error = self::_specificAPIError($rbody, $rcode, $rheaders, $resp, $errorData); + } + + throw $error; + } + + private static function _specificAPIError($rbody, $rcode, $rheaders, $resp, $errorData) + { + $msg = isset($errorData['message']) ? $errorData['message'] : null; + $param = isset($errorData['param']) ? $errorData['param'] : null; + $code = isset($errorData['code']) ? $errorData['code'] : null; + $type = isset($errorData['type']) ? $errorData['type'] : null; switch ($rcode) { case 400: // 'rate_limit' code is deprecated, but left here for backwards compatibility // for API versions earlier than 2015-09-08 if ($code == 'rate_limit') { - throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + return new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + } + if ($type == 'idempotency_error') { + return new Error\Idempotency($msg, $rcode, $rbody, $resp, $rheaders); } // intentional fall-through case 404: - throw new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders); + return new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders); case 401: - throw new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders); + return new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders); case 402: - throw new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders); + return new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders); case 403: - throw new Error\Permission($msg, $rcode, $rbody, $resp, $rheaders); + return new Error\Permission($msg, $rcode, $rbody, $resp, $rheaders); case 429: - throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + return new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); default: - throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); + return new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); } } + private static function _specificOAuthError($rbody, $rcode, $rheaders, $resp, $errorCode) + { + $description = isset($resp['error_description']) ? $resp['error_description'] : $errorCode; + + switch ($errorCode) { + case 'invalid_client': + return new Error\OAuth\InvalidClient($errorCode, $description, $rcode, $rbody, $resp, $rheaders); + case 'invalid_grant': + return new Error\OAuth\InvalidGrant($errorCode, $description, $rcode, $rbody, $resp, $rheaders); + case 'invalid_request': + return new Error\OAuth\InvalidRequest($errorCode, $description, $rcode, $rbody, $resp, $rheaders); + case 'invalid_scope': + return new Error\OAuth\InvalidScope($errorCode, $description, $rcode, $rbody, $resp, $rheaders); + case 'unsupported_grant_type': + return new Error\OAuth\UnsupportedGrantType($errorCode, $description, $rcode, $rbody, $resp, $rheaders); + case 'unsupported_response_type': + return new Error\OAuth\UnsupportedResponseType($errorCode, $description, $rcode, $rbody, $resp, $rheaders); + } + + return null; + } + private static function _formatAppInfo($appInfo) { if ($appInfo !== null) { @@ -137,44 +174,34 @@ class ApiRequestor } } - private static function _defaultHeaders($apiKey) + private static function _defaultHeaders($apiKey, $clientInfo = null) { - $appInfo = Stripe::getAppInfo(); - $uaString = 'Stripe/v1 PhpBindings/' . Stripe::VERSION; $langVersion = phpversion(); $uname = php_uname(); - $httplib = 'unknown'; - $ssllib = 'unknown'; - - if (function_exists('curl_version')) { - $curlVersion = curl_version(); - $httplib = 'curl ' . $curlVersion['version']; - $ssllib = $curlVersion['ssl_version']; - } - $appInfo = Stripe::getAppInfo(); - $ua = array( + $ua = [ 'bindings_version' => Stripe::VERSION, 'lang' => 'php', 'lang_version' => $langVersion, 'publisher' => 'stripe', 'uname' => $uname, - 'httplib' => $httplib, - 'ssllib' => $ssllib, - ); + ]; + if ($clientInfo) { + $ua = array_merge($clientInfo, $ua); + } if ($appInfo !== null) { $uaString .= ' ' . self::_formatAppInfo($appInfo); $ua['application'] = $appInfo; } - $defaultHeaders = array( + $defaultHeaders = [ 'X-Stripe-Client-User-Agent' => json_encode($ua), 'User-Agent' => $uaString, 'Authorization' => 'Bearer ' . $apiKey, - ); + ]; return $defaultHeaders; } @@ -193,9 +220,17 @@ class ApiRequestor throw new Error\Authentication($msg); } + // Clients can supply arbitrary additional keys to be included in the + // X-Stripe-Client-User-Agent header via the optional getUserAgentInfo() + // method + $clientUAInfo = null; + if (method_exists($this->httpClient(), 'getUserAgentInfo')) { + $clientUAInfo = $this->httpClient()->getUserAgentInfo(); + } + $absUrl = $this->_apiBase.$url; $params = self::_encodeObjects($params); - $defaultHeaders = $this->_defaultHeaders($myApiKey); + $defaultHeaders = $this->_defaultHeaders($myApiKey, $clientUAInfo); if (Stripe::$apiVersion) { $defaultHeaders['Stripe-Version'] = Stripe::$apiVersion; } @@ -222,7 +257,7 @@ class ApiRequestor } $combinedHeaders = array_merge($defaultHeaders, $headers); - $rawHeaders = array(); + $rawHeaders = []; foreach ($combinedHeaders as $header => $value) { $rawHeaders[] = $header . ': ' . $value; @@ -235,7 +270,7 @@ class ApiRequestor $params, $hasFile ); - return array($rbody, $rcode, $rheaders, $myApiKey); + return [$rbody, $rcode, $rheaders, $myApiKey]; } private function _processResourceParam($resource, $hasCurlFile) @@ -272,7 +307,7 @@ class ApiRequestor } if ($rcode < 200 || $rcode >= 300) { - $this->handleApiError($rbody, $rcode, $rheaders, $resp); + $this->handleErrorResponse($rbody, $rcode, $rheaders, $resp); } return $resp; } diff --git a/htdocs/includes/stripe/lib/ApiResource.php b/htdocs/includes/stripe/lib/ApiResource.php index 0c03d49c3b9..f73b22c9663 100644 --- a/htdocs/includes/stripe/lib/ApiResource.php +++ b/htdocs/includes/stripe/lib/ApiResource.php @@ -9,11 +9,42 @@ namespace Stripe; */ abstract class ApiResource extends StripeObject { - private static $HEADERS_TO_PERSIST = array('Stripe-Account' => true, 'Stripe-Version' => true); + use ApiOperations\Request; - public static function baseUrl() + /** + * @return \Stripe\Util\Set A list of fields that can be their own type of + * API resource (say a nested card under an account for example), and if + * that resource is set, it should be transmitted to the API on a create or + * update. Doing so is not the default behavior because API resources + * should normally be persisted on their own RESTful endpoints. + */ + public static function getSavedNestedResources() { - return Stripe::$apiBase; + static $savedNestedResources = null; + if ($savedNestedResources === null) { + $savedNestedResources = new Util\Set(); + } + return $savedNestedResources; + } + + /** + * @var boolean A flag that can be set a behavior that will cause this + * resource to be encoded and sent up along with an update of its parent + * resource. This is usually not desirable because resources are updated + * individually on their own endpoints, but there are certain cases, + * replacing a customer's source for example, where this is allowed. + */ + public $saveWithParent = false; + + public function __set($k, $v) + { + parent::__set($k, $v); + $v = $this->$k; + if ((static::getSavedNestedResources()->includes($k)) && + ($v instanceof ApiResource)) { + $v->saveWithParent = true; + } + return $v; } /** @@ -59,6 +90,14 @@ abstract class ApiResource extends StripeObject return $name; } + /** + * @return string The base URL for the given class. + */ + public static function baseUrl() + { + return Stripe::$apiBase; + } + /** * @return string The endpoint URL for the given class. */ @@ -92,112 +131,4 @@ abstract class ApiResource extends StripeObject { return static::resourceUrl($this['id']); } - - protected static function _validateParams($params = null) - { - if ($params && !is_array($params)) { - $message = "You must pass an array as the first argument to Stripe API " - . "method calls. (HINT: an example call to create a charge " - . "would be: \"Stripe\\Charge::create(array('amount' => 100, " - . "'currency' => 'usd', 'card' => array('number' => " - . "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")"; - throw new Error\Api($message); - } - } - - protected function _request($method, $url, $params = array(), $options = null) - { - $opts = $this->_opts->merge($options); - list($resp, $options) = static::_staticRequest($method, $url, $params, $opts); - $this->setLastResponse($resp); - return array($resp->json, $options); - } - - protected static function _staticRequest($method, $url, $params, $options) - { - $opts = Util\RequestOptions::parse($options); - $requestor = new ApiRequestor($opts->apiKey, static::baseUrl()); - list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers); - foreach ($opts->headers as $k => $v) { - if (!array_key_exists($k, self::$HEADERS_TO_PERSIST)) { - unset($opts->headers[$k]); - } - } - return array($response, $opts); - } - - protected static function _retrieve($id, $options = null) - { - $opts = Util\RequestOptions::parse($options); - $instance = new static($id, $opts); - $instance->refresh(); - return $instance; - } - - protected static function _all($params = null, $options = null) - { - self::_validateParams($params); - $url = static::classUrl(); - - list($response, $opts) = static::_staticRequest('get', $url, $params, $options); - $obj = Util\Util::convertToStripeObject($response->json, $opts); - if (!is_a($obj, 'Stripe\\Collection')) { - $class = get_class($obj); - $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead"; - throw new Error\Api($message); - } - $obj->setLastResponse($response); - $obj->setRequestParams($params); - return $obj; - } - - protected static function _create($params = null, $options = null) - { - self::_validateParams($params); - $url = static::classUrl(); - - list($response, $opts) = static::_staticRequest('post', $url, $params, $options); - $obj = Util\Util::convertToStripeObject($response->json, $opts); - $obj->setLastResponse($response); - return $obj; - } - - /** - * @param string $id The ID of the API resource to update. - * @param array|null $params - * @param array|string|null $opts - * - * @return ApiResource the updated API resource - */ - protected static function _update($id, $params = null, $options = null) - { - self::_validateParams($params); - $url = static::resourceUrl($id); - - list($response, $opts) = static::_staticRequest('post', $url, $params, $options); - $obj = Util\Util::convertToStripeObject($response->json, $opts); - $obj->setLastResponse($response); - return $obj; - } - - protected function _save($options = null) - { - $params = $this->serializeParameters(); - if (count($params) > 0) { - $url = $this->instanceUrl(); - list($response, $opts) = $this->_request('post', $url, $params, $options); - $this->refreshFrom($response, $opts); - } - return $this; - } - - protected function _delete($params = null, $options = null) - { - self::_validateParams($params); - - $url = $this->instanceUrl(); - list($response, $opts) = $this->_request('delete', $url, $params, $options); - $this->refreshFrom($response, $opts); - return $this; - } } diff --git a/htdocs/includes/stripe/lib/ApplePayDomain.php b/htdocs/includes/stripe/lib/ApplePayDomain.php index 8c522c8b933..124c5dc0632 100644 --- a/htdocs/includes/stripe/lib/ApplePayDomain.php +++ b/htdocs/includes/stripe/lib/ApplePayDomain.php @@ -9,7 +9,11 @@ namespace Stripe; */ class ApplePayDomain extends ApiResource { - + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + /** * @return string The class URL for this resource. It needs to be special * cased because it doesn't fit into the standard resource pattern. @@ -18,48 +22,4 @@ class ApplePayDomain extends ApiResource { return '/v1/apple_pay/domains'; } - - /** - * @param string $id The ID of the domain to retrieve. - * @param array|string|null $opts - * - * @return ApplePayDomain - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return ApplePayDomain The created domain. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return ApplePayDomain The deleted domain. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of ApplePayDomains - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } } diff --git a/htdocs/includes/stripe/lib/ApplicationFee.php b/htdocs/includes/stripe/lib/ApplicationFee.php index 8145541c17e..05b94ea9235 100644 --- a/htdocs/includes/stripe/lib/ApplicationFee.php +++ b/htdocs/includes/stripe/lib/ApplicationFee.php @@ -5,10 +5,31 @@ namespace Stripe; /** * Class ApplicationFee * + * @property string $id + * @property string $object + * @property string $account + * @property int $amount + * @property int $amount_refunded + * @property string $application + * @property string $balance_transaction + * @property string $charge + * @property int $created + * @property string $currency + * @property bool $livemode + * @property string $originating_transaction + * @property bool $refunded + * @property Collection $refunds + * * @package Stripe */ class ApplicationFee extends ApiResource { + use ApiOperations\All; + use ApiOperations\NestedResource; + use ApiOperations\Retrieve; + + const PATH_REFUNDS = '/refunds'; + /** * This is a special case because the application fee endpoint has an * underscore in it. The parent `className` function strips underscores. @@ -20,40 +41,6 @@ class ApplicationFee extends ApiResource return 'application_fee'; } - /** - * @param string $id The ID of the application fee to retrieve. - * @param array|string|null $opts - * - * @return ApplicationFee - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param string $id The ID of the application fee to update. - * @param array|null $params - * @param array|string|null $options - * - * @return ApplicationFee The updated application fee. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of ApplicationFees - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - /** * @param array|null $params * @param array|string|null $opts @@ -66,4 +53,54 @@ class ApplicationFee extends ApiResource $this->refresh(); return $this; } + + /** + * @param array|null $id The ID of the application fee on which to create the refund. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApplicationFeeRefund + */ + public static function createRefund($id, $params = null, $opts = null) + { + return self::_createNestedResource($id, static::PATH_REFUNDS, $params, $opts); + } + + /** + * @param array|null $id The ID of the application fee to which the refund belongs. + * @param array|null $refundId The ID of the refund to retrieve. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApplicationFeeRefund + */ + public static function retrieveRefund($id, $refundId, $params = null, $opts = null) + { + return self::_retrieveNestedResource($id, static::PATH_REFUNDS, $refundId, $params, $opts); + } + + /** + * @param array|null $id The ID of the application fee to which the refund belongs. + * @param array|null $refundId The ID of the refund to update. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApplicationFeeRefund + */ + public static function updateRefund($id, $refundId, $params = null, $opts = null) + { + return self::_updateNestedResource($id, static::PATH_REFUNDS, $refundId, $params, $opts); + } + + /** + * @param array|null $id The ID of the application fee on which to retrieve the refunds. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApplicationFeeRefund + */ + public static function allRefunds($id, $params = null, $opts = null) + { + return self::_allNestedResources($id, static::PATH_REFUNDS, $params, $opts); + } } diff --git a/htdocs/includes/stripe/lib/ApplicationFeeRefund.php b/htdocs/includes/stripe/lib/ApplicationFeeRefund.php index 44695ef88cd..4b1c425a8ee 100644 --- a/htdocs/includes/stripe/lib/ApplicationFeeRefund.php +++ b/htdocs/includes/stripe/lib/ApplicationFeeRefund.php @@ -5,10 +5,23 @@ namespace Stripe; /** * Class ApplicationFeeRefund * + * @property string $id + * @property string $object + * @property int $amount + * @property string $balance_transaction + * @property int $created + * @property string $currency + * @property string $fee + * @property StripeObject $metadata + * * @package Stripe */ class ApplicationFeeRefund extends ApiResource { + use ApiOperations\Update { + save as protected _save; + } + /** * @return string The API URL for this Stripe refund. */ diff --git a/htdocs/includes/stripe/lib/AttachedObject.php b/htdocs/includes/stripe/lib/AttachedObject.php deleted file mode 100644 index 489517d6693..00000000000 --- a/htdocs/includes/stripe/lib/AttachedObject.php +++ /dev/null @@ -1,31 +0,0 @@ -_values), array_keys($properties)); - // Don't unset, but rather set to null so we send up '' for deletion. - foreach ($removed as $k) { - $this->$k = null; - } - - foreach ($properties as $k => $v) { - $this->$k = $v; - } - } -} diff --git a/htdocs/includes/stripe/lib/Balance.php b/htdocs/includes/stripe/lib/Balance.php index 3b551eb84df..115307f843b 100644 --- a/htdocs/includes/stripe/lib/Balance.php +++ b/htdocs/includes/stripe/lib/Balance.php @@ -6,9 +6,9 @@ namespace Stripe; * Class Balance * * @property string $object - * @property mixed $available - * @property bool $livedmode - * @property mixed $pending + * @property array $available + * @property bool $livemode + * @property array $pending * * @package Stripe */ diff --git a/htdocs/includes/stripe/lib/BalanceTransaction.php b/htdocs/includes/stripe/lib/BalanceTransaction.php index b84619ece81..f82888ab1dd 100644 --- a/htdocs/includes/stripe/lib/BalanceTransaction.php +++ b/htdocs/includes/stripe/lib/BalanceTransaction.php @@ -12,11 +12,11 @@ namespace Stripe; * @property int $created * @property string $currency * @property string $description + * @property float $exchange_rate * @property int $fee * @property mixed $fee_details * @property int $net * @property string $source - * @property mixed $sourced_transfers * @property string $status * @property string $type * @@ -24,6 +24,9 @@ namespace Stripe; */ class BalanceTransaction extends ApiResource { + use ApiOperations\All; + use ApiOperations\Retrieve; + /** * @return string The class URL for this resource. It needs to be special * cased because it doesn't fit into the standard resource pattern. @@ -32,26 +35,4 @@ class BalanceTransaction extends ApiResource { return "/v1/balance/history"; } - - /** - * @param string $id The ID of the balance transaction to retrieve. - * @param array|string|null $opts - * - * @return BalanceTransaction - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of BalanceTransactions - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } } diff --git a/htdocs/includes/stripe/lib/BankAccount.php b/htdocs/includes/stripe/lib/BankAccount.php index 2f11c067c36..2afc312d003 100644 --- a/htdocs/includes/stripe/lib/BankAccount.php +++ b/htdocs/includes/stripe/lib/BankAccount.php @@ -5,11 +5,82 @@ namespace Stripe; /** * Class BankAccount * + * @property string $id + * @property string $object + * @property string $account + * @property string $account_holder_name + * @property string $account_holder_type + * @property string $bank_name + * @property string $country + * @property string $currency + * @property string $customer + * @property bool $default_for_currency + * @property string $fingerprint + * @property string $last4 + * @property StripeObject $metadata + * @property string $routing_number + * @property string $status + * * @package Stripe */ -class BankAccount extends ExternalAccount +class BankAccount extends ApiResource { + use ApiOperations\Delete; + use ApiOperations\Update; + /** + * @return string The instance URL for this resource. It needs to be special + * cased because it doesn't fit into the standard resource pattern. + */ + public function instanceUrl() + { + if ($this['customer']) { + $base = Customer::classUrl(); + $parent = $this['customer']; + $path = 'sources'; + } elseif ($this['account']) { + $base = Account::classUrl(); + $parent = $this['account']; + $path = 'external_accounts'; + } else { + $msg = "Bank accounts cannot be accessed without a customer ID or account ID."; + throw new Error\InvalidRequest($msg, null); + } + $parentExtn = urlencode(Util\Util::utf8($parent)); + $extn = urlencode(Util\Util::utf8($this['id'])); + return "$base/$parentExtn/$path/$extn"; + } + + /** + * @param array|string $_id + * @param array|string|null $_opts + * + * @throws \Stripe\Error\InvalidRequest + */ + public static function retrieve($_id, $_opts = null) + { + $msg = "Bank accounts cannot be accessed without a customer ID or account ID. " . + "Retrieve a bank account using \$customer->sources->retrieve('bank_account_id') or " . + "\$account->external_accounts->retrieve('bank_account_id') instead."; + throw new Error\InvalidRequest($msg, null); + } + + /** + * @param string $_id + * @param array|null $_params + * @param array|string|null $_options + * + * @throws \Stripe\Error\InvalidRequest + */ + public static function update($_id, $_params = null, $_options = null) + { + $msg = "Bank accounts cannot be accessed without a customer ID or account ID. " . + "Call save() on \$customer->sources->retrieve('bank_account_id') or " . + "\$account->external_accounts->retrieve('bank_account_id') instead."; + throw new Error\InvalidRequest($msg, null); + } + + /** * @param array|null $params * @param array|string|null $options * diff --git a/htdocs/includes/stripe/lib/BitcoinReceiver.php b/htdocs/includes/stripe/lib/BitcoinReceiver.php index 3f2c835b96f..8d04256b5db 100644 --- a/htdocs/includes/stripe/lib/BitcoinReceiver.php +++ b/htdocs/includes/stripe/lib/BitcoinReceiver.php @@ -6,9 +6,15 @@ namespace Stripe; * Class BitcoinReceiver * * @package Stripe + * + * @deprecated Bitcoin receivers are deprecated. Please use the sources API instead. + * @link https://stripe.com/docs/sources/bitcoin */ -class BitcoinReceiver extends ExternalAccount +class BitcoinReceiver extends ApiResource { + use ApiOperations\All; + use ApiOperations\Retrieve; + /** * @return string The class URL for this resource. It needs to be special * cased because it doesn't fit into the standard resource pattern. @@ -24,62 +30,17 @@ class BitcoinReceiver extends ExternalAccount */ public function instanceUrl() { - $result = parent::instanceUrl(); - if ($result) { - return $result; + if ($this['customer']) { + $base = Customer::classUrl(); + $parent = $this['customer']; + $path = 'sources'; + $parentExtn = urlencode(Util\Util::utf8($parent)); + $extn = urlencode(Util\Util::utf8($this['id'])); + return "$base/$parentExtn/$path/$extn"; } else { - $id = $this['id']; - $id = Util\Util::utf8($id); - $extn = urlencode($id); $base = BitcoinReceiver::classUrl(); + $extn = urlencode(Util\Util::utf8($this['id'])); return "$base/$extn"; } } - - /** - * @param string $id The ID of the Bitcoin Receiver to retrieve. - * @param array|string|null $opts - * - * @return BitcoinReceiver - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of BitcoinReceivers - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return BitcoinReceiver The created Bitcoin Receiver item. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $options - * - * @return BitcoinReceiver The refunded Bitcoin Receiver item. - */ - public function refund($params = null, $options = null) - { - $url = $this->instanceUrl() . '/refund'; - list($response, $opts) = $this->_request('post', $url, $params, $options); - $this->refreshFrom($response, $opts); - return $this; - } } diff --git a/htdocs/includes/stripe/lib/Card.php b/htdocs/includes/stripe/lib/Card.php index 227bee13873..7e57423601a 100644 --- a/htdocs/includes/stripe/lib/Card.php +++ b/htdocs/includes/stripe/lib/Card.php @@ -5,9 +5,93 @@ namespace Stripe; /** * Class Card * + * @property string $id + * @property string $object + * @property string $address_city + * @property string $address_country + * @property string $address_line1 + * @property string $address_line1_check + * @property string $address_line2 + * @property string $address_state + * @property string $address_zip + * @property string $address_zip_check + * @property string $brand + * @property string $country + * @property string $customer + * @property string $cvc_check + * @property string $dynamic_last4 + * @property int $exp_month + * @property int $exp_year + * @property string $fingerprint + * @property string $funding + * @property string $last4 + * @property StripeObject $metadata + * @property string $name + * @property string $tokenization_method + * * @package Stripe */ -class Card extends ExternalAccount +class Card extends ApiResource { + use ApiOperations\Delete; + use ApiOperations\Update; + /** + * @return string The instance URL for this resource. It needs to be special + * cased because cards are nested resources that may belong to different + * top-level resources. + */ + public function instanceUrl() + { + if ($this['customer']) { + $base = Customer::classUrl(); + $parent = $this['customer']; + $path = 'sources'; + } elseif ($this['account']) { + $base = Account::classUrl(); + $parent = $this['account']; + $path = 'external_accounts'; + } elseif ($this['recipient']) { + $base = Recipient::classUrl(); + $parent = $this['recipient']; + $path = 'cards'; + } else { + $msg = "Cards cannot be accessed without a customer ID, account ID or recipient ID."; + throw new Error\InvalidRequest($msg, null); + } + $parentExtn = urlencode(Util\Util::utf8($parent)); + $extn = urlencode(Util\Util::utf8($this['id'])); + return "$base/$parentExtn/$path/$extn"; + } + + /** + * @param array|string $_id + * @param array|string|null $_opts + * + * @throws \Stripe\Error\InvalidRequest + */ + public static function retrieve($_id, $_opts = null) + { + $msg = "Cards cannot be accessed without a customer, recipient or account ID. " . + "Retrieve a card using \$customer->sources->retrieve('card_id'), " . + "\$recipient->cards->retrieve('card_id'), or"; + "\$account->external_accounts->retrieve('card_id') instead."; + throw new Error\InvalidRequest($msg, null); + } + + /** + * @param string $_id + * @param array|null $_params + * @param array|string|null $_options + * + * @throws \Stripe\Error\InvalidRequest + */ + public static function update($_id, $_params = null, $_options = null) + { + $msg = "Cards cannot be accessed without a customer, recipient or account ID. " . + "Call save() on \$customer->sources->retrieve('card_id'), " . + "\$recipient->cards->retrieve('card_id'), or"; + "\$account->external_accounts->retrieve('card_id') instead."; + throw new Error\InvalidRequest($msg, null); + } } diff --git a/htdocs/includes/stripe/lib/Charge.php b/htdocs/includes/stripe/lib/Charge.php index ab1d5a33f8a..6d448022877 100644 --- a/htdocs/includes/stripe/lib/Charge.php +++ b/htdocs/includes/stripe/lib/Charge.php @@ -9,91 +9,47 @@ namespace Stripe; * @property string $object * @property int $amount * @property int $amount_refunded - * @property mixed $application_fee + * @property string $application + * @property string $application_fee * @property string $balance_transaction * @property bool $captured * @property int $created * @property string $currency * @property string $customer - * @property mixed $description - * @property mixed $destination - * @property string|null $dispute - * @property mixed $failure_code - * @property mixed $failure_message + * @property string $description + * @property string $destination + * @property string $dispute + * @property string $failure_code + * @property string $failure_message * @property mixed $fraud_details - * @property mixed $invoice + * @property string $invoice * @property bool $livemode - * @property mixed $metadata - * @property mixed $order + * @property StripeObject $metadata + * @property string $on_behalf_of + * @property string $order + * @property mixed $outcome * @property bool $paid - * @property mixed $receipt_email - * @property mixed $receipt_number + * @property string $receipt_email + * @property string $receipt_number * @property bool $refunded - * @property mixed $refunds + * @property Collection $refunds + * @property string $review * @property mixed $shipping * @property mixed $source - * @property mixed $source_transfer - * @property mixed $statement_descriptor + * @property string $source_transfer + * @property string $statement_descriptor * @property string $status + * @property string $transfer + * @property string $transfer_group * * @package Stripe */ class Charge extends ApiResource { - /** - * @param string $id The ID of the charge to retrieve. - * @param array|string|null $options - * - * @return Charge - */ - public static function retrieve($id, $options = null) - { - return self::_retrieve($id, $options); - } - - /** - * @param array|null $params - * @param array|string|null $options - * - * @return Collection of Charges - */ - public static function all($params = null, $options = null) - { - return self::_all($params, $options); - } - - /** - * @param array|null $params - * @param array|string|null $options - * - * @return Charge The created charge. - */ - public static function create($params = null, $options = null) - { - return self::_create($params, $options); - } - - /** - * @param string $id The ID of the charge to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Charge The updated charge. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $options - * - * @return Charge The saved charge. - */ - public function save($options = null) - { - return $this->_save($options); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** * @param array|null $params @@ -135,7 +91,7 @@ class Charge extends ApiResource { $url = $this->instanceUrl() . '/dispute'; list($response, $opts) = $this->_request('post', $url, $params, $options); - $this->refreshFrom(array('dispute' => $response), $opts, true); + $this->refreshFrom(['dispute' => $response], $opts, true); return $this->dispute; } @@ -161,7 +117,7 @@ class Charge extends ApiResource */ public function markAsFraudulent($opts = null) { - $params = array('fraud_details' => array('user_report' => 'fraudulent')); + $params = ['fraud_details' => ['user_report' => 'fraudulent']]; $url = $this->instanceUrl(); list($response, $opts) = $this->_request('post', $url, $params, $opts); $this->refreshFrom($response, $opts); @@ -175,7 +131,7 @@ class Charge extends ApiResource */ public function markAsSafe($opts = null) { - $params = array('fraud_details' => array('user_report' => 'safe')); + $params = ['fraud_details' => ['user_report' => 'safe']]; $url = $this->instanceUrl(); list($response, $opts) = $this->_request('post', $url, $params, $opts); $this->refreshFrom($response, $opts); diff --git a/htdocs/includes/stripe/lib/Collection.php b/htdocs/includes/stripe/lib/Collection.php index 0beb3a18918..cfb4014a0fe 100644 --- a/htdocs/includes/stripe/lib/Collection.php +++ b/htdocs/includes/stripe/lib/Collection.php @@ -12,9 +12,19 @@ namespace Stripe; * * @package Stripe */ -class Collection extends ApiResource +class Collection extends StripeObject { - protected $_requestParams = array(); + use ApiOperations\Request; + + protected $_requestParams = []; + + /** + * @return string The base URL for the given class. + */ + public static function baseUrl() + { + return Stripe::$apiBase; + } public function setRequestParams($params) { @@ -56,7 +66,7 @@ class Collection extends ApiResource } /** - * @return AutoPagingIterator An iterator that can be used to iterate + * @return Util\AutoPagingIterator An iterator that can be used to iterate * across all objects across all pages. As page boundaries are * encountered, the next page will be fetched automatically for * continued iteration. @@ -76,12 +86,11 @@ class Collection extends ApiResource if (isset($url['query'])) { // If the URL contains a query param, parse it out into $params so they // don't interact weirdly with each other. - $query = array(); + $query = []; parse_str($url['query'], $query); - // PHP 5.2 doesn't support the ?: operator :( - $params = array_merge($params ? $params : array(), $query); + $params = array_merge($params ?: [], $query); } - return array($url['path'], $params); + return [$url['path'], $params]; } } diff --git a/htdocs/includes/stripe/lib/CountrySpec.php b/htdocs/includes/stripe/lib/CountrySpec.php index dabb88b4522..cdaa4e84d89 100644 --- a/htdocs/includes/stripe/lib/CountrySpec.php +++ b/htdocs/includes/stripe/lib/CountrySpec.php @@ -5,10 +5,21 @@ namespace Stripe; /** * Class CountrySpec * + * @property string $id + * @property string $object + * @property string $default_currency + * @property mixed $supported_bank_account_currencies + * @property string[] $supported_payment_currencies + * @property string[] $supported_payment_methods + * @property mixed $verification_fields + * * @package Stripe */ class CountrySpec extends ApiResource { + use ApiOperations\All; + use ApiOperations\Retrieve; + /** * This is a special case because the country specs endpoint has an * underscore in it. The parent `className` function strips underscores. @@ -19,26 +30,4 @@ class CountrySpec extends ApiResource { return 'country_spec'; } - - /** - * @param string $country The ISO country code of the country we retrieve the CountrySpec for. - * @param array|string|null $opts - * - * @return CountrySpec - */ - public static function retrieve($country, $opts = null) - { - return self::_retrieve($country, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of CountrySpecs - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } } diff --git a/htdocs/includes/stripe/lib/Coupon.php b/htdocs/includes/stripe/lib/Coupon.php index 079cc10b478..a2d44443ce5 100644 --- a/htdocs/includes/stripe/lib/Coupon.php +++ b/htdocs/includes/stripe/lib/Coupon.php @@ -5,73 +5,28 @@ namespace Stripe; /** * Class Coupon * + * @property string $id + * @property string $object + * @property int $amount_off + * @property int $created + * @property string $currency + * @property string $duration + * @property int $duration_in_months + * @property bool $livemode + * @property int $max_redemptions + * @property StripeObject $metadata + * @property int $percent_off + * @property int $redeem_by + * @property int $times_redeemed + * @property bool $valid + * * @package Stripe */ class Coupon extends ApiResource { - /** - * @param string $id The ID of the coupon to retrieve. - * @param array|string|null $opts - * - * @return Coupon - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Coupon The created coupon. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the coupon to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Coupon The updated coupon. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Coupon The deleted coupon. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } - - /** - * @param array|string|null $opts - * - * @return Coupon The saved coupon. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Coupons - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; } diff --git a/htdocs/includes/stripe/lib/Customer.php b/htdocs/includes/stripe/lib/Customer.php index 1dfbfc6a16b..8d31b5ac0aa 100644 --- a/htdocs/includes/stripe/lib/Customer.php +++ b/htdocs/includes/stripe/lib/Customer.php @@ -17,7 +17,7 @@ namespace Stripe; * @property mixed $discount * @property string $email * @property bool $livemode - * @property array $metadata + * @property StripeObject $metadata * @property mixed $shipping * @property Collection $sources * @property Collection $subscriptions @@ -26,71 +26,25 @@ namespace Stripe; */ class Customer extends ApiResource { - /** - * @param string $id The ID of the customer to retrieve. - * @param array|string|null $opts - * - * @return Customer - */ - public static function retrieve($id, $opts = null) + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\NestedResource; + use ApiOperations\Retrieve; + use ApiOperations\Update; + + public static function getSavedNestedResources() { - return self::_retrieve($id, $opts); + static $savedNestedResources = null; + if ($savedNestedResources === null) { + $savedNestedResources = new Util\Set([ + 'source', + ]); + } + return $savedNestedResources; } - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Customers - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Customer The created customer. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the customer to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Customer The updated customer. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Customer The saved customer. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Customer The deleted customer. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } + const PATH_SOURCES = '/sources'; /** * @param array|null $params @@ -99,9 +53,7 @@ class Customer extends ApiResource */ public function addInvoiceItem($params = null) { - if (!$params) { - $params = array(); - } + $params = $params ?: []; $params['customer'] = $this->id; $ii = InvoiceItem::create($params, $this->_opts); return $ii; @@ -114,9 +66,7 @@ class Customer extends ApiResource */ public function invoices($params = null) { - if (!$params) { - $params = array(); - } + $params = $params ?: []; $params['customer'] = $this->id; $invoices = Invoice::all($params, $this->_opts); return $invoices; @@ -129,9 +79,7 @@ class Customer extends ApiResource */ public function invoiceItems($params = null) { - if (!$params) { - $params = array(); - } + $params = $params ?: []; $params['customer'] = $this->id; $iis = InvoiceItem::all($params, $this->_opts); return $iis; @@ -144,9 +92,7 @@ class Customer extends ApiResource */ public function charges($params = null) { - if (!$params) { - $params = array(); - } + $params = $params ?: []; $params['customer'] = $this->id; $charges = Charge::all($params, $this->_opts); return $charges; @@ -161,7 +107,7 @@ class Customer extends ApiResource { $url = $this->instanceUrl() . '/subscription'; list($response, $opts) = $this->_request('post', $url, $params); - $this->refreshFrom(array('subscription' => $response), $opts, true); + $this->refreshFrom(['subscription' => $response], $opts, true); return $this->subscription; } @@ -174,7 +120,7 @@ class Customer extends ApiResource { $url = $this->instanceUrl() . '/subscription'; list($response, $opts) = $this->_request('delete', $url, $params); - $this->refreshFrom(array('subscription' => $response), $opts, true); + $this->refreshFrom(['subscription' => $response], $opts, true); return $this->subscription; } @@ -185,6 +131,69 @@ class Customer extends ApiResource { $url = $this->instanceUrl() . '/discount'; list($response, $opts) = $this->_request('delete', $url); - $this->refreshFrom(array('discount' => null), $opts, true); + $this->refreshFrom(['discount' => null], $opts, true); + } + + /** + * @param array|null $id The ID of the customer on which to create the source. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource + */ + public static function createSource($id, $params = null, $opts = null) + { + return self::_createNestedResource($id, static::PATH_SOURCES, $params, $opts); + } + + /** + * @param array|null $id The ID of the customer to which the source belongs. + * @param array|null $sourceId The ID of the source to retrieve. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource + */ + public static function retrieveSource($id, $sourceId, $params = null, $opts = null) + { + return self::_retrieveNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts); + } + + /** + * @param array|null $id The ID of the customer to which the source belongs. + * @param array|null $sourceId The ID of the source to update. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource + */ + public static function updateSource($id, $sourceId, $params = null, $opts = null) + { + return self::_updateNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts); + } + + /** + * @param array|null $id The ID of the customer to which the source belongs. + * @param array|null $sourceId The ID of the source to delete. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource + */ + public static function deleteSource($id, $sourceId, $params = null, $opts = null) + { + return self::_deleteNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts); + } + + /** + * @param array|null $id The ID of the customer on which to retrieve the sources. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource + */ + public static function allSources($id, $params = null, $opts = null) + { + return self::_allNestedResources($id, static::PATH_SOURCES, $params, $opts); } } diff --git a/htdocs/includes/stripe/lib/Dispute.php b/htdocs/includes/stripe/lib/Dispute.php index ce70d499b35..7f4c233b009 100644 --- a/htdocs/includes/stripe/lib/Dispute.php +++ b/htdocs/includes/stripe/lib/Dispute.php @@ -16,7 +16,7 @@ namespace Stripe; * @property mixed $evidence_details * @property bool $is_charge_refundable * @property bool $livemode - * @property mixed $metadata + * @property StripeObject $metadata * @property string $reason * @property string $status * @@ -24,49 +24,40 @@ namespace Stripe; */ class Dispute extends ApiResource { - /** - * @param string $id The ID of the dispute to retrieve. - * @param array|string|null $options - * - * @return Dispute - */ - public static function retrieve($id, $options = null) - { - return self::_retrieve($id, $options); - } + use ApiOperations\All; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** - * @param array|null $params - * @param array|string|null $options - * - * @return array An array of Disputes. + * Possible string representations of dispute reasons. + * @link https://stripe.com/docs/api#dispute_object */ - public static function all($params = null, $options = null) - { - return self::_all($params, $options); - } + const REASON_BANK_CANNOT_PROCESS = 'bank_cannot_process'; + const REASON_CREDIT_NOT_PROCESSED = 'credit_not_processed'; + const REASON_CUSTOMER_INITIATED = 'customer_initiated'; + const REASON_DEBIT_NOT_AUTHORIZED = 'debit_not_authorized'; + const REASON_DUPLICATE = 'duplicate'; + const REASON_FRAUDULENT = 'fraudulent'; + const REASON_GENERAL = 'general'; + const REASON_INCORRECT_ACCOUNT_DETAILS = 'incorrect_account_details'; + const REASON_INSUFFICIENT_FUNDS = 'insufficient_funds'; + const REASON_PRODUCT_NOT_RECEIVED = 'product_not_received'; + const REASON_PRODUCT_UNACCEPTABLE = 'product_unacceptable'; + const REASON_SUBSCRIPTION_CANCELED = 'subscription_canceled'; + const REASON_UNRECOGNIZED = 'unrecognized'; /** - * @param string $id The ID of the dispute to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Dispute The updated dispute. + * Possible string representations of dispute statuses. + * @link https://stripe.com/docs/api#dispute_object */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $options - * - * @return Dispute The saved charge. - */ - public function save($options = null) - { - return $this->_save($options); - } + const STATUS_CHARGE_REFUNDED = 'charge_refunded'; + const STATUS_LOST = 'lost'; + const STATUS_NEEDS_RESPONSE = 'needs_response'; + const STATUS_UNDER_REVIEW = 'under_review'; + const STATUS_WARNING_CLOSED = 'warning_closed'; + const STATUS_WARNING_NEEDS_RESPONSE = 'warning_needs_response'; + const STATUS_WARNING_UNDER_REVIEW = 'warning_under_review'; + const STATUS_WON = 'won'; /** * @param array|string|null $options diff --git a/htdocs/includes/stripe/lib/EphemeralKey.php b/htdocs/includes/stripe/lib/EphemeralKey.php new file mode 100644 index 00000000000..5bbef09165d --- /dev/null +++ b/htdocs/includes/stripe/lib/EphemeralKey.php @@ -0,0 +1,49 @@ +httpHeaders = $httpHeaders; $this->requestId = null; + // TODO: make this a proper constructor argument in the next major + // release. + $this->stripeCode = isset($jsonBody["error"]["code"]) ? $jsonBody["error"]["code"] : null; + if ($httpHeaders && isset($httpHeaders['Request-Id'])) { $this->requestId = $httpHeaders['Request-Id']; } } + public function getStripeCode() + { + return $this->stripeCode; + } + public function getHttpStatus() { return $this->httpStatus; diff --git a/htdocs/includes/stripe/lib/Error/Card.php b/htdocs/includes/stripe/lib/Error/Card.php index 9004db271e3..f3ff343d899 100644 --- a/htdocs/includes/stripe/lib/Error/Card.php +++ b/htdocs/includes/stripe/lib/Error/Card.php @@ -15,12 +15,16 @@ class Card extends Base ) { parent::__construct($message, $httpStatus, $httpBody, $jsonBody, $httpHeaders); $this->stripeParam = $stripeParam; + + // TODO: once Error\Base accepts the error code as an argument, pass it + // in the call to parent::__construct() and stop setting it here. $this->stripeCode = $stripeCode; // This one is not like the others because it was added later and we're // trying to do our best not to change the public interface of this class' - // constructor. We should consider changing its implementation on the - // next major version bump of this library. + // constructor. + // TODO: make this a proper constructor argument in the next major + // release. $this->declineCode = isset($jsonBody["error"]["decline_code"]) ? $jsonBody["error"]["decline_code"] : null; } @@ -29,11 +33,6 @@ class Card extends Base return $this->declineCode; } - public function getStripeCode() - { - return $this->stripeCode; - } - public function getStripeParam() { return $this->stripeParam; diff --git a/htdocs/includes/stripe/lib/Error/Idempotency.php b/htdocs/includes/stripe/lib/Error/Idempotency.php new file mode 100644 index 00000000000..ea44d12e4ea --- /dev/null +++ b/htdocs/includes/stripe/lib/Error/Idempotency.php @@ -0,0 +1,7 @@ +code = $code; + } + + public function getErrorCode() + { + return $this->code; + } +} diff --git a/htdocs/includes/stripe/lib/Error/OAuth/UnsupportedGrantType.php b/htdocs/includes/stripe/lib/Error/OAuth/UnsupportedGrantType.php new file mode 100644 index 00000000000..421adc99645 --- /dev/null +++ b/htdocs/includes/stripe/lib/Error/OAuth/UnsupportedGrantType.php @@ -0,0 +1,11 @@ +sigHeader = $sigHeader; + } + + public function getSigHeader() + { + return $this->sigHeader; + } +} diff --git a/htdocs/includes/stripe/lib/Event.php b/htdocs/includes/stripe/lib/Event.php index 6a1ed6c44ba..2add5ce11be 100644 --- a/htdocs/includes/stripe/lib/Event.php +++ b/htdocs/includes/stripe/lib/Event.php @@ -12,32 +12,103 @@ namespace Stripe; * @property mixed $data * @property bool $livemode * @property int $pending_webhooks - * @property string $request + * @property mixed $request * @property string $type * * @package Stripe */ class Event extends ApiResource { - /** - * @param string $id The ID of the event to retrieve. - * @param array|string|null $opts - * - * @return Event + /** + * Possible string representations of event types. + * @link https://stripe.com/docs/api#event_types */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } + const ACCOUNT_UPDATED = 'account.updated'; + const ACCOUNT_APPLICATION_DEAUTHORIZED = 'account.application.deauthorized'; + const ACCOUNT_EXTERNAL_ACCOUNT_CREATED = 'account.external_account.created'; + const ACCOUNT_EXTERNAL_ACCOUNT_DELETED = 'account.external_account.deleted'; + const ACCOUNT_EXTERNAL_ACCOUNT_UPDATED = 'account.external_account.updated'; + const APPLICATION_FEE_CREATED = 'application_fee.created'; + const APPLICATION_FEE_REFUNDED = 'application_fee.refunded'; + const APPLICATION_FEE_REFUND_UPDATED = 'application_fee.refund.updated'; + const BALANCE_AVAILABLE = 'balance.available'; + const CHARGE_CAPTURED = 'charge.captured'; + const CHARGE_FAILED = 'charge.failed'; + const CHARGE_PENDING = 'charge.pending'; + const CHARGE_REFUNDED = 'charge.refunded'; + const CHARGE_SUCCEEDED = 'charge.succeeded'; + const CHARGE_UPDATED = 'charge.updated'; + const CHARGE_DISPUTE_CLOSED = 'charge.dispute.closed'; + const CHARGE_DISPUTE_CREATED = 'charge.dispute.created'; + const CHARGE_DISPUTE_FUNDS_REINSTATED = 'charge.dispute.funds_reinstated'; + const CHARGE_DISPUTE_FUNDS_WITHDRAWN = 'charge.dispute.funds_withdrawn'; + const CHARGE_DISPUTE_UPDATED = 'charge.dispute.updated'; + const CHARGE_REFUND_UPDATED = 'charge.refund.updated'; + const COUPON_CREATED = 'coupon.created'; + const COUPON_DELETED = 'coupon.deleted'; + const COUPON_UPDATED = 'coupon.updated'; + const CUSTOMER_CREATED = 'customer.created'; + const CUSTOMER_DELETED = 'customer.deleted'; + const CUSTOMER_UPDATED = 'customer.updated'; + const CUSTOMER_DISCOUNT_CREATED = 'customer.discount.created'; + const CUSTOMER_DISCOUNT_DELETED = 'customer.discount.deleted'; + const CUSTOMER_DISCOUNT_UPDATED = 'customer.discount.updated'; + const CUSTOMER_SOURCE_CREATED = 'customer.source.created'; + const CUSTOMER_SOURCE_DELETED = 'customer.source.deleted'; + const CUSTOMER_SOURCE_EXPIRING = 'customer.source.expiring'; + const CUSTOMER_SOURCE_UPDATED = 'customer.source.updated'; + const CUSTOMER_SUBSCRIPTION_CREATED = 'customer.subscription.created'; + const CUSTOMER_SUBSCRIPTION_DELETED = 'customer.subscription.deleted'; + const CUSTOMER_SUBSCRIPTION_TRIAL_WILL_END = 'customer.subscription.trial_will_end'; + const CUSTOMER_SUBSCRIPTION_UPDATED = 'customer.subscription.updated'; + const FILE_CREATED = 'file.created'; + const INVOICE_CREATED = 'invoice.created'; + const INVOICE_PAYMENT_FAILED = 'invoice.payment_failed'; + const INVOICE_PAYMENT_SUCCEEDED = 'invoice.payment_succeeded'; + const INVOICE_SENT = 'invoice.sent'; + const INVOICE_UPCOMING = 'invoice.upcoming'; + const INVOICE_UPDATED = 'invoice.updated'; + const INVOICEITEM_CREATED = 'invoiceitem.created'; + const INVOICEITEM_DELETED = 'invoiceitem.deleted'; + const INVOICEITEM_UPDATED = 'invoiceitem.updated'; + const ORDER_CREATED = 'order.created'; + const ORDER_PAYMENT_FAILED = 'order.payment_failed'; + const ORDER_PAYMENT_SUCCEEDED = 'order.payment_succeeded'; + const ORDER_UPDATED = 'order.updated'; + const ORDER_RETURN_CREATED = 'order_return.created'; + const PAYOUT_CANCELED = 'payout.canceled'; + const PAYOUT_CREATED = 'payout.created'; + const PAYOUT_FAILED = 'payout.failed'; + const PAYOUT_PAID = 'payout.paid'; + const PAYOUT_UPDATED = 'payout.updated'; + const PING = 'ping'; + const PLAN_CREATED = 'plan.created'; + const PLAN_DELETED = 'plan.deleted'; + const PLAN_UPDATED = 'plan.updated'; + const PRODUCT_CREATED = 'product.created'; + const PRODUCT_DELETED = 'product.deleted'; + const PRODUCT_UPDATED = 'product.updated'; + const RECIPIENT_CREATED = 'recipient.created'; + const RECIPIENT_DELETED = 'recipient.deleted'; + const RECIPIENT_UPDATED = 'recipient.updated'; + const REVIEW_CLOSED = 'review.closed'; + const REVIEW_OPENED = 'review.opened'; + const SIGMA_SCHEDULED_QUERY_RUN_CREATED = 'sigma.scheduled_query_run.created'; + const SKU_CREATED = 'sku.created'; + const SKU_DELETED = 'sku.deleted'; + const SKU_UPDATED = 'sku.updated'; + const SOURCE_CANCELED = 'source.canceled'; + const SOURCE_CHARGEABLE = 'source.chargeable'; + const SOURCE_FAILED = 'source.failed'; + const SOURCE_MANDATE_NOTIFICATION = 'source.mandate_notification'; + const SOURCE_TRANSACTION_CREATED = 'source.transaction.created'; + const TOPUP_CREATED = 'topup.created'; + const TOPUP_FAILED = 'topup.failed'; + const TOPUP_SUCCEEDED = 'topup.succeeded'; + const TRANSFER_CREATED = 'transfer.created'; + const TRANSFER_REVERSED = 'transfer.reversed'; + const TRANSFER_UPDATED = 'transfer.updated'; - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Events - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Retrieve; } diff --git a/htdocs/includes/stripe/lib/ExchangeRate.php b/htdocs/includes/stripe/lib/ExchangeRate.php new file mode 100644 index 00000000000..40763261834 --- /dev/null +++ b/htdocs/includes/stripe/lib/ExchangeRate.php @@ -0,0 +1,25 @@ +_delete($params, $opts); - } - - /** - * @param array|string|null $opts - * - * @return ExternalAccount The saved external account. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return ExternalAccount The verified (or not) external account. - */ - public function verify($params = null, $opts = null) - { - if ($this['customer']) { - $url = $this->instanceUrl() . '/verify'; - list($response, $options) = $this->_request('post', $url, $params, $opts); - $this->refreshFrom($response, $options); - return $this; - } else { - $message = 'Only customer external accounts can be verified in this manner.'; - throw new Error\Api($message); - } - } -} diff --git a/htdocs/includes/stripe/lib/FileUpload.php b/htdocs/includes/stripe/lib/FileUpload.php index 7dc98c78d1c..aa0b733959c 100644 --- a/htdocs/includes/stripe/lib/FileUpload.php +++ b/htdocs/includes/stripe/lib/FileUpload.php @@ -16,6 +16,10 @@ namespace Stripe; */ class FileUpload extends ApiResource { + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Retrieve; + public static function baseUrl() { return Stripe::$apiUploadBase; @@ -25,37 +29,4 @@ class FileUpload extends ApiResource { return 'file'; } - - /** - * @param string $id The ID of the file upload to retrieve. - * @param array|string|null $opts - * - * @return FileUpload - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return FileUpload The created file upload. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of FileUploads - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } } diff --git a/htdocs/includes/stripe/lib/HttpClient/ClientInterface.php b/htdocs/includes/stripe/lib/HttpClient/ClientInterface.php index dc4af5d1dc2..606ddb1a597 100644 --- a/htdocs/includes/stripe/lib/HttpClient/ClientInterface.php +++ b/htdocs/includes/stripe/lib/HttpClient/ClientInterface.php @@ -11,8 +11,8 @@ interface ClientInterface * @param array $params KV pairs for parameters. Can be nested for arrays and hashes * @param boolean $hasFile Whether or not $params references a file (via an @ prefix or * CurlFile) - * @throws Error\Api & Error\ApiConnection - * @return array($rawBody, $httpStatusCode, $httpHeader) + * @throws \Stripe\Error\Api & \Stripe\Error\ApiConnection + * @return [$rawBody, $httpStatusCode, $httpHeader] */ public function request($method, $absUrl, $headers, $params, $hasFile); } diff --git a/htdocs/includes/stripe/lib/HttpClient/CurlClient.php b/htdocs/includes/stripe/lib/HttpClient/CurlClient.php index e070d324d72..6bc86e32b3a 100644 --- a/htdocs/includes/stripe/lib/HttpClient/CurlClient.php +++ b/htdocs/includes/stripe/lib/HttpClient/CurlClient.php @@ -36,6 +36,8 @@ class CurlClient implements ClientInterface protected $defaultOptions; + protected $userAgentInfo; + /** * CurlClient constructor. * @@ -49,9 +51,20 @@ class CurlClient implements ClientInterface * * @param array|callable|null $defaultOptions */ - public function __construct($defaultOptions = null) + public function __construct($defaultOptions = null, $randomGenerator = null) { $this->defaultOptions = $defaultOptions; + $this->randomGenerator = $randomGenerator ?: new Util\RandomGenerator(); + $this->initUserAgentInfo(); + } + + public function initUserAgentInfo() + { + $curlVersion = curl_version(); + $this->userAgentInfo = [ + 'httplib' => 'curl ' . $curlVersion['version'], + 'ssllib' => $curlVersion['ssl_version'], + ]; } public function getDefaultOptions() @@ -59,6 +72,11 @@ class CurlClient implements ClientInterface return $this->defaultOptions; } + public function getUserAgentInfo() + { + return $this->userAgentInfo; + } + // USER DEFINED TIMEOUTS const DEFAULT_TIMEOUT = 80; @@ -93,10 +111,9 @@ class CurlClient implements ClientInterface public function request($method, $absUrl, $headers, $params, $hasFile) { - $curl = curl_init(); $method = strtolower($method); - $opts = array(); + $opts = []; if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value $opts = call_user_func_array($this->defaultOptions, func_get_args()); if (!is_array($opts)) { @@ -114,24 +131,32 @@ class CurlClient implements ClientInterface } $opts[CURLOPT_HTTPGET] = 1; if (count($params) > 0) { - $encoded = self::encode($params); + $encoded = Util\Util::urlEncode($params); $absUrl = "$absUrl?$encoded"; } } elseif ($method == 'post') { $opts[CURLOPT_POST] = 1; - $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : self::encode($params); + $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : Util\Util::urlEncode($params); } elseif ($method == 'delete') { $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; if (count($params) > 0) { - $encoded = self::encode($params); + $encoded = Util\Util::urlEncode($params); $absUrl = "$absUrl?$encoded"; } } else { throw new Error\Api("Unrecognized method $method"); } + // It is only safe to retry network failures on POST requests if we + // add an Idempotency-Key header + if (($method == 'post') && (Stripe::$maxNetworkRetries > 0)) { + if (!isset($headers['Idempotency-Key'])) { + array_push($headers, 'Idempotency-Key: ' . $this->randomGenerator->uuid()); + } + } + // Create a callback to capture HTTP headers for the response - $rheaders = array(); + $rheaders = []; $headerCallback = function ($curl, $header_line) use (&$rheaders) { // Ignore the HTTP request line (HTTP/1.1 200 OK) if (strpos($header_line, ":") === false) { @@ -163,50 +188,63 @@ class CurlClient implements ClientInterface $opts[CURLOPT_TIMEOUT] = $this->timeout; $opts[CURLOPT_HEADERFUNCTION] = $headerCallback; $opts[CURLOPT_HTTPHEADER] = $headers; - if (!Stripe::$verifySslCerts) { + $opts[CURLOPT_CAINFO] = Stripe::getCABundlePath(); + if (!Stripe::getVerifySslCerts()) { $opts[CURLOPT_SSL_VERIFYPEER] = false; } - curl_setopt_array($curl, $opts); - $rbody = curl_exec($curl); + list($rbody, $rcode) = $this->executeRequestWithRetries($opts, $absUrl); - if (!defined('CURLE_SSL_CACERT_BADFILE')) { - define('CURLE_SSL_CACERT_BADFILE', 77); // constant not defined in PHP - } - - $errno = curl_errno($curl); - if ($errno == CURLE_SSL_CACERT || - $errno == CURLE_SSL_PEER_CERTIFICATE || - $errno == CURLE_SSL_CACERT_BADFILE - ) { - array_push( - $headers, - 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}' - ); - $cert = self::caBundle(); - curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); - curl_setopt($curl, CURLOPT_CAINFO, $cert); - $rbody = curl_exec($curl); - } - - if ($rbody === false) { - $errno = curl_errno($curl); - $message = curl_error($curl); - curl_close($curl); - $this->handleCurlError($absUrl, $errno, $message); - } - - $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); - curl_close($curl); - return array($rbody, $rcode, $rheaders); + return [$rbody, $rcode, $rheaders]; } /** - * @param number $errno + * @param array $opts cURL options + */ + private function executeRequestWithRetries($opts, $absUrl) + { + $numRetries = 0; + + while (true) { + $rcode = 0; + $errno = 0; + + $curl = curl_init(); + curl_setopt_array($curl, $opts); + $rbody = curl_exec($curl); + + if ($rbody === false) { + $errno = curl_errno($curl); + $message = curl_error($curl); + } else { + $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + } + curl_close($curl); + + if ($this->shouldRetry($errno, $rcode, $numRetries)) { + $numRetries += 1; + $sleepSeconds = $this->sleepTime($numRetries); + usleep(intval($sleepSeconds * 1000000)); + } else { + break; + } + } + + if ($rbody === false) { + $this->handleCurlError($absUrl, $errno, $message, $numRetries); + } + + return [$rbody, $rcode]; + } + + /** + * @param string $url + * @param int $errno * @param string $message + * @param int $numRetries * @throws Error\ApiConnection */ - private function handleCurlError($url, $errno, $message) + private function handleCurlError($url, $errno, $message, $numRetries) { switch ($errno) { case CURLE_COULDNT_CONNECT: @@ -231,52 +269,66 @@ class CurlClient implements ClientInterface $msg .= " let us know at support@stripe.com."; $msg .= "\n\n(Network error [errno $errno]: $message)"; + + if ($numRetries > 0) { + $msg .= "\n\nRequest was retried $numRetries times."; + } + throw new Error\ApiConnection($msg); } - private static function caBundle() + /** + * Checks if an error is a problem that we should retry on. This includes both + * socket errors that may represent an intermittent problem and some special + * HTTP statuses. + * @param int $errno + * @param int $rcode + * @param int $numRetries + * @return bool + */ + private function shouldRetry($errno, $rcode, $numRetries) { - return dirname(__FILE__) . '/../../data/ca-certificates.crt'; + if ($numRetries >= Stripe::getMaxNetworkRetries()) { + return false; + } + + // Retry on timeout-related problems (either on open or read). + if ($errno === CURLE_OPERATION_TIMEOUTED) { + return true; + } + + // Destination refused the connection, the connection was reset, or a + // variety of other connection failures. This could occur from a single + // saturated server, so retry in case it's intermittent. + if ($errno === CURLE_COULDNT_CONNECT) { + return true; + } + + // 409 conflict + if ($rcode === 409) { + return true; + } + + return false; } - /** - * @param array $arr An map of param keys to values. - * @param string|null $prefix - * - * Only public for testability, should not be called outside of CurlClient - * - * @return string A querystring, essentially. - */ - public static function encode($arr, $prefix = null) + private function sleepTime($numRetries) { - if (!is_array($arr)) { - return $arr; - } + // Apply exponential backoff with $initialNetworkRetryDelay on the + // number of $numRetries so far as inputs. Do not allow the number to exceed + // $maxNetworkRetryDelay. + $sleepSeconds = min( + Stripe::getInitialNetworkRetryDelay() * 1.0 * pow(2, $numRetries - 1), + Stripe::getMaxNetworkRetryDelay() + ); - $r = array(); - foreach ($arr as $k => $v) { - if (is_null($v)) { - continue; - } + // Apply some jitter by randomizing the value in the range of + // ($sleepSeconds / 2) to ($sleepSeconds). + $sleepSeconds *= 0.5 * (1 + $this->randomGenerator->randFloat()); - if ($prefix) { - if ($k !== null && (!is_int($k) || is_array($v))) { - $k = $prefix."[".$k."]"; - } else { - $k = $prefix."[]"; - } - } + // But never sleep less than the base sleep seconds. + $sleepSeconds = max(Stripe::getInitialNetworkRetryDelay(), $sleepSeconds); - if (is_array($v)) { - $enc = self::encode($v, $k); - if ($enc) { - $r[] = $enc; - } - } else { - $r[] = urlencode($k)."=".urlencode($v); - } - } - - return implode("&", $r); + return $sleepSeconds; } } diff --git a/htdocs/includes/stripe/lib/Invoice.php b/htdocs/includes/stripe/lib/Invoice.php index e994566cd96..07d67f74a8a 100644 --- a/htdocs/includes/stripe/lib/Invoice.php +++ b/htdocs/includes/stripe/lib/Invoice.php @@ -5,54 +5,52 @@ namespace Stripe; /** * Class Invoice * + * @property string $id + * @property string $object + * @property int $amount_due + * @property int $amount_paid + * @property int $amount_remaining + * @property int $application_fee + * @property int $attempt_count + * @property bool $attempted + * @property string $billing + * @property string $charge + * @property bool $closed + * @property string $currency + * @property string $customer + * @property int $date + * @property string $description + * @property mixed $discount + * @property int $due_date + * @property int $ending_balance + * @property bool $forgiven + * @property Collection $lines + * @property bool $livemode + * @property StripeObject $metadata + * @property int $next_payment_attempt + * @property string $number + * @property bool $paid + * @property int $period_end + * @property int $period_start + * @property string $receipt_number + * @property int $starting_balance + * @property string $statement_descriptor + * @property string $subscription + * @property int $subscription_proration_date + * @property int $subtotal + * @property int $tax + * @property float $tax_percent + * @property int $total + * @property int $webhooks_delivered_at + * * @package Stripe */ class Invoice extends ApiResource { - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Invoice The created invoice. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the invoice to retrieve. - * @param array|string|null $opts - * - * @return Invoice - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Invoices - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param string $id The ID of the invoice to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Invoice The updated invoice. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** * @param array|null $params @@ -69,23 +67,13 @@ class Invoice extends ApiResource return $obj; } - /** - * @param array|string|null $opts - * - * @return Invoice The saved invoice. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - /** * @return Invoice The paid invoice. */ - public function pay($opts = null) + public function pay($params = null, $opts = null) { $url = $this->instanceUrl() . '/pay'; - list($response, $opts) = $this->_request('post', $url, null, $opts); + list($response, $opts) = $this->_request('post', $url, $params, $opts); $this->refreshFrom($response, $opts); return $this; } diff --git a/htdocs/includes/stripe/lib/InvoiceItem.php b/htdocs/includes/stripe/lib/InvoiceItem.php index ab9c4ba3327..37e39f3e105 100644 --- a/htdocs/includes/stripe/lib/InvoiceItem.php +++ b/htdocs/includes/stripe/lib/InvoiceItem.php @@ -5,73 +5,31 @@ namespace Stripe; /** * Class InvoiceItem * + * @property string $id + * @property string $object + * @property int $amount + * @property string $currency + * @property string $customer + * @property int $date + * @property string $description + * @property bool $discountable + * @property string $invoice + * @property bool $livemode + * @property StripeObject $metadata + * @property mixed $period + * @property Plan $plan + * @property bool $proration + * @property int $quantity + * @property string $subscription + * @property string $subscription_item + * * @package Stripe */ class InvoiceItem extends ApiResource { - /** - * @param string $id The ID of the invoice item to retrieve. - * @param array|string|null $opts - * - * @return InvoiceItem - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of InvoiceItems - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return InvoiceItem The created invoice item. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the invoice item to update. - * @param array|null $params - * @param array|string|null $options - * - * @return InvoiceItem The updated invoice item. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return InvoiceItem The saved invoice item. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return InvoiceItem The deleted invoice item. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; } diff --git a/htdocs/includes/stripe/lib/JsonSerializable.php b/htdocs/includes/stripe/lib/JsonSerializable.php deleted file mode 100644 index 2fdf8526703..00000000000 --- a/htdocs/includes/stripe/lib/JsonSerializable.php +++ /dev/null @@ -1,18 +0,0 @@ -request( + 'post', + '/oauth/token', + $params, + null + ); + return Util\Util::convertToStripeObject($response->json, $opts); + } + + /** + * Disconnects an account from your platform. + * + * @param array|null $params + * @param array|null $opts + * + * @return StripeObject Object containing the response from the API. + */ + public static function deauthorize($params = null, $opts = null) + { + $params = $params ?: []; + $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase; + $requestor = new ApiRequestor(null, $base); + $params['client_id'] = self::_getClientId($params); + list($response, $apiKey) = $requestor->request( + 'post', + '/oauth/deauthorize', + $params, + null + ); + return Util\Util::convertToStripeObject($response->json, $opts); + } + + private static function _getClientId($params = null) + { + $clientId = ($params && array_key_exists('client_id', $params)) ? $params['client_id'] : null; + if ($clientId === null) { + $clientId = Stripe::getClientId(); + } + if ($clientId === null) { + $msg = 'No client_id provided. (HINT: set your client_id using ' + . '"Stripe::setClientId()". You can find your client_ids ' + . 'in your Stripe dashboard at ' + . 'https://dashboard.stripe.com/account/applications/settings, ' + . 'after registering your account as a platform. See ' + . 'https://stripe.com/docs/connect/standard-accounts for details, ' + . 'or email support@stripe.com if you have any questions.'; + throw new Error\Authentication($msg); + } + return $clientId; + } +} diff --git a/htdocs/includes/stripe/lib/Order.php b/htdocs/includes/stripe/lib/Order.php index 9fc65e2e166..14215bc9ee3 100644 --- a/htdocs/includes/stripe/lib/Order.php +++ b/htdocs/includes/stripe/lib/Order.php @@ -5,64 +5,38 @@ namespace Stripe; /** * Class Order * + * @property string $id + * @property string $object + * @property int $amount + * @property int $amount_returned + * @property string $application + * @property int $application_fee + * @property string $charge + * @property int $created + * @property string $currency + * @property string $customer + * @property string $email + * @property string $external_coupon_code + * @property mixed $items + * @property bool $livemode + * @property StripeObject $metadata + * @property Collection $returns + * @property string $selected_shipping_method + * @property mixed $shipping + * @property mixed $shipping_methods + * @property string $status + * @property mixed $status_transitions + * @property int $updated + * @property string $upstream_id + * * @package Stripe */ class Order extends ApiResource { - /** - * @param string $id The ID of the Order to retrieve. - * @param array|string|null $opts - * - * @return Order - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Order The created Order. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the order to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Order The updated order. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Order The saved Order. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Orders - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** * @return Order The paid order. diff --git a/htdocs/includes/stripe/lib/OrderReturn.php b/htdocs/includes/stripe/lib/OrderReturn.php index aa7fd4e9b99..45a69e37835 100644 --- a/htdocs/includes/stripe/lib/OrderReturn.php +++ b/htdocs/includes/stripe/lib/OrderReturn.php @@ -9,25 +9,17 @@ namespace Stripe; */ class OrderReturn extends ApiResource { - /** - * @param string $id The ID of the OrderReturn to retrieve. - * @param array|string|null $opts - * - * @return Order - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } + use ApiOperations\All; + use ApiOperations\Retrieve; /** - * @param array|null $params - * @param array|string|null $opts + * This is a special case because the order returns endpoint has an + * underscore in it. The parent `className` function strips underscores. * - * @return Collection of OrderReturns + * @return string The name of the class. */ - public static function all($params = null, $opts = null) + public static function className() { - return self::_all($params, $opts); + return 'order_return'; } } diff --git a/htdocs/includes/stripe/lib/Payout.php b/htdocs/includes/stripe/lib/Payout.php index 90dd0fcecc3..0849cae3f61 100644 --- a/htdocs/includes/stripe/lib/Payout.php +++ b/htdocs/includes/stripe/lib/Payout.php @@ -17,7 +17,7 @@ namespace Stripe; * @property string $failure_code * @property string $failure_message * @property bool $livemode - * @property mixed $metadata + * @property StripeObject $metadata * @property string $method * @property string $recipient * @property string $source_type @@ -29,50 +29,10 @@ namespace Stripe; */ class Payout extends ApiResource { - /** - * @param string $id The ID of the payout to retrieve. - * @param array|string|null $opts - * - * @return Payout - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Payouts - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Payout The created payout. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the payout to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Payout The updated payout. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** * @return Payout The canceled payout. @@ -84,14 +44,4 @@ class Payout extends ApiResource $this->refreshFrom($response, $opts); return $this; } - - /** - * @param array|string|null $opts - * - * @return Payout The saved payout. - */ - public function save($opts = null) - { - return $this->_save($opts); - } } diff --git a/htdocs/includes/stripe/lib/Plan.php b/htdocs/includes/stripe/lib/Plan.php index b911e17cbe6..8b0126b6f2d 100644 --- a/htdocs/includes/stripe/lib/Plan.php +++ b/htdocs/includes/stripe/lib/Plan.php @@ -7,84 +7,24 @@ namespace Stripe; * * @package Stripe * - * @property $id - * @property $object - * @property $amount - * @property $created - * @property $currency - * @property $interval - * @property $interval_count - * @property $livemode - * @property AttachedObject $metadata - * @property $name - * @property $statement_descriptor - * @property $trial_period_days + * @property string $id + * @property string $object + * @property int $amount + * @property int $created + * @property string $currency + * @property string $interval + * @property int $interval_count + * @property bool $livemode + * @property StripeObject $metadata + * @property string $nickname + * @property string $product + * @property int $trial_period_days */ class Plan extends ApiResource { - /** - * @param string $id The ID of the plan to retrieve. - * @param array|string|null $opts - * - * @return Plan - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Plan The created plan. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the plan to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Plan The updated plan. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Plan The deleted plan. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } - - /** - * @param array|string|null $opts - * - * @return Plan The saved plan. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Plans - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; } diff --git a/htdocs/includes/stripe/lib/Product.php b/htdocs/includes/stripe/lib/Product.php index 2fdd22a0827..20bd4e1ae44 100644 --- a/htdocs/includes/stripe/lib/Product.php +++ b/htdocs/includes/stripe/lib/Product.php @@ -5,73 +5,33 @@ namespace Stripe; /** * Class Product * + * @property string $id + * @property string $object + * @property bool $active + * @property string[] $attributes + * @property string $caption + * @property int $created + * @property string[] $deactivate_on + * @property string $description + * @property array $images + * @property bool $livemode + * @property StripeObject $metadata + * @property string $name + * @property mixed $package_dimensions + * @property bool $shippable + * @property Collection $skus + * @property string $statement_descriptor + * @property string $type + * @property int $updated + * @property string $url + * * @package Stripe */ class Product extends ApiResource { - /** - * @param string $id The ID of the Product to retrieve. - * @param array|string|null $opts - * - * @return Product - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Product The created Product. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the product to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Product The updated product. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Product The saved Product. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Products - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Product The deleted product. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; } diff --git a/htdocs/includes/stripe/lib/Recipient.php b/htdocs/includes/stripe/lib/Recipient.php index 04bcb7bb289..9ec6a1ec847 100644 --- a/htdocs/includes/stripe/lib/Recipient.php +++ b/htdocs/includes/stripe/lib/Recipient.php @@ -9,71 +9,11 @@ namespace Stripe; */ class Recipient extends ApiResource { - /** - * @param string $id The ID of the recipient to retrieve. - * @param array|string|null $opts - * - * @return Recipient - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Recipients - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Recipient The created recipient. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the recipient to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Recipient The updated recipient. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Recipient The saved recipient. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * - * @return Recipient The deleted recipient. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } - + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** * @param array|null $params @@ -82,9 +22,7 @@ class Recipient extends ApiResource */ public function transfers($params = null) { - if ($params === null) { - $params = array(); - } + $params = $params ?: []; $params['recipient'] = $this->id; $transfers = Transfer::all($params, $this->_opts); return $transfers; diff --git a/htdocs/includes/stripe/lib/RecipientTransfer.php b/htdocs/includes/stripe/lib/RecipientTransfer.php index f4e46db38dd..610514dc329 100644 --- a/htdocs/includes/stripe/lib/RecipientTransfer.php +++ b/htdocs/includes/stripe/lib/RecipientTransfer.php @@ -20,7 +20,7 @@ namespace Stripe; * @property string $failure_code * @property string $failure_message * @property bool $livemode - * @property mixed $metadata + * @property StripeObject $metadata * @property string $method * @property string $recipient * @property mixed $reversals diff --git a/htdocs/includes/stripe/lib/Refund.php b/htdocs/includes/stripe/lib/Refund.php index b4e0e76a7b0..0288895d68d 100644 --- a/htdocs/includes/stripe/lib/Refund.php +++ b/htdocs/includes/stripe/lib/Refund.php @@ -8,11 +8,13 @@ namespace Stripe; * @property string $id * @property string $object * @property int $amount - * @property mixed $balance_transaction + * @property string $balance_transaction * @property string $charge * @property int $created * @property string $currency - * @property mixed $metadata + * @property string $failure_balance_transaction + * @property string failure_reason + * @property StripeObject $metadata * @property mixed $reason * @property mixed $receipt_number * @property string $status @@ -21,59 +23,8 @@ namespace Stripe; */ class Refund extends ApiResource { - - /** - * @param string $id The ID of the refund to retrieve. - * @param array|string|null $options - * - * @return Refund - */ - public static function retrieve($id, $options = null) - { - return self::_retrieve($id, $options); - } - - /** - * @param string $id The ID of the refund to update. - * @param array|null $params - * @param array|string|null $options - * - * @return Refund The updated refund. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|null $params - * @param array|string|null $options - * - * @return Collection of Refunds - */ - public static function all($params = null, $options = null) - { - return self::_all($params, $options); - } - - /** - * @param array|null $params - * @param array|string|null $options - * - * @return Refund The created refund. - */ - public static function create($params = null, $options = null) - { - return self::_create($params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Refund The saved refund. - */ - public function save($opts = null) - { - return $this->_save($opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Retrieve; + use ApiOperations\Update; } diff --git a/htdocs/includes/stripe/lib/SKU.php b/htdocs/includes/stripe/lib/SKU.php index 7089604e62a..1f35da4c759 100644 --- a/htdocs/includes/stripe/lib/SKU.php +++ b/htdocs/includes/stripe/lib/SKU.php @@ -5,73 +5,28 @@ namespace Stripe; /** * Class SKU * + * @property string $id + * @property string $object + * @property bool $active + * @property mixed $attributes + * @property int $created + * @property string $currency + * @property string $image + * @property mixed $inventory + * @property bool $livemode + * @property StripeObject $metadata + * @property mixed $package_dimensions + * @property int $price + * @property string $product + * @property int $updated + * * @package Stripe */ class SKU extends ApiResource { - /** - * @param string $id The ID of the SKU to retrieve. - * @param array|string|null $opts - * - * @return SKU - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return SKU The created SKU. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the SKU to update. - * @param array|null $params - * @param array|string|null $options - * - * @return SKU The updated SKU. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return SKU The saved SKU. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of SKUs - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return SKU The deleted sku. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; } diff --git a/htdocs/includes/stripe/lib/Source.php b/htdocs/includes/stripe/lib/Source.php index 946586dec45..4a889c5a4e5 100644 --- a/htdocs/includes/stripe/lib/Source.php +++ b/htdocs/includes/stripe/lib/Source.php @@ -5,72 +5,39 @@ namespace Stripe; /** * Class Source * + * @property string $id + * @property string $object + * @property int $amount + * @property string $client_secret + * @property mixed $code_verification + * @property int $created + * @property string $currency + * @property string $flow + * @property bool $livemode + * @property StripeObject $metadata + * @property mixed $owner + * @property mixed $receiver + * @property mixed $redirect + * @property string $statement_descriptor + * @property string $status + * @property string $type + * @property string $usage + * * @package Stripe */ class Source extends ApiResource { - /** - * @param string $id The ID of the Source to retrieve. - * @param array|string|null $opts - * - * @return Source - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } + use ApiOperations\Create; + use ApiOperations\Retrieve; + use ApiOperations\Update; /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Sources - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Source The created Source. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the source to update. * @param array|null $params * @param array|string|null $options * - * @return Source The updated source. + * @return Source The detached source. */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return Source The saved source. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Source The deleted source. - */ - public function delete($params = null, $options = null) + public function detach($params = null, $options = null) { self::_validateParams($params); @@ -92,9 +59,8 @@ class Source extends ApiResource $this->refreshFrom($response, $opts); return $this; } else { - $message = "Source objects cannot be deleted, they can only be " - . "detached from customer objects. This source object does not " - . "appear to be currently attached to a customer object."; + $message = "This source object does not appear to be currently attached " + . "to a customer object."; throw new Error\Api($message); } } @@ -103,7 +69,35 @@ class Source extends ApiResource * @param array|null $params * @param array|string|null $options * - * @return BankAccount The verified bank account. + * @return Source The detached source. + * + * @deprecated Use the `detach` method instead. + */ + public function delete($params = null, $options = null) + { + $this->detach($params, $options); + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @return Collection The list of source transactions. + */ + public function sourceTransactions($params = null, $options = null) + { + $url = $this->instanceUrl() . '/source_transactions'; + list($response, $opts) = $this->_request('get', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @return Source The verified source. */ public function verify($params = null, $options = null) { diff --git a/htdocs/includes/stripe/lib/SourceTransaction.php b/htdocs/includes/stripe/lib/SourceTransaction.php new file mode 100644 index 00000000000..a0c8d776b9a --- /dev/null +++ b/htdocs/includes/stripe/lib/SourceTransaction.php @@ -0,0 +1,13 @@ +_retrieveOptions) = Util\Util::normalizeId($id); + $this->_opts = Util\RequestOptions::parse($opts); + $this->_originalValues = []; + $this->_values = []; + $this->_unsavedValues = new Util\Set(); + $this->_transientValues = new Util\Set(); + if ($id !== null) { + $this->_values['id'] = $id; + } + } + + // Standard accessor magic methods + public function __set($k, $v) + { + if (static::getPermanentAttributes()->includes($k)) { + throw new \InvalidArgumentException( + "Cannot set $k on this object. HINT: you can't set: " . + join(', ', static::getPermanentAttributes()->toArray()) + ); + } + + if ($v === "") { + throw new \InvalidArgumentException( + 'You cannot set \''.$k.'\'to an empty string. ' + .'We interpret empty strings as NULL in requests. ' + .'You may set obj->'.$k.' = NULL to delete the property' + ); + } + + $this->_values[$k] = Util\Util::convertToStripeObject($v, $this->_opts); + $this->dirtyValue($this->_values[$k]); + $this->_unsavedValues->add($k); + } + + public function __isset($k) + { + return isset($this->_values[$k]); + } + + public function __unset($k) + { + unset($this->_values[$k]); + $this->_transientValues->add($k); + $this->_unsavedValues->discard($k); + } + + public function &__get($k) + { + // function should return a reference, using $nullval to return a reference to null + $nullval = null; + if (!empty($this->_values) && array_key_exists($k, $this->_values)) { + return $this->_values[$k]; + } else if (!empty($this->_transientValues) && $this->_transientValues->includes($k)) { + $class = get_class($this); + $attrs = join(', ', array_keys($this->_values)); + $message = "Stripe Notice: Undefined property of $class instance: $k. " + . "HINT: The $k attribute was set in the past, however. " + . "It was then wiped when refreshing the object " + . "with the result returned by Stripe's API, " + . "probably as a result of a save(). The attributes currently " + . "available on this object are: $attrs"; + Stripe::getLogger()->error($message); + return $nullval; + } else { + $class = get_class($this); + Stripe::getLogger()->error("Stripe Notice: Undefined property of $class instance: $k"); + return $nullval; + } + } + + // Magic method for var_dump output. Only works with PHP >= 5.6 + public function __debugInfo() + { + return $this->_values; + } + + // ArrayAccess methods + public function offsetSet($k, $v) + { + $this->$k = $v; + } + + public function offsetExists($k) + { + return array_key_exists($k, $this->_values); + } + + public function offsetUnset($k) + { + unset($this->$k); + } + + public function offsetGet($k) + { + return array_key_exists($k, $this->_values) ? $this->_values[$k] : null; + } + + // Countable method + public function count() + { + return count($this->_values); + } + + public function keys() + { + return array_keys($this->_values); + } + + public function values() + { + return array_values($this->_values); + } + + /** + * This unfortunately needs to be public to be used in Util\Util + * + * @param array $values + * @param null|string|array|Util\RequestOptions $opts + * + * @return StripeObject The object constructed from the given values. + */ + public static function constructFrom($values, $opts = null) + { + $obj = new static(isset($values['id']) ? $values['id'] : null); + $obj->refreshFrom($values, $opts); + return $obj; + } + + /** + * Refreshes this object using the provided values. + * + * @param array $values + * @param null|string|array|Util\RequestOptions $opts + * @param boolean $partial Defaults to false. + */ + public function refreshFrom($values, $opts, $partial = false) + { + $this->_opts = Util\RequestOptions::parse($opts); + + $this->_originalValues = self::deepCopy($values); + + if ($values instanceof StripeObject) { + $values = $values->__toArray(true); + } + + // Wipe old state before setting new. This is useful for e.g. updating a + // customer, where there is no persistent card parameter. Mark those values + // which don't persist as transient + if ($partial) { + $removed = new Util\Set(); + } else { + $removed = new Util\Set(array_diff(array_keys($this->_values), array_keys($values))); + } + + foreach ($removed->toArray() as $k) { + unset($this->$k); + } + + $this->updateAttributes($values, $opts, false); + foreach ($values as $k => $v) { + $this->_transientValues->discard($k); + $this->_unsavedValues->discard($k); + } + } + + /** + * Mass assigns attributes on the model. + * + * @param array $values + * @param null|string|array|Util\RequestOptions $opts + * @param boolean $dirty Defaults to true. + */ + public function updateAttributes($values, $opts = null, $dirty = true) + { + foreach ($values as $k => $v) { + // Special-case metadata to always be cast as a StripeObject + // This is necessary in case metadata is empty, as PHP arrays do + // not differentiate between lists and hashes, and we consider + // empty arrays to be lists. + if ($k === "metadata") { + $this->_values[$k] = StripeObject::constructFrom($v, $opts); + } else { + $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts); + } + if ($dirty) { + $this->dirtyValue($this->_values[$k]); + } + $this->_unsavedValues->add($k); + } + } + + /** + * @return array A recursive mapping of attributes to values for this object, + * including the proper value for deleted attributes. + */ + public function serializeParameters($force = false) + { + $updateParams = []; + + foreach ($this->_values as $k => $v) { + // There are a few reasons that we may want to add in a parameter for + // update: + // + // 1. The `$force` option has been set. + // 2. We know that it was modified. + // 3. Its value is a StripeObject. A StripeObject may contain modified + // values within in that its parent StripeObject doesn't know about. + // + $original = array_key_exists($k, $this->_originalValues) ? $this->_originalValues[$k] : null; + $unsaved = $this->_unsavedValues->includes($k); + if ($force || $unsaved || $v instanceof StripeObject) { + $updateParams[$k] = $this->serializeParamsValue( + $this->_values[$k], + $original, + $unsaved, + $force, + $k + ); + } + } + + // a `null` that makes it out of `serializeParamsValue` signals an empty + // value that we shouldn't appear in the serialized form of the object + $updateParams = array_filter( + $updateParams, + function ($v) { + return $v !== null; + } + ); + + return $updateParams; + } + + + public function serializeParamsValue($value, $original, $unsaved, $force, $key = null) + { + // The logic here is that essentially any object embedded in another + // object that had a `type` is actually an API resource of a different + // type that's been included in the response. These other resources must + // be updated from their proper endpoints, and therefore they are not + // included when serializing even if they've been modified. + // + // There are _some_ known exceptions though. + // + // For example, if the value is unsaved (meaning the user has set it), and + // it looks like the API resource is persisted with an ID, then we include + // the object so that parameters are serialized with a reference to its + // ID. + // + // Another example is that on save API calls it's sometimes desirable to + // update a customer's default source by setting a new card (or other) + // object with `->source=` and then saving the customer. The + // `saveWithParent` flag to override the default behavior allows us to + // handle these exceptions. + // + // We throw an error if a property was set explicitly but we can't do + // anything with it because the integration is probably not working as the + // user intended it to. + if ($value === null) { + return ""; + } elseif (($value instanceof APIResource) && (!$value->saveWithParent)) { + if (!$unsaved) { + return null; + } elseif (isset($value->id)) { + return $value; + } else { + throw new \InvalidArgumentException( + "Cannot save property `$key` containing an API resource of type " . + get_class($value) . ". It doesn't appear to be persisted and is " . + "not marked as `saveWithParent`." + ); + } + } elseif (is_array($value)) { + if (Util\Util::isList($value)) { + // Sequential array, i.e. a list + $update = []; + foreach ($value as $v) { + array_push($update, $this->serializeParamsValue($v, null, true, $force)); + } + // This prevents an array that's unchanged from being resent. + if ($update !== $this->serializeParamsValue($original, null, true, $force, $key)) { + return $update; + } + } else { + // Associative array, i.e. a map + return Util\Util::convertToStripeObject($value, $this->_opts)->serializeParameters(); + } + } elseif ($value instanceof StripeObject) { + $update = $value->serializeParameters($force); + if ($original && $unsaved) { + $update = array_merge(self::emptyValues($original), $update); + } + return $update; + } else { + return $value; + } + } + + public function jsonSerialize() + { + return $this->__toArray(true); + } + + public function __toJSON() + { + return json_encode($this->__toArray(true), JSON_PRETTY_PRINT); + } + + public function __toString() + { + $class = get_class($this); + return $class . ' JSON: ' . $this->__toJSON(); + } + + public function __toArray($recursive = false) + { + if ($recursive) { + return Util\Util::convertStripeObjectToArray($this->_values); + } else { + return $this->_values; + } + } + + /** + * Sets all keys within the StripeObject as unsaved so that they will be + * included with an update when `serializeParameters` is called. This + * method is also recursive, so any StripeObjects contained as values or + * which are values in a tenant array are also marked as dirty. + */ + public function dirty() + { + $this->_unsavedValues = new Util\Set(array_keys($this->_values)); + foreach ($this->_values as $k => $v) { + $this->dirtyValue($v); + } + } + + protected function dirtyValue($value) + { + if (is_array($value)) { + foreach ($value as $v) { + $this->dirtyValue($v); + } + } elseif ($value instanceof StripeObject) { + $value->dirty(); + } + } + + /** + * Produces a deep copy of the given object including support for arrays + * and StripeObjects. + */ + protected static function deepCopy($obj) + { + if (is_array($obj)) { + $copy = []; + foreach ($obj as $k => $v) { + $copy[$k] = self::deepCopy($v); + } + return $copy; + } elseif ($obj instanceof StripeObject) { + return $obj::constructFrom( + self::deepCopy($obj->_values), + clone $obj->_opts + ); + } else { + return $obj; + } + } + + /** + * Returns a hash of empty values for all the values that are in the given + * StripeObject. + */ + public static function emptyValues($obj) + { + if (is_array($obj)) { + $values = $obj; + } elseif ($obj instanceof StripeObject) { + $values = $obj->_values; + } else { + throw new \InvalidArgumentException( + "empty_values got got unexpected object type: " . get_class($obj) + ); + } + $update = array_fill_keys(array_keys($values), ""); + return $update; } /** @@ -53,242 +440,4 @@ class StripeObject implements ArrayAccess, JsonSerializable { $this->_lastResponse = $resp; } - - protected $_opts; - protected $_values; - protected $_unsavedValues; - protected $_transientValues; - protected $_retrieveOptions; - protected $_lastResponse; - - public function __construct($id = null, $opts = null) - { - $this->_opts = $opts ? $opts : new Util\RequestOptions(); - $this->_values = array(); - $this->_unsavedValues = new Util\Set(); - $this->_transientValues = new Util\Set(); - - $this->_retrieveOptions = array(); - if (is_array($id)) { - foreach ($id as $key => $value) { - if ($key != 'id') { - $this->_retrieveOptions[$key] = $value; - } - } - $id = $id['id']; - } - - if ($id !== null) { - $this->id = $id; - } - } - - // Standard accessor magic methods - public function __set($k, $v) - { - if ($v === "") { - throw new InvalidArgumentException( - 'You cannot set \''.$k.'\'to an empty string. ' - .'We interpret empty strings as NULL in requests. ' - .'You may set obj->'.$k.' = NULL to delete the property' - ); - } - - if (self::$nestedUpdatableAttributes->includes($k) - && isset($this->$k) && $this->$k instanceof AttachedObject && is_array($v)) { - $this->$k->replaceWith($v); - } else { - // TODO: may want to clear from $_transientValues (Won't be user-visible). - $this->_values[$k] = $v; - } - if (!self::$permanentAttributes->includes($k)) { - $this->_unsavedValues->add($k); - } - } - - public function __isset($k) - { - return isset($this->_values[$k]); - } - public function __unset($k) - { - unset($this->_values[$k]); - $this->_transientValues->add($k); - $this->_unsavedValues->discard($k); - } - public function &__get($k) - { - // function should return a reference, using $nullval to return a reference to null - $nullval = null; - if (!empty($this->_values) && array_key_exists($k, $this->_values)) { - return $this->_values[$k]; - } else if (!empty($this->_transientValues) && $this->_transientValues->includes($k)) { - $class = get_class($this); - $attrs = join(', ', array_keys($this->_values)); - $message = "Stripe Notice: Undefined property of $class instance: $k. " - . "HINT: The $k attribute was set in the past, however. " - . "It was then wiped when refreshing the object " - . "with the result returned by Stripe's API, " - . "probably as a result of a save(). The attributes currently " - . "available on this object are: $attrs"; - error_log($message); - return $nullval; - } else { - $class = get_class($this); - error_log("Stripe Notice: Undefined property of $class instance: $k"); - return $nullval; - } - } - - // ArrayAccess methods - public function offsetSet($k, $v) - { - $this->$k = $v; - } - - public function offsetExists($k) - { - return array_key_exists($k, $this->_values); - } - - public function offsetUnset($k) - { - unset($this->$k); - } - public function offsetGet($k) - { - return array_key_exists($k, $this->_values) ? $this->_values[$k] : null; - } - - public function keys() - { - return array_keys($this->_values); - } - - /** - * This unfortunately needs to be public to be used in Util\Util - * - * @param array $values - * @param array $opts - * - * @return StripeObject The object constructed from the given values. - */ - public static function constructFrom($values, $opts) - { - $obj = new static(isset($values['id']) ? $values['id'] : null); - $obj->refreshFrom($values, $opts); - return $obj; - } - - /** - * Refreshes this object using the provided values. - * - * @param array $values - * @param array|Util\RequestOptions $opts - * @param boolean $partial Defaults to false. - */ - public function refreshFrom($values, $opts, $partial = false) - { - if (is_array($opts)) { - $opts = Util\RequestOptions::parse($opts); - } - - $this->_opts = $opts; - - // Wipe old state before setting new. This is useful for e.g. updating a - // customer, where there is no persistent card parameter. Mark those values - // which don't persist as transient - if ($partial) { - $removed = new Util\Set(); - } else { - $removed = array_diff(array_keys($this->_values), array_keys($values)); - } - - foreach ($removed as $k) { - if (self::$permanentAttributes->includes($k)) { - continue; - } - - unset($this->$k); - } - - foreach ($values as $k => $v) { - if (self::$permanentAttributes->includes($k) && isset($this[$k])) { - continue; - } - - if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) { - $this->_values[$k] = AttachedObject::constructFrom($v, $opts); - } else { - $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts); - } - - $this->_transientValues->discard($k); - $this->_unsavedValues->discard($k); - } - } - - /** - * @return array A recursive mapping of attributes to values for this object, - * including the proper value for deleted attributes. - */ - public function serializeParameters() - { - $params = array(); - if ($this->_unsavedValues) { - foreach ($this->_unsavedValues->toArray() as $k) { - $v = $this->$k; - if ($v === null) { - $v = ''; - } - - $params[$k] = $v; - } - } - - // Get nested updates. - foreach (self::$nestedUpdatableAttributes->toArray() as $property) { - if (isset($this->$property)) { - if ($this->$property instanceof StripeObject) { - $serialized = $this->$property->serializeParameters(); - if ($serialized) { - $params[$property] = $serialized; - } - } - } - } - - return $params; - } - - public function jsonSerialize() - { - return $this->__toArray(true); - } - - public function __toJSON() - { - if (defined('JSON_PRETTY_PRINT')) { - return json_encode($this->__toArray(true), JSON_PRETTY_PRINT); - } else { - return json_encode($this->__toArray(true)); - } - } - - public function __toString() - { - $class = get_class($this); - return $class . ' JSON: ' . $this->__toJSON(); - } - - public function __toArray($recursive = false) - { - if ($recursive) { - return Util\Util::convertStripeObjectToArray($this->_values); - } else { - return $this->_values; - } - } } - -StripeObject::init(); diff --git a/htdocs/includes/stripe/lib/Subscription.php b/htdocs/includes/stripe/lib/Subscription.php index a95ab0911c9..a5a8da1beb5 100644 --- a/htdocs/includes/stripe/lib/Subscription.php +++ b/htdocs/includes/stripe/lib/Subscription.php @@ -5,10 +5,42 @@ namespace Stripe; /** * Class Subscription * + * @property string $id + * @property string $object + * @property float $application_fee_percent + * @property string $billing + * @property bool $cancel_at_period_end + * @property int $canceled_at + * @property int $created + * @property int current_period_end + * @property int current_period_start + * @property string $customer + * @property int $days_until_due + * @property mixed $discount + * @property int $ended_at + * @property Collection $items + * @property boolean $livemode + * @property StripeObject $metadata + * @property Plan $plan + * @property int $quantity + * @property int $start + * @property string $status + * @property float $tax_percent + * @property int $trial_end + * @property int $trial_start + * * @package Stripe */ class Subscription extends ApiResource { + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete { + delete as protected _delete; + } + use ApiOperations\Retrieve; + use ApiOperations\Update; + /** * These constants are possible representations of the status field. * @@ -20,49 +52,15 @@ class Subscription extends ApiResource const STATUS_TRIALING = 'trialing'; const STATUS_UNPAID = 'unpaid'; - /** - * @param string $id The ID of the subscription to retrieve. - * @param array|string|null $opts - * - * @return Subscription - */ - public static function retrieve($id, $opts = null) + public static function getSavedNestedResources() { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of Subscriptions - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Subscription The created subscription. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the subscription to retrieve. - * @param array|null $params - * @param array|string|null $options - * - * @return Subscription The updated subscription. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); + static $savedNestedResources = null; + if ($savedNestedResources === null) { + $savedNestedResources = new Util\Set([ + 'source', + ]); + } + return $savedNestedResources; } /** @@ -75,16 +73,6 @@ class Subscription extends ApiResource return $this->_delete($params, $opts); } - /** - * @param array|string|null $opts - * - * @return Subscription The saved subscription. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - /** * @return Subscription The updated subscription. */ @@ -92,6 +80,15 @@ class Subscription extends ApiResource { $url = $this->instanceUrl() . '/discount'; list($response, $opts) = $this->_request('delete', $url); - $this->refreshFrom(array('discount' => null), $opts, true); + $this->refreshFrom(['discount' => null], $opts, true); + } + + public function serializeParameters($force = false) + { + $update = parent::serializeParameters($force); + if ($this->_unsavedValues->includes('items')) { + $update['items'] = $this->serializeParamsValue($this->items, null, true, $force, 'items'); + } + return $update; } } diff --git a/htdocs/includes/stripe/lib/SubscriptionItem.php b/htdocs/includes/stripe/lib/SubscriptionItem.php index 947d83fcb5f..9efc726aa66 100644 --- a/htdocs/includes/stripe/lib/SubscriptionItem.php +++ b/htdocs/includes/stripe/lib/SubscriptionItem.php @@ -5,10 +5,24 @@ namespace Stripe; /** * Class SubscriptionItem * + * @property string $id + * @property string $object + * @property int $created + * @property StripeObject $metadata + * @property Plan $plan + * @property int $quantity + * @property string $subscription + * * @package Stripe */ class SubscriptionItem extends ApiResource { + use ApiOperations\All; + use ApiOperations\Create; + use ApiOperations\Delete; + use ApiOperations\Retrieve; + use ApiOperations\Update; + /** * This is a special case because the subscription items endpoint has an * underscore in it. The parent `className` function strips underscores. @@ -19,70 +33,4 @@ class SubscriptionItem extends ApiResource { return 'subscription_item'; } - - /** - * @param string $id The ID of the subscription item to retrieve. - * @param array|string|null $opts - * - * @return SubscriptionItem - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Collection of SubscriptionItems - */ - public static function all($params = null, $opts = null) - { - return self::_all($params, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return SubscriptionItem The created subscription item. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } - - /** - * @param string $id The ID of the subscription item to update. - * @param array|null $params - * @param array|string|null $options - * - * @return SubscriptionItem The updated subscription item. - */ - public static function update($id, $params = null, $options = null) - { - return self::_update($id, $params, $options); - } - - /** - * @param array|string|null $opts - * - * @return SubscriptionItem The saved subscription item. - */ - public function save($opts = null) - { - return $this->_save($opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return SubscriptionItem The deleted subscription item. - */ - public function delete($params = null, $opts = null) - { - return $this->_delete($params, $opts); - } } diff --git a/htdocs/includes/stripe/lib/ThreeDSecure.php b/htdocs/includes/stripe/lib/ThreeDSecure.php index 1f189b7daeb..7210bfd1f71 100644 --- a/htdocs/includes/stripe/lib/ThreeDSecure.php +++ b/htdocs/includes/stripe/lib/ThreeDSecure.php @@ -4,6 +4,9 @@ namespace Stripe; class ThreeDSecure extends ApiResource { + use ApiOperations\Create; + use ApiOperations\Retrieve; + /** * @return string The endpoint URL for the given class. */ @@ -11,26 +14,4 @@ class ThreeDSecure extends ApiResource { return "/v1/3d_secure"; } - - /** - * @param string $id The ID of the 3DS auth to retrieve. - * @param array|string|null $options - * - * @return ThreeDSecure - */ - public static function retrieve($id, $options = null) - { - return self::_retrieve($id, $options); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return ThreeDSecure The created 3D Secure object. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } } diff --git a/htdocs/includes/stripe/lib/Token.php b/htdocs/includes/stripe/lib/Token.php index d3e5a4bcef9..fbf19bbf7b1 100644 --- a/htdocs/includes/stripe/lib/Token.php +++ b/htdocs/includes/stripe/lib/Token.php @@ -9,7 +9,7 @@ namespace Stripe; * @property string $object * @property mixed $bank_account * @property mixed $card - * @property mixed $client_ip + * @property string $client_ip * @property int $created * @property bool $livemode * @property string $type @@ -19,25 +19,6 @@ namespace Stripe; */ class Token extends ApiResource { - /** - * @param string $id The ID of the token to retrieve. - * @param array|string|null $opts - * - * @return Token - */ - public static function retrieve($id, $opts = null) - { - return self::_retrieve($id, $opts); - } - - /** - * @param array|null $params - * @param array|string|null $opts - * - * @return Token The created token. - */ - public static function create($params = null, $opts = null) - { - return self::_create($params, $opts); - } + use ApiOperations\Create; + use ApiOperations\Retrieve; } diff --git a/htdocs/includes/stripe/lib/Topup.php b/htdocs/includes/stripe/lib/Topup.php new file mode 100644 index 00000000000..33d081e3191 --- /dev/null +++ b/htdocs/includes/stripe/lib/Topup.php @@ -0,0 +1,32 @@ +_save($opts); + return self::_createNestedResource($id, static::PATH_REVERSALS, $params, $opts); + } + + /** + * @param array|null $id The ID of the transfer to which the reversal belongs. + * @param array|null $reversalId The ID of the reversal to retrieve. + * @param array|null $params + * @param array|string|null $opts + * + * @return TransferReversal + */ + public static function retrieveReversal($id, $reversalId, $params = null, $opts = null) + { + return self::_retrieveNestedResource($id, static::PATH_REVERSALS, $reversalId, $params, $opts); + } + + /** + * @param array|null $id The ID of the transfer to which the reversal belongs. + * @param array|null $reversalId The ID of the reversal to update. + * @param array|null $params + * @param array|string|null $opts + * + * @return TransferReversal + */ + public static function updateReversal($id, $reversalId, $params = null, $opts = null) + { + return self::_updateNestedResource($id, static::PATH_REVERSALS, $reversalId, $params, $opts); + } + + /** + * @param array|null $id The ID of the transfer on which to retrieve the reversals. + * @param array|null $params + * @param array|string|null $opts + * + * @return TransferReversal + */ + public static function allReversals($id, $params = null, $opts = null) + { + return self::_allNestedResources($id, static::PATH_REVERSALS, $params, $opts); } } diff --git a/htdocs/includes/stripe/lib/TransferReversal.php b/htdocs/includes/stripe/lib/TransferReversal.php index 33948c93e14..cf2a3946d85 100644 --- a/htdocs/includes/stripe/lib/TransferReversal.php +++ b/htdocs/includes/stripe/lib/TransferReversal.php @@ -11,13 +11,17 @@ namespace Stripe; * @property string $balance_transaction * @property int $created * @property string $currency - * @property mixed $metadata + * @property StripeObject $metadata * @property string $transfer * * @package Stripe */ class TransferReversal extends ApiResource { + use ApiOperations\Update { + save as protected _save; + } + /** * @return string The API URL for this Stripe transfer reversal. */ diff --git a/htdocs/includes/stripe/lib/Util/AutoPagingIterator.php b/htdocs/includes/stripe/lib/Util/AutoPagingIterator.php index 80877e6742e..167d1258a26 100644 --- a/htdocs/includes/stripe/lib/Util/AutoPagingIterator.php +++ b/htdocs/includes/stripe/lib/Util/AutoPagingIterator.php @@ -7,7 +7,7 @@ class AutoPagingIterator implements \Iterator private $lastId = null; private $page = null; private $pageOffset = 0; - private $params = array(); + private $params = []; public function __construct($collection, $params) { @@ -42,8 +42,8 @@ class AutoPagingIterator implements \Iterator $this->pageOffset += count($this->page->data); if ($this->page['has_more']) { $this->params = array_merge( - $this->params ? $this->params : array(), - array('starting_after' => $this->lastId) + $this->params ?: [], + ['starting_after' => $this->lastId] ); $this->page = $this->page->all($this->params); } else { diff --git a/htdocs/includes/stripe/lib/Util/DefaultLogger.php b/htdocs/includes/stripe/lib/Util/DefaultLogger.php new file mode 100644 index 00000000000..1a8663699f5 --- /dev/null +++ b/htdocs/includes/stripe/lib/Util/DefaultLogger.php @@ -0,0 +1,18 @@ + 0) { + throw new \Exception('DefaultLogger does not currently implement context. Please implement if you need it.'); + } + error_log($message); + } +} diff --git a/htdocs/includes/stripe/lib/Util/LoggerInterface.php b/htdocs/includes/stripe/lib/Util/LoggerInterface.php new file mode 100644 index 00000000000..bbdfc929982 --- /dev/null +++ b/htdocs/includes/stripe/lib/Util/LoggerInterface.php @@ -0,0 +1,36 @@ +apiKey = $key; $this->headers = $headers; @@ -32,6 +40,18 @@ class RequestOptions return $other_options; } + /** + * Discards all headers that we don't want to persist across requests. + */ + public function discardNonPersistentHeaders() + { + foreach ($this->headers as $k => $v) { + if (!in_array($k, self::$HEADERS_TO_PERSIST)) { + unset($this->headers[$k]); + } + } + } + /** * Unpacks an options array into an RequestOptions object * @param array|string|null $options a key => value array @@ -45,15 +65,15 @@ class RequestOptions } if (is_null($options)) { - return new RequestOptions(null, array()); + return new RequestOptions(null, []); } if (is_string($options)) { - return new RequestOptions($options, array()); + return new RequestOptions($options, []); } if (is_array($options)) { - $headers = array(); + $headers = []; $key = null; if (array_key_exists('api_key', $options)) { $key = $options['api_key']; diff --git a/htdocs/includes/stripe/lib/Util/Set.php b/htdocs/includes/stripe/lib/Util/Set.php index 2a500cd639b..d8beb8b1a39 100644 --- a/htdocs/includes/stripe/lib/Util/Set.php +++ b/htdocs/includes/stripe/lib/Util/Set.php @@ -9,9 +9,9 @@ class Set implements IteratorAggregate { private $_elts; - public function __construct($members = array()) + public function __construct($members = []) { - $this->_elts = array(); + $this->_elts = []; foreach ($members as $item) { $this->_elts[$item] = true; } diff --git a/htdocs/includes/stripe/lib/Util/Util.php b/htdocs/includes/stripe/lib/Util/Util.php index 1c8235df9bd..82fd337dd34 100644 --- a/htdocs/includes/stripe/lib/Util/Util.php +++ b/htdocs/includes/stripe/lib/Util/Util.php @@ -7,24 +7,26 @@ use Stripe\StripeObject; abstract class Util { private static $isMbstringAvailable = null; + private static $isHashEqualsAvailable = null; /** * Whether the provided array (or other) is a list rather than a dictionary. + * A list is defined as an array for which all the keys are consecutive + * integers starting at 0. Empty arrays are considered to be lists. * * @param array|mixed $array - * @return boolean True if the given object is a list. + * @return boolean true if the given object is a list. */ public static function isList($array) { if (!is_array($array)) { return false; } - - // TODO: generally incorrect, but it's correct given Stripe's response - foreach (array_keys($array) as $k) { - if (!is_numeric($k)) { - return false; - } + if ($array === []) { + return true; + } + if (array_keys($array) !== range(0, count($array) - 1)) { + return false; } return true; } @@ -37,7 +39,7 @@ abstract class Util */ public static function convertStripeObjectToArray($values) { - $results = array(); + $results = []; foreach ($values as $k => $v) { // FIXME: this is an encapsulation violation if ($k[0] == '_') { @@ -63,26 +65,34 @@ abstract class Util */ public static function convertToStripeObject($resp, $opts) { - $types = array( + $types = [ + // data structures + 'list' => 'Stripe\\Collection', + + // business objects 'account' => 'Stripe\\Account', 'alipay_account' => 'Stripe\\AlipayAccount', 'apple_pay_domain' => 'Stripe\\ApplePayDomain', - 'bank_account' => 'Stripe\\BankAccount', + 'application_fee' => 'Stripe\\ApplicationFee', + 'balance' => 'Stripe\\Balance', 'balance_transaction' => 'Stripe\\BalanceTransaction', + 'bank_account' => 'Stripe\\BankAccount', + 'bitcoin_receiver' => 'Stripe\\BitcoinReceiver', + 'bitcoin_transaction' => 'Stripe\\BitcoinTransaction', 'card' => 'Stripe\\Card', 'charge' => 'Stripe\\Charge', 'country_spec' => 'Stripe\\CountrySpec', 'coupon' => 'Stripe\\Coupon', 'customer' => 'Stripe\\Customer', 'dispute' => 'Stripe\\Dispute', - 'list' => 'Stripe\\Collection', + 'ephemeral_key' => 'Stripe\\EphemeralKey', + 'event' => 'Stripe\\Event', + 'exchange_rate' => 'Stripe\\ExchangeRate', + 'fee_refund' => 'Stripe\\ApplicationFeeRefund', + 'file_upload' => 'Stripe\\FileUpload', 'invoice' => 'Stripe\\Invoice', 'invoiceitem' => 'Stripe\\InvoiceItem', - 'event' => 'Stripe\\Event', - 'file' => 'Stripe\\FileUpload', - 'token' => 'Stripe\\Token', - 'transfer' => 'Stripe\\Transfer', - 'transfer_reversal' => 'Stripe\\TransferReversal', + 'login_link' => 'Stripe\\LoginLink', 'order' => 'Stripe\\Order', 'order_return' => 'Stripe\\OrderReturn', 'payout' => 'Stripe\\Payout', @@ -93,15 +103,17 @@ abstract class Util 'refund' => 'Stripe\\Refund', 'sku' => 'Stripe\\SKU', 'source' => 'Stripe\\Source', + 'source_transaction' => 'Stripe\\SourceTransaction', 'subscription' => 'Stripe\\Subscription', 'subscription_item' => 'Stripe\\SubscriptionItem', 'three_d_secure' => 'Stripe\\ThreeDSecure', - 'fee_refund' => 'Stripe\\ApplicationFeeRefund', - 'bitcoin_receiver' => 'Stripe\\BitcoinReceiver', - 'bitcoin_transaction' => 'Stripe\\BitcoinTransaction', - ); + 'token' => 'Stripe\\Token', + 'topup' => 'Stripe\\Topup', + 'transfer' => 'Stripe\\Transfer', + 'transfer_reversal' => 'Stripe\\TransferReversal', + ]; if (self::isList($resp)) { - $mapped = array(); + $mapped = []; foreach ($resp as $i) { array_push($mapped, self::convertToStripeObject($i, $opts)); } @@ -143,4 +155,84 @@ abstract class Util return $value; } } + + /** + * Compares two strings for equality. The time taken is independent of the + * number of characters that match. + * + * @param string $a one of the strings to compare. + * @param string $b the other string to compare. + * @return bool true if the strings are equal, false otherwise. + */ + public static function secureCompare($a, $b) + { + if (self::$isHashEqualsAvailable === null) { + self::$isHashEqualsAvailable = function_exists('hash_equals'); + } + + if (self::$isHashEqualsAvailable) { + return hash_equals($a, $b); + } else { + if (strlen($a) != strlen($b)) { + return false; + } + + $result = 0; + for ($i = 0; $i < strlen($a); $i++) { + $result |= ord($a[$i]) ^ ord($b[$i]); + } + return ($result == 0); + } + } + + /** + * @param array $arr A map of param keys to values. + * @param string|null $prefix + * + * @return string A querystring, essentially. + */ + public static function urlEncode($arr, $prefix = null) + { + if (!is_array($arr)) { + return $arr; + } + + $r = []; + foreach ($arr as $k => $v) { + if (is_null($v)) { + continue; + } + + if ($prefix) { + if ($k !== null && (!is_int($k) || is_array($v))) { + $k = $prefix."[".$k."]"; + } else { + $k = $prefix."[]"; + } + } + + if (is_array($v)) { + $enc = self::urlEncode($v, $k); + if ($enc) { + $r[] = $enc; + } + } else { + $r[] = urlencode($k)."=".urlencode($v); + } + } + + return implode("&", $r); + } + + public static function normalizeId($id) + { + if (is_array($id)) { + $params = $id; + $id = $params['id']; + unset($params['id']); + } else { + $params = []; + } + return [$id, $params]; + } } diff --git a/htdocs/includes/stripe/lib/Webhook.php b/htdocs/includes/stripe/lib/Webhook.php new file mode 100644 index 00000000000..e0ab3021a89 --- /dev/null +++ b/htdocs/includes/stripe/lib/Webhook.php @@ -0,0 +1,40 @@ + 0) && ((time() - $timestamp) > $tolerance)) { + throw new Error\SignatureVerification( + "Timestamp outside the tolerance zone", + $header, + $payload + ); + } + + return true; + } + + /** + * Extracts the timestamp in a signature header. + * + * @param string $header the signature header + * @return int the timestamp contained in the header, or -1 if no valid + * timestamp is found + */ + private static function getTimestamp($header) + { + $items = explode(",", $header); + + foreach ($items as $item) { + $itemParts = explode("=", $item, 2); + if ($itemParts[0] == "t") { + if (!is_numeric($itemParts[1])) { + return -1; + } + return intval($itemParts[1]); + } + } + + return -1; + } + + /** + * Extracts the signatures matching a given scheme in a signature header. + * + * @param string $header the signature header + * @param string $scheme the signature scheme to look for. + * @return array the list of signatures matching the provided scheme. + */ + private static function getSignatures($header, $scheme) + { + $signatures = []; + $items = explode(",", $header); + + foreach ($items as $item) { + $itemParts = explode("=", $item, 2); + if ($itemParts[0] == $scheme) { + array_push($signatures, $itemParts[1]); + } + } + + return $signatures; + } + + /** + * Computes the signature for a given payload and secret. + * + * The current scheme used by Stripe ("v1") is HMAC/SHA-256. + * + * @param string $payload the payload to sign. + * @param string $secret the secret used to generate the signature. + * @return string the signature as a string. + */ + private static function computeSignature($payload, $secret) + { + return hash_hmac("sha256", $payload, $secret); + } +} diff --git a/htdocs/includes/stripe/tests/AccountTest.php b/htdocs/includes/stripe/tests/AccountTest.php deleted file mode 100644 index 994ba6f6a8a..00000000000 --- a/htdocs/includes/stripe/tests/AccountTest.php +++ /dev/null @@ -1,300 +0,0 @@ - $id, - 'currencies_supported' => array('usd', 'aed', 'afn', '...'), - 'object' => 'account', - 'business_name' => 'Stripe.com', - 'bank_accounts' => array( - 'object' => 'list', - 'total_count' => 0, - 'has_more' => false, - 'url' => '/v1/accounts/' . $id . '/bank_accounts', - 'data' => array() - ), - 'verification' => array( - 'fields_needed' => array( - 'product_description', - 'business_url', - 'support_phone', - 'bank_account', - 'tos_acceptance.ip', - 'tos_acceptance.date' - ), - 'due_by' => null, - 'contacted' => false - ), - 'tos_acceptance' => array( - 'ip' => null, - 'date' => null, - 'user_agent' => null - ), - 'legal_entity' => array( - 'type' => null, - 'business_name' => null, - 'address' => array( - 'line1' => null, - 'line2' => null, - 'city' => null, - 'state' => null, - 'postal_code' => null, - 'country' => 'US' - ), - 'first_name' => null, - 'last_name' => null, - 'additional_owners' => null, - 'verification' => array( - 'status' => 'unverified', - 'document' => null, - 'details' => null - ) - ) - ); - } - - private function deletedAccountResponse($id) - { - return array( - 'id' => $id, - 'deleted' => true - ); - } - - public function testBasicRetrieve() - { - $this->mockRequest('GET', '/v1/account', array(), $this->managedAccountResponse('acct_ABC')); - $account = Account::retrieve(); - $this->assertSame($account->id, 'acct_ABC'); - } - - public function testIDRetrieve() - { - $this->mockRequest('GET', '/v1/accounts/acct_DEF', array(), $this->managedAccountResponse('acct_DEF')); - $account = Account::retrieve('acct_DEF'); - $this->assertSame($account->id, 'acct_DEF'); - } - - public function testCreate() - { - $this->mockRequest( - 'POST', - '/v1/accounts', - array('managed' => 'true'), - $this->managedAccountResponse('acct_ABC') - ); - $account = Account::create(array( - 'managed' => true - )); - $this->assertSame($account->id, 'acct_ABC'); - } - - public function testDelete() - { - $account = self::createTestAccount(); - - $this->mockRequest( - 'DELETE', - '/v1/accounts/' . $account->id, - array(), - $this->deletedAccountResponse('acct_ABC') - ); - $deleted = $account->delete(); - $this->assertSame($deleted->id, $account->id); - $this->assertTrue($deleted->deleted); - } - - public function testReject() - { - $account = self::createTestAccount(); - - $this->mockRequest( - 'POST', - '/v1/accounts/' . $account->id . '/reject', - array('reason' => 'fraud'), - $this->deletedAccountResponse('acct_ABC') - ); - $rejected = $account->reject(array('reason' => 'fraud')); - $this->assertSame($rejected->id, $account->id); - } - - public function testSaveLegalEntity() - { - $response = $this->managedAccountResponse('acct_ABC'); - $this->mockRequest('POST', '/v1/accounts', array('managed' => 'true'), $response); - - $response['legal_entity']['first_name'] = 'Bob'; - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('first_name' => 'Bob')), - $response - ); - - $account = Account::create(array('managed' => true)); - $account->legal_entity->first_name = 'Bob'; - $account->save(); - - $this->assertSame('Bob', $account->legal_entity->first_name); - } - - public function testUpdateLegalEntity() - { - $response = $this->managedAccountResponse('acct_ABC'); - $this->mockRequest('POST', '/v1/accounts', array('managed' => 'true'), $response); - - $response['legal_entity']['first_name'] = 'Bob'; - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('first_name' => 'Bob')), - $response - ); - - $account = Account::create(array('managed' => true)); - $account = Account::update($account['id'], array( - 'legal_entity' => array( - 'first_name' => 'Bob' - ) - )); - - $this->assertSame('Bob', $account->legal_entity->first_name); - } - - public function testCreateAdditionalOwners() - { - $request = array( - 'managed' => true, - 'country' => 'GB', - 'legal_entity' => array( - 'additional_owners' => array( - 0 => array( - 'dob' => array( - 'day' => 12, - 'month' => 5, - 'year' => 1970, - ), - 'first_name' => 'xgvukvfrde', - 'last_name' => 'rtcyvubhy', - ), - 1 => array( - 'dob' => array( - 'day' => 8, - 'month' => 4, - 'year' => 1979, - ), - 'first_name' => 'yutreuk', - 'last_name' => 'dfcgvhbjihmv', - ), - ), - ), - ); - - $acct = Account::create($request); - $response = $acct->__toArray(true); - - $req_ao = $request['legal_entity']['additional_owners']; - $resp_ao = $response['legal_entity']['additional_owners']; - - $this->assertSame($req_ao[0]['dob'], $resp_ao[0]['dob']); - $this->assertSame($req_ao[1]['dob'], $resp_ao[1]['dob']); - - $this->assertSame($req_ao[0]['first_name'], $resp_ao[0]['first_name']); - $this->assertSame($req_ao[1]['first_name'], $resp_ao[1]['first_name']); - } - - public function testUpdateAdditionalOwners() - { - $response = $this->managedAccountResponse('acct_ABC'); - $this->mockRequest('POST', '/v1/accounts', array('managed' => 'true'), $response); - - $response['legal_entity']['additional_owners'] = array(array( - 'first_name' => 'Bob', - 'last_name' => null, - 'address' => array( - 'line1' => null, - 'line2' => null, - 'city' => null, - 'state' => null, - 'postal_code' => null, - 'country' => null - ), - 'verification' => array( - 'status' => 'unverified', - 'document' => null, - 'details' => null - ) - )); - - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('additional_owners' => array(array('first_name' => 'Bob')))), - $response - ); - - $response['legal_entity']['additional_owners'][0]['last_name'] = 'Smith'; - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('additional_owners' => array(array('last_name' => 'Smith')))), - $response - ); - - $response['legal_entity']['additional_owners'][0]['last_name'] = 'Johnson'; - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('additional_owners' => array(array('last_name' => 'Johnson')))), - $response - ); - - $response['legal_entity']['additional_owners'][0]['verification']['document'] = 'file_123'; - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('additional_owners' => array(array('verification' => array('document' => 'file_123'))))), - $response - ); - - $response['legal_entity']['additional_owners'][1] = array( - 'first_name' => 'Jane', - 'last_name' => 'Doe' - ); - $this->mockRequest( - 'POST', - '/v1/accounts/acct_ABC', - array('legal_entity' => array('additional_owners' => array(1 => array('first_name' => 'Jane')))), - $response - ); - - $account = Account::create(array('managed' => true)); - $account->legal_entity->additional_owners = array(array('first_name' => 'Bob')); - $account->save(); - $this->assertSame(1, count($account->legal_entity->additional_owners)); - $this->assertSame('Bob', $account->legal_entity->additional_owners[0]->first_name); - - $account->legal_entity->additional_owners[0]->last_name = 'Smith'; - $account->save(); - $this->assertSame(1, count($account->legal_entity->additional_owners)); - $this->assertSame('Smith', $account->legal_entity->additional_owners[0]->last_name); - - $account['legal_entity']['additional_owners'][0]['last_name'] = 'Johnson'; - $account->save(); - $this->assertSame(1, count($account->legal_entity->additional_owners)); - $this->assertSame('Johnson', $account->legal_entity->additional_owners[0]->last_name); - - $account->legal_entity->additional_owners[0]->verification->document = 'file_123'; - $account->save(); - $this->assertSame('file_123', $account->legal_entity->additional_owners[0]->verification->document); - - $account->legal_entity->additional_owners[1] = array('first_name' => 'Jane'); - $account->save(); - $this->assertSame('Jane', $account->legal_entity->additional_owners[1]->first_name); - } -} diff --git a/htdocs/includes/stripe/tests/ApiRequestorTest.php b/htdocs/includes/stripe/tests/ApiRequestorTest.php deleted file mode 100644 index 859fb63a347..00000000000 --- a/htdocs/includes/stripe/tests/ApiRequestorTest.php +++ /dev/null @@ -1,68 +0,0 @@ -getMethod('_encodeObjects'); - $method->setAccessible(true); - - $a = array('customer' => new Customer('abcd')); - $enc = $method->invoke(null, $a); - $this->assertSame($enc, array('customer' => 'abcd')); - - // Preserves UTF-8 - $v = array('customer' => "☃"); - $enc = $method->invoke(null, $v); - $this->assertSame($enc, $v); - - // Encodes latin-1 -> UTF-8 - $v = array('customer' => "\xe9"); - $enc = $method->invoke(null, $v); - $this->assertSame($enc, array('customer' => "\xc3\xa9")); - } - - public function testHttpClientInjection() - { - $reflector = new \ReflectionClass('Stripe\\ApiRequestor'); - $method = $reflector->getMethod('httpClient'); - $method->setAccessible(true); - - $curl = new CurlClient(); - $curl->setTimeout(10); - ApiRequestor::setHttpClient($curl); - - $injectedCurl = $method->invoke(new ApiRequestor()); - $this->assertSame($injectedCurl, $curl); - } - - public function testDefaultHeaders() - { - $reflector = new \ReflectionClass('Stripe\\ApiRequestor'); - $method = $reflector->getMethod('_defaultHeaders'); - $method->setAccessible(true); - - // no way to stub static methods with PHPUnit 4.x :( - Stripe::setAppInfo('MyTestApp', '1.2.34', 'https://mytestapp.example'); - $apiKey = 'sk_test_notarealkey'; - - $headers = $method->invoke(null, $apiKey); - - $ua = json_decode($headers['X-Stripe-Client-User-Agent']); - $this->assertSame($ua->application->name, 'MyTestApp'); - $this->assertSame($ua->application->version, '1.2.34'); - $this->assertSame($ua->application->url, 'https://mytestapp.example'); - - $this->assertSame( - $headers['User-Agent'], - 'Stripe/v1 PhpBindings/' . Stripe::VERSION . ' MyTestApp/1.2.34 (https://mytestapp.example)' - ); - - $this->assertSame($headers['Authorization'], 'Bearer ' . $apiKey); - } -} diff --git a/htdocs/includes/stripe/tests/ApplePayDomainTest.php b/htdocs/includes/stripe/tests/ApplePayDomainTest.php deleted file mode 100644 index ebb3e7cfa9d..00000000000 --- a/htdocs/includes/stripe/tests/ApplePayDomainTest.php +++ /dev/null @@ -1,72 +0,0 @@ -mockRequest( - 'POST', - '/v1/apple_pay/domains', - array('domain_name' => 'test.com'), - array( - 'id' => 'apwc_create', - 'object' => 'apple_pay_domain' - ) - ); - $d = ApplePayDomain::create(array( - 'domain_name' => 'test.com' - )); - $this->assertSame('apwc_create', $d->id); - $this->assertInstanceOf('Stripe\\ApplePayDomain', $d); - } - - public function testRetrieve() - { - $this->mockRequest( - 'GET', - '/v1/apple_pay/domains/apwc_retrieve', - array(), - array( - 'id' => 'apwc_retrieve', - 'object' => 'apple_pay_domain' - ) - ); - $d = ApplePayDomain::retrieve('apwc_retrieve'); - $this->assertSame('apwc_retrieve', $d->id); - $this->assertInstanceOf('Stripe\\ApplePayDomain', $d); - } - - public function testDeletion() - { - self::authorizeFromEnv(); - $d = ApplePayDomain::create(array( - 'domain_name' => 'jackshack.website' - )); - $this->assertInstanceOf('Stripe\\ApplePayDomain', $d); - $this->mockRequest( - 'DELETE', - '/v1/apple_pay/domains/' . $d->id, - array(), - array('deleted' => true) - ); - $d->delete(); - $this->assertTrue($d->deleted); - } - - public function testList() - { - $this->mockRequest( - 'GET', - '/v1/apple_pay/domains', - array(), - array( - 'url' => '/v1/apple_pay/domains', - 'object' => 'list' - ) - ); - $all = ApplePayDomain::all(); - $this->assertSame($all->url, '/v1/apple_pay/domains'); - } -} diff --git a/htdocs/includes/stripe/tests/ApplicationFeeRefundTest.php b/htdocs/includes/stripe/tests/ApplicationFeeRefundTest.php deleted file mode 100644 index b8d266ad1eb..00000000000 --- a/htdocs/includes/stripe/tests/ApplicationFeeRefundTest.php +++ /dev/null @@ -1,18 +0,0 @@ -id = 'refund_id'; - $refund->fee = 'fee_id'; - - $this->assertSame( - $refund->instanceUrl(), - '/v1/application_fees/fee_id/refunds/refund_id' - ); - } -} diff --git a/htdocs/includes/stripe/tests/ApplicationFeeTest.php b/htdocs/includes/stripe/tests/ApplicationFeeTest.php deleted file mode 100644 index fe6df923084..00000000000 --- a/htdocs/includes/stripe/tests/ApplicationFeeTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertSame( - $applicationFee->instanceUrl(), - '/v1/application_fees/abcd%2Fefgh' - ); - } - - public function testList() - { - self::authorizeFromEnv(); - $d = ApplicationFee::all(); - $this->assertSame($d->url, '/v1/application_fees'); - } -} diff --git a/htdocs/includes/stripe/tests/AuthenticationErrorTest.php b/htdocs/includes/stripe/tests/AuthenticationErrorTest.php deleted file mode 100644 index 1003d6990c8..00000000000 --- a/htdocs/includes/stripe/tests/AuthenticationErrorTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertSame(401, $e->getHttpStatus()); - } - } -} diff --git a/htdocs/includes/stripe/tests/BalanceTest.php b/htdocs/includes/stripe/tests/BalanceTest.php deleted file mode 100644 index 3d6a2a668f0..00000000000 --- a/htdocs/includes/stripe/tests/BalanceTest.php +++ /dev/null @@ -1,15 +0,0 @@ -assertSame($d->object, "balance"); - $this->assertTrue(Util\Util::isList($d->available)); - $this->assertTrue(Util\Util::isList($d->pending)); - } -} diff --git a/htdocs/includes/stripe/tests/BalanceTransactionTest.php b/htdocs/includes/stripe/tests/BalanceTransactionTest.php deleted file mode 100644 index b196a288014..00000000000 --- a/htdocs/includes/stripe/tests/BalanceTransactionTest.php +++ /dev/null @@ -1,13 +0,0 @@ -assertSame($d->url, '/v1/balance/history'); - } -} diff --git a/htdocs/includes/stripe/tests/BankAccountTest.php b/htdocs/includes/stripe/tests/BankAccountTest.php deleted file mode 100644 index 285078f00ad..00000000000 --- a/htdocs/includes/stripe/tests/BankAccountTest.php +++ /dev/null @@ -1,32 +0,0 @@ -sources->create(array( - 'source' => array( - 'object' => 'bank_account', - 'account_holder_type' => 'individual', - 'account_number' => '000123456789', - 'account_holder_name' => 'John Doe', - 'routing_number' => '110000000', - 'country' => 'US' - ) - )); - - $this->assertSame($bankAccount->status, 'new'); - - $bankAccount = $bankAccount->verify(array( - 'amounts' => array(32, 45) - )); - - $this->assertSame($bankAccount->status, 'verified'); - } -} diff --git a/htdocs/includes/stripe/tests/BitcoinReceiverTest.php b/htdocs/includes/stripe/tests/BitcoinReceiverTest.php deleted file mode 100644 index fc71cf973a5..00000000000 --- a/htdocs/includes/stripe/tests/BitcoinReceiverTest.php +++ /dev/null @@ -1,120 +0,0 @@ -assertSame($classUrl, '/v1/bitcoin/receivers'); - $receiver = new BitcoinReceiver('abcd/efgh'); - $instanceUrl = $receiver->instanceUrl(); - $this->assertSame($instanceUrl, '/v1/bitcoin/receivers/abcd%2Fefgh'); - } - - public function testCreate() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $this->assertSame(100, $receiver->amount); - $this->assertNotNull($receiver->id); - } - - public function testRetrieve() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $r = BitcoinReceiver::retrieve($receiver->id); - $this->assertSame($receiver->id, $r->id); - - $this->assertInstanceOf('Stripe\\BitcoinTransaction', $r->transactions->data[0]); - } - - public function testList() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $receivers = BitcoinReceiver::all(); - $this->assertGreaterThan(0, count($receivers->data)); - } - - public function testListTransactions() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - $this->assertSame(0, count($receiver->transactions->data)); - - $transactions = $receiver->transactions->all(array("limit" => 1)); - $this->assertSame(1, count($transactions->data)); - } - - public function testDeleteWithCustomer() - { - self::authorizeFromEnv(); - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - $customer = Customer::create(array("source" => $receiver->id)); - $charge = Charge::create(array( - "customer" => $customer->id, - "amount" => $receiver->amount, - "currency" => $receiver->currency - )); - $receiver = BitcoinReceiver::retrieve($receiver->id); - $response = $receiver->delete(); - $this->assertTrue($response->deleted); - } - - public function testUpdateWithCustomer() - { - self::authorizeFromEnv(); - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - $customer = Customer::create(array("source" => $receiver->id)); - $receiver = BitcoinReceiver::retrieve($receiver->id); - - $receiver->description = "a new description"; - $receiver->save(); - - $base = Customer::classUrl(); - $parentExtn = $receiver['customer']; - $extn = $receiver['id']; - $this->assertEquals("$base/$parentExtn/sources/$extn", $receiver->instanceUrl()); - - $updatedReceiver = BitcoinReceiver::retrieve($receiver->id); - $this->assertEquals($receiver["description"], $updatedReceiver["description"]); - } - - public function testUpdateWithoutCustomer() - { - self::authorizeFromEnv(); - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $receiver->description = "a new description"; - $receiver->save(); - - $this->assertEquals(BitcoinReceiver::classUrl() . "/" . $receiver['id'], $receiver->instanceUrl()); - - $updatedReceiver = BitcoinReceiver::retrieve($receiver->id); - $this->assertEquals($receiver["description"], $updatedReceiver["description"]); - } - - public function testRefund() - { - self::authorizeFromEnv(); - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $receiver = BitcoinReceiver::retrieve($receiver->id); - $this->assertNull($receiver->refund_address); - - $refundAddress = "REFUNDHERE"; - $receiver->refund(array("refund_address" => $refundAddress)); - - $this->assertSame($refundAddress, $receiver->refund_address); - } -} diff --git a/htdocs/includes/stripe/tests/CardErrorTest.php b/htdocs/includes/stripe/tests/CardErrorTest.php deleted file mode 100644 index a0de752e512..00000000000 --- a/htdocs/includes/stripe/tests/CardErrorTest.php +++ /dev/null @@ -1,41 +0,0 @@ - '4000000000000002', - 'exp_month' => '3', - 'exp_year' => '2020' - ); - - $charge = array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ); - - try { - Charge::create($charge); - } catch (Error\Card $e) { - $this->assertSame(402, $e->getHttpStatus()); - $this->assertTrue(strpos($e->getRequestId(), "req_") === 0, $e->getRequestId()); - $actual = $e->getJsonBody(); - $this->assertSame( - array('error' => array( - 'message' => 'Your card was declined.', - 'type' => 'card_error', - 'code' => 'card_declined', - 'decline_code' => 'generic_decline', - 'charge' => $actual['error']['charge'], - )), - $actual - ); - } - } -} diff --git a/htdocs/includes/stripe/tests/ChargeTest.php b/htdocs/includes/stripe/tests/ChargeTest.php deleted file mode 100644 index 6ec0fa1f1d4..00000000000 --- a/htdocs/includes/stripe/tests/ChargeTest.php +++ /dev/null @@ -1,204 +0,0 @@ -assertSame(Charge::classUrl(), '/v1/charges'); - $charge = new Charge('abcd/efgh'); - $this->assertSame($charge->instanceUrl(), '/v1/charges/abcd%2Fefgh'); - } - - public function testCreate() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $c = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - $this->assertTrue($c->paid); - $this->assertFalse($c->refunded); - } - - public function testIdempotentCreate() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $c = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ), - array( - 'idempotency_key' => self::generateRandomString(), - ) - ); - - $this->assertTrue($c->paid); - $this->assertSame(200, $c->getLastResponse()->code); - } - - public function testRetrieve() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $c = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - $d = Charge::retrieve($c->id); - $this->assertSame(200, $d->getLastResponse()->code); - $this->assertSame($d->id, $c->id); - } - - public function testUpdateMetadata() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $charge = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - - $charge->metadata['test'] = 'foo bar'; - $charge->save(); - - $updatedCharge = Charge::retrieve($charge->id); - $this->assertSame('foo bar', $updatedCharge->metadata['test']); - } - - public function testUpdateMetadataAll() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $charge = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - - $charge->metadata = array('test' => 'foo bar'); - $charge->save(); - $this->assertSame(200, $charge->getLastResponse()->code); - - $updatedCharge = Charge::retrieve($charge->id); - $this->assertSame('foo bar', $updatedCharge->metadata['test']); - } - - public function testMarkAsFraudulent() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $charge = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - - $charge->refunds->create(); - $charge->markAsFraudulent(); - - $updatedCharge = Charge::retrieve($charge->id); - $this->assertSame( - 'fraudulent', - $updatedCharge['fraud_details']['user_report'] - ); - } - - public function testCreateWithBitcoinReceiverSource() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $charge = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'source' => $receiver->id - ) - ); - - $this->assertSame($receiver->id, $charge->source->id); - $this->assertSame("bitcoin_receiver", $charge->source->object); - $this->assertSame("succeeded", $charge->status); - $this->assertInstanceOf('Stripe\\BitcoinReceiver', $charge->source); - } - - public function markAsSafe() - { - self::authorizeFromEnv(); - - $card = array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $charge = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - - $charge->markAsSafe(); - - $updatedCharge = Charge::retrieve($charge->id); - $this->assertSame('safe', $updatedCharge['fraud_details']['user_report']); - } -} diff --git a/htdocs/includes/stripe/tests/CollectionTest.php b/htdocs/includes/stripe/tests/CollectionTest.php deleted file mode 100644 index f8f923ff00d..00000000000 --- a/htdocs/includes/stripe/tests/CollectionTest.php +++ /dev/null @@ -1,105 +0,0 @@ - $id, - 'object' => 'pageablemodel' - )); - } - return array( - 'object' => 'list', - 'url' => '/v1/pageablemodels', - 'data' => $data, - 'has_more' => $hasMore - ); - } - - public function testAutoPagingOnePage() - { - $collection = Collection::constructFrom( - $this->pageableModelResponse(array('pm_123', 'pm_124'), false), - new Util\RequestOptions() - ); - - $seen = array(); - foreach ($collection->autoPagingIterator() as $item) { - array_push($seen, $item['id']); - } - - $this->assertSame($seen, array('pm_123', 'pm_124')); - } - - public function testAutoPagingThreePages() - { - $collection = Collection::constructFrom( - $this->pageableModelResponse(array('pm_123', 'pm_124'), true), - new Util\RequestOptions() - ); - $collection->setRequestParams(array('foo' => 'bar')); - - $this->mockRequest( - 'GET', - '/v1/pageablemodels', - array( - 'foo' => 'bar', - 'starting_after' => 'pm_124' - ), - $this->pageableModelResponse(array('pm_125', 'pm_126'), true) - ); - $this->mockRequest( - 'GET', - '/v1/pageablemodels', - array( - 'foo' => 'bar', - 'starting_after' => 'pm_126' - ), - $this->pageableModelResponse(array('pm_127'), false) - ); - - $seen = array(); - foreach ($collection->autoPagingIterator() as $item) { - array_push($seen, $item['id']); - } - - $this->assertSame($seen, array('pm_123', 'pm_124', 'pm_125', 'pm_126', 'pm_127')); - } - - public function testIteratorToArray() - { - $collection = Collection::constructFrom( - $this->pageableModelResponse(array('pm_123', 'pm_124'), true), - new Util\RequestOptions() - ); - - $this->mockRequest( - 'GET', - '/v1/pageablemodels', - array( - 'starting_after' => 'pm_124' - ), - $this->pageableModelResponse(array('pm_125', 'pm_126'), true) - ); - $this->mockRequest( - 'GET', - '/v1/pageablemodels', - array( - 'starting_after' => 'pm_126' - ), - $this->pageableModelResponse(array('pm_127'), false) - ); - - $seen = array(); - foreach (iterator_to_array($collection->autoPagingIterator()) as $item) { - array_push($seen, $item['id']); - } - - $this->assertSame($seen, array('pm_123', 'pm_124', 'pm_125', 'pm_126', 'pm_127')); - } -} diff --git a/htdocs/includes/stripe/tests/CountrySpecTest.php b/htdocs/includes/stripe/tests/CountrySpecTest.php deleted file mode 100644 index 19dc37062e5..00000000000 --- a/htdocs/includes/stripe/tests/CountrySpecTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertSame($d->object, "country_spec"); - $this->assertSame($d->id, $country); - $this->assertGreaterThan(0, count($d->supported_bank_account_currencies)); - $this->assertGreaterThan(0, count($d->supported_payment_currencies)); - $this->assertGreaterThan(0, count($d->supported_payment_methods)); - $this->assertGreaterThan(0, count($d->verification_fields)); - } - - public function testList() - { - self::authorizeFromEnv(); - - $d = CountrySpec::all(); - $this->assertSame($d->object, "list"); - $this->assertGreaterThan(0, count($d->data)); - $this->assertSame($d->data[0]->object, "country_spec"); - $this->assertInstanceOf("Stripe\\CountrySpec", $d->data[0]); - } -} diff --git a/htdocs/includes/stripe/tests/CouponTest.php b/htdocs/includes/stripe/tests/CouponTest.php deleted file mode 100644 index d23163e338e..00000000000 --- a/htdocs/includes/stripe/tests/CouponTest.php +++ /dev/null @@ -1,29 +0,0 @@ - 25, - 'duration' => 'repeating', - 'duration_in_months' => 5, - 'id' => $id, - ) - ); - $this->assertSame($id, $c->id); - // @codingStandardsIgnoreStart - $this->assertSame(25, $c->percent_off); - // @codingStandardsIgnoreEnd - $c->metadata['foo'] = 'bar'; - $c->save(); - - $stripeCoupon = Coupon::retrieve($id); - $this->assertEquals($c->metadata, $stripeCoupon->metadata); - } -} diff --git a/htdocs/includes/stripe/tests/CurlClientTest.php b/htdocs/includes/stripe/tests/CurlClientTest.php deleted file mode 100644 index 864607f088c..00000000000 --- a/htdocs/includes/stripe/tests/CurlClientTest.php +++ /dev/null @@ -1,100 +0,0 @@ -assertSame(CurlClient::DEFAULT_TIMEOUT, $curl->getTimeout()); - $this->assertSame(CurlClient::DEFAULT_CONNECT_TIMEOUT, $curl->getConnectTimeout()); - - // implicitly tests whether we're returning the CurlClient instance - $curl = $curl->setConnectTimeout(1)->setTimeout(10); - $this->assertSame(1, $curl->getConnectTimeout()); - $this->assertSame(10, $curl->getTimeout()); - - $curl->setTimeout(-1); - $curl->setConnectTimeout(-999); - $this->assertSame(0, $curl->getTimeout()); - $this->assertSame(0, $curl->getConnectTimeout()); - } - - public function testDefaultOptions() - { - // make sure options array loads/saves properly - $optionsArray = array(CURLOPT_PROXY => 'localhost:80'); - $withOptionsArray = new CurlClient($optionsArray); - $this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray); - - // make sure closure-based options work properly, including argument passing - $ref = null; - $withClosure = new CurlClient(function ($method, $absUrl, $headers, $params, $hasFile) use (&$ref) { - $ref = func_get_args(); - return array(); - }); - - $withClosure->request('get', 'https://httpbin.org/status/200', array(), array(), false); - $this->assertSame($ref, array('get', 'https://httpbin.org/status/200', array(), array(), false)); - - // this is the last test case that will run, since it'll throw an exception at the end - $withBadClosure = new CurlClient(function () { - return 'thisShouldNotWork'; - }); - $this->setExpectedException('Stripe\Error\Api', "Non-array value returned by defaultOptions CurlClient callback"); - $withBadClosure->request('get', 'https://httpbin.org/status/200', array(), array(), false); - } - - public function testEncode() - { - $a = array( - 'my' => 'value', - 'that' => array('your' => 'example'), - 'bar' => 1, - 'baz' => null - ); - - $enc = CurlClient::encode($a); - $this->assertSame('my=value&that%5Byour%5D=example&bar=1', $enc); - - $a = array('that' => array('your' => 'example', 'foo' => null)); - $enc = CurlClient::encode($a); - $this->assertSame('that%5Byour%5D=example', $enc); - - $a = array('that' => 'example', 'foo' => array('bar', 'baz')); - $enc = CurlClient::encode($a); - $this->assertSame('that=example&foo%5B%5D=bar&foo%5B%5D=baz', $enc); - - $a = array( - 'my' => 'value', - 'that' => array('your' => array('cheese', 'whiz', null)), - 'bar' => 1, - 'baz' => null - ); - - $enc = CurlClient::encode($a); - $expected = 'my=value&that%5Byour%5D%5B%5D=cheese' - . '&that%5Byour%5D%5B%5D=whiz&bar=1'; - $this->assertSame($expected, $enc); - - // Ignores an empty array - $enc = CurlClient::encode(array('foo' => array(), 'bar' => 'baz')); - $expected = 'bar=baz'; - $this->assertSame($expected, $enc); - - $a = array('foo' => array(array('bar' => 'baz'), array('bar' => 'bin'))); - $enc = CurlClient::encode($a); - $this->assertSame('foo%5B0%5D%5Bbar%5D=baz&foo%5B1%5D%5Bbar%5D=bin', $enc); - } - - public function testSslOption() - { - // make sure options array loads/saves properly - $optionsArray = array(CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1); - $withOptionsArray = new CurlClient($optionsArray); - $this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray); - } -} diff --git a/htdocs/includes/stripe/tests/CustomerTest.php b/htdocs/includes/stripe/tests/CustomerTest.php deleted file mode 100644 index 606536ee86f..00000000000 --- a/htdocs/includes/stripe/tests/CustomerTest.php +++ /dev/null @@ -1,278 +0,0 @@ -delete(); - - $this->assertTrue($customer->deleted); - $this->assertNull($customer['active_card']); - } - - public function testSave() - { - $customer = self::createTestCustomer(); - - $customer->email = 'gdb@stripe.com'; - $customer->save(); - $this->assertSame($customer->email, 'gdb@stripe.com'); - - $stripeCustomer = Customer::retrieve($customer->id); - $this->assertSame($customer->email, $stripeCustomer->email); - - Stripe::setApiKey(null); - $customer = Customer::create(null, self::API_KEY); - $customer->email = 'gdb@stripe.com'; - $customer->save(); - - self::authorizeFromEnv(); - $updatedCustomer = Customer::retrieve($customer->id); - $this->assertSame($updatedCustomer->email, 'gdb@stripe.com'); - } - - /** - * @expectedException Stripe\Error\InvalidRequest - */ - public function testBogusAttribute() - { - $customer = self::createTestCustomer(); - $customer->bogus = 'bogus'; - $customer->save(); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testUpdateDescriptionEmpty() - { - $customer = self::createTestCustomer(); - $customer->description = ''; - } - - public function testUpdateDescriptionNull() - { - $customer = self::createTestCustomer(array('description' => 'foo bar')); - $customer->description = null; - - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $this->assertSame(null, $updatedCustomer->description); - } - - public function testUpdateMetadata() - { - $customer = self::createTestCustomer(); - - $customer->metadata['test'] = 'foo bar'; - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $this->assertSame('foo bar', $updatedCustomer->metadata['test']); - } - - public function testDeleteMetadata() - { - $customer = self::createTestCustomer(); - - $customer->metadata = null; - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $this->assertSame(0, count($updatedCustomer->metadata->keys())); - } - - public function testUpdateSomeMetadata() - { - $customer = self::createTestCustomer(); - $customer->metadata['shoe size'] = '7'; - $customer->metadata['shirt size'] = 'XS'; - $customer->save(); - - $customer->metadata['shoe size'] = '9'; - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $this->assertSame('XS', $updatedCustomer->metadata['shirt size']); - $this->assertSame('9', $updatedCustomer->metadata['shoe size']); - } - - public function testUpdateAllMetadata() - { - $customer = self::createTestCustomer(); - $customer->metadata['shoe size'] = '7'; - $customer->metadata['shirt size'] = 'XS'; - $customer->save(); - - $customer->metadata = array('shirt size' => 'XL'); - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $this->assertSame('XL', $updatedCustomer->metadata['shirt size']); - $this->assertFalse(isset($updatedCustomer->metadata['shoe size'])); - } - - /** - * @expectedException Stripe\Error\InvalidRequest - */ - public function testUpdateInvalidMetadata() - { - $customer = self::createTestCustomer(); - $customer->metadata = 'something'; - $customer->save(); - } - - public function testCancelSubscription() - { - $planID = 'gold-' . self::generateRandomString(20); - self::retrieveOrCreatePlan($planID); - - $customer = self::createTestCustomer( - array( - 'plan' => $planID, - ) - ); - - $customer->cancelSubscription(array('at_period_end' => true)); - $this->assertSame($customer->subscription->status, 'active'); - $this->assertTrue($customer->subscription->cancel_at_period_end); - $customer->cancelSubscription(); - $this->assertSame($customer->subscription->status, 'canceled'); - } - - public function testCustomerAddCard() - { - $token = Token::create( - array("card" => array( - "number" => "4242424242424242", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $customer = $this->createTestCustomer(); - $createdCard = $customer->sources->create(array("card" => $token->id)); - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $updatedCards = $updatedCustomer->sources->all(); - $this->assertSame(count($updatedCards["data"]), 2); - } - - public function testCustomerUpdateCard() - { - $customer = $this->createTestCustomer(); - $customer->save(); - - $sources = $customer->sources->all(); - $this->assertSame(count($sources["data"]), 1); - - $card = $sources['data'][0]; - $card->name = "Jane Austen"; - $card->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $updatedCards = $updatedCustomer->sources->all(); - $this->assertSame($updatedCards["data"][0]->name, "Jane Austen"); - } - - public function testCustomerDeleteCard() - { - $token = Token::create( - array("card" => array( - "number" => "4242424242424242", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $customer = $this->createTestCustomer(); - $createdCard = $customer->sources->create(array("card" => $token->id)); - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $updatedCards = $updatedCustomer->sources->all(); - $this->assertSame(count($updatedCards["data"]), 2); - - $deleteStatus = $updatedCustomer->sources->retrieve($createdCard->id)->delete(); - $this->assertTrue($deleteStatus->deleted); - $updatedCustomer->save(); - - $postDeleteCustomer = Customer::retrieve($customer->id); - $postDeleteCards = $postDeleteCustomer->sources->all(); - $this->assertSame(count($postDeleteCards["data"]), 1); - } - - public function testCustomerAddSource() - { - self::authorizeFromEnv(); - $token = Token::create( - array("card" => array( - "number" => "4242424242424242", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $customer = $this->createTestCustomer(); - $createdSource = $customer->sources->create(array("source" => $token->id)); - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $updatedSources = $updatedCustomer->sources->all(); - $this->assertSame(count($updatedSources["data"]), 2); - } - - public function testCustomerUpdateSource() - { - $customer = $this->createTestCustomer(); - $customer->save(); - - $sources = $customer->sources->all(); - $this->assertSame(count($sources["data"]), 1); - - $source = $sources['data'][0]; - $source->name = "Jane Austen"; - $source->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $updatedSources = $updatedCustomer->sources->all(); - $this->assertSame($updatedSources["data"][0]->name, "Jane Austen"); - } - - public function testCustomerDeleteSource() - { - self::authorizeFromEnv(); - $token = Token::create( - array("card" => array( - "number" => "4242424242424242", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $customer = $this->createTestCustomer(); - $createdSource = $customer->sources->create(array("source" => $token->id)); - $customer->save(); - - $updatedCustomer = Customer::retrieve($customer->id); - $updatedSources = $updatedCustomer->sources->all(); - $this->assertSame(count($updatedSources["data"]), 2); - - $deleteStatus = $updatedCustomer->sources->retrieve($createdSource->id)->delete(); - $this->assertTrue($deleteStatus->deleted); - $updatedCustomer->save(); - - $postDeleteCustomer = Customer::retrieve($customer->id); - $postDeleteSources = $postDeleteCustomer->sources->all(); - $this->assertSame(count($postDeleteSources["data"]), 1); - } -} diff --git a/htdocs/includes/stripe/tests/DiscountTest.php b/htdocs/includes/stripe/tests/DiscountTest.php deleted file mode 100644 index 1e77029ad2d..00000000000 --- a/htdocs/includes/stripe/tests/DiscountTest.php +++ /dev/null @@ -1,31 +0,0 @@ - 25, - 'duration' => 'repeating', - 'duration_in_months' => 5, - 'id' => $id, - ) - ); - $customer = self::createTestCustomer(array('coupon' => $id)); - - $this->assertTrue(isset($customer->discount)); - $this->assertTrue(isset($customer->discount->coupon)); - $this->assertSame($id, $customer->discount->coupon->id); - - $customer->deleteDiscount(); - $this->assertFalse(isset($customer->discount)); - - $customer = Customer::retrieve($customer->id); - $this->assertFalse(isset($customer->discount)); - } -} diff --git a/htdocs/includes/stripe/tests/DisputeTest.php b/htdocs/includes/stripe/tests/DisputeTest.php deleted file mode 100644 index 3c4f29678ea..00000000000 --- a/htdocs/includes/stripe/tests/DisputeTest.php +++ /dev/null @@ -1,96 +0,0 @@ -assertSame(Dispute::classUrl(), '/v1/disputes'); - $dispute = new Dispute('dp_123'); - $this->assertSame($dispute->instanceUrl(), '/v1/disputes/dp_123'); - } - - private function createDisputedCharge() - { - $card = array( - 'number' => '4000000000000259', - 'exp_month' => 5, - 'exp_year' => date('Y') + 1 - ); - - $c = Charge::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'card' => $card - ) - ); - $c = Charge::retrieve($c->id); - - $attempts = 0; - - while ($c->dispute === null) { - if ($attempts > 5) { - throw new \Exception("Charge is taking too long to be disputed"); - } - sleep(1); - $c = Charge::retrieve($c->id); - $attempts += 1; - } - - return $c; - } - - public function testAll() - { - self::authorizeFromEnv(); - - $sublist = Dispute::all( - array( - 'limit' => 3, - ) - ); - $this->assertSame(3, count($sublist->data)); - } - - - public function testUpdate() - { - self::authorizeFromEnv(); - - $c = $this->createDisputedCharge(); - - $d = Dispute::retrieve($c->dispute); - $d->evidence["customer_name"] = "Bob"; - $s = $d->save(); - - $this->assertSame($c->dispute, $s->id); - $this->assertSame("Bob", $s->evidence["customer_name"]); - } - - public function testClose() - { - self::authorizeFromEnv(); - - $c = $this->createDisputedCharge(); - $d = Dispute::retrieve($c->dispute); - - $this->assertNotSame("lost", $d->status); - - $d->close(); - - $this->assertSame("lost", $d->status); - } - - public function testRetrieve() - { - self::authorizeFromEnv(); - - $c = $this->createDisputedCharge(); - - $d = Dispute::retrieve($c->dispute); - - $this->assertSame($c->dispute, $d->id); - } -} diff --git a/htdocs/includes/stripe/tests/ErrorTest.php b/htdocs/includes/stripe/tests/ErrorTest.php deleted file mode 100644 index 75362427510..00000000000 --- a/htdocs/includes/stripe/tests/ErrorTest.php +++ /dev/null @@ -1,61 +0,0 @@ - 'bar') - ); - $this->fail("Did not raise error"); - } catch (Error\Api $e) { - $this->assertSame("hello", $e->getMessage()); - $this->assertSame(500, $e->getHttpStatus()); - $this->assertSame("{'foo':'bar'}", $e->getHttpBody()); - $this->assertSame(array('foo' => 'bar'), $e->getJsonBody()); - $this->assertSame(null, $e->getHttpHeaders()); - $this->assertSame(null, $e->getRequestId()); - } - } - - public function testResponseHeaders() - { - try { - throw new Error\Api( - "hello", - 500, - "{'foo':'bar'}", - array('foo' => 'bar'), - array('Request-Id' => 'req_bar') - ); - $this->fail("Did not raise error"); - } catch (Error\Api $e) { - $this->assertSame(array('Request-Id' => 'req_bar'), $e->getHttpHeaders()); - $this->assertSame('req_bar', $e->getRequestId()); - } - } - - public function testCode() - { - try { - throw new Error\Card( - "hello", - "some_param", - "some_code", - 400, - "{'foo':'bar'}", - array('foo' => 'bar') - ); - $this->fail("Did not raise error"); - } catch (Error\Card $e) { - $this->assertSame("some_param", $e->getStripeParam()); - $this->assertSame('some_code', $e->getStripeCode()); - } - } -} diff --git a/htdocs/includes/stripe/tests/ExternalAccountTest.php b/htdocs/includes/stripe/tests/ExternalAccountTest.php deleted file mode 100644 index 70c0068bc45..00000000000 --- a/htdocs/includes/stripe/tests/ExternalAccountTest.php +++ /dev/null @@ -1,30 +0,0 @@ - array( - 'country' => 'US', - 'routing_number' => '110000000', - 'account_number' => '000123456789', - 'account_holder_name' => 'Jane Austen', - 'account_holder_type' => 'company' - ) - ) - ); - $customer = Customer::create(); - $externalAccount = $customer->sources->create(array('bank_account' => $bankAccountToken->id)); - $verifiedAccount = $externalAccount->verify(array('amounts' => array(32, 45)), null); - - $base = Customer::classUrl(); - $parentExtn = $externalAccount['customer']; - $extn = $externalAccount['id']; - $this->assertEquals("$base/$parentExtn/sources/$extn", $externalAccount->instanceUrl()); - } -} diff --git a/htdocs/includes/stripe/tests/FileUploadTest.php b/htdocs/includes/stripe/tests/FileUploadTest.php deleted file mode 100644 index 7d1faf3b00f..00000000000 --- a/htdocs/includes/stripe/tests/FileUploadTest.php +++ /dev/null @@ -1,44 +0,0 @@ - 'dispute_evidence', - 'file' => $fp, - ) - ); - fclose($fp); - $this->assertSame(95, $file->size); - $this->assertSame('png', $file->type); - } - - public function testCreateAndRetrieveCurlFile() - { - if (!class_exists('\CurlFile', false)) { - // Older PHP versions don't support this - return; - } - - $curlFile = new \CurlFile(dirname(__FILE__).'/../data/test.png'); - self::authorizeFromEnv(); - $file = FileUpload::create( - array( - 'purpose' => 'dispute_evidence', - 'file' => $curlFile, - ) - ); - $this->assertSame(95, $file->size); - $this->assertSame('png', $file->type); - - // Just check that we don't get exceptions - $file = FileUpload::retrieve($file->id); - $file->refresh(); - } -} diff --git a/htdocs/includes/stripe/tests/InvalidRequestErrorTest.php b/htdocs/includes/stripe/tests/InvalidRequestErrorTest.php deleted file mode 100644 index 6cc6d682911..00000000000 --- a/htdocs/includes/stripe/tests/InvalidRequestErrorTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertSame(404, $e->getHttpStatus()); - } - } - - public function testBadData() - { - self::authorizeFromEnv(); - try { - Charge::create(); - } catch (Error\InvalidRequest $e) { - $this->assertSame(400, $e->getHttpStatus()); - } - } -} diff --git a/htdocs/includes/stripe/tests/InvoiceTest.php b/htdocs/includes/stripe/tests/InvoiceTest.php deleted file mode 100644 index a55ab83e4ac..00000000000 --- a/htdocs/includes/stripe/tests/InvoiceTest.php +++ /dev/null @@ -1,56 +0,0 @@ - $customer->id, - 'amount' => 0, - 'currency' => 'usd', - )); - - $invoice = Invoice::upcoming(array( - 'customer' => $customer->id, - )); - $this->assertSame($invoice->customer, $customer->id); - $this->assertSame($invoice->attempted, false); - } - - public function testItemsAccessWithParameter() - { - self::authorizeFromEnv(); - $customer = self::createTestCustomer(); - - InvoiceItem::create(array( - 'customer' => $customer->id, - 'amount' => 100, - 'currency' => 'usd', - )); - - $invoice = Invoice::upcoming( - array( - 'customer' => $customer->id, - ) - ); - - $lines = $invoice->lines->all(array('limit' => 10)); - - $this->assertSame(count($lines->data), 1); - $this->assertSame($lines->data[0]->amount, 100); - } - - // This is really just making sure that this operation does not trigger any - // warnings, as it's highly nested. - public function testAll() - { - self::authorizeFromEnv(); - $invoices = Invoice::all(); - $this->assertGreaterThan(0, count($invoices)); - } -} diff --git a/htdocs/includes/stripe/tests/PayoutTest.php b/htdocs/includes/stripe/tests/PayoutTest.php deleted file mode 100644 index 5e99faede87..00000000000 --- a/htdocs/includes/stripe/tests/PayoutTest.php +++ /dev/null @@ -1,94 +0,0 @@ -managedAccount === null) { - self::authorizeFromEnv(); - $account = self::createTestManagedAccount(); - - $charge = \Stripe\Charge::create(array( - 'currency' => 'usd', - 'amount' => '10000', - 'source' => array( - 'object' => 'card', - 'number' => '4000000000000077', - 'exp_month' => '09', - 'exp_year' => date('Y') + 3, - ), - 'destination' => array( - 'account' => $account->id - ) - )); - - $this->managedAccount = $account; - } - - return $this->managedAccount; - } - - private function createPayoutFromManagedAccount($accountId) - { - $payout = Payout::create( - array( - 'amount' => 100, - 'currency' => 'usd', - ), - array( - 'stripe_account' => $accountId - ) - ); - - return $payout; - } - - public function testCreate() - { - $account = self::createAccountWithBalance(); - $payout = self::createPayoutFromManagedAccount($account->id); - - $this->assertSame('pending', $payout->status); - } - - public function testRetrieve() - { - $account = self::createAccountWithBalance(); - $payout = self::createPayoutFromManagedAccount($account->id); - $reloaded = Payout::retrieve($payout->id, array('stripe_account' => $account->id)); - $this->assertSame($reloaded->id, $payout->id); - } - - public function testPayoutUpdateMetadata() - { - $account = self::createAccountWithBalance(); - $payout = self::createPayoutFromManagedAccount($account->id); - $payout->metadata['test'] = 'foo bar'; - $payout->save(); - - $updatedPayout = Payout::retrieve($payout->id, array('stripe_account' => $account->id)); - $this->assertSame('foo bar', $updatedPayout->metadata['test']); - } - - public function testPayoutUpdateMetadataAll() - { - $account = self::createAccountWithBalance(); - $payout = self::createPayoutFromManagedAccount($account->id); - - $payout->metadata = array('test' => 'foo bar'); - $payout->save(); - - $updatedPayout = Payout::retrieve($payout->id, array('stripe_account' => $account->id)); - $this->assertSame('foo bar', $updatedPayout->metadata['test']); - } -} diff --git a/htdocs/includes/stripe/tests/PermissionsErrorTest.php b/htdocs/includes/stripe/tests/PermissionsErrorTest.php deleted file mode 100644 index cc8a5cd0be0..00000000000 --- a/htdocs/includes/stripe/tests/PermissionsErrorTest.php +++ /dev/null @@ -1,22 +0,0 @@ - array(), - ); - } - - /** - * @expectedException Stripe\Error\Permission - */ - public function testPermission() - { - $this->mockRequest('GET', '/v1/accounts/acct_DEF', array(), $this->permissionErrorResponse(), 403); - Account::retrieve('acct_DEF'); - } -} diff --git a/htdocs/includes/stripe/tests/PlanTest.php b/htdocs/includes/stripe/tests/PlanTest.php deleted file mode 100644 index 9aa3589c719..00000000000 --- a/htdocs/includes/stripe/tests/PlanTest.php +++ /dev/null @@ -1,51 +0,0 @@ - 2000, - 'interval' => 'month', - 'currency' => 'usd', - 'name' => 'Plan', - 'id' => 'gold-' . self::generateRandomString(20) - )); - $p->delete(); - $this->assertTrue($p->deleted); - } - - public function testFalseyId() - { - try { - $retrievedPlan = Plan::retrieve('0'); - } catch (Error\InvalidRequest $e) { - // Can either succeed or 404, all other errors are bad - if ($e->httpStatus !== 404) { - $this->fail(); - } - } - } - - public function testSave() - { - self::authorizeFromEnv(); - $planID = 'gold-' . self::generateRandomString(20); - $p = Plan::create(array( - 'amount' => 2000, - 'interval' => 'month', - 'currency' => 'usd', - 'name' => 'Plan', - 'id' => $planID - )); - $p->name = 'A new plan name'; - $p->save(); - $this->assertSame($p->name, 'A new plan name'); - - $stripePlan = Plan::retrieve($planID); - $this->assertSame($p->name, $stripePlan->name); - } -} diff --git a/htdocs/includes/stripe/tests/ProductTest.php b/htdocs/includes/stripe/tests/ProductTest.php deleted file mode 100644 index 668d015e380..00000000000 --- a/htdocs/includes/stripe/tests/ProductTest.php +++ /dev/null @@ -1,158 +0,0 @@ -httpStatus !== 404) { - $this->fail(); - } - } - } - - public function testProductCreateUpdateRead() - { - - Stripe::setApiKey('sk_test_JieJALRz7rPz7boV17oMma7a'); - $ProductID = 'gold-' . self::generateRandomString(20); - $p = Product::create(array( - 'name' => 'Gold Product', - 'id' => $ProductID, - 'url' => 'www.stripe.com/gold' - )); - $this->assertSame($p->url, 'www.stripe.com/gold'); - - $p->name = 'A new Product name'; - $p->save(); - $this->assertSame($p->name, 'A new Product name'); - $this->assertSame($p->url, 'www.stripe.com/gold'); - - $stripeProduct = Product::retrieve($ProductID); - $this->assertSame($p->name, $stripeProduct->name); - $this->assertSame($stripeProduct->url, 'www.stripe.com/gold'); - } - - public function testSKUCreateUpdateRead() - { - Stripe::setApiKey('sk_test_JieJALRz7rPz7boV17oMma7a'); - $ProductID = 'silver-' . self::generateRandomString(20); - $p = Product::create(array( - 'name' => 'Silver Product', - 'id' => $ProductID, - 'url' => 'www.stripe.com/silver' - )); - - $SkuID = 'silver-sku-' . self::generateRandomString(20); - $sku = SKU::create(array( - 'price' => 500, - 'currency' => 'usd', - 'id' => $SkuID, - 'inventory' => array( - 'type' => 'finite', - 'quantity' => 40 - ), - 'product' => $ProductID - )); - - $sku->price = 600; - $sku->inventory->quantity = 50; - $sku->save(); - $this->assertSame($sku->price, 600); - $this->assertSame(50, $sku->inventory->quantity); - - $stripeSku = SKU::retrieve($SkuID); - $this->assertSame($sku->price, 600); - $this->assertSame('finite', $sku->inventory->type); - $this->assertSame(50, $sku->inventory->quantity); - } - - public function testSKUProductDelete() - { - Stripe::setApiKey('sk_test_JieJALRz7rPz7boV17oMma7a'); - $ProductID = 'silver-' . self::generateRandomString(20); - $p = Product::create(array( - 'name' => 'Silver Product', - 'id' => $ProductID, - 'url' => 'stripe.com/silver' - )); - - $SkuID = 'silver-sku-' . self::generateRandomString(20); - $sku = SKU::create(array( - 'price' => 500, - 'currency' => 'usd', - 'id' => $SkuID, - 'inventory' => array( - 'type' => 'finite', - 'quantity' => 40 - ), - 'product' => $ProductID - )); - - $deletedSku = $sku->delete(); - $this->assertTrue($deletedSku->deleted); - - $deletedProduct = $p->delete(); - $this->assertTrue($deletedProduct->deleted); - } - - public function testOrderCreateUpdateRetrievePayReturn() - { - Stripe::setApiKey('sk_test_JieJALRz7rPz7boV17oMma7a'); - $ProductID = 'silver-' . self::generateRandomString(20); - $p = Product::create(array( - 'name' => 'Silver Product', - 'id' => $ProductID, - 'url' => 'www.stripe.com/silver', - 'shippable' => false, - )); - - $SkuID = 'silver-sku-' . self::generateRandomString(20); - $sku = SKU::create(array( - 'price' => 500, - 'currency' => 'usd', - 'id' => $SkuID, - 'inventory' => array( - 'type' => 'finite', - 'quantity' => 40 - ), - 'product' => $ProductID - )); - - $order = Order::create(array( - 'items' => array( - 0 => array( - 'type' => 'sku', - 'parent' => $SkuID, - ), - ), - 'currency' => 'usd', - 'email' => 'foo@bar.com', - )); - - $order->metadata->foo = "bar"; - $order->save(); - - $stripeOrder = Order::retrieve($order->id); - $this->assertSame($order->metadata->foo, "bar"); - - $order->pay(array( - 'source' => array( - 'object' => 'card', - 'number' => '4242424242424242', - 'exp_month' => '05', - 'exp_year' => '2017' - ), - )); - $this->assertSame($order->status, 'paid'); - - $orderReturn = $order->returnOrder(); - $this->assertSame($orderReturn->order, $order->id); - } -} diff --git a/htdocs/includes/stripe/tests/RateLimitErrorTest.php b/htdocs/includes/stripe/tests/RateLimitErrorTest.php deleted file mode 100644 index 2d6a6b22a2b..00000000000 --- a/htdocs/includes/stripe/tests/RateLimitErrorTest.php +++ /dev/null @@ -1,22 +0,0 @@ - array(), - ); - } - - /** - * @expectedException Stripe\Error\RateLimit - */ - public function testRateLimit() - { - $this->mockRequest('GET', '/v1/accounts/acct_DEF', array(), $this->rateLimitErrorResponse(), 429); - Account::retrieve('acct_DEF'); - } -} diff --git a/htdocs/includes/stripe/tests/RecipientTest.php b/htdocs/includes/stripe/tests/RecipientTest.php deleted file mode 100644 index e26bfe8cddd..00000000000 --- a/htdocs/includes/stripe/tests/RecipientTest.php +++ /dev/null @@ -1,112 +0,0 @@ -delete(); - - $this->assertTrue($recipient->deleted); - } - - public function testSave() - { - $recipient = self::createTestRecipient(); - - $recipient->email = 'gdb@stripe.com'; - $recipient->save(); - $this->assertSame($recipient->email, 'gdb@stripe.com'); - - $stripeRecipient = Recipient::retrieve($recipient->id); - $this->assertSame($recipient->email, $stripeRecipient->email); - } - - /** - * @expectedException Stripe\Error\InvalidRequest - */ - public function testBogusAttribute() - { - $recipient = self::createTestRecipient(); - $recipient->bogus = 'bogus'; - $recipient->save(); - } - - public function testRecipientAddCard() - { - $token = Token::create( - array("card" => array( - "number" => "4000056655665556", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $recipient = $this->createTestRecipient(); - $createdCard = $recipient->cards->create(array("card" => $token->id)); - $recipient->save(); - - $updatedRecipient = Recipient::retrieve($recipient->id); - $updatedCards = $updatedRecipient->cards->all(); - $this->assertSame(count($updatedCards["data"]), 1); - } - - public function testRecipientUpdateCard() - { - $token = Token::create( - array("card" => array( - "number" => "4000056655665556", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $recipient = $this->createTestRecipient(); - $createdCard = $recipient->cards->create(array("card" => $token->id)); - $recipient->save(); - - $createdCards = $recipient->cards->all(); - $this->assertSame(count($createdCards["data"]), 1); - - $card = $createdCards['data'][0]; - $card->name = "Jane Austen"; - $card->save(); - - $updatedRecipient = Recipient::retrieve($recipient->id); - $updatedCards = $updatedRecipient->cards->all(); - $this->assertSame($updatedCards["data"][0]->name, "Jane Austen"); - } - - public function testRecipientDeleteCard() - { - $token = Token::create( - array("card" => array( - "number" => "4000056655665556", - "exp_month" => 5, - "exp_year" => date('Y') + 3, - "cvc" => "314" - )) - ); - - $recipient = $this->createTestRecipient(); - $createdCard = $recipient->cards->create(array("card" => $token->id)); - $recipient->save(); - - $updatedRecipient = Recipient::retrieve($recipient->id); - $updatedCards = $updatedRecipient->cards->all(); - $this->assertSame(count($updatedCards["data"]), 1); - - $deleteStatus = - $updatedRecipient->cards->retrieve($createdCard->id)->delete(); - $this->assertTrue($deleteStatus->deleted); - $updatedRecipient->save(); - - $postDeleteRecipient = Recipient::retrieve($recipient->id); - $postDeleteCards = $postDeleteRecipient->cards->all(); - $this->assertSame(count($postDeleteCards["data"]), 0); - } -} diff --git a/htdocs/includes/stripe/tests/RefundTest.php b/htdocs/includes/stripe/tests/RefundTest.php deleted file mode 100644 index e9dd8509cd3..00000000000 --- a/htdocs/includes/stripe/tests/RefundTest.php +++ /dev/null @@ -1,131 +0,0 @@ - 100, 'charge' => $charge->id)); - $this->assertSame(100, $refund->amount); - $this->assertSame($charge->id, $refund->charge); - } - - public function testUpdateAndRetrieve() - { - $charge = self::createTestCharge(); - $ref = Refund::create(array('amount' => 100, 'charge' => $charge->id)); - $ref->metadata["key"] = "value"; - $ref->save(); - $ref = Refund::retrieve($ref->id); - $this->assertSame("value", $ref->metadata["key"], "value"); - } - - public function testListForCharge() - { - $charge = self::createTestCharge(); - $refA = Refund::create(array('amount' => 100, 'charge' => $charge->id)); - $refB = Refund::create(array('amount' => 50, 'charge' => $charge->id)); - - $all = Refund::all(array('charge' => $charge)); - $this->assertSame(false, $all['has_more']); - $this->assertSame(2, count($all->data)); - $this->assertSame($refB->id, $all->data[0]->id); - $this->assertSame($refA->id, $all->data[1]->id); - } - - public function testList() - { - $all = Refund::all(); - - // Fetches all refunds on this test account. - $this->assertSame(true, $all['has_more']); - $this->assertSame(10, count($all->data)); - } - - public function testCreateForBitcoin() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $charge = Charge::create( - array( - 'amount' => $receiver->amount, - 'currency' => $receiver->currency, - 'description' => $receiver->description, - 'source' => $receiver->id - ) - ); - - $ref = Refund::create( - array( - 'amount' => $receiver->amount, - 'refund_address' => 'ABCDEF', - 'charge' => $charge->id - ) - ); - $this->assertSame($receiver->amount, $ref->amount); - $this->assertNotNull($ref->id); - } - - // Deprecated charge endpoints: - - public function testCreateViaCharge() - { - $charge = self::createTestCharge(); - $ref = $charge->refunds->create(array('amount' => 100)); - $this->assertSame(100, $ref->amount); - $this->assertSame($charge->id, $ref->charge); - } - - public function testUpdateAndRetrieveViaCharge() - { - $charge = self::createTestCharge(); - $ref = $charge->refunds->create(array('amount' => 100)); - $ref->metadata["key"] = "value"; - $ref->save(); - $ref = $charge->refunds->retrieve($ref->id); - $this->assertSame("value", $ref->metadata["key"], "value"); - } - - public function testListViaCharge() - { - $charge = self::createTestCharge(); - $refA = $charge->refunds->create(array('amount' => 50)); - $refB = $charge->refunds->create(array('amount' => 50)); - - $all = $charge->refunds->all(); - $this->assertSame(false, $all['has_more']); - $this->assertSame(2, count($all->data)); - $this->assertSame($refB->id, $all->data[0]->id); - $this->assertSame($refA->id, $all->data[1]->id); - } - - public function testCreateForBitcoinViaCharge() - { - self::authorizeFromEnv(); - - $receiver = $this->createTestBitcoinReceiver("do+fill_now@stripe.com"); - - $charge = Charge::create( - array( - 'amount' => $receiver->amount, - 'currency' => $receiver->currency, - 'description' => $receiver->description, - 'source' => $receiver->id - ) - ); - - $ref = $charge->refunds->create( - array( - 'amount' => $receiver->amount, - 'refund_address' => 'ABCDEF' - ) - ); - $this->assertSame($receiver->amount, $ref->amount); - $this->assertNotNull($ref->id); - } -} diff --git a/htdocs/includes/stripe/tests/SourceTest.php b/htdocs/includes/stripe/tests/SourceTest.php deleted file mode 100644 index 5ba142b9be2..00000000000 --- a/htdocs/includes/stripe/tests/SourceTest.php +++ /dev/null @@ -1,218 +0,0 @@ -mockRequest( - 'GET', - '/v1/sources/src_foo', - array(), - array( - 'id' => 'src_foo', - 'object' => 'source', - ) - ); - $source = Source::retrieve('src_foo'); - $this->assertSame($source->id, 'src_foo'); - } - - public function testCreate() - { - $this->mockRequest( - 'POST', - '/v1/sources', - array( - 'type' => 'bitcoin', - 'amount' => 1000, - 'currency' => 'usd', - 'owner' => array('email' => 'jenny.rosen@example.com'), - ), - array( - 'id' => 'src_foo', - 'object' => 'source' - ) - ); - $source = Source::create(array( - 'type' => 'bitcoin', - 'amount' => 1000, - 'currency' => 'usd', - 'owner' => array('email' => 'jenny.rosen@example.com'), - )); - $this->assertSame($source->id, 'src_foo'); - } - - public function testSave() - { - $response = array( - 'id' => 'src_foo', - 'object' => 'source', - 'metadata' => array(), - ); - $this->mockRequest( - 'GET', - '/v1/sources/src_foo', - array(), - $response - ); - - $response['metadata'] = array('foo' => 'bar'); - $this->mockRequest( - 'POST', - '/v1/sources/src_foo', - array( - 'metadata' => array('foo' => 'bar'), - ), - $response - ); - - $source = Source::retrieve('src_foo'); - $source->metadata['foo'] = 'bar'; - $source->save(); - $this->assertSame($source->metadata['foo'], 'bar'); - } - - public function testSaveOwner() - { - $response = array( - 'id' => 'src_foo', - 'object' => 'source', - 'owner' => array( - 'name' => null, - 'address' => null, - ), - ); - $this->mockRequest( - 'GET', - '/v1/sources/src_foo', - array(), - $response - ); - - $response['owner'] = array( - 'name' => "Stripey McStripe", - 'address' => array( - 'line1' => "Test Address", - 'city' => "Test City", - 'postal_code' => "12345", - 'state' => "Test State", - 'country' => "Test Country", - ) - ); - $this->mockRequest( - 'POST', - '/v1/sources/src_foo', - array( - 'owner' => array( - 'name' => "Stripey McStripe", - 'address' => array( - 'line1' => "Test Address", - 'city' => "Test City", - 'postal_code' => "12345", - 'state' => "Test State", - 'country' => "Test Country", - ), - ), - ), - $response - ); - - $source = Source::retrieve('src_foo'); - $source->owner['name'] = "Stripey McStripe"; - $source->owner['address'] = array( - 'line1' => "Test Address", - 'city' => "Test City", - 'postal_code' => "12345", - 'state' => "Test State", - 'country' => "Test Country", - ); - $source->save(); - $this->assertSame($source->owner['name'], "Stripey McStripe"); - $this->assertSame($source->owner['address']['line1'], "Test Address"); - $this->assertSame($source->owner['address']['city'], "Test City"); - $this->assertSame($source->owner['address']['postal_code'], "12345"); - $this->assertSame($source->owner['address']['state'], "Test State"); - $this->assertSame($source->owner['address']['country'], "Test Country"); - } - - public function testDeleteAttached() - { - $response = array( - 'id' => 'src_foo', - 'object' => 'source', - 'customer' => 'cus_bar', - ); - $this->mockRequest( - 'GET', - '/v1/sources/src_foo', - array(), - $response - ); - - unset($response['customer']); - $this->mockRequest( - 'DELETE', - '/v1/customers/cus_bar/sources/src_foo', - array(), - $response - ); - - $source = Source::retrieve('src_foo'); - $source->delete(); - $this->assertFalse(array_key_exists('customer', $source)); - } - - /** - * @expectedException Stripe\Error\Api - */ - public function testDeleteUnattached() - { - $response = array( - 'id' => 'src_foo', - 'object' => 'source', - ); - $this->mockRequest( - 'GET', - '/v1/sources/src_foo', - array(), - $response - ); - - $source = Source::retrieve('src_foo'); - $source->delete(); - } - - public function testVerify() - { - $response = array( - 'id' => 'src_foo', - 'object' => 'source', - 'verification' => array('status' => 'pending'), - ); - $this->mockRequest( - 'GET', - '/v1/sources/src_foo', - array(), - $response - ); - - $response['verification']['status'] = 'succeeded'; - $this->mockRequest( - 'POST', - '/v1/sources/src_foo/verify', - array( - 'values' => array(32, 45), - ), - $response - ); - - $source = Source::retrieve('src_foo'); - $this->assertSame($source->verification->status, 'pending'); - $source->verify(array( - 'values' => array(32, 45), - )); - $this->assertSame($source->verification->status, 'succeeded'); - } -} diff --git a/htdocs/includes/stripe/tests/Stripe/AccountTest.php b/htdocs/includes/stripe/tests/Stripe/AccountTest.php new file mode 100644 index 00000000000..4c621690dcf --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/AccountTest.php @@ -0,0 +1,365 @@ +expectsRequest( + 'get', + '/v1/accounts' + ); + $resources = Account::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Account", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/accounts/' . self::TEST_RESOURCE_ID + ); + $resource = Account::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Account", $resource); + } + + public function testIsRetrievableWithoutId() + { + $this->expectsRequest( + 'get', + '/v1/account' + ); + $resource = Account::retrieve(); + $this->assertInstanceOf("Stripe\\Account", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/accounts' + ); + $resource = Account::create(["type" => "custom"]); + $this->assertInstanceOf("Stripe\\Account", $resource); + } + + public function testIsSaveable() + { + $resource = Account::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/accounts/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Account", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/accounts/' . self::TEST_RESOURCE_ID + ); + $resource = Account::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Account", $resource); + } + + public function testIsDeletable() + { + $resource = Account::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/accounts/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Account", $resource); + } + + public function testIsRejectable() + { + $account = Account::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/accounts/' . $account->id . '/reject' + ); + $resource = $account->reject(["reason" => "fraud"]); + $this->assertInstanceOf("Stripe\\Account", $resource); + $this->assertSame($resource, $account); + } + + public function testIsDeauthorizable() + { + $resource = Account::retrieve(self::TEST_RESOURCE_ID); + $this->stubRequest( + 'post', + '/oauth/deauthorize', + [ + 'client_id' => Stripe::getClientId(), + 'stripe_user_id' => $resource->id, + ], + null, + false, + [ + 'stripe_user_id' => $resource->id, + ], + 200, + Stripe::$connectBase + ); + $resource->deauthorize(); + } + + public function testCanCreateExternalAccount() + { + $this->expectsRequest( + 'post', + '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts' + ); + $resource = Account::createExternalAccount(self::TEST_RESOURCE_ID, [ + "external_account" => "btok_123", + ]); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanRetrieveExternalAccount() + { + $this->expectsRequest( + 'get', + '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts/' . self::TEST_EXTERNALACCOUNT_ID + ); + $resource = Account::retrieveExternalAccount(self::TEST_RESOURCE_ID, self::TEST_EXTERNALACCOUNT_ID); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanUpdateExternalAccount() + { + $this->expectsRequest( + 'post', + '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts/' . self::TEST_EXTERNALACCOUNT_ID + ); + $resource = Account::updateExternalAccount(self::TEST_RESOURCE_ID, self::TEST_EXTERNALACCOUNT_ID, [ + "name" => "name", + ]); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanDeleteExternalAccount() + { + $this->expectsRequest( + 'delete', + '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts/' . self::TEST_EXTERNALACCOUNT_ID + ); + $resource = Account::deleteExternalAccount(self::TEST_RESOURCE_ID, self::TEST_EXTERNALACCOUNT_ID); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanListExternalAccounts() + { + $this->expectsRequest( + 'get', + '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts' + ); + $resources = Account::allExternalAccounts(self::TEST_RESOURCE_ID); + $this->assertTrue(is_array($resources->data)); + } + + public function testCanCreateLoginLink() + { + $this->expectsRequest( + 'post', + '/v1/accounts/' . self::TEST_RESOURCE_ID . '/login_links' + ); + $resource = Account::createLoginLink(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\LoginLink", $resource); + } + + public function testSerializeNewAdditionalOwners() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + 'legal_entity' => StripeObject::constructFrom([]), + ], null); + $obj->legal_entity->additional_owners = [ + ['first_name' => 'Joe'], + ['first_name' => 'Jane'], + ]; + + $expected = [ + 'legal_entity' => [ + 'additional_owners' => [ + 0 => ['first_name' => 'Joe'], + 1 => ['first_name' => 'Jane'], + ], + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializePartiallyChangedAdditionalOwners() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + 'legal_entity' => [ + 'additional_owners' => [ + StripeObject::constructFrom(['first_name' => 'Joe']), + StripeObject::constructFrom(['first_name' => 'Jane']), + ], + ], + ], null); + $obj->legal_entity->additional_owners[1]->first_name = 'Stripe'; + + $expected = [ + 'legal_entity' => [ + 'additional_owners' => [ + 1 => ['first_name' => 'Stripe'], + ], + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializeUnchangedAdditionalOwners() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + 'legal_entity' => [ + 'additional_owners' => [ + StripeObject::constructFrom(['first_name' => 'Joe']), + StripeObject::constructFrom(['first_name' => 'Jane']), + ], + ], + ], null); + + $expected = [ + 'legal_entity' => [ + 'additional_owners' => [], + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializeUnsetAdditionalOwners() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + 'legal_entity' => [ + 'additional_owners' => [ + StripeObject::constructFrom(['first_name' => 'Joe']), + StripeObject::constructFrom(['first_name' => 'Jane']), + ], + ], + ], null); + $obj->legal_entity->additional_owners = null; + + // Note that the empty string that we send for this one has a special + // meaning for the server, which interprets it as an array unset. + $expected = [ + 'legal_entity' => [ + 'additional_owners' => '', + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSerializeAdditionalOwnersDeletedItem() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + 'legal_entity' => [ + 'additional_owners' => [ + StripeObject::constructFrom(['first_name' => 'Joe']), + StripeObject::constructFrom(['first_name' => 'Jane']), + ], + ], + ], null); + unset($obj->legal_entity->additional_owners[0]); + + $obj->serializeParameters(); + } + + public function testSerializeExternalAccountString() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + ], null); + $obj->external_account = 'btok_123'; + + $expected = [ + 'external_account' => 'btok_123', + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializeExternalAccountHash() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + ], null); + $obj->external_account = [ + 'object' => 'bank_account', + 'routing_number' => '110000000', + 'account_number' => '000123456789', + 'country' => 'US', + 'currency' => 'usd', + ]; + + $expected = [ + 'external_account' => [ + 'object' => 'bank_account', + 'routing_number' => '110000000', + 'account_number' => '000123456789', + 'country' => 'US', + 'currency' => 'usd', + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializeBankAccountString() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + ], null); + $obj->bank_account = 'btok_123'; + + $expected = [ + 'bank_account' => 'btok_123', + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializeBankAccountHash() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'account', + ], null); + $obj->bank_account = [ + 'object' => 'bank_account', + 'routing_number' => '110000000', + 'account_number' => '000123456789', + 'country' => 'US', + 'currency' => 'usd', + ]; + + $expected = [ + 'bank_account' => [ + 'object' => 'bank_account', + 'routing_number' => '110000000', + 'account_number' => '000123456789', + 'country' => 'US', + 'currency' => 'usd', + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/AlipayAccountTest.php b/htdocs/includes/stripe/tests/Stripe/AlipayAccountTest.php new file mode 100644 index 00000000000..7159f5444df --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/AlipayAccountTest.php @@ -0,0 +1,76 @@ + self::TEST_RESOURCE_ID, + 'object' => 'card', + 'metadata' => [], + ]; + return AlipayAccount::constructFrom( + array_merge($params, $base), + new Util\RequestOptions() + ); + } + + public function testHasCorrectUrlForCustomer() + { + $resource = $this->createFixture(['customer' => 'cus_123']); + $this->assertSame( + "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + /** + * @expectedException \Stripe\Error\InvalidRequest + */ + public function testIsNotDirectlyRetrievable() + { + AlipayAccount::retrieve(self::TEST_RESOURCE_ID); + } + + public function testIsSaveable() + { + $resource = $this->createFixture(); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID + ); + $resource->save(); + $this->assertSame("Stripe\\AlipayAccount", get_class($resource)); + } + + /** + * @expectedException \Stripe\Error\InvalidRequest + */ + public function testIsNotDirectlyUpdatable() + { + AlipayAccount::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + } + + public function testIsDeletable() + { + $resource = $this->createFixture(); + $this->expectsRequest( + 'delete', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID + ); + $resource->delete(); + $this->assertSame("Stripe\\AlipayAccount", get_class($resource)); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ApiRequestorTest.php b/htdocs/includes/stripe/tests/Stripe/ApiRequestorTest.php new file mode 100644 index 00000000000..6fb8229c57d --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ApiRequestorTest.php @@ -0,0 +1,572 @@ +getMethod('_encodeObjects'); + $method->setAccessible(true); + + $a = ['customer' => new Customer('abcd')]; + $enc = $method->invoke(null, $a); + $this->assertSame($enc, ['customer' => 'abcd']); + + // Preserves UTF-8 + $v = ['customer' => "☃"]; + $enc = $method->invoke(null, $v); + $this->assertSame($enc, $v); + + // Encodes latin-1 -> UTF-8 + $v = ['customer' => "\xe9"]; + $enc = $method->invoke(null, $v); + $this->assertSame($enc, ['customer' => "\xc3\xa9"]); + + // Encodes booleans + $v = true; + $enc = $method->invoke(null, $v); + $this->assertSame('true', $enc); + + $v = false; + $enc = $method->invoke(null, $v); + $this->assertSame('false', $enc); + } + + public function testHttpClientInjection() + { + $reflector = new \ReflectionClass('Stripe\\ApiRequestor'); + $method = $reflector->getMethod('httpClient'); + $method->setAccessible(true); + + $curl = new CurlClient(); + $curl->setTimeout(10); + ApiRequestor::setHttpClient($curl); + + $injectedCurl = $method->invoke(new ApiRequestor()); + $this->assertSame($injectedCurl, $curl); + } + + public function testDefaultHeaders() + { + $reflector = new \ReflectionClass('Stripe\\ApiRequestor'); + $method = $reflector->getMethod('_defaultHeaders'); + $method->setAccessible(true); + + // no way to stub static methods with PHPUnit 4.x :( + Stripe::setAppInfo('MyTestApp', '1.2.34', 'https://mytestapp.example'); + $apiKey = 'sk_test_notarealkey'; + $clientInfo = ['httplib' => 'testlib 0.1.2']; + + $headers = $method->invoke(null, $apiKey, $clientInfo); + + $ua = json_decode($headers['X-Stripe-Client-User-Agent']); + $this->assertSame($ua->application->name, 'MyTestApp'); + $this->assertSame($ua->application->version, '1.2.34'); + $this->assertSame($ua->application->url, 'https://mytestapp.example'); + + $this->assertSame($ua->httplib, 'testlib 0.1.2'); + + $this->assertSame( + $headers['User-Agent'], + 'Stripe/v1 PhpBindings/' . Stripe::VERSION . ' MyTestApp/1.2.34 (https://mytestapp.example)' + ); + + $this->assertSame($headers['Authorization'], 'Bearer ' . $apiKey); + } + + /** + * @expectedException \Stripe\Error\Authentication + * @expectedExceptionMessageRegExp #No API key provided# + */ + public function testRaisesAuthenticationErrorWhenNoApiKey() + { + Stripe::setApiKey(null); + Charge::create(); + } + + public function testRaisesInvalidRequestErrorOn400() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + null, + false, + [ + 'error' => [ + 'type' => 'invalid_request_error', + 'message' => 'Missing id', + 'param' => 'id', + ], + ], + 400 + ); + + try { + Charge::create(); + $this->fail("Did not raise error"); + } catch (Error\InvalidRequest $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame('Missing id', $e->getMessage()); + $this->assertSame('id', $e->getStripeParam()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesIdempotencyErrorOn400AndTypeIdempotencyError() + { + $this->stubRequest( + 'POST', + '/v1/charges', + array(), + null, + false, + array( + 'error' => array( + 'type' => 'idempotency_error', + 'message' => "Keys for idempotent requests can only be used with the same parameters they were first used with. Try using a key other than 'abc' if you meant to execute a different request.", + ), + ), + 400 + ); + + try { + Charge::create(); + $this->fail("Did not raise error"); + } catch (Error\Idempotency $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame("Keys for idempotent requests can only be used with the same parameters they were first used with. Try using a key other than 'abc' if you meant to execute a different request.", $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesAuthenticationErrorOn401() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + null, + false, + [ + 'error' => [ + 'type' => 'invalid_request_error', + 'message' => 'You did not provide an API key.', + ], + ], + 401 + ); + + try { + Charge::create(); + $this->fail("Did not raise error"); + } catch (Error\Authentication $e) { + $this->assertSame(401, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame('You did not provide an API key.', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesCardErrorOn402() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + null, + false, + [ + 'error' => [ + 'type' => 'card_error', + 'message' => 'Your card was declined.', + 'code' => 'card_declined', + 'decline_code' => 'generic_decline', + 'charge' => 'ch_declined_charge', + 'param' => 'exp_month', + ], + ], + 402 + ); + + try { + Charge::create(); + $this->fail("Did not raise error"); + } catch (Error\Card $e) { + $this->assertSame(402, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame('Your card was declined.', $e->getMessage()); + $this->assertSame('card_declined', $e->getStripeCode()); + $this->assertSame('generic_decline', $e->getDeclineCode()); + $this->assertSame('exp_month', $e->getStripeParam()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesPermissionErrorOn403() + { + $this->stubRequest( + 'GET', + '/v1/accounts/foo', + [], + null, + false, + [ + 'error' => [ + 'type' => 'invalid_request_error', + 'message' => "The provided key 'sk_test_********************1234' does not have access to account 'foo' (or that account does not exist). Application access may have been revoked.", + ], + ], + 403 + ); + + try { + Account::retrieve('foo'); + $this->fail("Did not raise error"); + } catch (Error\Permission $e) { + $this->assertSame(403, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame("The provided key 'sk_test_********************1234' does not have access to account 'foo' (or that account does not exist). Application access may have been revoked.", $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesInvalidRequestErrorOn404() + { + $this->stubRequest( + 'GET', + '/v1/charges/foo', + [], + null, + false, + [ + 'error' => [ + 'type' => 'invalid_request_error', + 'message' => 'No such charge: foo', + 'param' => 'id', + ], + ], + 404 + ); + + try { + Charge::retrieve('foo'); + $this->fail("Did not raise error"); + } catch (Error\InvalidRequest $e) { + $this->assertSame(404, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame('No such charge: foo', $e->getMessage()); + $this->assertSame('id', $e->getStripeParam()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesRateLimitErrorOn429() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + null, + false, + [ + 'error' => [ + 'message' => 'Too many requests', + ], + ], + 429 + ); + + try { + Charge::create(); + $this->fail("Did not raise error"); + } catch (Error\RateLimit $e) { + $this->assertSame(429, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame('Too many requests', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesRateLimitErrorOn400AndCodeRateLimit() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + null, + false, + [ + 'error' => [ + 'code' => 'rate_limit', + 'message' => 'Too many requests', + ], + ], + 400 + ); + + try { + Charge::create(); + $this->fail("Did not raise error"); + } catch (Error\RateLimit $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertTrue(is_array($e->getJsonBody())); + $this->assertSame('Too many requests', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesOAuthInvalidRequestError() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [], + null, + false, + [ + 'error' => 'invalid_request', + 'error_description' => 'No grant type specified', + ], + 400, + Stripe::$connectBase + ); + + try { + OAuth::token(); + $this->fail("Did not raise error"); + } catch (Error\OAuth\InvalidRequest $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertSame('invalid_request', $e->getErrorCode()); + $this->assertSame('No grant type specified', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesOAuthInvalidClientError() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [], + null, + false, + [ + 'error' => 'invalid_client', + 'error_description' => 'No authentication was provided. Send your secret API key using the Authorization header, or as a client_secret POST parameter.', + ], + 401, + Stripe::$connectBase + ); + + try { + OAuth::token(); + $this->fail("Did not raise error"); + } catch (Error\OAuth\InvalidClient $e) { + $this->assertSame(401, $e->getHttpStatus()); + $this->assertSame('invalid_client', $e->getErrorCode()); + $this->assertSame('No authentication was provided. Send your secret API key using the Authorization header, or as a client_secret POST parameter.', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesOAuthInvalidGrantError() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [], + null, + false, + [ + 'error' => 'invalid_grant', + 'error_description' => 'This authorization code has already been used. All tokens issued with this code have been revoked.', + ], + 400, + Stripe::$connectBase + ); + + try { + OAuth::token(); + $this->fail("Did not raise error"); + } catch (Error\OAuth\InvalidGrant $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertSame('invalid_grant', $e->getErrorCode()); + $this->assertSame('This authorization code has already been used. All tokens issued with this code have been revoked.', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesOAuthInvalidScopeError() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [], + null, + false, + [ + 'error' => 'invalid_scope', + 'error_description' => 'Invalid scope provided: invalid_scope.', + ], + 400, + Stripe::$connectBase + ); + + try { + OAuth::token(); + $this->fail("Did not raise error"); + } catch (Error\OAuth\InvalidScope $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertSame('invalid_scope', $e->getErrorCode()); + $this->assertSame('Invalid scope provided: invalid_scope.', $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesOAuthUnsupportedGrantTypeError() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [], + null, + false, + [ + 'error' => 'unsupported_grant_type', + ], + 400, + Stripe::$connectBase + ); + + try { + OAuth::token(); + $this->fail("Did not raise error"); + } catch (Error\OAuth\UnsupportedGrantType $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertSame('unsupported_grant_type', $e->getErrorCode()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testRaisesOAuthUnsupportedResponseTypeError() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [], + null, + false, + [ + 'error' => 'unsupported_response_type', + 'error_description' => "Only 'code' response_type is supported, but 'unsupported_response_type' was provided", + ], + 400, + Stripe::$connectBase + ); + + try { + OAuth::token(); + $this->fail("Did not raise error"); + } catch (Error\OAuth\UnsupportedResponseType $e) { + $this->assertSame(400, $e->getHttpStatus()); + $this->assertSame('unsupported_response_type', $e->getErrorCode()); + $this->assertSame("Only 'code' response_type is supported, but 'unsupported_response_type' was provided", $e->getMessage()); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testHeaderStripeVersionGlobal() + { + Stripe::setApiVersion('2222-22-22'); + $this->stubRequest( + 'POST', + '/v1/charges', + [], + [ + 'Stripe-Version: 2222-22-22', + ], + false, + [ + 'id' => 'ch_123', + 'object' => 'charge', + ] + ); + Charge::create(); + } + + public function testHeaderStripeVersionRequestOptions() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + [ + 'Stripe-Version: 2222-22-22', + ], + false, + [ + 'id' => 'ch_123', + 'object' => 'charge', + ] + ); + Charge::create([], ['stripe_version' => '2222-22-22']); + } + + public function testHeaderStripeAccountGlobal() + { + Stripe::setAccountId('acct_123'); + $this->stubRequest( + 'POST', + '/v1/charges', + [], + [ + 'Stripe-Account: acct_123', + ], + false, + [ + 'id' => 'ch_123', + 'object' => 'charge', + ] + ); + Charge::create(); + } + + public function testHeaderStripeAccountRequestOptions() + { + $this->stubRequest( + 'POST', + '/v1/charges', + [], + [ + 'Stripe-Account: acct_123', + ], + false, + [ + 'id' => 'ch_123', + 'object' => 'charge', + ] + ); + Charge::create([], ['stripe_account' => 'acct_123']); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ApplePayDomainTest.php b/htdocs/includes/stripe/tests/Stripe/ApplePayDomainTest.php new file mode 100644 index 00000000000..d033f55cbb0 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ApplePayDomainTest.php @@ -0,0 +1,52 @@ +expectsRequest( + 'get', + '/v1/apple_pay/domains' + ); + $resources = ApplePayDomain::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\ApplePayDomain", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/apple_pay/domains/' . self::TEST_RESOURCE_ID + ); + $resource = ApplePayDomain::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\ApplePayDomain", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/apple_pay/domains' + ); + $resource = ApplePayDomain::create([ + "domain_name" => "domain", + ]); + $this->assertInstanceOf("Stripe\\ApplePayDomain", $resource); + } + + public function testIsDeletable() + { + $resource = ApplePayDomain::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/apple_pay/domains/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\ApplePayDomain", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ApplicationFeeRefundTest.php b/htdocs/includes/stripe/tests/Stripe/ApplicationFeeRefundTest.php new file mode 100644 index 00000000000..9af059ff7f3 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ApplicationFeeRefundTest.php @@ -0,0 +1,21 @@ +metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/application_fees/' . $resource->fee . '/refunds/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ApplicationFeeTest.php b/htdocs/includes/stripe/tests/Stripe/ApplicationFeeTest.php new file mode 100644 index 00000000000..66e007ff3d2 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ApplicationFeeTest.php @@ -0,0 +1,83 @@ +expectsRequest( + 'get', + '/v1/application_fees' + ); + $resources = ApplicationFee::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\ApplicationFee", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/application_fees/' . self::TEST_RESOURCE_ID + ); + $resource = ApplicationFee::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\ApplicationFee", $resource); + } + + public function testIsRefundable() + { + $fee = ApplicationFee::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/application_fees/' . $fee->id . '/refunds' + ); + $resource = $fee->refund(); + $this->assertInstanceOf("Stripe\\ApplicationFee", $resource); + $this->assertSame($resource, $fee); + } + + public function testCanCreateRefund() + { + $this->expectsRequest( + 'post', + '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds' + ); + $resource = ApplicationFee::createRefund(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource); + } + + public function testCanRetrieveRefund() + { + $this->expectsRequest( + 'get', + '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds/' . self::TEST_FEEREFUND_ID + ); + $resource = ApplicationFee::retrieveRefund(self::TEST_RESOURCE_ID, self::TEST_FEEREFUND_ID); + $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource); + } + + public function testCanUpdateRefund() + { + $this->expectsRequest( + 'post', + '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds/' . self::TEST_FEEREFUND_ID + ); + $resource = ApplicationFee::updateRefund(self::TEST_RESOURCE_ID, self::TEST_FEEREFUND_ID); + $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource); + } + + public function testCanListRefunds() + { + $this->expectsRequest( + 'get', + '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds' + ); + $resources = ApplicationFee::allRefunds(self::TEST_RESOURCE_ID); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resources->data[0]); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/BalanceTest.php b/htdocs/includes/stripe/tests/Stripe/BalanceTest.php new file mode 100644 index 00000000000..ccbdbdfb747 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/BalanceTest.php @@ -0,0 +1,16 @@ +expectsRequest( + 'get', + '/v1/balance' + ); + $resource = Balance::retrieve(); + $this->assertInstanceOf("Stripe\\Balance", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/BalanceTransactionTest.php b/htdocs/includes/stripe/tests/Stripe/BalanceTransactionTest.php new file mode 100644 index 00000000000..8785e859113 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/BalanceTransactionTest.php @@ -0,0 +1,29 @@ +expectsRequest( + 'get', + '/v1/balance/history' + ); + $resources = BalanceTransaction::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\BalanceTransaction", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/balance/history/' . self::TEST_RESOURCE_ID + ); + $resource = BalanceTransaction::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\BalanceTransaction", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/BankAccountTest.php b/htdocs/includes/stripe/tests/Stripe/BankAccountTest.php new file mode 100644 index 00000000000..ab1dff9d5a8 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/BankAccountTest.php @@ -0,0 +1,99 @@ + self::TEST_RESOURCE_ID, + 'object' => 'bank_account', + 'metadata' => [], + ]; + return BankAccount::constructFrom( + array_merge($params, $base), + new Util\RequestOptions() + ); + } + + public function testHasCorrectUrlForCustomer() + { + $resource = $this->createFixture(['customer' => 'cus_123']); + $this->assertSame( + "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + public function testHasCorrectUrlForAccount() + { + $resource = $this->createFixture(['account' => 'acct_123']); + $this->assertSame( + "/v1/accounts/acct_123/external_accounts/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + /** + * @expectedException \Stripe\Error\InvalidRequest + */ + public function testIsNotDirectlyRetrievable() + { + BankAccount::retrieve(self::TEST_RESOURCE_ID); + } + + public function testIsSaveable() + { + $resource = $this->createFixture(); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID + ); + $resource->save(); + $this->assertSame("Stripe\\BankAccount", get_class($resource)); + } + + /** + * @expectedException \Stripe\Error\InvalidRequest + */ + public function testIsNotDirectlyUpdatable() + { + BankAccount::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + } + + public function testIsDeletable() + { + $resource = $this->createFixture(); + $this->expectsRequest( + 'delete', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID + ); + $resource->delete(); + $this->assertSame("Stripe\\BankAccount", get_class($resource)); + } + + public function testIsVerifiable() + { + $resource = $this->createFixture(); + $this->expectsRequest( + 'post', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID . "/verify", + [ + "amounts" => [1, 2] + ] + ); + $resource->verify(["amounts" => [1, 2]]); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/BitcoinReceiverTest.php b/htdocs/includes/stripe/tests/Stripe/BitcoinReceiverTest.php new file mode 100644 index 00000000000..cea999fc4f2 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/BitcoinReceiverTest.php @@ -0,0 +1,62 @@ + self::TEST_RESOURCE_ID, + 'object' => 'bitcoin_receiver', + 'metadata' => [], + ]; + return BitcoinReceiver::constructFrom( + array_merge($params, $base), + new Util\RequestOptions() + ); + } + + public function testHasCorrectStandaloneUrl() + { + $resource = $this->createFixture(); + $this->assertSame( + "/v1/bitcoin/receivers/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + public function testHasCorrectUrlForCustomer() + { + $resource = $this->createFixture(['customer' => 'cus_123']); + $this->assertSame( + "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + public function testIsListable() + { + $this->expectsRequest( + 'get', + '/v1/bitcoin/receivers' + ); + $resources = BitcoinReceiver::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertSame("Stripe\\BitcoinReceiver", get_class($resources->data[0])); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/bitcoin/receivers/' . self::TEST_RESOURCE_ID + ); + $resource = BitcoinReceiver::retrieve(self::TEST_RESOURCE_ID); + $this->assertSame("Stripe\\BitcoinReceiver", get_class($resource)); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/CardTest.php b/htdocs/includes/stripe/tests/Stripe/CardTest.php new file mode 100644 index 00000000000..8976eff6679 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/CardTest.php @@ -0,0 +1,94 @@ + self::TEST_RESOURCE_ID, + 'object' => 'card', + 'metadata' => [], + ]; + return Card::constructFrom( + array_merge($params, $base), + new Util\RequestOptions() + ); + } + + public function testHasCorrectUrlForCustomer() + { + $resource = $this->createFixture(['customer' => 'cus_123']); + $this->assertSame( + "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + public function testHasCorrectUrlForAccount() + { + $resource = $this->createFixture(['account' => 'acct_123']); + $this->assertSame( + "/v1/accounts/acct_123/external_accounts/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + public function testHasCorrectUrlForRecipient() + { + $resource = $this->createFixture(['recipient' => 'rp_123']); + $this->assertSame( + "/v1/recipients/rp_123/cards/" . self::TEST_RESOURCE_ID, + $resource->instanceUrl() + ); + } + + /** + * @expectedException \Stripe\Error\InvalidRequest + */ + public function testIsNotDirectlyRetrievable() + { + Card::retrieve(self::TEST_RESOURCE_ID); + } + + public function testIsSaveable() + { + $resource = $this->createFixture(); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID + ); + $resource->save(); + $this->assertSame("Stripe\\Card", get_class($resource)); + } + + /** + * @expectedException \Stripe\Error\InvalidRequest + */ + public function testIsNotDirectlyUpdatable() + { + Card::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + } + + public function testIsDeletable() + { + $resource = $this->createFixture(); + $this->expectsRequest( + 'delete', + '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID + ); + $resource->delete(); + $this->assertSame("Stripe\\Card", get_class($resource)); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ChargeTest.php b/htdocs/includes/stripe/tests/Stripe/ChargeTest.php new file mode 100644 index 00000000000..b8c6cbabf3b --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ChargeTest.php @@ -0,0 +1,140 @@ +expectsRequest( + 'get', + '/v1/charges' + ); + $resources = Charge::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Charge", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/charges/' . self::TEST_RESOURCE_ID + ); + $resource = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Charge", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/charges' + ); + $resource = Charge::create([ + "amount" => 100, + "currency" => "usd", + "source" => "tok_123" + ]); + $this->assertInstanceOf("Stripe\\Charge", $resource); + } + + public function testIsSaveable() + { + $resource = Charge::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/charges/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Charge", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/charges/' . self::TEST_RESOURCE_ID + ); + $resource = Charge::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Charge", $resource); + } + + public function testCanRefund() + { + $charge = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/charges/' . $charge->id . '/refund' + ); + $resource = $charge->refund(); + $this->assertInstanceOf("Stripe\\Charge", $resource); + $this->assertSame($resource, $charge); + } + + public function testCanCapture() + { + $charge = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/charges/' . $charge->id . '/capture' + ); + $resource = $charge->capture(); + $this->assertInstanceOf("Stripe\\Charge", $resource); + $this->assertSame($resource, $charge); + } + + public function testCanUpdateDispute() + { + $charge = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/charges/' . $charge->id . '/dispute' + ); + $resource = $charge->updateDispute(); + $this->assertInstanceOf("Stripe\\Dispute", $resource); + } + + public function testCanCloseDispute() + { + $charge = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/charges/' . $charge->id . '/dispute/close' + ); + $resource = $charge->closeDispute(); + $this->assertInstanceOf("Stripe\\Charge", $resource); + $this->assertSame($resource, $charge); + } + + public function testCanMarkAsFraudulent() + { + $charge = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/charges/' . $charge->id, + ['fraud_details' => ['user_report' => 'fraudulent']] + ); + $resource = $charge->markAsFraudulent(); + $this->assertInstanceOf("Stripe\\Charge", $resource); + $this->assertSame($resource, $charge); + } + + public function testCanMarkAsSafe() + { + $charge = Charge::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/charges/' . $charge->id, + ['fraud_details' => ['user_report' => 'safe']] + ); + $resource = $charge->markAsSafe(); + $this->assertInstanceOf("Stripe\\Charge", $resource); + $this->assertSame($resource, $charge); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/CollectionTest.php b/htdocs/includes/stripe/tests/Stripe/CollectionTest.php new file mode 100644 index 00000000000..560085aede2 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/CollectionTest.php @@ -0,0 +1,147 @@ +fixture = Collection::constructFrom([ + 'data' => [['id' => 1]], + 'has_more' => true, + 'url' => '/things', + ]); + } + + public function testCanList() + { + $this->stubRequest( + 'GET', + '/things', + [], + null, + false, + [ + 'data' => [['id' => 1]], + 'has_more' => true, + 'url' => '/things', + ] + ); + + $resources = $this->fixture->all(); + $this->assertTrue(is_array($resources->data)); + } + + public function testCanRetrieve() + { + $this->stubRequest( + 'GET', + '/things/1', + [], + null, + false, + [ + 'id' => 1, + ] + ); + + $this->fixture->retrieve(1); + } + + public function testCanCreate() + { + $this->stubRequest( + 'POST', + '/things', + [ + 'foo' => 'bar', + ], + null, + false, + [ + 'id' => 2, + ] + ); + + $this->fixture->create([ + 'foo' => 'bar', + ]); + } + + public function testProvidesAutoPagingIterator() + { + $this->stubRequest( + 'GET', + '/things', + [ + 'starting_after' => 1, + ], + null, + false, + [ + 'data' => [['id' => 2], ['id' => 3]], + 'has_more' => false, + ] + ); + + $seen = []; + foreach ($this->fixture->autoPagingIterator() as $item) { + array_push($seen, $item['id']); + } + + $this->assertSame([1, 2, 3], $seen); + } + + public function testSupportsIteratorToArray() + { + $this->stubRequest( + 'GET', + '/things', + [ + 'starting_after' => 1, + ], + null, + false, + [ + 'data' => [['id' => 2], ['id' => 3]], + 'has_more' => false, + ] + ); + + $seen = []; + foreach (iterator_to_array($this->fixture->autoPagingIterator()) as $item) { + array_push($seen, $item['id']); + } + + $this->assertSame([1, 2, 3], $seen); + } + + public function testHeaders() + { + $this->stubRequest( + 'POST', + '/things', + [ + 'foo' => 'bar', + ], + [ + 'Stripe-Account: acct_foo', + 'Idempotency-Key: qwertyuiop', + ], + false, + [ + 'id' => 2, + ] + ); + + $this->fixture->create([ + 'foo' => 'bar', + ], [ + 'stripe_account' => 'acct_foo', + 'idempotency_key' => 'qwertyuiop', + ]); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/CountrySpecTest.php b/htdocs/includes/stripe/tests/Stripe/CountrySpecTest.php new file mode 100644 index 00000000000..cccd4116845 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/CountrySpecTest.php @@ -0,0 +1,29 @@ +expectsRequest( + 'get', + '/v1/country_specs' + ); + $resources = CountrySpec::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\CountrySpec", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/country_specs/' . self::TEST_RESOURCE_ID + ); + $resource = CountrySpec::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\CountrySpec", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/CouponTest.php b/htdocs/includes/stripe/tests/Stripe/CouponTest.php new file mode 100644 index 00000000000..8a6fbf630cd --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/CouponTest.php @@ -0,0 +1,79 @@ +expectsRequest( + 'get', + '/v1/coupons' + ); + $resources = Coupon::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Coupon", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/coupons/' . self::TEST_RESOURCE_ID + ); + $resource = Coupon::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Coupon", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/coupons' + ); + $resource = Coupon::create([ + "percent_off" => 25, + "duration" => "repeating", + "duration_in_months" => 3, + "id" => self::TEST_RESOURCE_ID, + ]); + $this->assertInstanceOf("Stripe\\Coupon", $resource); + } + + public function testIsSaveable() + { + $resource = Coupon::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/coupons/' . self::TEST_RESOURCE_ID + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Coupon", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/coupons/' . self::TEST_RESOURCE_ID + ); + $resource = Coupon::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Coupon", $resource); + } + + public function testIsDeletable() + { + $resource = Coupon::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/coupons/' . self::TEST_RESOURCE_ID + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Coupon", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/CustomerTest.php b/htdocs/includes/stripe/tests/Stripe/CustomerTest.php new file mode 100644 index 00000000000..e279e5489e8 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/CustomerTest.php @@ -0,0 +1,269 @@ +expectsRequest( + 'get', + '/v1/customers' + ); + $resources = Customer::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Customer", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/customers/' . self::TEST_RESOURCE_ID + ); + $resource = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Customer", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/customers' + ); + $resource = Customer::create(); + $this->assertInstanceOf("Stripe\\Customer", $resource); + } + + public function testIsSaveable() + { + $resource = Customer::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/customers/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Customer", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/customers/' . self::TEST_RESOURCE_ID + ); + $resource = Customer::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Customer", $resource); + } + + public function testIsDeletable() + { + $resource = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/customers/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Customer", $resource); + } + + public function testCanAddInvoiceItem() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/invoiceitems', + [ + "amount" => 100, + "currency" => "usd", + "customer" => $customer->id + ] + ); + $resource = $customer->addInvoiceItem([ + "amount" => 100, + "currency" => "usd" + ]); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resource); + } + + public function testCanListInvoices() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'get', + '/v1/invoices', + ["customer" => $customer->id] + ); + $resources = $customer->invoices(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Invoice", $resources->data[0]); + } + + public function testCanListInvoiceItems() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'get', + '/v1/invoiceitems', + ["customer" => $customer->id] + ); + $resources = $customer->invoiceItems(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resources->data[0]); + } + + public function testCanListCharges() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'get', + '/v1/charges', + ["customer" => $customer->id] + ); + $resources = $customer->charges(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Charge", $resources->data[0]); + } + + public function testCanUpdateSubscription() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->stubRequest( + 'post', + '/v1/customers/' . $customer->id . '/subscription', + ["plan" => "plan"], + null, + false, + [ + "object" => "subscription", + "id" => "sub_foo" + ] + ); + $resource = $customer->updateSubscription(["plan" => "plan"]); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + $this->assertSame("sub_foo", $customer->subscription->id); + } + + public function testCanCancelSubscription() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->stubRequest( + 'delete', + '/v1/customers/' . $customer->id . '/subscription', + [], + null, + false, + [ + "object" => "subscription", + "id" => "sub_foo" + ] + ); + $resource = $customer->cancelSubscription(); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + $this->assertSame("sub_foo", $customer->subscription->id); + } + + public function testCanDeleteDiscount() + { + $customer = Customer::retrieve(self::TEST_RESOURCE_ID); + $this->stubRequest( + 'delete', + '/v1/customers/' . $customer->id . '/discount' + ); + $customer->deleteDiscount(); + $this->assertSame($customer->discount, null); + } + + public function testCanCreateSource() + { + $this->expectsRequest( + 'post', + '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources' + ); + $resource = Customer::createSource(self::TEST_RESOURCE_ID, ["source" => "btok_123"]); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanRetrieveSource() + { + $this->expectsRequest( + 'get', + '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources/' . self::TEST_SOURCE_ID + ); + $resource = Customer::retrieveSource(self::TEST_RESOURCE_ID, self::TEST_SOURCE_ID); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanUpdateSource() + { + $this->expectsRequest( + 'post', + '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources/' . self::TEST_SOURCE_ID + ); + $resource = Customer::updateSource(self::TEST_RESOURCE_ID, self::TEST_SOURCE_ID, ["name" => "name"]); + // stripe-mock returns a Card on this method and not a bank account + $this->assertInstanceOf("Stripe\\Card", $resource); + } + + public function testCanDeleteSource() + { + $this->expectsRequest( + 'delete', + '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources/' . self::TEST_SOURCE_ID + ); + $resource = Customer::deleteSource(self::TEST_RESOURCE_ID, self::TEST_SOURCE_ID); + $this->assertInstanceOf("Stripe\\BankAccount", $resource); + } + + public function testCanListSources() + { + $this->expectsRequest( + 'get', + '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources' + ); + $resources = Customer::allSources(self::TEST_RESOURCE_ID); + $this->assertTrue(is_array($resources->data)); + } + + public function testSerializeSourceString() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'customer', + ], null); + $obj->source = 'tok_visa'; + + $expected = [ + 'source' => 'tok_visa', + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } + + public function testSerializeSourceMap() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'customer', + ], null); + $obj->source = [ + 'object' => 'card', + 'number' => '4242424242424242', + 'exp_month' => 12, + 'exp_year' => 2032, + ]; + + $expected = [ + 'source' => [ + 'object' => 'card', + 'number' => '4242424242424242', + 'exp_month' => 12, + 'exp_year' => 2032, + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/DisputeTest.php b/htdocs/includes/stripe/tests/Stripe/DisputeTest.php new file mode 100644 index 00000000000..81d93da0213 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/DisputeTest.php @@ -0,0 +1,65 @@ +expectsRequest( + 'get', + '/v1/disputes' + ); + $resources = Dispute::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Dispute", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/disputes/' . self::TEST_RESOURCE_ID + ); + $resource = Dispute::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Dispute", $resource); + } + + public function testIsSaveable() + { + $resource = Dispute::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/disputes/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Dispute", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/disputes/' . self::TEST_RESOURCE_ID + ); + $resource = Dispute::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Dispute", $resource); + } + + public function testIsClosable() + { + $dispute = Dispute::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/disputes/' . $dispute->id . '/close' + ); + $resource = $dispute->close(); + $this->assertInstanceOf("Stripe\\Dispute", $resource); + $this->assertSame($resource, $dispute); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/EphemeralKeyTest.php b/htdocs/includes/stripe/tests/Stripe/EphemeralKeyTest.php new file mode 100644 index 00000000000..e8a1fc0e9ef --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/EphemeralKeyTest.php @@ -0,0 +1,43 @@ +expectsRequest( + 'post', + '/v1/ephemeral_keys', + null, + ["Stripe-Version: 2017-05-25"] + ); + $resource = EphemeralKey::create([ + "customer" => "cus_123", + ], ["stripe_version" => "2017-05-25"]); + $this->assertInstanceOf("Stripe\\EphemeralKey", $resource); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testIsNotCreatableWithoutAnExplicitApiVersion() + { + $resource = EphemeralKey::create([ + "customer" => "cus_123", + ]); + } + + public function testIsDeletable() + { + $key = EphemeralKey::create([ + "customer" => "cus_123", + ], ["stripe_version" => "2017-05-25"]); + $this->expectsRequest( + 'delete', + '/v1/ephemeral_keys/' . $key->id + ); + $resource = $key->delete(); + $this->assertInstanceOf("Stripe\\EphemeralKey", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/Error/BaseTest.php b/htdocs/includes/stripe/tests/Stripe/Error/BaseTest.php new file mode 100644 index 00000000000..4c2732ed1d1 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/Error/BaseTest.php @@ -0,0 +1,36 @@ +getMockForAbstractClass('Stripe\\Error\\Base', [ + 'message', + 200, + '{"key": "value"}', + ['key' => 'value'], + [ + 'Some-Header' => 'Some Value', + 'Request-Id' => 'req_test', + ], + ]); + } + + public function testGetters() + { + $e = $this->createFixture(); + $this->assertSame(200, $e->getHttpStatus()); + $this->assertSame('{"key": "value"}', $e->getHttpBody()); + $this->assertSame(['key' => 'value'], $e->getJsonBody()); + $this->assertSame('Some Value', $e->getHttpHeaders()['Some-Header']); + $this->assertSame('req_test', $e->getRequestId()); + } + + public function testToString() + { + $e = $this->createFixture(); + $this->assertContains("from API request 'req_test'", (string)$e); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/Error/SignatureVerificationTest.php b/htdocs/includes/stripe/tests/Stripe/Error/SignatureVerificationTest.php new file mode 100644 index 00000000000..020a41f8f76 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/Error/SignatureVerificationTest.php @@ -0,0 +1,12 @@ +assertSame('sig_header', $e->getSigHeader()); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/EventTest.php b/htdocs/includes/stripe/tests/Stripe/EventTest.php new file mode 100644 index 00000000000..2e3c92f0e49 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/EventTest.php @@ -0,0 +1,29 @@ +expectsRequest( + 'get', + '/v1/events' + ); + $resources = Event::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Event", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/events/' . self::TEST_RESOURCE_ID + ); + $resource = Event::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Event", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ExchangeRateTest.php b/htdocs/includes/stripe/tests/Stripe/ExchangeRateTest.php new file mode 100644 index 00000000000..8b07b5a1358 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ExchangeRateTest.php @@ -0,0 +1,54 @@ +stubRequest( + 'get', + '/v1/exchange_rates', + [], + null, + false, + [ + 'object' => 'list', + 'data' => [ + [ + 'id' => 'eur', + 'object' => 'exchange_rate', + 'rates' => ['usd' => 1.18221], + ], + [ + 'id' => 'usd', + 'object' => 'exchange_rate', + 'rates' => ['eur' => 0.845876], + ], + ], + ] + ); + + $listRates = ExchangeRate::all(); + $this->assertTrue(is_array($listRates->data)); + $this->assertEquals('exchange_rate', $listRates->data[0]->object); + } + + public function testIsRetrievable() + { + $this->stubRequest( + 'get', + '/v1/exchange_rates/usd', + [], + null, + false, + [ + 'id' => 'usd', + 'object' => 'exchange_rate', + 'rates' => ['eur' => 0.845876], + ] + ); + $rates = ExchangeRate::retrieve("usd"); + $this->assertEquals('exchange_rate', $rates->object); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/FileUploadTest.php b/htdocs/includes/stripe/tests/Stripe/FileUploadTest.php new file mode 100644 index 00000000000..21bb0b568f6 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/FileUploadTest.php @@ -0,0 +1,104 @@ +fixture = [ + 'id' => self::TEST_RESOURCE_ID, + 'object' => 'file_upload', + ]; + } + + public function testIsListable() + { + $this->stubRequest( + 'get', + '/v1/files', + [], + null, + false, + [ + 'object' => 'list', + 'data' => [$this->fixture], + 'resource_url' => '/v1/files', + ], + 200, + Stripe::$apiUploadBase + ); + + $resources = FileUpload::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\FileUpload", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->stubRequest( + 'get', + '/v1/files/' . self::TEST_RESOURCE_ID, + [], + null, + false, + $this->fixture, + 200, + Stripe::$apiUploadBase + ); + $resource = FileUpload::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\FileUpload", $resource); + } + + public function testIsCreatableWithFileHandle() + { + $this->stubRequest( + 'post', + '/v1/files', + null, + ['Content-Type: multipart/form-data'], + true, + $this->fixture, + 200, + Stripe::$apiUploadBase + ); + $fp = fopen(dirname(__FILE__) . '/../data/test.png', 'r'); + $resource = FileUpload::create([ + "purpose" => "dispute_evidence", + "file" => $fp, + ]); + $this->assertInstanceOf("Stripe\\FileUpload", $resource); + } + + public function testIsCreatableWithCurlFile() + { + if (!class_exists('\CurlFile', false)) { + // Older PHP versions don't support this + return; + } + + $this->stubRequest( + 'post', + '/v1/files', + null, + ['Content-Type: multipart/form-data'], + true, + $this->fixture, + 200, + Stripe::$apiUploadBase + ); + $curlFile = new \CurlFile(dirname(__FILE__) . '/../data/test.png'); + $resource = FileUpload::create([ + "purpose" => "dispute_evidence", + "file" => $curlFile, + ]); + $this->assertInstanceOf("Stripe\\FileUpload", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/HttpClient/CurlClientTest.php b/htdocs/includes/stripe/tests/Stripe/HttpClient/CurlClientTest.php new file mode 100644 index 00000000000..086e6d3ee8d --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/HttpClient/CurlClientTest.php @@ -0,0 +1,228 @@ +origMaxNetworkRetries = Stripe::getMaxNetworkRetries(); + $this->origMaxNetworkRetryDelay = Stripe::getMaxNetworkRetryDelay(); + $this->origInitialNetworkRetryDelay = Stripe::getInitialNetworkRetryDelay(); + } + + /** + * @before + */ + public function setUpReflectors() + { + $stripeReflector = new \ReflectionClass('\Stripe\Stripe'); + + $this->maxNetworkRetryDelayProperty = $stripeReflector->getProperty('maxNetworkRetryDelay'); + $this->maxNetworkRetryDelayProperty->setAccessible(true); + + $this->initialNetworkRetryDelayProperty = $stripeReflector->getProperty('initialNetworkRetryDelay'); + $this->initialNetworkRetryDelayProperty->setAccessible(true); + + $curlClientReflector = new \ReflectionClass('Stripe\HttpClient\CurlClient'); + + $this->shouldRetryMethod = $curlClientReflector->getMethod('shouldRetry'); + $this->shouldRetryMethod->setAccessible(true); + + $this->sleepTimeMethod = $curlClientReflector->getMethod('sleepTime'); + $this->sleepTimeMethod->setAccessible(true); + } + + /** + * @after + */ + public function restoreOriginalNetworkValues() + { + Stripe::setMaxNetworkRetries($this->origMaxNetworkRetries); + $this->setMaxNetworkRetryDelay($this->origMaxNetworkRetryDelay); + $this->setInitialNetworkRetryDelay($this->origInitialNetworkRetryDelay); + } + + private function setMaxNetworkRetryDelay($maxNetworkRetryDelay) + { + $this->maxNetworkRetryDelayProperty->setValue(null, $maxNetworkRetryDelay); + } + + private function setInitialNetworkRetryDelay($initialNetworkRetryDelay) + { + $this->initialNetworkRetryDelayProperty->setValue(null, $initialNetworkRetryDelay); + } + + private function createFakeRandomGenerator($returnValue = 1.0) + { + $fakeRandomGenerator = $this->getMock('Stripe\Util\RandomGenetator', ['randFloat']); + $fakeRandomGenerator->method('randFloat')->willReturn($returnValue); + return $fakeRandomGenerator; + } + + public function testTimeout() + { + $curl = new CurlClient(); + $this->assertSame(CurlClient::DEFAULT_TIMEOUT, $curl->getTimeout()); + $this->assertSame(CurlClient::DEFAULT_CONNECT_TIMEOUT, $curl->getConnectTimeout()); + + // implicitly tests whether we're returning the CurlClient instance + $curl = $curl->setConnectTimeout(1)->setTimeout(10); + $this->assertSame(1, $curl->getConnectTimeout()); + $this->assertSame(10, $curl->getTimeout()); + + $curl->setTimeout(-1); + $curl->setConnectTimeout(-999); + $this->assertSame(0, $curl->getTimeout()); + $this->assertSame(0, $curl->getConnectTimeout()); + } + + public function testUserAgentInfo() + { + $curl = new CurlClient(); + $uaInfo = $curl->getUserAgentInfo(); + $this->assertNotNull($uaInfo); + $this->assertNotNull($uaInfo['httplib']); + $this->assertNotNull($uaInfo['ssllib']); + } + + public function testDefaultOptions() + { + // make sure options array loads/saves properly + $optionsArray = [CURLOPT_PROXY => 'localhost:80']; + $withOptionsArray = new CurlClient($optionsArray); + $this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray); + + // make sure closure-based options work properly, including argument passing + $ref = null; + $withClosure = new CurlClient(function ($method, $absUrl, $headers, $params, $hasFile) use (&$ref) { + $ref = func_get_args(); + return []; + }); + + $withClosure->request('get', 'https://httpbin.org/status/200', [], [], false); + $this->assertSame($ref, ['get', 'https://httpbin.org/status/200', [], [], false]); + + // this is the last test case that will run, since it'll throw an exception at the end + $withBadClosure = new CurlClient(function () { + return 'thisShouldNotWork'; + }); + $this->setExpectedException('Stripe\Error\Api', "Non-array value returned by defaultOptions CurlClient callback"); + $withBadClosure->request('get', 'https://httpbin.org/status/200', [], [], false); + } + + public function testSslOption() + { + // make sure options array loads/saves properly + $optionsArray = [CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1]; + $withOptionsArray = new CurlClient($optionsArray); + $this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray); + } + + public function testShouldRetryOnTimeout() + { + Stripe::setMaxNetworkRetries(2); + + $curlClient = new CurlClient(); + + $this->assertTrue($this->shouldRetryMethod->invoke($curlClient, CURLE_OPERATION_TIMEOUTED, 0, 0)); + } + + public function testShouldRetryOnConnectionFailure() + { + Stripe::setMaxNetworkRetries(2); + + $curlClient = new CurlClient(); + + $this->assertTrue($this->shouldRetryMethod->invoke($curlClient, CURLE_COULDNT_CONNECT, 0, 0)); + } + + public function testShouldRetryOnConflict() + { + Stripe::setMaxNetworkRetries(2); + + $curlClient = new CurlClient(); + + $this->assertTrue($this->shouldRetryMethod->invoke($curlClient, 0, 409, 0)); + } + + public function testShouldNotRetryAtMaximumCount() + { + Stripe::setMaxNetworkRetries(2); + + $curlClient = new CurlClient(); + + $this->assertFalse($this->shouldRetryMethod->invoke($curlClient, 0, 0, Stripe::getMaxNetworkRetries())); + } + + public function testShouldNotRetryOnCertValidationError() + { + Stripe::setMaxNetworkRetries(2); + + $curlClient = new CurlClient(); + + $this->assertFalse($this->shouldRetryMethod->invoke($curlClient, CURLE_SSL_PEER_CERTIFICATE, -1, 0)); + } + + public function testSleepTimeShouldGrowExponentially() + { + $this->setMaxNetworkRetryDelay(999); + + $curlClient = new CurlClient(null, $this->createFakeRandomGenerator()); + + $this->assertEquals( + Stripe::getInitialNetworkRetryDelay() * 1, + $this->sleepTimeMethod->invoke($curlClient, 1) + ); + $this->assertEquals( + Stripe::getInitialNetworkRetryDelay() * 2, + $this->sleepTimeMethod->invoke($curlClient, 2) + ); + $this->assertEquals( + Stripe::getInitialNetworkRetryDelay() * 4, + $this->sleepTimeMethod->invoke($curlClient, 3) + ); + $this->assertEquals( + Stripe::getInitialNetworkRetryDelay() * 8, + $this->sleepTimeMethod->invoke($curlClient, 4) + ); + } + + public function testSleepTimeShouldEnforceMaxNetworkRetryDelay() + { + $this->setInitialNetworkRetryDelay(1); + $this->setMaxNetworkRetryDelay(2); + + $curlClient = new CurlClient(null, $this->createFakeRandomGenerator()); + + $this->assertEquals(1, $this->sleepTimeMethod->invoke($curlClient, 1)); + $this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 2)); + $this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 3)); + $this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 4)); + } + + public function testSleepTimeShouldAddSomeRandomness() + { + $randomValue = 0.8; + $this->setInitialNetworkRetryDelay(1); + $this->setMaxNetworkRetryDelay(8); + + $curlClient = new CurlClient(null, $this->createFakeRandomGenerator($randomValue)); + + $baseValue = Stripe::getInitialNetworkRetryDelay() * (0.5 * (1 + $randomValue)); + + // the initial value cannot be smaller than the base, + // so the randomness is ignored + $this->assertEquals(Stripe::getInitialNetworkRetryDelay(), $this->sleepTimeMethod->invoke($curlClient, 1)); + + // after the first one, the randomness is applied + $this->assertEquals($baseValue * 2, $this->sleepTimeMethod->invoke($curlClient, 2)); + $this->assertEquals($baseValue * 4, $this->sleepTimeMethod->invoke($curlClient, 3)); + $this->assertEquals($baseValue * 8, $this->sleepTimeMethod->invoke($curlClient, 4)); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/InvoiceItemTest.php b/htdocs/includes/stripe/tests/Stripe/InvoiceItemTest.php new file mode 100644 index 00000000000..ffe140aa7ca --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/InvoiceItemTest.php @@ -0,0 +1,78 @@ +expectsRequest( + 'get', + '/v1/invoiceitems' + ); + $resources = InvoiceItem::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/invoiceitems/' . self::TEST_RESOURCE_ID + ); + $resource = InvoiceItem::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/invoiceitems' + ); + $resource = InvoiceItem::create([ + "amount" => 100, + "currency" => "usd", + "customer" => "cus_123" + ]); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resource); + } + + public function testIsSaveable() + { + $resource = InvoiceItem::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/invoiceitems/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/invoiceitems/' . self::TEST_RESOURCE_ID + ); + $resource = InvoiceItem::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resource); + } + + public function testIsDeletable() + { + $invoiceItem = InvoiceItem::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/invoiceitems/' . $invoiceItem->id + ); + $resource = $invoiceItem->delete(); + $this->assertInstanceOf("Stripe\\InvoiceItem", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/InvoiceTest.php b/htdocs/includes/stripe/tests/Stripe/InvoiceTest.php new file mode 100644 index 00000000000..f1ae9378628 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/InvoiceTest.php @@ -0,0 +1,87 @@ +expectsRequest( + 'get', + '/v1/invoices' + ); + $resources = Invoice::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Invoice", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/invoices/' . self::TEST_RESOURCE_ID + ); + $resource = Invoice::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Invoice", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/invoices' + ); + $resource = Invoice::create([ + "customer" => "cus_123" + ]); + $this->assertInstanceOf("Stripe\\Invoice", $resource); + } + + public function testIsSaveable() + { + $resource = Invoice::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/invoices/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Invoice", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/invoices/' . self::TEST_RESOURCE_ID + ); + $resource = Invoice::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Invoice", $resource); + } + + public function testCanRetrieveUpcoming() + { + $this->expectsRequest( + 'get', + '/v1/invoices/upcoming' + ); + $resource = Invoice::upcoming(["customer" => "cus_123"]); + $this->assertInstanceOf("Stripe\\Invoice", $resource); + } + + public function testIsPayable() + { + $invoice = Invoice::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/invoices/' . $invoice->id . '/pay' + ); + $resource = $invoice->pay(); + $this->assertInstanceOf("Stripe\\Invoice", $resource); + $this->assertSame($resource, $invoice); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/OAuthTest.php b/htdocs/includes/stripe/tests/Stripe/OAuthTest.php new file mode 100644 index 00000000000..b4e43a88103 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/OAuthTest.php @@ -0,0 +1,97 @@ + 'read_write', + 'state' => 'csrf_token', + 'stripe_user' => [ + 'email' => 'test@example.com', + 'url' => 'https://example.com/profile/test', + 'country' => 'US', + ], + ]); + + $uri = parse_url($uriStr); + parse_str($uri['query'], $params); + + $this->assertSame('https', $uri['scheme']); + $this->assertSame('connect.stripe.com', $uri['host']); + $this->assertSame('/oauth/authorize', $uri['path']); + + $this->assertSame('ca_123', $params['client_id']); + $this->assertSame('read_write', $params['scope']); + $this->assertSame('test@example.com', $params['stripe_user']['email']); + $this->assertSame('https://example.com/profile/test', $params['stripe_user']['url']); + $this->assertSame('US', $params['stripe_user']['country']); + } + + /** + * @expectedException \Stripe\Error\Authentication + * @expectedExceptionMessageRegExp #No client_id provided# + */ + public function testRaisesAuthenticationErrorWhenNoClientId() + { + Stripe::setClientId(null); + OAuth::authorizeUrl(); + } + + public function testToken() + { + $this->stubRequest( + 'POST', + '/oauth/token', + [ + 'grant_type' => 'authorization_code', + 'code' => 'this_is_an_authorization_code', + ], + null, + false, + [ + 'access_token' => 'sk_access_token', + 'scope' => 'read_only', + 'livemode' => false, + 'token_type' => 'bearer', + 'refresh_token' => 'sk_refresh_token', + 'stripe_user_id' => 'acct_test', + 'stripe_publishable_key' => 'pk_test', + ], + 200, + Stripe::$connectBase + ); + + $resp = OAuth::token([ + 'grant_type' => 'authorization_code', + 'code' => 'this_is_an_authorization_code', + ]); + $this->assertSame('sk_access_token', $resp->access_token); + } + + public function testDeauthorize() + { + $this->stubRequest( + 'POST', + '/oauth/deauthorize', + [ + 'stripe_user_id' => 'acct_test_deauth', + 'client_id' => 'ca_123', + ], + null, + false, + [ + 'stripe_user_id' => 'acct_test_deauth', + ], + 200, + Stripe::$connectBase + ); + + $resp = OAuth::deauthorize([ + 'stripe_user_id' => 'acct_test_deauth', + ]); + $this->assertSame('acct_test_deauth', $resp->stripe_user_id); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/OrderReturnTest.php b/htdocs/includes/stripe/tests/Stripe/OrderReturnTest.php new file mode 100644 index 00000000000..bb2d65c437a --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/OrderReturnTest.php @@ -0,0 +1,29 @@ +expectsRequest( + 'get', + '/v1/order_returns' + ); + $resources = OrderReturn::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\OrderReturn", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/order_returns/' . self::TEST_RESOURCE_ID + ); + $resource = OrderReturn::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\OrderReturn", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/OrderTest.php b/htdocs/includes/stripe/tests/Stripe/OrderTest.php new file mode 100644 index 00000000000..51d17e94fc7 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/OrderTest.php @@ -0,0 +1,87 @@ +expectsRequest( + 'get', + '/v1/orders' + ); + $resources = Order::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Order", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/orders/' . self::TEST_RESOURCE_ID + ); + $resource = Order::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Order", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/orders' + ); + $resource = Order::create([ + 'currency' => 'usd' + ]); + $this->assertInstanceOf("Stripe\\Order", $resource); + } + + public function testIsSaveable() + { + $resource = Order::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/orders/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Order", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/orders/' . self::TEST_RESOURCE_ID + ); + $resource = Order::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Order", $resource); + } + + public function testIsPayable() + { + $resource = Order::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/orders/' . $resource->id . '/pay' + ); + $resource->pay(); + $this->assertInstanceOf("Stripe\\Order", $resource); + } + + public function testIsReturnable() + { + $order = Order::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/orders/' . $order->id . '/returns' + ); + $resource = $order->returnOrder(); + $this->assertInstanceOf("Stripe\\OrderReturn", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/PayoutTest.php b/htdocs/includes/stripe/tests/Stripe/PayoutTest.php new file mode 100644 index 00000000000..79c7b5fd015 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/PayoutTest.php @@ -0,0 +1,77 @@ +expectsRequest( + 'get', + '/v1/payouts' + ); + $resources = Payout::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Payout", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/payouts/' . self::TEST_RESOURCE_ID + ); + $resource = Payout::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Payout", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/payouts' + ); + $resource = Payout::create([ + "amount" => 100, + "currency" => "usd" + ]); + $this->assertInstanceOf("Stripe\\Payout", $resource); + } + + public function testIsSaveable() + { + $resource = Payout::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/payouts/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Payout", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/payouts/' . self::TEST_RESOURCE_ID + ); + $resource = Payout::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Payout", $resource); + } + + public function testIsCancelable() + { + $resource = Payout::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/payouts/' . $resource->id . '/cancel' + ); + $resource->cancel(); + $this->assertInstanceOf("Stripe\\Payout", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/PlanTest.php b/htdocs/includes/stripe/tests/Stripe/PlanTest.php new file mode 100644 index 00000000000..8d71745ed56 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/PlanTest.php @@ -0,0 +1,80 @@ +expectsRequest( + 'get', + '/v1/plans' + ); + $resources = Plan::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Plan", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/plans/' . self::TEST_RESOURCE_ID + ); + $resource = Plan::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Plan", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/plans' + ); + $resource = Plan::create([ + 'amount' => 100, + 'interval' => 'month', + 'currency' => 'usd', + 'name' => self::TEST_RESOURCE_ID, + 'id' => self::TEST_RESOURCE_ID + ]); + $this->assertInstanceOf("Stripe\\Plan", $resource); + } + + public function testIsSaveable() + { + $resource = Plan::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/plans/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Plan", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/plans/' . self::TEST_RESOURCE_ID + ); + $resource = Plan::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Plan", $resource); + } + + public function testIsDeletable() + { + $resource = Plan::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/plans/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Plan", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ProductTest.php b/htdocs/includes/stripe/tests/Stripe/ProductTest.php new file mode 100644 index 00000000000..c2a3813c870 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ProductTest.php @@ -0,0 +1,77 @@ +expectsRequest( + 'get', + '/v1/products' + ); + $resources = Product::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Product", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/products/' . self::TEST_RESOURCE_ID + ); + $resource = Product::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Product", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/products' + ); + $resource = Product::create([ + 'name' => 'name', + 'type' => 'good' + ]); + $this->assertInstanceOf("Stripe\\Product", $resource); + } + + public function testIsSaveable() + { + $resource = Product::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/products/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Product", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/products/' . self::TEST_RESOURCE_ID + ); + $resource = Product::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Product", $resource); + } + + public function testIsDeletable() + { + $resource = Product::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/products/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Product", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/RecipientTest.php b/htdocs/includes/stripe/tests/Stripe/RecipientTest.php new file mode 100644 index 00000000000..43dd6e82a79 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/RecipientTest.php @@ -0,0 +1,90 @@ +expectsRequest( + 'get', + '/v1/recipients' + ); + $resources = Recipient::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Recipient", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/recipients/' . self::TEST_RESOURCE_ID + ); + $resource = Recipient::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Recipient", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/recipients' + ); + $resource = Recipient::create([ + "name" => "name", + "type" => "individual" + ]); + $this->assertInstanceOf("Stripe\\Recipient", $resource); + } + + public function testIsSaveable() + { + $resource = Recipient::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/recipients/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Recipient", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/recipients/' . self::TEST_RESOURCE_ID + ); + $resource = Recipient::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Recipient", $resource); + } + + public function testIsDeletable() + { + $resource = Recipient::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/recipients/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Recipient", $resource); + } + + public function testCanListTransfers() + { + $recipient = Recipient::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'get', + '/v1/transfers', + ["recipient" => $recipient->id] + ); + $resources = $recipient->transfers(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Transfer", $resources->data[0]); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/RefundTest.php b/htdocs/includes/stripe/tests/Stripe/RefundTest.php new file mode 100644 index 00000000000..788361d4d9d --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/RefundTest.php @@ -0,0 +1,65 @@ +expectsRequest( + 'get', + '/v1/refunds' + ); + $resources = Refund::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Refund", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/refunds/' . self::TEST_RESOURCE_ID + ); + $resource = Refund::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Refund", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/refunds' + ); + $resource = Refund::create([ + "charge" => "ch_123" + ]); + $this->assertInstanceOf("Stripe\\Refund", $resource); + } + + public function testIsSaveable() + { + $resource = Refund::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/refunds/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Refund", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/refunds/' . self::TEST_RESOURCE_ID + ); + $resource = Refund::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Refund", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/SKUTest.php b/htdocs/includes/stripe/tests/Stripe/SKUTest.php new file mode 100644 index 00000000000..d4fc2ad408b --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/SKUTest.php @@ -0,0 +1,82 @@ +expectsRequest( + 'get', + '/v1/skus' + ); + $resources = SKU::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\SKU", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/skus/' . self::TEST_RESOURCE_ID + ); + $resource = SKU::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\SKU", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/skus' + ); + $resource = SKU::create([ + 'currency' => 'usd', + 'inventory' => [ + 'type' => 'finite', + 'quantity' => 1 + ], + 'price' => 100, + 'product' => "prod_123" + ]); + $this->assertInstanceOf("Stripe\\SKU", $resource); + } + + public function testIsSaveable() + { + $resource = SKU::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/skus/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\SKU", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/skus/' . self::TEST_RESOURCE_ID + ); + $resource = SKU::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\SKU", $resource); + } + + public function testIsDeletable() + { + $resource = SKU::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/skus/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\SKU", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/SourceTest.php b/htdocs/includes/stripe/tests/Stripe/SourceTest.php new file mode 100644 index 00000000000..ad11b0d3455 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/SourceTest.php @@ -0,0 +1,134 @@ +expectsRequest( + 'get', + '/v1/sources/' . self::TEST_RESOURCE_ID + ); + $resource = Source::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Source", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/sources' + ); + $resource = Source::create([ + "type" => "card" + ]); + $this->assertInstanceOf("Stripe\\Source", $resource); + } + + public function testIsSaveable() + { + $resource = Source::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/sources/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Source", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/sources/' . self::TEST_RESOURCE_ID + ); + $resource = Source::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Source", $resource); + } + + public function testCanSaveCardExpiryDate() + { + $response = [ + 'id' => 'src_foo', + 'object' => 'source', + 'card' => [ + 'exp_month' => 8, + 'exp_year' => 2019, + ], + ]; + $source = Source::constructFrom($response); + + $response['card']['exp_month'] = 12; + $response['card']['exp_year'] = 2022; + $this->stubRequest( + 'POST', + '/v1/sources/src_foo', + [ + 'card' => [ + 'exp_month' => 12, + 'exp_year' => 2022, + ] + ], + null, + false, + $response + ); + + $source->card->exp_month = 12; + $source->card->exp_year = 2022; + $source->save(); + + $this->assertSame(12, $source->card->exp_month); + $this->assertSame(2022, $source->card->exp_year); + } + + public function testIsDetachableWhenAttached() + { + $resource = Source::retrieve(self::TEST_RESOURCE_ID); + $resource->customer = "cus_123"; + $this->expectsRequest( + 'delete', + '/v1/customers/cus_123/sources/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\Source", $resource); + } + + /** + * @expectedException \Stripe\Error\Api + */ + public function testIsNotDetachableWhenUnattached() + { + $resource = Source::retrieve(self::TEST_RESOURCE_ID); + $resource->detach(); + } + + public function testCanListSourceTransactions() + { + $source = Source::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'get', + '/v1/sources/' . $source->id . "/source_transactions" + ); + $resources = $source->sourceTransactions(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\SourceTransaction", $resources->data[0]); + } + + public function testCanVerify() + { + $resource = Source::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/sources/' . $resource->id . "/verify" + ); + $resource->verify(["values" => [32, 45]]); + $this->assertInstanceOf("Stripe\\Source", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/StripeObjectTest.php b/htdocs/includes/stripe/tests/Stripe/StripeObjectTest.php new file mode 100644 index 00000000000..af1cd936e1c --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/StripeObjectTest.php @@ -0,0 +1,453 @@ +deepCopyReflector = new \ReflectionMethod('Stripe\\StripeObject', 'deepCopy'); + $this->deepCopyReflector->setAccessible(true); + + // This is used to access the `_opts` protected variable + $this->optsReflector = new \ReflectionProperty('Stripe\\StripeObject', '_opts'); + $this->optsReflector->setAccessible(true); + } + + public function testArrayAccessorsSemantics() + { + $s = new StripeObject(); + $s['foo'] = 'a'; + $this->assertSame($s['foo'], 'a'); + $this->assertTrue(isset($s['foo'])); + unset($s['foo']); + $this->assertFalse(isset($s['foo'])); + } + + public function testNormalAccessorsSemantics() + { + $s = new StripeObject(); + $s->foo = 'a'; + $this->assertSame($s->foo, 'a'); + $this->assertTrue(isset($s->foo)); + unset($s->foo); + $this->assertFalse(isset($s->foo)); + } + + public function testArrayAccessorsMatchNormalAccessors() + { + $s = new StripeObject(); + $s->foo = 'a'; + $this->assertSame($s['foo'], 'a'); + + $s['bar'] = 'b'; + $this->assertSame($s->bar, 'b'); + } + + public function testCount() + { + $s = new StripeObject(); + $this->assertSame(0, count($s)); + + $s['key1'] = 'value1'; + $this->assertSame(1, count($s)); + + $s['key2'] = 'value2'; + $this->assertSame(2, count($s)); + + unset($s['key1']); + $this->assertSame(1, count($s)); + } + + public function testKeys() + { + $s = new StripeObject(); + $s->foo = 'bar'; + $this->assertSame($s->keys(), ['foo']); + } + + public function testValues() + { + $s = new StripeObject(); + $s->foo = 'bar'; + $this->assertSame($s->values(), ['bar']); + } + + public function testToArray() + { + $s = new StripeObject(); + $s->foo = 'a'; + + $converted = $s->__toArray(); + + $this->assertInternalType('array', $converted); + $this->assertArrayHasKey('foo', $converted); + $this->assertEquals('a', $converted['foo']); + } + + public function testRecursiveToArray() + { + $s = new StripeObject(); + $z = new StripeObject(); + + $s->child = $z; + $z->foo = 'a'; + + $converted = $s->__toArray(true); + + $this->assertInternalType('array', $converted); + $this->assertArrayHasKey('child', $converted); + $this->assertInternalType('array', $converted['child']); + $this->assertArrayHasKey('foo', $converted['child']); + $this->assertEquals('a', $converted['child']['foo']); + } + + public function testNonexistentProperty() + { + $s = new StripeObject(); + $this->assertNull($s->nonexistent); + } + + public function testPropertyDoesNotExists() + { + $s = new StripeObject(); + $this->assertNull($s['nonexistent']); + } + + public function testJsonEncode() + { + $s = new StripeObject(); + $s->foo = 'a'; + + $this->assertEquals('{"foo":"a"}', json_encode($s)); + } + + public function testToString() + { + $s = new StripeObject(); + $s->foo = 'a'; + + $string = $s->__toString(); + $expected = <<assertEquals($expected, $string); + } + + public function testReplaceNewNestedUpdatable() + { + $s = new StripeObject(); + + $s->metadata = ['bar']; + $this->assertSame($s->metadata, ['bar']); + $s->metadata = ['baz', 'qux']; + $this->assertSame($s->metadata, ['baz', 'qux']); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetPermanentAttribute() + { + $s = new StripeObject(); + $s->id = 'abc_123'; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetEmptyStringValue() + { + $s = new StripeObject(); + $s->foo = ''; + } + + public function testSerializeParametersOnEmptyObject() + { + $obj = StripeObject::constructFrom([]); + $this->assertSame([], $obj->serializeParameters()); + } + + public function testSerializeParametersOnNewObjectWithSubObject() + { + $obj = new StripeObject(); + $obj->metadata = ['foo' => 'bar']; + $this->assertSame(['metadata' => ['foo' => 'bar']], $obj->serializeParameters()); + } + + public function testSerializeParametersOnBasicObject() + { + $obj = StripeObject::constructFrom(['foo' => null]); + $obj->updateAttributes(['foo' => 'bar']); + $this->assertSame(['foo' => 'bar'], $obj->serializeParameters()); + } + + public function testSerializeParametersOnMoreComplexObject() + { + $obj = StripeObject::constructFrom([ + 'foo' => StripeObject::constructFrom([ + 'bar' => null, + 'baz' => null, + ]), + ]); + $obj->foo->bar = 'newbar'; + $this->assertSame(['foo' => ['bar' => 'newbar']], $obj->serializeParameters()); + } + + public function testSerializeParametersOnArray() + { + $obj = StripeObject::constructFrom([ + 'foo' => null, + ]); + $obj->foo = ['new-value']; + $this->assertSame(['foo' => ['new-value']], $obj->serializeParameters()); + } + + public function testSerializeParametersOnArrayThatShortens() + { + $obj = StripeObject::constructFrom([ + 'foo' => ['0-index', '1-index', '2-index'], + ]); + $obj->foo = ['new-value']; + $this->assertSame(['foo' => ['new-value']], $obj->serializeParameters()); + } + + public function testSerializeParametersOnArrayThatLengthens() + { + $obj = StripeObject::constructFrom([ + 'foo' => ['0-index', '1-index', '2-index'], + ]); + $obj->foo = array_fill(0, 4, 'new-value'); + $this->assertSame(['foo' => array_fill(0, 4, 'new-value')], $obj->serializeParameters()); + } + + public function testSerializeParametersOnArrayOfHashes() + { + $obj = StripeObject::constructFrom(['foo' => null]); + $obj->foo = [ + StripeObject::constructFrom(['bar' => null]), + ]; + + $obj->foo[0]->bar = 'baz'; + $this->assertSame(['foo' => [['bar' => 'baz']]], $obj->serializeParameters()); + } + + public function testSerializeParametersDoesNotIncludeUnchangedValues() + { + $obj = StripeObject::constructFrom([ + 'foo' => null, + ]); + $this->assertSame([], $obj->serializeParameters()); + } + + public function testSerializeParametersOnUnchangedArray() + { + $obj = StripeObject::constructFrom([ + 'foo' => ['0-index', '1-index', '2-index'], + ]); + $obj->foo = ['0-index', '1-index', '2-index']; + $this->assertSame([], $obj->serializeParameters()); + } + + public function testSerializeParametersWithStripeObject() + { + $obj = StripeObject::constructFrom([]); + $obj->metadata = StripeObject::constructFrom(['foo' => 'bar']); + + $serialized = $obj->serializeParameters(); + $this->assertSame(['foo' => 'bar'], $serialized['metadata']); + } + + public function testSerializeParametersOnReplacedStripeObject() + { + $obj = StripeObject::constructFrom([ + 'metadata' => StripeObject::constructFrom(['bar' => 'foo']), + ]); + $obj->metadata = StripeObject::constructFrom(['baz' => 'foo']); + + $serialized = $obj->serializeParameters(); + $this->assertSame(['bar' => '', 'baz' => 'foo'], $serialized['metadata']); + } + + public function testSerializeParametersOnArrayOfStripeObjects() + { + $obj = StripeObject::constructFrom([]); + $obj->metadata = [ + StripeObject::constructFrom(['foo' => 'bar']), + ]; + + $serialized = $obj->serializeParameters(); + $this->assertSame([['foo' => 'bar']], $serialized['metadata']); + } + + public function testSerializeParametersOnSetApiResource() + { + $customer = Customer::constructFrom(['id' => 'cus_123']); + $obj = StripeObject::constructFrom([]); + + // the key here is that the property is set explicitly (and therefore + // marked as unsaved), which is why it gets included below + $obj->customer = $customer; + + $serialized = $obj->serializeParameters(); + $this->assertSame(['customer' => $customer], $serialized); + } + + public function testSerializeParametersOnNotSetApiResource() + { + $customer = Customer::constructFrom(['id' => 'cus_123']); + $obj = StripeObject::constructFrom(['customer' => $customer]); + + $serialized = $obj->serializeParameters(); + $this->assertSame([], $serialized); + } + + public function testSerializeParametersOnApiResourceFlaggedWithSaveWithParent() + { + $customer = Customer::constructFrom(['id' => 'cus_123']); + $customer->saveWithParent = true; + + $obj = StripeObject::constructFrom(['customer' => $customer]); + + $serialized = $obj->serializeParameters(); + $this->assertSame(['customer' => []], $serialized); + } + + public function testSerializeParametersRaisesExceotionOnOtherEmbeddedApiResources() + { + // This customer doesn't have an ID and therefore the library doesn't know + // what to do with it and throws an InvalidArgumentException because it's + // probably not what the user expected to happen. + $customer = Customer::constructFrom([]); + + $obj = StripeObject::constructFrom([]); + $obj->customer = $customer; + + try { + $serialized = $obj->serializeParameters(); + $this->fail("Did not raise error"); + } catch (\InvalidArgumentException $e) { + $this->assertSame( + "Cannot save property `customer` containing an API resource of type Stripe\Customer. " . + "It doesn't appear to be persisted and is not marked as `saveWithParent`.", + $e->getMessage() + ); + } catch (\Exception $e) { + $this->fail("Unexpected exception: " . get_class($e)); + } + } + + public function testSerializeParametersForce() + { + $obj = StripeObject::constructFrom([ + 'id' => 'id', + 'metadata' => StripeObject::constructFrom([ + 'bar' => 'foo', + ]), + ]); + + $serialized = $obj->serializeParameters(true); + $this->assertSame(['id' => 'id', 'metadata' => ['bar' => 'foo']], $serialized); + } + + public function testDirty() + { + $obj = StripeObject::constructFrom([ + 'id' => 'id', + 'metadata' => StripeObject::constructFrom([ + 'bar' => 'foo', + ]), + ]); + + // note that `$force` and `dirty()` are for different things, but are + // functionally equivalent + $obj->dirty(); + + $serialized = $obj->serializeParameters(); + $this->assertSame(['id' => 'id', 'metadata' => ['bar' => 'foo']], $serialized); + } + + public function testDeepCopy() + { + $opts = [ + "api_base" => Stripe::$apiBase, + "api_key" => "apikey", + ]; + $values = [ + "id" => 1, + "name" => "Stripe", + "arr" => [ + StripeObject::constructFrom(["id" => "index0"], $opts), + "index1", + 2, + ], + "map" => [ + "0" => StripeObject::constructFrom(["id" => "index0"], $opts), + "1" => "index1", + "2" => 2 + ], + ]; + + $copyValues = $this->deepCopyReflector->invoke(null, $values); + + // we can't compare the hashes directly because they have embedded + // objects which are different from each other + $this->assertEquals($values["id"], $copyValues["id"]); + $this->assertEquals($values["name"], $copyValues["name"]); + $this->assertEquals(count($values["arr"]), count($copyValues["arr"])); + + // internal values of the copied StripeObject should be the same, + // but the object itself should be new (hence the assertNotSame) + $this->assertEquals($values["arr"][0]["id"], $copyValues["arr"][0]["id"]); + $this->assertNotSame($values["arr"][0], $copyValues["arr"][0]); + + // likewise, the Util\RequestOptions instance in _opts should have + // copied values but be a new instance + $this->assertEquals( + $this->optsReflector->getValue($values["arr"][0]), + $this->optsReflector->getValue($copyValues["arr"][0]) + ); + $this->assertNotSame( + $this->optsReflector->getValue($values["arr"][0]), + $this->optsReflector->getValue($copyValues["arr"][0]) + ); + + // scalars however, can be compared + $this->assertEquals($values["arr"][1], $copyValues["arr"][1]); + $this->assertEquals($values["arr"][2], $copyValues["arr"][2]); + + // and a similar story with the hash + $this->assertEquals($values["map"]["0"]["id"], $copyValues["map"]["0"]["id"]); + $this->assertNotSame($values["map"]["0"], $copyValues["map"]["0"]); + $this->assertNotSame( + $this->optsReflector->getValue($values["arr"][0]), + $this->optsReflector->getValue($copyValues["arr"][0]) + ); + $this->assertEquals( + $this->optsReflector->getValue($values["map"]["0"]), + $this->optsReflector->getValue($copyValues["map"]["0"]) + ); + $this->assertNotSame( + $this->optsReflector->getValue($values["map"]["0"]), + $this->optsReflector->getValue($copyValues["map"]["0"]) + ); + $this->assertEquals($values["map"]["1"], $copyValues["map"]["1"]); + $this->assertEquals($values["map"]["2"], $copyValues["map"]["2"]); + } + + public function testDeepCopyMaintainClass() + { + $charge = Charge::constructFrom(["id" => 1], null); + $copyCharge = $this->deepCopyReflector->invoke(null, $charge); + $this->assertEquals(get_class($charge), get_class($copyCharge)); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/StripeTest.php b/htdocs/includes/stripe/tests/Stripe/StripeTest.php new file mode 100644 index 00000000000..f594518d13d --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/StripeTest.php @@ -0,0 +1,30 @@ +orig = [ + 'caBundlePath' => Stripe::$caBundlePath, + ]; + } + + /** + * @after + */ + public function restoreOriginalValues() + { + Stripe::$caBundlePath = $this->orig['caBundlePath']; + } + + public function testCABundlePathAccessors() + { + Stripe::setCABundlePath('path/to/ca/bundle'); + $this->assertEquals('path/to/ca/bundle', Stripe::getCABundlePath()); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/SubscriptionItemTest.php b/htdocs/includes/stripe/tests/Stripe/SubscriptionItemTest.php new file mode 100644 index 00000000000..09e766c1bec --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/SubscriptionItemTest.php @@ -0,0 +1,77 @@ +expectsRequest( + 'get', + '/v1/subscription_items' + ); + $resources = SubscriptionItem::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\SubscriptionItem", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/subscription_items/' . self::TEST_RESOURCE_ID + ); + $resource = SubscriptionItem::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/subscription_items' + ); + $resource = SubscriptionItem::create([ + "plan" => "plan", + "subscription" => "sub_123" + ]); + $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource); + } + + public function testIsSaveable() + { + $resource = SubscriptionItem::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/subscription_items/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/subscription_items/' . self::TEST_RESOURCE_ID + ); + $resource = SubscriptionItem::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource); + } + + public function testIsDeletable() + { + $resource = SubscriptionItem::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/subscription_items/' . $resource->id + ); + $resource->delete(); + $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/SubscriptionTest.php b/htdocs/includes/stripe/tests/Stripe/SubscriptionTest.php new file mode 100644 index 00000000000..4a42e218224 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/SubscriptionTest.php @@ -0,0 +1,115 @@ +expectsRequest( + 'get', + '/v1/subscriptions' + ); + $resources = Subscription::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Subscription", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/subscriptions/' . self::TEST_RESOURCE_ID + ); + $resource = Subscription::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/subscriptions' + ); + $resource = Subscription::create([ + "customer" => "cus_123", + "plan" => "plan" + ]); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + } + + public function testIsSaveable() + { + $resource = Subscription::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/subscriptions/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/subscriptions/' . self::TEST_RESOURCE_ID + ); + $resource = Subscription::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + } + + public function testIsCancelable() + { + $resource = Subscription::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/subscriptions/' . $resource->id, + [ + 'at_period_end' => 'true', + ] + ); + $resource->cancel([ + 'at_period_end' => true, + ]); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + } + + public function testCanDeleteDiscount() + { + $resource = Subscription::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'delete', + '/v1/subscriptions/' . $resource->id . '/discount' + ); + $resource->deleteDiscount(); + $this->assertInstanceOf("Stripe\\Subscription", $resource); + } + + public function testSerializeParametersItems() + { + $obj = Util\Util::convertToStripeObject([ + 'object' => 'subscription', + 'items' => Util\Util::convertToStripeObject([ + 'object' => 'list', + 'data' => [], + ], null), + ], null); + $obj->items = [ + ['id' => 'si_foo', 'deleted' => true], + ['plan' => 'plan_bar'], + ]; + $expected = [ + 'items' => [ + 0 => ['id' => 'si_foo', 'deleted' => true], + 1 => ['plan' => 'plan_bar'], + ], + ]; + $this->assertSame($expected, $obj->serializeParameters()); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/ThreeDSecureTest.php b/htdocs/includes/stripe/tests/Stripe/ThreeDSecureTest.php new file mode 100644 index 00000000000..f56e649cbc1 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/ThreeDSecureTest.php @@ -0,0 +1,32 @@ +expectsRequest( + 'get', + '/v1/3d_secure/' . self::TEST_RESOURCE_ID + ); + $resource = ThreeDSecure::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\ThreeDSecure", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/3d_secure' + ); + $resource = ThreeDSecure::create([ + "amount" => 100, + "currency" => "usd", + "return_url" => "url" + ]); + $this->assertInstanceOf("Stripe\\ThreeDSecure", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/TokenTest.php b/htdocs/includes/stripe/tests/Stripe/TokenTest.php new file mode 100644 index 00000000000..36bb4bc8283 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/TokenTest.php @@ -0,0 +1,28 @@ +expectsRequest( + 'get', + '/v1/tokens/' . self::TEST_RESOURCE_ID + ); + $resource = Token::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Token", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/tokens' + ); + $resource = Token::create(["card" => "tok_visa"]); + $this->assertInstanceOf("Stripe\\Token", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/TopupTest.php b/htdocs/includes/stripe/tests/Stripe/TopupTest.php new file mode 100644 index 00000000000..994447636c6 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/TopupTest.php @@ -0,0 +1,69 @@ +expectsRequest( + 'get', + '/v1/topups' + ); + $resources = Topup::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Topup", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/topups/' . self::TEST_RESOURCE_ID + ); + $resource = Topup::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Topup", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/topups' + ); + $resource = Topup::create([ + "amount" => 100, + "currency" => "usd", + "source" => "tok_123", + "description" => "description", + "statement_descriptor" => "statement descriptor" + ]); + $this->assertInstanceOf("Stripe\\Topup", $resource); + } + + public function testIsSaveable() + { + $resource = Topup::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/topups/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Topup", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/topups/' . self::TEST_RESOURCE_ID + ); + $resource = Topup::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Topup", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/TransferReversalTest.php b/htdocs/includes/stripe/tests/Stripe/TransferReversalTest.php new file mode 100644 index 00000000000..37101c73208 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/TransferReversalTest.php @@ -0,0 +1,21 @@ +metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/transfers/' . $resource->transfer . '/reversals/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\TransferReversal", $resource); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/TransferTest.php b/htdocs/includes/stripe/tests/Stripe/TransferTest.php new file mode 100644 index 00000000000..d60d560e7c5 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/TransferTest.php @@ -0,0 +1,140 @@ +expectsRequest( + 'get', + '/v1/transfers' + ); + $resources = Transfer::all(); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\Transfer", $resources->data[0]); + } + + public function testIsRetrievable() + { + $this->expectsRequest( + 'get', + '/v1/transfers/' . self::TEST_RESOURCE_ID + ); + $resource = Transfer::retrieve(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\Transfer", $resource); + } + + public function testIsCreatable() + { + $this->expectsRequest( + 'post', + '/v1/transfers' + ); + $resource = Transfer::create([ + "amount" => 100, + "currency" => "usd", + "destination" => "acct_123" + ]); + $this->assertInstanceOf("Stripe\\Transfer", $resource); + } + + public function testIsSaveable() + { + $resource = Transfer::retrieve(self::TEST_RESOURCE_ID); + $resource->metadata["key"] = "value"; + $this->expectsRequest( + 'post', + '/v1/transfers/' . $resource->id + ); + $resource->save(); + $this->assertInstanceOf("Stripe\\Transfer", $resource); + } + + public function testIsUpdatable() + { + $this->expectsRequest( + 'post', + '/v1/transfers/' . self::TEST_RESOURCE_ID + ); + $resource = Transfer::update(self::TEST_RESOURCE_ID, [ + "metadata" => ["key" => "value"], + ]); + $this->assertInstanceOf("Stripe\\Transfer", $resource); + } + + public function testIsReversable() + { + $resource = Transfer::retrieve(self::TEST_RESOURCE_ID); + $this->expectsRequest( + 'post', + '/v1/transfers/' . $resource->id . '/reversals' + ); + $resource->reverse(); + $this->assertInstanceOf("Stripe\\Transfer", $resource); + } + + public function testIsCancelable() + { + $transfer = Transfer::retrieve(self::TEST_RESOURCE_ID); + + // stripe-mock does not support this anymore so we stub it + $this->stubRequest( + 'post', + '/v1/transfers/' . $transfer->id . '/cancel' + ); + $resource = $transfer->cancel(); + $this->assertInstanceOf("Stripe\\Transfer", $resource); + $this->assertSame($resource, $transfer); + } + + public function testCanCreateReversal() + { + $this->expectsRequest( + 'post', + '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals' + ); + $resource = Transfer::createReversal(self::TEST_RESOURCE_ID); + $this->assertInstanceOf("Stripe\\TransferReversal", $resource); + } + + public function testCanRetrieveReversal() + { + $this->expectsRequest( + 'get', + '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals/' . self::TEST_REVERSAL_ID + ); + $resource = Transfer::retrieveReversal(self::TEST_RESOURCE_ID, self::TEST_REVERSAL_ID); + $this->assertInstanceOf("Stripe\\TransferReversal", $resource); + } + + public function testCanUpdateReversal() + { + $this->expectsRequest( + 'post', + '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals/' . self::TEST_REVERSAL_ID + ); + $resource = Transfer::updateReversal( + self::TEST_RESOURCE_ID, + self::TEST_REVERSAL_ID, + [ + "metadata" => ["key" => "value"], + ] + ); + $this->assertInstanceOf("Stripe\\TransferReversal", $resource); + } + + public function testCanListReversal() + { + $this->expectsRequest( + 'get', + '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals' + ); + $resources = Transfer::allReversals(self::TEST_RESOURCE_ID); + $this->assertTrue(is_array($resources->data)); + $this->assertInstanceOf("Stripe\\TransferReversal", $resources->data[0]); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/Util/DefaultLoggerTest.php b/htdocs/includes/stripe/tests/Stripe/Util/DefaultLoggerTest.php new file mode 100644 index 00000000000..711af03c4a0 --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/Util/DefaultLoggerTest.php @@ -0,0 +1,28 @@ +error("message"); + + global $lastMessage; + $this->assertSame($lastMessage, "message"); + } +} + +// This is a little terrible, but unfortunately there's no clean way to stub a +// call to `error_log`. Here we overwrite it so that we can get the last arguments +// that went to it. This is obviously bad, but luckily it's constrained to +// being just in \Stripe\Util (i.e. won't interfere with PHPUnit for example) +// and _just_ present when tests are running. +function error_log($message) +{ + global $lastMessage; + $lastMessage = $message; +} diff --git a/htdocs/includes/stripe/tests/RequestOptionsTest.php b/htdocs/includes/stripe/tests/Stripe/Util/RequestOptionsTest.php similarity index 60% rename from htdocs/includes/stripe/tests/RequestOptionsTest.php rename to htdocs/includes/stripe/tests/Stripe/Util/RequestOptionsTest.php index 11546dd6ece..558f73982ea 100644 --- a/htdocs/includes/stripe/tests/RequestOptionsTest.php +++ b/htdocs/includes/stripe/tests/Stripe/Util/RequestOptionsTest.php @@ -8,55 +8,55 @@ class RequestOptionsTest extends TestCase { $opts = Util\RequestOptions::parse("foo"); $this->assertSame("foo", $opts->apiKey); - $this->assertSame(array(), $opts->headers); + $this->assertSame([], $opts->headers); } public function testNull() { $opts = Util\RequestOptions::parse(null); $this->assertSame(null, $opts->apiKey); - $this->assertSame(array(), $opts->headers); + $this->assertSame([], $opts->headers); } public function testEmptyArray() { - $opts = Util\RequestOptions::parse(array()); + $opts = Util\RequestOptions::parse([]); $this->assertSame(null, $opts->apiKey); - $this->assertSame(array(), $opts->headers); + $this->assertSame([], $opts->headers); } public function testAPIKeyArray() { $opts = Util\RequestOptions::parse( - array( + [ 'api_key' => 'foo', - ) + ] ); $this->assertSame('foo', $opts->apiKey); - $this->assertSame(array(), $opts->headers); + $this->assertSame([], $opts->headers); } public function testIdempotentKeyArray() { $opts = Util\RequestOptions::parse( - array( + [ 'idempotency_key' => 'foo', - ) + ] ); $this->assertSame(null, $opts->apiKey); - $this->assertSame(array('Idempotency-Key' => 'foo'), $opts->headers); + $this->assertSame(['Idempotency-Key' => 'foo'], $opts->headers); } public function testKeyArray() { $opts = Util\RequestOptions::parse( - array( + [ 'idempotency_key' => 'foo', 'api_key' => 'foo' - ) + ] ); $this->assertSame('foo', $opts->apiKey); - $this->assertSame(array('Idempotency-Key' => 'foo'), $opts->headers); + $this->assertSame(['Idempotency-Key' => 'foo'], $opts->headers); } /** @@ -66,4 +66,16 @@ class RequestOptionsTest extends TestCase { $opts = Util\RequestOptions::parse(5); } + + public function testDiscardNonPersistentHeaders() + { + $opts = Util\RequestOptions::parse( + [ + 'stripe_account' => 'foo', + 'idempotency_key' => 'foo', + ] + ); + $opts->discardNonPersistentHeaders(); + $this->assertSame(['Stripe-Account' => 'foo'], $opts->headers); + } } diff --git a/htdocs/includes/stripe/tests/Stripe/Util/UtilTest.php b/htdocs/includes/stripe/tests/Stripe/Util/UtilTest.php new file mode 100644 index 00000000000..cf5130208db --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/Util/UtilTest.php @@ -0,0 +1,90 @@ +assertTrue(Util\Util::isList($list)); + + $notlist = [5, 'nstaoush', [], 'bar' => 'baz']; + $this->assertFalse(Util\Util::isList($notlist)); + } + + public function testThatPHPHasValueSemanticsForArrays() + { + $original = ['php-arrays' => 'value-semantics']; + $derived = $original; + $derived['php-arrays'] = 'reference-semantics'; + + $this->assertSame('value-semantics', $original['php-arrays']); + } + + public function testConvertStripeObjectToArrayIncludesId() + { + $customer = Util\Util::convertToStripeObject([ + 'id' => 'cus_123', + 'object' => 'customer', + ], null); + $this->assertTrue(array_key_exists("id", $customer->__toArray(true))); + } + + public function testUtf8() + { + // UTF-8 string + $x = "\xc3\xa9"; + $this->assertSame(Util\Util::utf8($x), $x); + + // Latin-1 string + $x = "\xe9"; + $this->assertSame(Util\Util::utf8($x), "\xc3\xa9"); + + // Not a string + $x = true; + $this->assertSame(Util\Util::utf8($x), $x); + } + + public function testUrlEncode() + { + $a = [ + 'my' => 'value', + 'that' => ['your' => 'example'], + 'bar' => 1, + 'baz' => null + ]; + + $enc = Util\Util::urlEncode($a); + $this->assertSame('my=value&that%5Byour%5D=example&bar=1', $enc); + + $a = ['that' => ['your' => 'example', 'foo' => null]]; + $enc = Util\Util::urlEncode($a); + $this->assertSame('that%5Byour%5D=example', $enc); + + $a = ['that' => 'example', 'foo' => ['bar', 'baz']]; + $enc = Util\Util::urlEncode($a); + $this->assertSame('that=example&foo%5B%5D=bar&foo%5B%5D=baz', $enc); + + $a = [ + 'my' => 'value', + 'that' => ['your' => ['cheese', 'whiz', null]], + 'bar' => 1, + 'baz' => null + ]; + + $enc = Util\Util::urlEncode($a); + $expected = 'my=value&that%5Byour%5D%5B%5D=cheese' + . '&that%5Byour%5D%5B%5D=whiz&bar=1'; + $this->assertSame($expected, $enc); + + // Ignores an empty array + $enc = Util\Util::urlEncode(['foo' => [], 'bar' => 'baz']); + $expected = 'bar=baz'; + $this->assertSame($expected, $enc); + + $a = ['foo' => [['bar' => 'baz'], ['bar' => 'bin']]]; + $enc = Util\Util::urlEncode($a); + $this->assertSame('foo%5B0%5D%5Bbar%5D=baz&foo%5B1%5D%5Bbar%5D=bin', $enc); + } +} diff --git a/htdocs/includes/stripe/tests/Stripe/WebhookTest.php b/htdocs/includes/stripe/tests/Stripe/WebhookTest.php new file mode 100644 index 00000000000..dcd82671afc --- /dev/null +++ b/htdocs/includes/stripe/tests/Stripe/WebhookTest.php @@ -0,0 +1,110 @@ +generateHeader(); + $event = Webhook::constructEvent(self::EVENT_PAYLOAD, $sigHeader, self::SECRET); + $this->assertEquals("evt_test_webhook", $event->id); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testInvalidJson() + { + $payload = "this is not valid JSON"; + $sigHeader = $this->generateHeader(["payload" => $payload]); + Webhook::constructEvent($payload, $sigHeader, self::SECRET); + } + + /** + * @expectedException \Stripe\Error\SignatureVerification + */ + public function testValidJsonAndInvalidHeader() + { + $sigHeader = "bad_header"; + Webhook::constructEvent(self::EVENT_PAYLOAD, $sigHeader, self::SECRET); + } + + /** + * @expectedException \Stripe\Error\SignatureVerification + * @expectedExceptionMessage Unable to extract timestamp and signatures from header + */ + public function testMalformedHeader() + { + $sigHeader = "i'm not even a real signature header"; + WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET); + } + + /** + * @expectedException \Stripe\Error\SignatureVerification + * @expectedExceptionMessage No signatures found with expected scheme + */ + public function testNoSignaturesWithExpectedScheme() + { + $sigHeader = $this->generateHeader(["scheme" => "v0"]); + WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET); + } + + /** + * @expectedException \Stripe\Error\SignatureVerification + * @expectedExceptionMessage No signatures found matching the expected signature for payload + */ + public function testNoValidSignatureForPayload() + { + $sigHeader = $this->generateHeader(["signature" => "bad_signature"]); + WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET); + } + + /** + * @expectedException \Stripe\Error\SignatureVerification + * @expectedExceptionMessage Timestamp outside the tolerance zone + */ + public function testTimestampOutsideTolerance() + { + $sigHeader = $this->generateHeader(["timestamp" => time() - 15]); + WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10); + } + + public function testValidHeaderAndSignature() + { + $sigHeader = $this->generateHeader(); + $this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10)); + } + + public function testHeaderContainsValidSignature() + { + $sigHeader = $this->generateHeader() . ",v1=bad_signature"; + $this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10)); + } + + public function testTimestampOffButNoTolerance() + { + $sigHeader = $this->generateHeader(["timestamp" => 12345]); + $this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET)); + } +} diff --git a/htdocs/includes/stripe/tests/StripeObjectTest.php b/htdocs/includes/stripe/tests/StripeObjectTest.php deleted file mode 100644 index 5f922228270..00000000000 --- a/htdocs/includes/stripe/tests/StripeObjectTest.php +++ /dev/null @@ -1,108 +0,0 @@ -assertSame($s['foo'], 'a'); - $this->assertTrue(isset($s['foo'])); - unset($s['foo']); - $this->assertFalse(isset($s['foo'])); - } - - public function testNormalAccessorsSemantics() - { - $s = new StripeObject(); - $s->foo = 'a'; - $this->assertSame($s->foo, 'a'); - $this->assertTrue(isset($s->foo)); - unset($s->foo); - $this->assertFalse(isset($s->foo)); - } - - public function testArrayAccessorsMatchNormalAccessors() - { - $s = new StripeObject(); - $s->foo = 'a'; - $this->assertSame($s['foo'], 'a'); - - $s['bar'] = 'b'; - $this->assertSame($s->bar, 'b'); - } - - public function testKeys() - { - $s = new StripeObject(); - $s->foo = 'a'; - $this->assertSame($s->keys(), array('foo')); - } - - public function testToArray() - { - $s = new StripeObject(); - $s->foo = 'a'; - - $converted = $s->__toArray(); - - $this->assertInternalType('array', $converted); - $this->assertArrayHasKey('foo', $converted); - $this->assertEquals('a', $converted['foo']); - } - - public function testRecursiveToArray() - { - $s = new StripeObject(); - $z = new StripeObject(); - - $s->child = $z; - $z->foo = 'a'; - - $converted = $s->__toArray(true); - - $this->assertInternalType('array', $converted); - $this->assertArrayHasKey('child', $converted); - $this->assertInternalType('array', $converted['child']); - $this->assertArrayHasKey('foo', $converted['child']); - $this->assertEquals('a', $converted['child']['foo']); - } - - public function testNonexistentProperty() - { - $s = new StripeObject(); - $this->assertNull($s->nonexistent); - } - - public function testPropertyDoesNotExists() - { - $s = new StripeObject(); - $this->assertNull($s['nonexistent']); - } - - public function testJsonEncode() - { - // We can only JSON encode our objects in PHP 5.4+. 5.3 must use ->__toJSON() - if (version_compare(phpversion(), '5.4.0', '<')) { - return; - } - - $s = new StripeObject(); - $s->foo = 'a'; - - $this->assertEquals('{"foo":"a"}', json_encode($s->__toArray())); - } - - public function testReplaceNewNestedUpdatable() - { - StripeObject::init(); // Populate the $nestedUpdatableAttributes Set - $s = new StripeObject(); - - $s->metadata = array('bar'); - $this->assertSame($s->metadata, array('bar')); - $s->metadata = array('baz', 'qux'); - $this->assertSame($s->metadata, array('baz', 'qux')); - } -} diff --git a/htdocs/includes/stripe/tests/SubscriptionItemTest.php b/htdocs/includes/stripe/tests/SubscriptionItemTest.php deleted file mode 100644 index 4e4a545a5b1..00000000000 --- a/htdocs/includes/stripe/tests/SubscriptionItemTest.php +++ /dev/null @@ -1,38 +0,0 @@ - $plan0ID, 'customer' => $customer->id)); - - $plan1ID = 'gold-' . self::generateRandomString(20); - self::retrieveOrCreatePlan($plan1ID); - - $subItem = SubscriptionItem::create(array('plan' => $plan1ID, 'subscription' => $sub->id)); - $this->assertSame($subItem->plan->id, $plan1ID); - - $subItem->quantity = 2; - $subItem->save(); - - $subItem = SubscriptionItem::retrieve($subItem->id); - $this->assertSame($subItem->quantity, 2); - - // Update the quantity parameter one more time - $subItem = SubscriptionItem::update($subItem->id, array('quantity' => 3)); - $this->assertSame($subItem->quantity, 3); - - $subItems = SubscriptionItem::all(array('subscription'=>$sub->id, 'limit'=>3)); - $this->assertSame(get_class($subItems->data[0]), 'Stripe\SubscriptionItem'); - $this->assertSame(2, count($subItems->data)); - - $subItem->delete(); - $this->assertTrue($subItem->deleted); - } -} diff --git a/htdocs/includes/stripe/tests/SubscriptionTest.php b/htdocs/includes/stripe/tests/SubscriptionTest.php deleted file mode 100644 index 08f74108061..00000000000 --- a/htdocs/includes/stripe/tests/SubscriptionTest.php +++ /dev/null @@ -1,132 +0,0 @@ -subscriptions->create(array('plan' => $planID)); - - $this->assertSame($sub->status, 'active'); - $this->assertSame($sub->plan->id, $planID); - - $sub->quantity = 2; - $sub->save(); - - $sub = $customer->subscriptions->retrieve($sub->id); - $this->assertSame($sub->status, 'active'); - $this->assertSame($sub->plan->id, $planID); - $this->assertSame($sub->quantity, 2); - - $subs = $customer->subscriptions->all(array('limit'=>3)); - $this->assertSame(get_class($subs->data[0]), 'Stripe\Subscription'); - - $sub->cancel(array('at_period_end' => true)); - - $sub = $customer->subscriptions->retrieve($sub->id); - $this->assertSame($sub->status, 'active'); - // @codingStandardsIgnoreStart - $this->assertTrue($sub->cancel_at_period_end); - // @codingStandardsIgnoreEnd - } - - public function testCreateUpdateListCancel() - { - $planID = 'gold-' . self::generateRandomString(20); - self::retrieveOrCreatePlan($planID); - - $customer = self::createTestCustomer(); - - $sub = Subscription::create(array('plan' => $planID, 'customer' => $customer->id)); - - $this->assertSame($sub->status, 'active'); - $this->assertSame($sub->plan->id, $planID); - - $sub->quantity = 2; - $sub->save(); - - $sub = Subscription::retrieve($sub->id); - $this->assertSame($sub->status, 'active'); - $this->assertSame($sub->plan->id, $planID); - $this->assertSame($sub->quantity, 2); - - // Update the quantity parameter one more time - $sub = Subscription::update($sub->id, array("quantity" => 3)); - $this->assertSame($sub->status, 'active'); - $this->assertSame($sub->plan->id, $planID); - $this->assertSame($sub->quantity, 3); - - $subs = Subscription::all(array('customer'=>$customer->id, 'plan'=>$planID, 'limit'=>3)); - $this->assertSame(get_class($subs->data[0]), 'Stripe\Subscription'); - - $sub->cancel(array('at_period_end' => true)); - - $sub = Subscription::retrieve($sub->id); - $this->assertSame($sub->status, 'active'); - $this->assertTrue($sub->cancel_at_period_end); - } - - public function testCreateUpdateListCancelWithItems() - { - $plan0ID = 'gold-' . self::generateRandomString(20); - self::retrieveOrCreatePlan($plan0ID); - - $customer = self::createTestCustomer(); - - $sub = Subscription::create(array( - 'customer' => $customer->id, - 'items' => array( - array('plan' => $plan0ID), - ), - )); - - $this->assertSame(count($sub->items->data), 1); - $this->assertSame($sub->items->data[0]->plan->id, $plan0ID); - - $plan1ID = 'gold-' . self::generateRandomString(20); - self::retrieveOrCreatePlan($plan1ID); - - $sub = Subscription::update($sub->id, array( - 'items' => array( - array('plan' => $plan1ID), - ), - )); - - $this->assertSame(count($sub->items->data), 2); - $this->assertSame($sub->items->data[0]->plan->id, $plan0ID); - $this->assertSame($sub->items->data[1]->plan->id, $plan1ID); - } - - public function testDeleteDiscount() - { - $planID = 'gold-' . self::generateRandomString(20); - self::retrieveOrCreatePlan($planID); - - $couponID = '25off-' . self::generateRandomString(20); - self::retrieveOrCreateCoupon($couponID); - - $customer = self::createTestCustomer(); - - $sub = $customer->subscriptions->create( - array( - 'plan' => $planID, - 'coupon' => $couponID - ) - ); - - $this->assertSame($sub->status, 'active'); - $this->assertSame($sub->plan->id, $planID); - $this->assertSame($sub->discount->coupon->id, $couponID); - - $sub->deleteDiscount(); - $sub = $customer->subscriptions->retrieve($sub->id); - $this->assertNull($sub->discount); - } -} diff --git a/htdocs/includes/stripe/tests/TestCase.php b/htdocs/includes/stripe/tests/TestCase.php index a1fdc94f917..840c0ec58c1 100644 --- a/htdocs/includes/stripe/tests/TestCase.php +++ b/htdocs/includes/stripe/tests/TestCase.php @@ -3,269 +3,175 @@ namespace Stripe; /** - * Base class for Stripe test cases, provides some utility methods for creating - * objects. + * Base class for Stripe test cases. */ class TestCase extends \PHPUnit_Framework_TestCase { - const API_KEY = 'tGN0bIwXnHdwOa85VABjPdSn8nWY7G7I'; + /** @var string original API base URL */ + protected $origApiBase; - private $mock; + /** @var string original API key */ + protected $origApiKey; - protected static function authorizeFromEnv() - { - $apiKey = getenv('STRIPE_API_KEY'); - if (!$apiKey) { - $apiKey = self::API_KEY; - } + /** @var string original client ID */ + protected $origClientId; - Stripe::setApiKey($apiKey); - } + /** @var string original API version */ + protected $origApiVersion; + + /** @var string original account ID */ + protected $origAccountId; + + /** @var object HTTP client mocker */ + protected $clientMock; protected function setUp() { + // Save original values so that we can restore them after running tests + $this->origApiBase = Stripe::$apiBase; + $this->origApiKey = Stripe::getApiKey(); + $this->origClientId = Stripe::getClientId(); + $this->origApiVersion = Stripe::getApiVersion(); + $this->origAccountId = Stripe::getAccountId(); + + // Set up host and credentials for stripe-mock + Stripe::$apiBase = "http://localhost:" . MOCK_PORT; + Stripe::setApiKey("sk_test_123"); + Stripe::setClientId("ca_123"); + Stripe::setApiVersion(null); + Stripe::setAccountId(null); + + // Set up the HTTP client mocker + $this->clientMock = $this->getMock('\Stripe\HttpClient\ClientInterface'); + + // By default, use the real HTTP client ApiRequestor::setHttpClient(HttpClient\CurlClient::instance()); - - // Peg the API version so that it can be varied independently of the - // one set on the test account. - Stripe::setApiVersion('2017-04-06'); - - $this->mock = null; - $this->call = 0; } - protected function mockRequest($method, $path, $params = array(), $return = array('id' => 'myId'), $rcode = 200) + protected function tearDown() { - $mock = $this->setUpMockRequest(); - $mock->expects($this->at($this->call++)) - ->method('request') - ->with(strtolower($method), 'https://api.stripe.com' . $path, $this->anything(), $params, false) - ->willReturn(array(json_encode($return), $rcode, array())); + // Restore original values + Stripe::$apiBase = $this->origApiBase; + Stripe::setApiKey($this->origApiKey); + Stripe::setClientId($this->origClientId); + Stripe::setApiVersion($this->origApiVersion); + Stripe::setAccountId($this->origAccountId); } - private function setUpMockRequest() - { - if (!$this->mock) { - self::authorizeFromEnv(); - $this->mock = $this->getMock('\Stripe\HttpClient\ClientInterface'); - ApiRequestor::setHttpClient($this->mock); + /** + * Sets up a request expectation with the provided parameters. The request + * will actually go through and be emitted. + * + * @param string $method HTTP method (e.g. 'post', 'get', etc.) + * @param string $path relative path (e.g. '/v1/charges') + * @param array|null $params array of parameters. If null, parameters will + * not be checked. + * @param string[]|null $headers array of headers. Does not need to be + * exhaustive. If null, headers are not checked. + * @param bool $hasFile Whether the request parameters contains a file. + * Defaults to false. + */ + protected function expectsRequest( + $method, + $path, + $params = null, + $headers = null, + $hasFile = false + ) { + $this->prepareRequestMock($method, $path, $params, $headers, $hasFile) + ->will($this->returnCallback( + function ($method, $absUrl, $headers, $params, $hasFile) { + $curlClient = HttpClient\CurlClient::instance(); + ApiRequestor::setHttpClient($curlClient); + return $curlClient->request($method, $absUrl, $headers, $params, $hasFile); + } + )); + } + + /** + * Sets up a request expectation with the provided parameters. The request + * will not actually be emitted, instead the provided response parameters + * will be returned. + * + * @param string $method HTTP method (e.g. 'post', 'get', etc.) + * @param string $path relative path (e.g. '/v1/charges') + * @param array|null $params array of parameters. If null, parameters will + * not be checked. + * @param string[]|null $headers array of headers. Does not need to be + * exhaustive. If null, headers are not checked. + * @param bool $hasFile Whether the request parameters contains a file. + * Defaults to false. + * @param array $response + * @param integer $rcode + * @param string|null $base + * + * @return array + */ + protected function stubRequest( + $method, + $path, + $params = null, + $headers = null, + $hasFile = false, + $response = [], + $rcode = 200, + $base = null + ) { + $this->prepareRequestMock($method, $path, $params, $headers, $hasFile, $base) + ->willReturn([json_encode($response), $rcode, []]); + } + + /** + * Prepares the client mocker for an invocation of the `request` method. + * This helper method is used by both `expectsRequest` and `stubRequest` to + * prepare the client mocker to expect an invocation of the `request` method + * with the provided arguments. + * + * @param string $method HTTP method (e.g. 'post', 'get', etc.) + * @param string $path relative path (e.g. '/v1/charges') + * @param array|null $params array of parameters. If null, parameters will + * not be checked. + * @param string[]|null $headers array of headers. Does not need to be + * exhaustive. If null, headers are not checked. + * @param bool $hasFile Whether the request parameters contains a file. + * Defaults to false. + * @param string|null $base base URL (e.g. 'https://api.stripe.com') + * + * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker + */ + private function prepareRequestMock( + $method, + $path, + $params = null, + $headers = null, + $hasFile = false, + $base = null + ) { + ApiRequestor::setHttpClient($this->clientMock); + + if ($base === null) { + $base = Stripe::$apiBase; } - return $this->mock; - } + $absUrl = $base . $path; - /** - * Create a valid test charge. - */ - protected static function createTestCharge(array $attributes = array()) - { - self::authorizeFromEnv(); - - return Charge::create( - $attributes + array( - 'amount' => 2000, - 'currency' => 'usd', - 'description' => 'Charge for test@example.com', - 'card' => array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 3, - ), - ) - ); - } - - /** - * Create a valid test transfer. - */ - protected static function createTestTransfer(array $attributes = array(), $opts = null) - { - self::authorizeFromEnv(); - - $recipient = self::createTestRecipient(); - - return Transfer::create( - $attributes + array( - 'amount' => 2000, - 'currency' => 'usd', - 'description' => 'Transfer to test@example.com', - 'recipient' => $recipient->id - ), - $opts - ); - } - - /** - * Create a valid test customer. - */ - protected static function createTestCustomer(array $attributes = array()) - { - self::authorizeFromEnv(); - - return Customer::create( - $attributes + array( - 'card' => array( - 'number' => '4242424242424242', - 'exp_month' => 5, - 'exp_year' => date('Y') + 3, - ), - ) - ); - } - - /** - * Create a valid test recipient - */ - protected static function createTestRecipient(array $attributes = array()) - { - self::authorizeFromEnv(); - - return Recipient::create( - $attributes + array( - 'name' => 'PHP Test', - 'type' => 'individual', - 'tax_id' => '000000000', - 'bank_account' => array( - 'country' => 'US', - 'routing_number' => '110000000', - 'account_number' => '000123456789' - ), - ) - ); - } - - /** - * Create a test account - */ - protected static function createTestAccount(array $attributes = array()) - { - self::authorizeFromEnv(); - - return Account::create( - $attributes + array( - 'managed' => false, - 'country' => 'US', - 'email' => self::generateRandomEmail(), - ) - ); - } - - /** - * Create a test account - */ - protected static function createTestManagedAccount(array $attributes = array()) - { - self::authorizeFromEnv(); - - return Account::create( - $attributes + array( - 'managed' => true, - 'country' => 'US', - 'external_account' => array( - 'object' => 'bank_account', - 'country' => 'US', - 'currency' => 'usd', - 'routing_number' => '110000000', - 'account_number' => '000123456789' - ), - 'legal_entity' => array( - 'type' => 'individual', - 'personal_id_number' => '000000000', - 'type' => 'individual', - 'dob' => array('year' => '1980', 'month' => '01', 'day' => '01'), - 'first_name' => 'John', - 'last_name' => 'Doe', - 'address' => array( - 'line1' => '1234 Main Street', - 'postal_code' => '94110', - 'city' => 'San Francisco' - ), - 'personal_address' => array( - 'line1' => '1234 Main Street', - 'postal_code' => '94110', - 'city' => 'San Francisco' - ) - ), - 'tos_acceptance' => array('date' => time(), 'ip' => '127.0.0.1') - ) - ); - } - - /** - * Verify that a plan with a given ID exists, or create a new one if it does - * not. - */ - protected static function retrieveOrCreatePlan($id) - { - self::authorizeFromEnv(); - - try { - $plan = Plan::retrieve($id); - } catch (Error\InvalidRequest $exception) { - $plan = Plan::create( - array( - 'id' => $id, - 'amount' => 0, - 'currency' => 'usd', - 'interval' => 'month', - 'name' => 'Gold Test Plan', - ) + return $this->clientMock + ->expects($this->once()) + ->method('request') + ->with( + $this->identicalTo(strtolower($method)), + $this->identicalTo($absUrl), + // for headers, we only check that all of the headers provided in $headers are + // present in the list of headers of the actual request + $headers === null ? $this->anything() : $this->callback(function ($array) use ($headers) { + foreach ($headers as $header) { + if (!in_array($header, $array)) { + return false; + } + } + return true; + }), + $params === null ? $this->anything() : $this->identicalTo($params), + $this->identicalTo($hasFile) ); - } - } - - /** - * Verify that a coupon with a given ID exists, or create a new one if it - * does not. - */ - protected static function retrieveOrCreateCoupon($id) - { - self::authorizeFromEnv(); - - try { - $coupon = Coupon::retrieve($id); - } catch (Error\InvalidRequest $exception) { - $coupon = Coupon::create( - array( - 'id' => $id, - 'duration' => 'forever', - 'percent_off' => 25, - ) - ); - } - } - - /** - * Generate a semi-random string - */ - protected static function generateRandomString($length = 24) - { - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < $length; $i++) { - $randomString .= $characters[rand(0, $charactersLength - 1)]; - } - return $randomString; - } - - /** - * Generate a semi-random email. - */ - protected static function generateRandomEmail() - { - return 'dev-platform-bots+php-'.self::generateRandomString(12).'@stripe.com'; - } - - protected static function createTestBitcoinReceiver($email) - { - $receiver = BitcoinReceiver::create( - array( - 'amount' => 100, - 'currency' => 'usd', - 'description' => 'some details', - 'email' => $email - ) - ); - return $receiver; } } diff --git a/htdocs/includes/stripe/tests/ThreeDSecureTest.php b/htdocs/includes/stripe/tests/ThreeDSecureTest.php deleted file mode 100644 index ebdd62335ee..00000000000 --- a/htdocs/includes/stripe/tests/ThreeDSecureTest.php +++ /dev/null @@ -1,46 +0,0 @@ -mockRequest( - 'GET', - '/v1/3d_secure/tdsrc_test', - array(), - array( - 'id' => 'tdsrc_test', - 'object' => 'three_d_secure' - ) - ); - $three_d_secure = ThreeDSecure::retrieve('tdsrc_test'); - $this->assertSame($three_d_secure->id, 'tdsrc_test'); - } - - public function testCreate() - { - $this->mockRequest( - 'POST', - '/v1/3d_secure', - array( - 'card' => 'tok_test', - 'amount' => 1500, - 'currency' => 'usd', - 'return_url' => 'https://example.org/3d-secure-result' - ), - array( - 'id' => 'tdsrc_test', - 'object' => 'three_d_secure' - ) - ); - $three_d_secure = ThreeDSecure::create(array( - 'card' => 'tok_test', - 'amount' => 1500, - 'currency' => 'usd', - 'return_url' => 'https://example.org/3d-secure-result' - )); - $this->assertSame($three_d_secure->id, 'tdsrc_test'); - } -} diff --git a/htdocs/includes/stripe/tests/TokenTest.php b/htdocs/includes/stripe/tests/TokenTest.php deleted file mode 100644 index 60ec76a1544..00000000000 --- a/htdocs/includes/stripe/tests/TokenTest.php +++ /dev/null @@ -1,13 +0,0 @@ -assertSame(Token::classUrl(), '/v1/tokens'); - $token = new Token('abcd/efgh'); - $this->assertSame($token->instanceUrl(), '/v1/tokens/abcd%2Fefgh'); - } -} diff --git a/htdocs/includes/stripe/tests/TransferReversalTest.php b/htdocs/includes/stripe/tests/TransferReversalTest.php deleted file mode 100644 index 86d746650f5..00000000000 --- a/htdocs/includes/stripe/tests/TransferReversalTest.php +++ /dev/null @@ -1,19 +0,0 @@ - '2017-02-14'); - - public function testList() - { - $transfer = self::createTestTransfer(array(), $this->opts); - $all = $transfer->reversals->all(); - $this->assertSame(false, $all['has_more']); - $this->assertSame(0, count($all->data)); - } -} diff --git a/htdocs/includes/stripe/tests/TransferTest.php b/htdocs/includes/stripe/tests/TransferTest.php deleted file mode 100644 index f4fd347dc4e..00000000000 --- a/htdocs/includes/stripe/tests/TransferTest.php +++ /dev/null @@ -1,46 +0,0 @@ - '2017-02-14'); - - public function testCreate() - { - $transfer = self::createTestTransfer(array(), $this->opts); - $this->assertSame('transfer', $transfer->object); - } - - public function testRetrieve() - { - $transfer = self::createTestTransfer(array(), $this->opts); - $reloaded = Transfer::retrieve($transfer->id, $this->opts); - $this->assertSame($reloaded->id, $transfer->id); - } - - public function testTransferUpdateMetadata() - { - $transfer = self::createTestTransfer(array(), $this->opts); - - $transfer->metadata['test'] = 'foo bar'; - $transfer->save(); - - $updatedTransfer = Transfer::retrieve($transfer->id, $this->opts); - $this->assertSame('foo bar', $updatedTransfer->metadata['test']); - } - - public function testTransferUpdateMetadataAll() - { - $transfer = self::createTestTransfer(array(), $this->opts); - - $transfer->metadata = array('test' => 'foo bar'); - $transfer->save(); - - $updatedTransfer = Transfer::retrieve($transfer->id, $this->opts); - $this->assertSame('foo bar', $updatedTransfer->metadata['test']); - } -} diff --git a/htdocs/includes/stripe/tests/UtilTest.php b/htdocs/includes/stripe/tests/UtilTest.php deleted file mode 100644 index a1206d698f7..00000000000 --- a/htdocs/includes/stripe/tests/UtilTest.php +++ /dev/null @@ -1,45 +0,0 @@ -assertTrue(Util\Util::isList($list)); - - $notlist = array(5, 'nstaoush', array(), 'bar' => 'baz'); - $this->assertFalse(Util\Util::isList($notlist)); - } - - public function testThatPHPHasValueSemanticsForArrays() - { - $original = array('php-arrays' => 'value-semantics'); - $derived = $original; - $derived['php-arrays'] = 'reference-semantics'; - - $this->assertSame('value-semantics', $original['php-arrays']); - } - - public function testConvertStripeObjectToArrayIncludesId() - { - $customer = self::createTestCustomer(); - $this->assertTrue(array_key_exists("id", $customer->__toArray(true))); - } - - public function testUtf8() - { - // UTF-8 string - $x = "\xc3\xa9"; - $this->assertSame(Util\Util::utf8($x), $x); - - // Latin-1 string - $x = "\xe9"; - $this->assertSame(Util\Util::utf8($x), "\xc3\xa9"); - - // Not a string - $x = true; - $this->assertSame(Util\Util::utf8($x), $x); - } -} diff --git a/htdocs/includes/stripe/tests/bootstrap.no_autoload.php b/htdocs/includes/stripe/tests/bootstrap.no_autoload.php index 7358058780a..7011a3f4782 100644 --- a/htdocs/includes/stripe/tests/bootstrap.no_autoload.php +++ b/htdocs/includes/stripe/tests/bootstrap.no_autoload.php @@ -1,4 +1,5 @@ $fp, + CURLOPT_TIMEOUT => 3600, + CURLOPT_URL => 'https://curl.haxx.se/ca/cacert.pem', +); + +$ch = curl_init(); +curl_setopt_array($ch, $options); +curl_exec($ch); +curl_close($ch); +fclose($fp); diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailLexer.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailLexer.php new file mode 100644 index 00000000000..882c968154d --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailLexer.php @@ -0,0 +1,221 @@ + self::S_OPENPARENTHESIS, + ')' => self::S_CLOSEPARENTHESIS, + '<' => self::S_LOWERTHAN, + '>' => self::S_GREATERTHAN, + '[' => self::S_OPENBRACKET, + ']' => self::S_CLOSEBRACKET, + ':' => self::S_COLON, + ';' => self::S_SEMICOLON, + '@' => self::S_AT, + '\\' => self::S_BACKSLASH, + '/' => self::S_SLASH, + ',' => self::S_COMMA, + '.' => self::S_DOT, + '"' => self::S_DQUOTE, + '-' => self::S_HYPHEN, + '::' => self::S_DOUBLECOLON, + ' ' => self::S_SP, + "\t" => self::S_HTAB, + "\r" => self::S_CR, + "\n" => self::S_LF, + "\r\n" => self::CRLF, + 'IPv6' => self::S_IPV6TAG, + '{' => self::S_OPENQBRACKET, + '}' => self::S_CLOSEQBRACKET, + '' => self::S_EMPTY, + '\0' => self::C_NUL, + ); + + protected $hasInvalidTokens = false; + + protected $previous; + + public function reset() + { + $this->hasInvalidTokens = false; + parent::reset(); + } + + public function hasInvalidTokens() + { + return $this->hasInvalidTokens; + } + + /** + * @param $type + * @throws \UnexpectedValueException + * @return boolean + */ + public function find($type) + { + $search = clone $this; + $search->skipUntil($type); + + if (!$search->lookahead) { + throw new \UnexpectedValueException($type . ' not found'); + } + return true; + } + + /** + * getPrevious + * + * @return array token + */ + public function getPrevious() + { + return $this->previous; + } + + /** + * moveNext + * + * @return boolean + */ + public function moveNext() + { + $this->previous = $this->token; + + return parent::moveNext(); + } + + /** + * Lexical catchable patterns. + * + * @return string[] + */ + protected function getCatchablePatterns() + { + return array( + '[a-zA-Z_]+[46]?', //ASCII and domain literal + '[^\x00-\x7F]', //UTF-8 + '[0-9]+', + '\r\n', + '::', + '\s+?', + '.', + ); + } + + /** + * Lexical non-catchable patterns. + * + * @return string[] + */ + protected function getNonCatchablePatterns() + { + return array('[\xA0-\xff]+'); + } + + /** + * Retrieve token type. Also processes the token value if necessary. + * + * @param string $value + * @throws \InvalidArgumentException + * @return integer + */ + protected function getType(&$value) + { + if ($this->isNullType($value)) { + return self::C_NUL; + } + + if ($this->isValid($value)) { + return $this->charValue[$value]; + } + + if ($this->isUTF8Invalid($value)) { + $this->hasInvalidTokens = true; + return self::INVALID; + } + + return self::GENERIC; + } + + protected function isValid($value) + { + if (isset($this->charValue[$value])) { + return true; + } + + return false; + } + + /** + * @param $value + * @return bool + */ + protected function isNullType($value) + { + if ($value === "\0") { + return true; + } + + return false; + } + + /** + * @param $value + * @return bool + */ + protected function isUTF8Invalid($value) + { + if (preg_match('/\p{Cc}+/u', $value)) { + return true; + } + + return false; + } + + protected function getModifiers() + { + return 'iu'; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailParser.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailParser.php new file mode 100644 index 00000000000..d0627d82410 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailParser.php @@ -0,0 +1,104 @@ + + */ +class EmailParser +{ + const EMAIL_MAX_LENGTH = 254; + + protected $warnings; + protected $domainPart = ''; + protected $localPart = ''; + protected $lexer; + protected $localPartParser; + protected $domainPartParser; + + public function __construct(EmailLexer $lexer) + { + $this->lexer = $lexer; + $this->localPartParser = new LocalPart($this->lexer); + $this->domainPartParser = new DomainPart($this->lexer); + $this->warnings = new \SplObjectStorage(); + } + + /** + * @param $str + * @return array + */ + public function parse($str) + { + $this->lexer->setInput($str); + + if (!$this->hasAtToken()) { + throw new NoLocalPart(); + } + + + $this->localPartParser->parse($str); + $this->domainPartParser->parse($str); + + $this->setParts($str); + + if ($this->lexer->hasInvalidTokens()) { + throw new ExpectingATEXT(); + } + + return array('local' => $this->localPart, 'domain' => $this->domainPart); + } + + public function getWarnings() + { + $localPartWarnings = $this->localPartParser->getWarnings(); + $domainPartWarnings = $this->domainPartParser->getWarnings(); + $this->warnings = array_merge($localPartWarnings, $domainPartWarnings); + + $this->addLongEmailWarning($this->localPart, $this->domainPart); + + return $this->warnings; + } + + public function getParsedDomainPart() + { + return $this->domainPart; + } + + protected function setParts($email) + { + $parts = explode('@', $email); + $this->domainPart = $this->domainPartParser->getDomainPart(); + $this->localPart = $parts[0]; + } + + protected function hasAtToken() + { + $this->lexer->moveNext(); + $this->lexer->moveNext(); + if ($this->lexer->token['type'] === EmailLexer::S_AT) { + return false; + } + + return true; + } + + /** + * @param string $localPart + * @param string $parsedDomainPart + */ + protected function addLongEmailWarning($localPart, $parsedDomainPart) + { + if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) { + $this->warnings[EmailTooLong::CODE] = new EmailTooLong(); + } + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailValidator.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailValidator.php new file mode 100644 index 00000000000..44b4b93c6bb --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/EmailValidator.php @@ -0,0 +1,67 @@ +lexer = new EmailLexer(); + } + + /** + * @param $email + * @param EmailValidation $emailValidation + * @return bool + */ + public function isValid($email, EmailValidation $emailValidation) + { + $isValid = $emailValidation->isValid($email, $this->lexer); + $this->warnings = $emailValidation->getWarnings(); + $this->error = $emailValidation->getError(); + + return $isValid; + } + + /** + * @return boolean + */ + public function hasWarnings() + { + return !empty($this->warnings); + } + + /** + * @return array + */ + public function getWarnings() + { + return $this->warnings; + } + + /** + * @return InvalidEmail + */ + public function getError() + { + return $this->error; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Exception/AtextAfterCFWS.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Exception/AtextAfterCFWS.php new file mode 100644 index 00000000000..97f41a2caae --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Exception/AtextAfterCFWS.php @@ -0,0 +1,9 @@ +lexer->moveNext(); + + if ($this->lexer->token['type'] === EmailLexer::S_DOT) { + throw new DotAtStart(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) { + throw new NoDomainPart(); + } + if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) { + throw new DomainHyphened(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { + $this->warnings[DeprecatedComment::CODE] = new DeprecatedComment(); + $this->parseDomainComments(); + } + + $domain = $this->doParseDomainPart(); + + $prev = $this->lexer->getPrevious(); + $length = strlen($domain); + + if ($prev['type'] === EmailLexer::S_DOT) { + throw new DotAtEnd(); + } + if ($prev['type'] === EmailLexer::S_HYPHEN) { + throw new DomainHyphened(); + } + if ($length > self::DOMAIN_MAX_LENGTH) { + $this->warnings[DomainTooLong::CODE] = new DomainTooLong(); + } + if ($prev['type'] === EmailLexer::S_CR) { + throw new CRLFAtTheEnd(); + } + $this->domainPart = $domain; + } + + public function getDomainPart() + { + return $this->domainPart; + } + + public function checkIPV6Tag($addressLiteral, $maxGroups = 8) + { + $prev = $this->lexer->getPrevious(); + if ($prev['type'] === EmailLexer::S_COLON) { + $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd(); + } + + $IPv6 = substr($addressLiteral, 5); + //Daniel Marschall's new IPv6 testing strategy + $matchesIP = explode(':', $IPv6); + $groupCount = count($matchesIP); + $colons = strpos($IPv6, '::'); + + if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) { + $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar(); + } + + if ($colons === false) { + // We need exactly the right number of groups + if ($groupCount !== $maxGroups) { + $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount(); + } + return; + } + + if ($colons !== strrpos($IPv6, '::')) { + $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon(); + return; + } + + if ($colons === 0 || $colons === (strlen($IPv6) - 2)) { + // RFC 4291 allows :: at the start or end of an address + //with 7 other groups in addition + ++$maxGroups; + } + + if ($groupCount > $maxGroups) { + $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups(); + } elseif ($groupCount === $maxGroups) { + $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated(); + } + } + + protected function doParseDomainPart() + { + $domain = ''; + $openedParenthesis = 0; + do { + $prev = $this->lexer->getPrevious(); + + $this->checkNotAllowedChars($this->lexer->token); + + if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { + $this->parseComments(); + $openedParenthesis += $this->getOpenedParenthesis(); + $this->lexer->moveNext(); + $tmpPrev = $this->lexer->getPrevious(); + if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) { + $openedParenthesis--; + } + } + if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { + if ($openedParenthesis === 0) { + throw new UnopenedComment(); + } else { + $openedParenthesis--; + } + } + + $this->checkConsecutiveDots(); + $this->checkDomainPartExceptions($prev); + + if ($this->hasBrackets()) { + $this->parseDomainLiteral(); + } + + $this->checkLabelLength($prev); + + if ($this->isFWS()) { + $this->parseFWS(); + } + + $domain .= $this->lexer->token['value']; + $this->lexer->moveNext(); + } while ($this->lexer->token); + + return $domain; + } + + private function checkNotAllowedChars($token) + { + $notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true]; + if (isset($notAllowed[$token['type']])) { + throw new CharNotAllowed(); + } + } + + protected function parseDomainLiteral() + { + if ($this->lexer->isNextToken(EmailLexer::S_COLON)) { + $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart(); + } + if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) { + $lexer = clone $this->lexer; + $lexer->moveNext(); + if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) { + $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart(); + } + } + + return $this->doParseDomainLiteral(); + } + + protected function doParseDomainLiteral() + { + $IPv6TAG = false; + $addressLiteral = ''; + do { + if ($this->lexer->token['type'] === EmailLexer::C_NUL) { + throw new ExpectingDTEXT(); + } + + if ($this->lexer->token['type'] === EmailLexer::INVALID || + $this->lexer->token['type'] === EmailLexer::C_DEL || + $this->lexer->token['type'] === EmailLexer::S_LF + ) { + $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT(); + } + + if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) { + throw new ExpectingDTEXT(); + } + + if ($this->lexer->isNextTokenAny( + array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF) + )) { + $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); + $this->parseFWS(); + } + + if ($this->lexer->isNextToken(EmailLexer::S_CR)) { + throw new CRNoLF(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) { + $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT(); + $addressLiteral .= $this->lexer->token['value']; + $this->lexer->moveNext(); + $this->validateQuotedPair(); + } + if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) { + $IPv6TAG = true; + } + if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) { + break; + } + + $addressLiteral .= $this->lexer->token['value']; + + } while ($this->lexer->moveNext()); + + $addressLiteral = str_replace('[', '', $addressLiteral); + $addressLiteral = $this->checkIPV4Tag($addressLiteral); + + if (false === $addressLiteral) { + return $addressLiteral; + } + + if (!$IPv6TAG) { + $this->warnings[DomainLiteral::CODE] = new DomainLiteral(); + return $addressLiteral; + } + + $this->warnings[AddressLiteral::CODE] = new AddressLiteral(); + + $this->checkIPV6Tag($addressLiteral); + + return $addressLiteral; + } + + protected function checkIPV4Tag($addressLiteral) + { + $matchesIP = array(); + + // Extract IPv4 part from the end of the address-literal (if there is one) + if (preg_match( + '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', + $addressLiteral, + $matchesIP + ) > 0 + ) { + $index = strrpos($addressLiteral, $matchesIP[0]); + if ($index === 0) { + $this->warnings[AddressLiteral::CODE] = new AddressLiteral(); + return false; + } + // Convert IPv4 part to IPv6 format for further testing + $addressLiteral = substr($addressLiteral, 0, $index) . '0:0'; + } + + return $addressLiteral; + } + + protected function checkDomainPartExceptions($prev) + { + $invalidDomainTokens = array( + EmailLexer::S_DQUOTE => true, + EmailLexer::S_SEMICOLON => true, + EmailLexer::S_GREATERTHAN => true, + EmailLexer::S_LOWERTHAN => true, + ); + + if (isset($invalidDomainTokens[$this->lexer->token['type']])) { + throw new ExpectingATEXT(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_COMMA) { + throw new CommaInDomain(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_AT) { + throw new ConsecutiveAt(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) { + throw new ExpectingATEXT(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) { + throw new DomainHyphened(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH + && $this->lexer->isNextToken(EmailLexer::GENERIC)) { + throw new ExpectingATEXT(); + } + } + + protected function hasBrackets() + { + if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) { + return false; + } + + try { + $this->lexer->find(EmailLexer::S_CLOSEBRACKET); + } catch (\RuntimeException $e) { + throw new ExpectingDomainLiteralClose(); + } + + return true; + } + + protected function checkLabelLength($prev) + { + if ($this->lexer->token['type'] === EmailLexer::S_DOT && + $prev['type'] === EmailLexer::GENERIC && + strlen($prev['value']) > 63 + ) { + $this->warnings[LabelTooLong::CODE] = new LabelTooLong(); + } + } + + protected function parseDomainComments() + { + $this->isUnclosedComment(); + while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { + $this->warnEscaping(); + $this->lexer->moveNext(); + } + + $this->lexer->moveNext(); + if ($this->lexer->isNextToken(EmailLexer::S_DOT)) { + throw new ExpectingATEXT(); + } + } + + protected function addTLDWarnings() + { + if ($this->warnings[DomainLiteral::CODE]) { + $this->warnings[TLD::CODE] = new TLD(); + } + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/LocalPart.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/LocalPart.php new file mode 100644 index 00000000000..8ab16ab4a1f --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/LocalPart.php @@ -0,0 +1,138 @@ +lexer->token['type'] !== EmailLexer::S_AT && $this->lexer->token) { + if ($this->lexer->token['type'] === EmailLexer::S_DOT && !$this->lexer->getPrevious()) { + throw new DotAtStart(); + } + + $closingQuote = $this->checkDQUOTE($closingQuote); + if ($closingQuote && $parseDQuote) { + $parseDQuote = $this->parseDoubleQuote(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { + $this->parseComments(); + $openedParenthesis += $this->getOpenedParenthesis(); + } + if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { + if ($openedParenthesis === 0) { + throw new UnopenedComment(); + } else { + $openedParenthesis--; + } + } + + $this->checkConsecutiveDots(); + + if ($this->lexer->token['type'] === EmailLexer::S_DOT && + $this->lexer->isNextToken(EmailLexer::S_AT) + ) { + throw new DotAtEnd(); + } + + $this->warnEscaping(); + $this->isInvalidToken($this->lexer->token, $closingQuote); + + if ($this->isFWS()) { + $this->parseFWS(); + } + + $this->lexer->moveNext(); + } + + $prev = $this->lexer->getPrevious(); + if (strlen($prev['value']) > LocalTooLong::LOCAL_PART_LENGTH) { + $this->warnings[LocalTooLong::CODE] = new LocalTooLong(); + } + } + + protected function parseDoubleQuote() + { + $parseAgain = true; + $special = array( + EmailLexer::S_CR => true, + EmailLexer::S_HTAB => true, + EmailLexer::S_LF => true + ); + + $invalid = array( + EmailLexer::C_NUL => true, + EmailLexer::S_HTAB => true, + EmailLexer::S_CR => true, + EmailLexer::S_LF => true + ); + $setSpecialsWarning = true; + + $this->lexer->moveNext(); + + while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) { + $parseAgain = false; + if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) { + $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); + $setSpecialsWarning = false; + } + if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) { + $this->lexer->moveNext(); + } + + $this->lexer->moveNext(); + + if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) { + throw new ExpectingATEXT(); + } + } + + $prev = $this->lexer->getPrevious(); + + if ($prev['type'] === EmailLexer::S_BACKSLASH) { + if (!$this->checkDQUOTE(false)) { + throw new UnclosedQuotedString(); + } + } + + if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) { + throw new ExpectingAT(); + } + + return $parseAgain; + } + + protected function isInvalidToken($token, $closingQuote) + { + $forbidden = array( + EmailLexer::S_COMMA, + EmailLexer::S_CLOSEBRACKET, + EmailLexer::S_OPENBRACKET, + EmailLexer::S_GREATERTHAN, + EmailLexer::S_LOWERTHAN, + EmailLexer::S_COLON, + EmailLexer::S_SEMICOLON, + EmailLexer::INVALID + ); + + if (in_array($token['type'], $forbidden) && !$closingQuote) { + throw new ExpectingATEXT(); + } + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/Parser.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/Parser.php new file mode 100644 index 00000000000..e5042e1a859 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Parser/Parser.php @@ -0,0 +1,215 @@ +lexer = $lexer; + } + + public function getWarnings() + { + return $this->warnings; + } + + abstract public function parse($str); + + /** @return int */ + public function getOpenedParenthesis() + { + return $this->openedParenthesis; + } + + /** + * validateQuotedPair + */ + protected function validateQuotedPair() + { + if (!($this->lexer->token['type'] === EmailLexer::INVALID + || $this->lexer->token['type'] === EmailLexer::C_DEL)) { + throw new ExpectedQPair(); + } + + $this->warnings[QuotedPart::CODE] = + new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); + } + + protected function parseComments() + { + $this->openedParenthesis = 1; + $this->isUnclosedComment(); + $this->warnings[Comment::CODE] = new Comment(); + while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { + if ($this->lexer->isNextToken(EmailLexer::S_OPENPARENTHESIS)) { + $this->openedParenthesis++; + } + $this->warnEscaping(); + $this->lexer->moveNext(); + } + + $this->lexer->moveNext(); + if ($this->lexer->isNextTokenAny(array(EmailLexer::GENERIC, EmailLexer::S_EMPTY))) { + throw new ExpectingATEXT(); + } + + if ($this->lexer->isNextToken(EmailLexer::S_AT)) { + $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); + } + } + + protected function isUnclosedComment() + { + try { + $this->lexer->find(EmailLexer::S_CLOSEPARENTHESIS); + return true; + } catch (\RuntimeException $e) { + throw new UnclosedComment(); + } + } + + protected function parseFWS() + { + $previous = $this->lexer->getPrevious(); + + $this->checkCRLFInFWS(); + + if ($this->lexer->token['type'] === EmailLexer::S_CR) { + throw new CRNoLF(); + } + + if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] !== EmailLexer::S_AT) { + throw new AtextAfterCFWS(); + } + + if ($this->lexer->token['type'] === EmailLexer::S_LF || $this->lexer->token['type'] === EmailLexer::C_NUL) { + throw new ExpectingCTEXT(); + } + + if ($this->lexer->isNextToken(EmailLexer::S_AT) || $previous['type'] === EmailLexer::S_AT) { + $this->warnings[CFWSNearAt::CODE] = new CFWSNearAt(); + } else { + $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); + } + } + + protected function checkConsecutiveDots() + { + if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) { + throw new ConsecutiveDot(); + } + } + + protected function isFWS() + { + if ($this->escaped()) { + return false; + } + + if ($this->lexer->token['type'] === EmailLexer::S_SP || + $this->lexer->token['type'] === EmailLexer::S_HTAB || + $this->lexer->token['type'] === EmailLexer::S_CR || + $this->lexer->token['type'] === EmailLexer::S_LF || + $this->lexer->token['type'] === EmailLexer::CRLF + ) { + return true; + } + + return false; + } + + protected function escaped() + { + $previous = $this->lexer->getPrevious(); + + if ($previous['type'] === EmailLexer::S_BACKSLASH + && + $this->lexer->token['type'] !== EmailLexer::GENERIC + ) { + return true; + } + + return false; + } + + protected function warnEscaping() + { + if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) { + return false; + } + + if ($this->lexer->isNextToken(EmailLexer::GENERIC)) { + throw new ExpectingATEXT(); + } + + if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) { + return false; + } + + $this->warnings[QuotedPart::CODE] = + new QuotedPart($this->lexer->getPrevious()['type'], $this->lexer->token['type']); + return true; + + } + + protected function checkDQUOTE($hasClosingQuote) + { + if ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE) { + return $hasClosingQuote; + } + if ($hasClosingQuote) { + return $hasClosingQuote; + } + $previous = $this->lexer->getPrevious(); + if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { + throw new ExpectingATEXT(); + } + + try { + $this->lexer->find(EmailLexer::S_DQUOTE); + $hasClosingQuote = true; + } catch (\Exception $e) { + throw new UnclosedQuotedString(); + } + $this->warnings[QuotedString::CODE] = new QuotedString($previous['value'], $this->lexer->token['value']); + + return $hasClosingQuote; + } + + protected function checkCRLFInFWS() + { + if ($this->lexer->token['type'] !== EmailLexer::CRLF) { + return; + } + + if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { + throw new CRLFX2(); + } + + if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB))) { + throw new CRLFAtTheEnd(); + } + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/DNSCheckValidation.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/DNSCheckValidation.php new file mode 100644 index 00000000000..ecca1dd976a --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/DNSCheckValidation.php @@ -0,0 +1,61 @@ +checkDNS($host); + } + + public function getError() + { + return $this->error; + } + + public function getWarnings() + { + return $this->warnings; + } + + protected function checkDNS($host) + { + $host = rtrim($host, '.') . '.'; + + $Aresult = true; + $MXresult = checkdnsrr($host, 'MX'); + + if (!$MXresult) { + $this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord(); + $Aresult = checkdnsrr($host, 'A') || checkdnsrr($host, 'AAAA'); + if (!$Aresult) { + $this->error = new NoDNSRecord(); + } + } + return $MXresult || $Aresult; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/EmailValidation.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/EmailValidation.php new file mode 100644 index 00000000000..d5a015be574 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/EmailValidation.php @@ -0,0 +1,34 @@ +errors = $errors; + parent::__construct(); + } + + public function getErrors() + { + return $this->errors; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/MultipleValidationWithAnd.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/MultipleValidationWithAnd.php new file mode 100644 index 00000000000..43fa42a64b7 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/MultipleValidationWithAnd.php @@ -0,0 +1,110 @@ +validations = $validations; + $this->mode = $mode; + } + + /** + * {@inheritdoc} + */ + public function isValid($email, EmailLexer $emailLexer) + { + $result = true; + $errors = []; + foreach ($this->validations as $validation) { + $emailLexer->reset(); + $result = $result && $validation->isValid($email, $emailLexer); + $this->warnings = array_merge($this->warnings, $validation->getWarnings()); + $errors = $this->addNewError($validation->getError(), $errors); + + if ($this->shouldStop($result)) { + break; + } + } + + if (!empty($errors)) { + $this->error = new MultipleErrors($errors); + } + + return $result; + } + + private function addNewError($possibleError, array $errors) + { + if (null !== $possibleError) { + $errors[] = $possibleError; + } + + return $errors; + } + + private function shouldStop($result) + { + return !$result && $this->mode === self::STOP_ON_ERROR; + } + + /** + * {@inheritdoc} + */ + public function getError() + { + return $this->error; + } + + /** + * {@inheritdoc} + */ + public function getWarnings() + { + return $this->warnings; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/NoRFCWarningsValidation.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/NoRFCWarningsValidation.php new file mode 100644 index 00000000000..e4bf0cc4df9 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/NoRFCWarningsValidation.php @@ -0,0 +1,41 @@ +getWarnings())) { + return true; + } + + $this->error = new RFCWarnings(); + + return false; + } + + /** + * {@inheritdoc} + */ + public function getError() + { + return $this->error ?: parent::getError(); + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/RFCValidation.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/RFCValidation.php new file mode 100644 index 00000000000..c4ffe35034b --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/RFCValidation.php @@ -0,0 +1,49 @@ +parser = new EmailParser($emailLexer); + try { + $this->parser->parse((string)$email); + } catch (InvalidEmail $invalid) { + $this->error = $invalid; + return false; + } + + $this->warnings = $this->parser->getWarnings(); + return true; + } + + public function getError() + { + return $this->error; + } + + public function getWarnings() + { + return $this->warnings; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/SpoofCheckValidation.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/SpoofCheckValidation.php new file mode 100644 index 00000000000..dd270556e8a --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Validation/SpoofCheckValidation.php @@ -0,0 +1,45 @@ +setChecks(Spoofchecker::SINGLE_SCRIPT); + + if ($checker->isSuspicious($email)) { + $this->error = new SpoofEmail(); + } + + return $this->error === null; + } + + public function getError() + { + return $this->error; + } + + public function getWarnings() + { + return []; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/AddressLiteral.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/AddressLiteral.php new file mode 100644 index 00000000000..77e70f7f554 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/AddressLiteral.php @@ -0,0 +1,14 @@ +message = 'Address literal in domain part'; + $this->rfcNumber = 5321; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/CFWSNearAt.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/CFWSNearAt.php new file mode 100644 index 00000000000..be43bbe6f62 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/CFWSNearAt.php @@ -0,0 +1,13 @@ +message = "Deprecated folding white space near @"; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/CFWSWithFWS.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/CFWSWithFWS.php new file mode 100644 index 00000000000..dea3450ec8e --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/CFWSWithFWS.php @@ -0,0 +1,13 @@ +message = 'Folding whites space followed by folding white space'; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Comment.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Comment.php new file mode 100644 index 00000000000..704c2908b9a --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Comment.php @@ -0,0 +1,13 @@ +message = "Comments found in this email"; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DeprecatedComment.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DeprecatedComment.php new file mode 100644 index 00000000000..ad43bd7c970 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DeprecatedComment.php @@ -0,0 +1,13 @@ +message = 'Deprecated comments'; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DomainLiteral.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DomainLiteral.php new file mode 100644 index 00000000000..6f36b5e293d --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DomainLiteral.php @@ -0,0 +1,14 @@ +message = 'Domain Literal'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DomainTooLong.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DomainTooLong.php new file mode 100644 index 00000000000..61ff17a767d --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/DomainTooLong.php @@ -0,0 +1,14 @@ +message = 'Domain is too long, exceeds 255 chars'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/EmailTooLong.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/EmailTooLong.php new file mode 100644 index 00000000000..497309dbb0f --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/EmailTooLong.php @@ -0,0 +1,15 @@ +message = 'Email is too long, exceeds ' . EmailParser::EMAIL_MAX_LENGTH; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6BadChar.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6BadChar.php new file mode 100644 index 00000000000..ba2fcc01b83 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6BadChar.php @@ -0,0 +1,14 @@ +message = 'Bad char in IPV6 domain literal'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6ColonEnd.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6ColonEnd.php new file mode 100644 index 00000000000..41afa78c63f --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6ColonEnd.php @@ -0,0 +1,14 @@ +message = ':: found at the end of the domain literal'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6ColonStart.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6ColonStart.php new file mode 100644 index 00000000000..1bf754e30cd --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6ColonStart.php @@ -0,0 +1,14 @@ +message = ':: found at the start of the domain literal'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6Deprecated.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6Deprecated.php new file mode 100644 index 00000000000..d752caaa19d --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6Deprecated.php @@ -0,0 +1,14 @@ +message = 'Deprecated form of IPV6'; + $this->rfcNumber = 5321; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6DoubleColon.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6DoubleColon.php new file mode 100644 index 00000000000..4f823949962 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6DoubleColon.php @@ -0,0 +1,14 @@ +message = 'Double colon found after IPV6 tag'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6GroupCount.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6GroupCount.php new file mode 100644 index 00000000000..a59d317f287 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6GroupCount.php @@ -0,0 +1,14 @@ +message = 'Group count is not IPV6 valid'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6MaxGroups.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6MaxGroups.php new file mode 100644 index 00000000000..936274c13ee --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/IPV6MaxGroups.php @@ -0,0 +1,14 @@ +message = 'Reached the maximum number of IPV6 groups allowed'; + $this->rfcNumber = 5321; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LabelTooLong.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LabelTooLong.php new file mode 100644 index 00000000000..daf07f4083c --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LabelTooLong.php @@ -0,0 +1,14 @@ +message = 'Label too long'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LocalTooLong.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LocalTooLong.php new file mode 100644 index 00000000000..0d08d8b35c6 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/LocalTooLong.php @@ -0,0 +1,15 @@ +message = 'Local part is too long, exceeds 64 chars (octets)'; + $this->rfcNumber = 5322; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/NoDNSMXRecord.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/NoDNSMXRecord.php new file mode 100644 index 00000000000..b3c21a1f3bf --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/NoDNSMXRecord.php @@ -0,0 +1,14 @@ +message = 'No MX DSN record was found for this email'; + $this->rfcNumber = 5321; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/ObsoleteDTEXT.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/ObsoleteDTEXT.php new file mode 100644 index 00000000000..10f19e33409 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/ObsoleteDTEXT.php @@ -0,0 +1,14 @@ +rfcNumber = 5322; + $this->message = 'Obsolete DTEXT in domain literal'; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/QuotedPart.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/QuotedPart.php new file mode 100644 index 00000000000..7be9e6a3f25 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/QuotedPart.php @@ -0,0 +1,13 @@ +message = "Deprecated Quoted String found between $prevToken and $postToken"; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/QuotedString.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/QuotedString.php new file mode 100644 index 00000000000..e9d56e1e032 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/QuotedString.php @@ -0,0 +1,13 @@ +message = "Quoted String found between $prevToken and $postToken"; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/TLD.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/TLD.php new file mode 100644 index 00000000000..2338b9f442a --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/TLD.php @@ -0,0 +1,13 @@ +message = "RFC5321, TLD"; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Warning.php b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Warning.php new file mode 100644 index 00000000000..ec6a365ffb7 --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/EmailValidator/Warning/Warning.php @@ -0,0 +1,30 @@ +message; + } + + public function code() + { + return self::CODE; + } + + public function RFCNumber() + { + return $this->rfcNumber; + } + + public function __toString() + { + return $this->message() . " rfc: " . $this->rfcNumber . "interal code: " . static::CODE; + } +} diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/LICENSE b/htdocs/includes/swiftmailer/egulias/email-validator/LICENSE new file mode 100644 index 00000000000..c34d2c197da --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2016 Eduardo Gulias Davis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/htdocs/includes/swiftmailer/egulias/email-validator/README.md b/htdocs/includes/swiftmailer/egulias/email-validator/README.md new file mode 100644 index 00000000000..fc7c89ea67c --- /dev/null +++ b/htdocs/includes/swiftmailer/egulias/email-validator/README.md @@ -0,0 +1,79 @@ +# EmailValidator +[![Build Status](https://travis-ci.org/egulias/EmailValidator.png?branch=master)](https://travis-ci.org/egulias/EmailValidator) [![Coverage Status](https://coveralls.io/repos/egulias/EmailValidator/badge.png?branch=master)](https://coveralls.io/r/egulias/EmailValidator?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/egulias/EmailValidator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6/small.png)](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6) +============================= +With the help of [PHPStorm](https://www.jetbrains.com/phpstorm/) + +## Requirements ## + + * [Composer](https://getcomposer.org) is required for installation + * [Spoofchecking](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php) validation requires that your PHP system have the [PHP Internationalization Libraries](http://php.net/manual/en/book.intl.php) (also known as PHP Intl) + +## Installation ## + +Run the command below to install via Composer + +```shell +composer require egulias/email-validator "~2.1" +``` + +## Getting Started ## +`EmailValidator`requires you to decide which (or combination of them) validation/s strategy/ies you'd like to follow for each [validation](#available-validations). + +A basic example with the RFC validation +```php +isValid("example@example.com", new RFCValidation()); //true +``` + + +### Available validations ### + +1. [RFCValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/RFCValidation.php) +2. [NoRFCWarningsValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/NoRFCWarningsValidation.php) +3. [DNSCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/DNSCheckValidation.php) +4. [SpoofCheckValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/SpoofCheckValidation.php) +5. [MultipleValidationWithAnd](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/MultipleValidationWithAnd.php) +6. [Your own validation](#how-to-extend) + +`MultipleValidationWithAnd` + +It is a validation that operates over other validations performing a logical and (&&) over the result of each validation. + +```php +isValid("example@example.com", $multipleValidations); //true +``` + +### How to extend ### + +It's easy! You just need to extend [EmailValidation](https://github.com/egulias/EmailValidator/blob/master/EmailValidator/Validation/EmailValidation.php) and you can use your own validation. + + +## Other Contributors ## +(You can find current contributors [here](https://github.com/egulias/EmailValidator/graphs/contributors)) + +As this is a port from another library and work, here are other people related to the previous one: + +* Ricard Clau [@ricardclau](http://github.com/ricardclau): Performance against PHP built-in filter_var +* Josepf Bielawski [@stloyd](http://github.com/stloyd): For its first re-work of Dominic's lib +* Dominic Sayers [@dominicsayers](http://github.com/dominicsayers): The original isemail function + +## License ## +Released under the MIT License attached with this code. + diff --git a/htdocs/includes/swiftmailer/lexer/LICENSE b/htdocs/includes/swiftmailer/lexer/LICENSE new file mode 100644 index 00000000000..5e781fce4bb --- /dev/null +++ b/htdocs/includes/swiftmailer/lexer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/htdocs/includes/swiftmailer/lexer/README.md b/htdocs/includes/swiftmailer/lexer/README.md new file mode 100644 index 00000000000..66f443089eb --- /dev/null +++ b/htdocs/includes/swiftmailer/lexer/README.md @@ -0,0 +1,5 @@ +# Doctrine Lexer + +Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers. + +This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL). diff --git a/htdocs/includes/swiftmailer/lexer/composer.json b/htdocs/includes/swiftmailer/lexer/composer.json new file mode 100644 index 00000000000..8cd694c6525 --- /dev/null +++ b/htdocs/includes/swiftmailer/lexer/composer.json @@ -0,0 +1,24 @@ +{ + "name": "doctrine/lexer", + "type": "library", + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "keywords": ["lexer", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/htdocs/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php b/htdocs/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php new file mode 100644 index 00000000000..399a55230b0 --- /dev/null +++ b/htdocs/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php @@ -0,0 +1,327 @@ +. + */ + +namespace Doctrine\Common\Lexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractLexer +{ + /** + * Lexer original input string. + * + * @var string + */ + private $input; + + /** + * Array of scanned tokens. + * + * Each token is an associative array containing three items: + * - 'value' : the string value of the token in the input string + * - 'type' : the type of the token (identifier, numeric, string, input + * parameter, none) + * - 'position' : the position of the token in the input string + * + * @var array + */ + private $tokens = array(); + + /** + * Current lexer position in input string. + * + * @var integer + */ + private $position = 0; + + /** + * Current peek of current lexer position. + * + * @var integer + */ + private $peek = 0; + + /** + * The next token in the input. + * + * @var array + */ + public $lookahead; + + /** + * The last matched/seen token. + * + * @var array + */ + public $token; + + /** + * Sets the input data to be tokenized. + * + * The Lexer is immediately reset and the new input tokenized. + * Any unprocessed tokens from any previous input are lost. + * + * @param string $input The input to be tokenized. + * + * @return void + */ + public function setInput($input) + { + $this->input = $input; + $this->tokens = array(); + + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + * + * @return void + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + * + * @return void + */ + public function resetPeek() + { + $this->peek = 0; + } + + /** + * Resets the lexer position on the input to the given position. + * + * @param integer $position Position to place the lexical scanner. + * + * @return void + */ + public function resetPosition($position = 0) + { + $this->position = $position; + } + + /** + * Retrieve the original lexer's input until a given position. + * + * @param integer $position + * + * @return string + */ + public function getInputUntilPosition($position) + { + return substr($this->input, 0, $position); + } + + /** + * Checks whether a given token matches the current lookahead. + * + * @param integer|string $token + * + * @return boolean + */ + public function isNextToken($token) + { + return null !== $this->lookahead && $this->lookahead['type'] === $token; + } + + /** + * Checks whether any of the given tokens matches the current lookahead. + * + * @param array $tokens + * + * @return boolean + */ + public function isNextTokenAny(array $tokens) + { + return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true); + } + + /** + * Moves to the next token in the input string. + * + * @return boolean + */ + public function moveNext() + { + $this->peek = 0; + $this->token = $this->lookahead; + $this->lookahead = (isset($this->tokens[$this->position])) + ? $this->tokens[$this->position++] : null; + + return $this->lookahead !== null; + } + + /** + * Tells the lexer to skip input tokens until it sees a token with the given value. + * + * @param string $type The token type to skip until. + * + * @return void + */ + public function skipUntil($type) + { + while ($this->lookahead !== null && $this->lookahead['type'] !== $type) { + $this->moveNext(); + } + } + + /** + * Checks if given value is identical to the given token. + * + * @param mixed $value + * @param integer $token + * + * @return boolean + */ + public function isA($value, $token) + { + return $this->getType($value) === $token; + } + + /** + * Moves the lookahead token forward. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function peek() + { + if (isset($this->tokens[$this->position + $this->peek])) { + return $this->tokens[$this->position + $this->peek++]; + } else { + return null; + } + } + + /** + * Peeks at the next token, returns it and immediately resets the peek. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function glimpse() + { + $peek = $this->peek(); + $this->peek = 0; + return $peek; + } + + /** + * Scans the input string for tokens. + * + * @param string $input A query string. + * + * @return void + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = sprintf( + '/(%s)|%s/%s', + implode(')|(', $this->getCatchablePatterns()), + implode('|', $this->getNonCatchablePatterns()), + $this->getModifiers() + ); + } + + $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; + $matches = preg_split($regex, $input, -1, $flags); + + foreach ($matches as $match) { + // Must remain before 'value' assignment since it can change content + $type = $this->getType($match[0]); + + $this->tokens[] = array( + 'value' => $match[0], + 'type' => $type, + 'position' => $match[1], + ); + } + } + + /** + * Gets the literal for a given token. + * + * @param integer $token + * + * @return string + */ + public function getLiteral($token) + { + $className = get_class($this); + $reflClass = new \ReflectionClass($className); + $constants = $reflClass->getConstants(); + + foreach ($constants as $name => $value) { + if ($value === $token) { + return $className . '::' . $name; + } + } + + return $token; + } + + /** + * Regex modifiers + * + * @return string + */ + protected function getModifiers() + { + return 'i'; + } + + /** + * Lexical catchable patterns. + * + * @return array + */ + abstract protected function getCatchablePatterns(); + + /** + * Lexical non-catchable patterns. + * + * @return array + */ + abstract protected function getNonCatchablePatterns(); + + /** + * Retrieve token type. Also processes the token value if necessary. + * + * @param string $value + * + * @return integer + */ + abstract protected function getType(&$value); +} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift.php b/htdocs/includes/swiftmailer/lib/classes/Swift.php index 72419b334b7..e89703eaebb 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift.php @@ -16,8 +16,7 @@ */ abstract class Swift { - /** Swift Mailer Version number generated during dist release process */ - const VERSION = '@SWIFT_VERSION_NUMBER@'; + const VERSION = '6.0.2'; public static $initialized = false; public static $inits = array(); @@ -47,7 +46,7 @@ abstract class Swift return; } - $path = dirname(__FILE__).'/'.str_replace('_', '/', $class).'.php'; + $path = __DIR__.'/'.str_replace('_', '/', $class).'.php'; if (!file_exists($path)) { return; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Attachment.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Attachment.php index a95bccfdf97..1fb232c9fe6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Attachment.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Attachment.php @@ -9,7 +9,7 @@ */ /** - * Attachment class for attaching files to a {@link Swift_Mime_Message}. + * Attachment class for attaching files to a {@link Swift_Mime_SimpleMessage}. * * @author Chris Corbyn */ @@ -39,20 +39,6 @@ class Swift_Attachment extends Swift_Mime_Attachment } } - /** - * Create a new Attachment. - * - * @param string|Swift_OutputByteStream $data - * @param string $filename - * @param string $contentType - * - * @return Swift_Mime_Attachment - */ - public static function newInstance($data = null, $filename = null, $contentType = null) - { - return new self($data, $filename, $contentType); - } - /** * Create a new Attachment from a filesystem path. * @@ -63,9 +49,9 @@ class Swift_Attachment extends Swift_Mime_Attachment */ public static function fromPath($path, $contentType = null) { - return self::newInstance()->setFile( + return (new self())->setFile( new Swift_ByteStream_FileByteStream($path), $contentType - ); + ); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php b/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php index a7b0e3a6207..bfd2c79bcbd 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -18,38 +18,38 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I /** * Write sequence. */ - protected $_sequence = 0; + protected $sequence = 0; /** * StreamFilters. * * @var Swift_StreamFilter[] */ - private $_filters = array(); + private $filters = array(); /** * A buffer for writing. */ - private $_writeBuffer = ''; + private $writeBuffer = ''; /** * Bound streams. * * @var Swift_InputByteStream[] */ - private $_mirrors = array(); + private $mirrors = array(); /** * Commit the given bytes to the storage medium immediately. * * @param string $bytes */ - abstract protected function _commit($bytes); + abstract protected function doCommit($bytes); /** * Flush any buffers/content with immediate effect. */ - abstract protected function _flush(); + abstract protected function flush(); /** * Add a StreamFilter to this InputByteStream. @@ -59,7 +59,7 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function addFilter(Swift_StreamFilter $filter, $key) { - $this->_filters[$key] = $filter; + $this->filters[$key] = $filter; } /** @@ -69,7 +69,7 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function removeFilter($key) { - unset($this->_filters[$key]); + unset($this->filters[$key]); } /** @@ -83,15 +83,15 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function write($bytes) { - $this->_writeBuffer .= $bytes; - foreach ($this->_filters as $filter) { - if ($filter->shouldBuffer($this->_writeBuffer)) { + $this->writeBuffer .= $bytes; + foreach ($this->filters as $filter) { + if ($filter->shouldBuffer($this->writeBuffer)) { return; } } - $this->_doWrite($this->_writeBuffer); + $this->doWrite($this->writeBuffer); - return ++$this->_sequence; + return ++$this->sequence; } /** @@ -102,7 +102,7 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function commit() { - $this->_doWrite($this->_writeBuffer); + $this->doWrite($this->writeBuffer); } /** @@ -115,7 +115,7 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function bind(Swift_InputByteStream $is) { - $this->_mirrors[] = $is; + $this->mirrors[] = $is; } /** @@ -129,12 +129,12 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function unbind(Swift_InputByteStream $is) { - foreach ($this->_mirrors as $k => $stream) { + foreach ($this->mirrors as $k => $stream) { if ($is === $stream) { - if ($this->_writeBuffer !== '') { - $stream->write($this->_writeBuffer); + if ($this->writeBuffer !== '') { + $stream->write($this->writeBuffer); } - unset($this->_mirrors[$k]); + unset($this->mirrors[$k]); } } } @@ -147,20 +147,20 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I */ public function flushBuffers() { - if ($this->_writeBuffer !== '') { - $this->_doWrite($this->_writeBuffer); + if ($this->writeBuffer !== '') { + $this->doWrite($this->writeBuffer); } - $this->_flush(); + $this->flush(); - foreach ($this->_mirrors as $stream) { + foreach ($this->mirrors as $stream) { $stream->flushBuffers(); } } /** Run $bytes through all filters */ - private function _filter($bytes) + private function filter($bytes) { - foreach ($this->_filters as $filter) { + foreach ($this->filters as $filter) { $bytes = $filter->filter($bytes); } @@ -168,14 +168,14 @@ abstract class Swift_ByteStream_AbstractFilterableInputStream implements Swift_I } /** Just write the bytes to the stream */ - private function _doWrite($bytes) + private function doWrite($bytes) { - $this->_commit($this->_filter($bytes)); + $this->doCommit($this->filter($bytes)); - foreach ($this->_mirrors as $stream) { + foreach ($this->mirrors as $stream) { $stream->write($bytes); } - $this->_writeBuffer = ''; + $this->writeBuffer = ''; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php b/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php index ef05a6d5e6e..31b85e0e02a 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -11,7 +11,7 @@ /** * Allows reading and writing of bytes to and from an array. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_OutputByteStream { @@ -20,28 +20,28 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O * * @var string[] */ - private $_array = array(); + private $array = array(); /** * The size of the stack. * * @var int */ - private $_arraySize = 0; + private $arraySize = 0; /** * The internal pointer offset. * * @var int */ - private $_offset = 0; + private $offset = 0; /** * Bound streams. * * @var Swift_InputByteStream[] */ - private $_mirrors = array(); + private $mirrors = array(); /** * Create a new ArrayByteStream. @@ -53,12 +53,12 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O public function __construct($stack = null) { if (is_array($stack)) { - $this->_array = $stack; - $this->_arraySize = count($stack); + $this->array = $stack; + $this->arraySize = count($stack); } elseif (is_string($stack)) { $this->write($stack); } else { - $this->_array = array(); + $this->array = array(); } } @@ -76,16 +76,16 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O */ public function read($length) { - if ($this->_offset == $this->_arraySize) { + if ($this->offset == $this->arraySize) { return false; } // Don't use array slice - $end = $length + $this->_offset; - $end = $this->_arraySize < $end ? $this->_arraySize : $end; + $end = $length + $this->offset; + $end = $this->arraySize < $end ? $this->arraySize : $end; $ret = ''; - for (; $this->_offset < $end; ++$this->_offset) { - $ret .= $this->_array[$this->_offset]; + for (; $this->offset < $end; ++$this->offset) { + $ret .= $this->array[$this->offset]; } return $ret; @@ -100,11 +100,11 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O { $to_add = str_split($bytes); foreach ($to_add as $value) { - $this->_array[] = $value; + $this->array[] = $value; } - $this->_arraySize = count($this->_array); + $this->arraySize = count($this->array); - foreach ($this->_mirrors as $stream) { + foreach ($this->mirrors as $stream) { $stream->write($bytes); } } @@ -126,7 +126,7 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O */ public function bind(Swift_InputByteStream $is) { - $this->_mirrors[] = $is; + $this->mirrors[] = $is; } /** @@ -140,9 +140,9 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O */ public function unbind(Swift_InputByteStream $is) { - foreach ($this->_mirrors as $k => $stream) { + foreach ($this->mirrors as $k => $stream) { if ($is === $stream) { - unset($this->_mirrors[$k]); + unset($this->mirrors[$k]); } } } @@ -156,13 +156,13 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O */ public function setReadPointer($byteOffset) { - if ($byteOffset > $this->_arraySize) { - $byteOffset = $this->_arraySize; + if ($byteOffset > $this->arraySize) { + $byteOffset = $this->arraySize; } elseif ($byteOffset < 0) { $byteOffset = 0; } - $this->_offset = $byteOffset; + $this->offset = $byteOffset; } /** @@ -171,11 +171,11 @@ class Swift_ByteStream_ArrayByteStream implements Swift_InputByteStream, Swift_O */ public function flushBuffers() { - $this->_offset = 0; - $this->_array = array(); - $this->_arraySize = 0; + $this->offset = 0; + $this->array = array(); + $this->arraySize = 0; - foreach ($this->_mirrors as $stream) { + foreach ($this->mirrors as $stream) { $stream->flushBuffers(); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php b/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php index 406104371e8..61f820e5a14 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -11,30 +11,27 @@ /** * Allows reading and writing of bytes to and from a file. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream { /** The internal pointer offset */ - private $_offset = 0; + private $offset = 0; /** The path to the file */ - private $_path; + private $path; /** The mode this file is opened in for writing */ - private $_mode; + private $mode; /** A lazy-loaded resource handle for reading the file */ - private $_reader; + private $reader; /** A lazy-loaded resource handle for writing the file */ - private $_writer; - - /** If magic_quotes_runtime is on, this will be true */ - private $_quotes = false; + private $writer; /** If stream is seekable true/false, or null if not known */ - private $_seekable = null; + private $seekable = null; /** * Create a new FileByteStream for $path. @@ -47,12 +44,8 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl if (empty($path)) { throw new Swift_IoException('The path cannot be empty'); } - $this->_path = $path; - $this->_mode = $writable ? 'w+b' : 'rb'; - - if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { - $this->_quotes = true; - } + $this->path = $path; + $this->mode = $writable ? 'w+b' : 'rb'; } /** @@ -62,7 +55,7 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl */ public function getPath() { - return $this->_path; + return $this->path; } /** @@ -75,27 +68,21 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl * * @param int $length * - * @throws Swift_IoException - * * @return string|bool + * + * @throws Swift_IoException */ public function read($length) { - $fp = $this->_getReadHandle(); + $fp = $this->getReadHandle(); if (!feof($fp)) { - if ($this->_quotes) { - ini_set('magic_quotes_runtime', 0); - } $bytes = fread($fp, $length); - if ($this->_quotes) { - ini_set('magic_quotes_runtime', 1); - } - $this->_offset = ftell($fp); + $this->offset = ftell($fp); // If we read one byte after reaching the end of the file // feof() will return false and an empty string is returned if ($bytes === '' && feof($fp)) { - $this->_resetReadHandle(); + $this->resetReadHandle(); return false; } @@ -103,7 +90,7 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl return $bytes; } - $this->_resetReadHandle(); + $this->resetReadHandle(); return false; } @@ -117,93 +104,93 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl */ public function setReadPointer($byteOffset) { - if (isset($this->_reader)) { - $this->_seekReadStreamToPosition($byteOffset); + if (isset($this->reader)) { + $this->seekReadStreamToPosition($byteOffset); } - $this->_offset = $byteOffset; + $this->offset = $byteOffset; } /** Just write the bytes to the file */ - protected function _commit($bytes) + protected function doCommit($bytes) { - fwrite($this->_getWriteHandle(), $bytes); - $this->_resetReadHandle(); + fwrite($this->getWriteHandle(), $bytes); + $this->resetReadHandle(); } /** Not used */ - protected function _flush() + protected function flush() { } /** Get the resource for reading */ - private function _getReadHandle() + private function getReadHandle() { - if (!isset($this->_reader)) { - if (!$this->_reader = fopen($this->_path, 'rb')) { - throw new Swift_IoException( - 'Unable to open file for reading ['.$this->_path.']' - ); + if (!isset($this->reader)) { + $pointer = @fopen($this->path, 'rb'); + if (!$pointer) { + throw new Swift_IoException('Unable to open file for reading ['.$this->path.']'); } - if ($this->_offset != 0) { - $this->_getReadStreamSeekableStatus(); - $this->_seekReadStreamToPosition($this->_offset); + $this->reader = $pointer; + if ($this->offset != 0) { + $this->getReadStreamSeekableStatus(); + $this->seekReadStreamToPosition($this->offset); } } - return $this->_reader; + return $this->reader; } /** Get the resource for writing */ - private function _getWriteHandle() + private function getWriteHandle() { - if (!isset($this->_writer)) { - if (!$this->_writer = fopen($this->_path, $this->_mode)) { + if (!isset($this->writer)) { + if (!$this->writer = fopen($this->path, $this->mode)) { throw new Swift_IoException( - 'Unable to open file for writing ['.$this->_path.']' + 'Unable to open file for writing ['.$this->path.']' ); } } - return $this->_writer; + return $this->writer; } /** Force a reload of the resource for reading */ - private function _resetReadHandle() + private function resetReadHandle() { - if (isset($this->_reader)) { - fclose($this->_reader); - $this->_reader = null; + if (isset($this->reader)) { + fclose($this->reader); + $this->reader = null; } } /** Check if ReadOnly Stream is seekable */ - private function _getReadStreamSeekableStatus() + private function getReadStreamSeekableStatus() { - $metas = stream_get_meta_data($this->_reader); - $this->_seekable = $metas['seekable']; + $metas = stream_get_meta_data($this->reader); + $this->seekable = $metas['seekable']; } /** Streams in a readOnly stream ensuring copy if needed */ - private function _seekReadStreamToPosition($offset) + private function seekReadStreamToPosition($offset) { - if ($this->_seekable === null) { - $this->_getReadStreamSeekableStatus(); + if ($this->seekable === null) { + $this->getReadStreamSeekableStatus(); } - if ($this->_seekable === false) { - $currentPos = ftell($this->_reader); + if ($this->seekable === false) { + $currentPos = ftell($this->reader); if ($currentPos < $offset) { $toDiscard = $offset - $currentPos; - fread($this->_reader, $toDiscard); + fread($this->reader, $toDiscard); return; } - $this->_copyReadStream(); + $this->copyReadStream(); } - fseek($this->_reader, $offset, SEEK_SET); + fseek($this->reader, $offset, SEEK_SET); } /** Copy a readOnly Stream to ensure seekability */ - private function _copyReadStream() + private function copyReadStream() { if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) { /* We have opened a php:// Stream Should work without problem */ @@ -212,11 +199,11 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl } else { throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); } - $currentPos = ftell($this->_reader); - fclose($this->_reader); - $source = fopen($this->_path, 'rb'); + $currentPos = ftell($this->reader); + fclose($this->reader); + $source = fopen($this->path, 'rb'); if (!$source) { - throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']'); + throw new Swift_IoException('Unable to open file for copying ['.$this->path.']'); } fseek($tmpFile, 0, SEEK_SET); while (!feof($source)) { @@ -224,6 +211,6 @@ class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterabl } fseek($tmpFile, $currentPos, SEEK_SET); fclose($source); - $this->_reader = $tmpFile; + $this->reader = $tmpFile; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader.php index 3d5e854a884..4267adbc593 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -48,8 +48,8 @@ interface Swift_CharacterReader * A value of zero means this is already a valid character. * A value of -1 means this cannot possibly be a valid character. * - * @param integer[] $bytes - * @param int $size + * @param int[] $bytes + * @param int $size * * @return int */ diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php index ab8ebfda708..b09bb5b6c38 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -11,8 +11,8 @@ /** * Provides fixed-width byte sizes for reading fixed-width character sets. * - * @author Chris Corbyn - * @author Xavier De Cock + * @author Chris Corbyn + * @author Xavier De Cock */ class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader { @@ -21,7 +21,7 @@ class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterRe * * @var int */ - private $_width; + private $width; /** * Creates a new GenericFixedWidthReader using $width bytes per character. @@ -30,7 +30,7 @@ class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterRe */ public function __construct($width) { - $this->_width = $width; + $this->width = $width; } /** @@ -47,11 +47,11 @@ class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterRe { $strlen = strlen($string); // % and / are CPU intensive, so, maybe find a better way - $ignored = $strlen % $this->_width; - $ignoredChars = substr($string, -$ignored); - $currentMap = $this->_width; + $ignored = $strlen % $this->width; + $ignoredChars = $ignored ? substr($string, -$ignored) : ''; + $currentMap = $this->width; - return ($strlen - $ignored) / $this->_width; + return ($strlen - $ignored) / $this->width; } /** @@ -80,7 +80,7 @@ class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterRe */ public function validateByteSequence($bytes, $size) { - $needed = $this->_width - $size; + $needed = $this->width - $size; return $needed > -1 ? $needed : -1; } @@ -92,6 +92,6 @@ class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterRe */ public function getInitialByteSize() { - return $this->_width; + return $this->width; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php index 7379bda258f..22746bd8575 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -19,22 +19,22 @@ class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader /** Pre-computed for optimization */ private static $length_map = array( // N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6N - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7N - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8N - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9N - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAN - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBN - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCN - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDN - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEN - 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, // 0xFN + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x2N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x4N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x5N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x6N + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x7N + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x8N + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x9N + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xAN + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xBN + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xCN + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xDN + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEN + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // 0xFN ); private static $s_length_map = array( diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php index 9171a0bacbf..f3641f46b46 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php @@ -20,14 +20,14 @@ class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift * * @var array */ - private static $_map = array(); + private static $map = array(); /** * Factories which have already been loaded. * * @var Swift_CharacterReaderFactory[] */ - private static $_loaded = array(); + private static $loaded = array(); /** * Creates a new CharacterReaderFactory. @@ -44,7 +44,7 @@ class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift public function init() { - if (count(self::$_map) > 0) { + if (count(self::$map) > 0) { return; } @@ -66,32 +66,32 @@ class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift ); // Utf-8 - self::$_map['utf-?8'] = array( + self::$map['utf-?8'] = array( 'class' => $prefix.'Utf8Reader', 'constructor' => array(), ); //7-8 bit charsets - self::$_map['(us-)?ascii'] = $singleByte; - self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; - self::$_map['windows-?125[0-9]'] = $singleByte; - self::$_map['cp-?[0-9]+'] = $singleByte; - self::$_map['ansi'] = $singleByte; - self::$_map['macintosh'] = $singleByte; - self::$_map['koi-?7'] = $singleByte; - self::$_map['koi-?8-?.+'] = $singleByte; - self::$_map['mik'] = $singleByte; - self::$_map['(cork|t1)'] = $singleByte; - self::$_map['v?iscii'] = $singleByte; + self::$map['(us-)?ascii'] = $singleByte; + self::$map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; + self::$map['windows-?125[0-9]'] = $singleByte; + self::$map['cp-?[0-9]+'] = $singleByte; + self::$map['ansi'] = $singleByte; + self::$map['macintosh'] = $singleByte; + self::$map['koi-?7'] = $singleByte; + self::$map['koi-?8-?.+'] = $singleByte; + self::$map['mik'] = $singleByte; + self::$map['(cork|t1)'] = $singleByte; + self::$map['v?iscii'] = $singleByte; //16 bits - self::$_map['(ucs-?2|utf-?16)'] = $doubleByte; + self::$map['(ucs-?2|utf-?16)'] = $doubleByte; //32 bits - self::$_map['(ucs-?4|utf-?32)'] = $fourBytes; + self::$map['(ucs-?4|utf-?32)'] = $fourBytes; // Fallback - self::$_map['.*'] = $singleByte; + self::$map['.*'] = $singleByte; } /** @@ -103,21 +103,21 @@ class Swift_CharacterReaderFactory_SimpleCharacterReaderFactory implements Swift */ public function getReaderFor($charset) { - $charset = trim(strtolower($charset)); - foreach (self::$_map as $pattern => $spec) { + $charset = strtolower(trim($charset)); + foreach (self::$map as $pattern => $spec) { $re = '/^'.$pattern.'$/D'; if (preg_match($re, $charset)) { - if (!array_key_exists($pattern, self::$_loaded)) { + if (!array_key_exists($pattern, self::$loaded)) { $reflector = new ReflectionClass($spec['class']); if ($reflector->getConstructor()) { $reader = $reflector->newInstanceArgs($spec['constructor']); } else { $reader = $reflector->newInstance(); } - self::$_loaded[$pattern] = $reader; + self::$loaded[$pattern] = $reader; } - return self::$_loaded[$pattern]; + return self::$loaded[$pattern]; } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php index d695a6e172b..fbfbceb01d1 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php @@ -16,28 +16,28 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream { /** A map of byte values and their respective characters */ - private static $_charMap; + private static $charMap; /** A map of characters and their derivative byte values */ - private static $_byteMap; + private static $byteMap; /** The char reader (lazy-loaded) for the current charset */ - private $_charReader; + private $charReader; /** A factory for creating CharacterReader instances */ - private $_charReaderFactory; + private $charReaderFactory; /** The character set this stream is using */ - private $_charset; + private $charset; /** Array of characters */ - private $_array = array(); + private $array = array(); /** Size of the array of character */ - private $_array_size = array(); + private $array_size = array(); /** The current character offset in the stream */ - private $_offset = 0; + private $offset = 0; /** * Create a new CharacterStream with the given $chars, if set. @@ -47,7 +47,7 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function __construct(Swift_CharacterReaderFactory $factory, $charset) { - self::_initializeMaps(); + self::initializeMaps(); $this->setCharacterReaderFactory($factory); $this->setCharacterSet($charset); } @@ -59,8 +59,8 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function setCharacterSet($charset) { - $this->_charset = $charset; - $this->_charReader = null; + $this->charset = $charset; + $this->charReader = null; } /** @@ -70,7 +70,7 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) { - $this->_charReaderFactory = $factory; + $this->charReaderFactory = $factory; } /** @@ -80,28 +80,28 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function importByteStream(Swift_OutputByteStream $os) { - if (!isset($this->_charReader)) { - $this->_charReader = $this->_charReaderFactory - ->getReaderFor($this->_charset); + if (!isset($this->charReader)) { + $this->charReader = $this->charReaderFactory + ->getReaderFor($this->charset); } - $startLength = $this->_charReader->getInitialByteSize(); + $startLength = $this->charReader->getInitialByteSize(); while (false !== $bytes = $os->read($startLength)) { $c = array(); for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { - $c[] = self::$_byteMap[$bytes[$i]]; + $c[] = self::$byteMap[$bytes[$i]]; } $size = count($c); - $need = $this->_charReader + $need = $this->charReader ->validateByteSequence($c, $size); if ($need > 0 && false !== $bytes = $os->read($need)) { for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { - $c[] = self::$_byteMap[$bytes[$i]]; + $c[] = self::$byteMap[$bytes[$i]]; } } - $this->_array[] = $c; - ++$this->_array_size; + $this->array[] = $c; + ++$this->array_size; } } @@ -127,20 +127,20 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function read($length) { - if ($this->_offset == $this->_array_size) { + if ($this->offset == $this->array_size) { return false; } // Don't use array slice $arrays = array(); - $end = $length + $this->_offset; - for ($i = $this->_offset; $i < $end; ++$i) { - if (!isset($this->_array[$i])) { + $end = $length + $this->offset; + for ($i = $this->offset; $i < $end; ++$i) { + if (!isset($this->array[$i])) { break; } - $arrays[] = $this->_array[$i]; + $arrays[] = $this->array[$i]; } - $this->_offset += $i - $this->_offset; // Limit function calls + $this->offset += $i - $this->offset; // Limit function calls $chars = false; foreach ($arrays as $array) { $chars .= implode('', array_map('chr', $array)); @@ -155,24 +155,24 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea * * @param int $length * - * @return integer[] + * @return int[] */ public function readBytes($length) { - if ($this->_offset == $this->_array_size) { + if ($this->offset == $this->array_size) { return false; } $arrays = array(); - $end = $length + $this->_offset; - for ($i = $this->_offset; $i < $end; ++$i) { - if (!isset($this->_array[$i])) { + $end = $length + $this->offset; + for ($i = $this->offset; $i < $end; ++$i) { + if (!isset($this->array[$i])) { break; } - $arrays[] = $this->_array[$i]; + $arrays[] = $this->array[$i]; } - $this->_offset += ($i - $this->_offset); // Limit function calls + $this->offset += ($i - $this->offset); // Limit function calls - return call_user_func_array('array_merge', $arrays); + return array_merge(...$arrays); } /** @@ -182,12 +182,12 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function write($chars) { - if (!isset($this->_charReader)) { - $this->_charReader = $this->_charReaderFactory->getReaderFor( - $this->_charset); + if (!isset($this->charReader)) { + $this->charReader = $this->charReaderFactory->getReaderFor( + $this->charset); } - $startLength = $this->_charReader->getInitialByteSize(); + $startLength = $this->charReader->getInitialByteSize(); $fp = fopen('php://memory', 'w+b'); fwrite($fp, $chars); @@ -203,7 +203,7 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea // Buffer Filing if ($buf_len - $buf_pos < $startLength) { $buf = array_splice($buffer, $buf_pos); - $new = $this->_reloadBuffer($fp, 100); + $new = $this->reloadBuffer($fp, 100); if ($new) { $buffer = array_merge($buf, $new); $buf_len = count($buffer); @@ -218,11 +218,11 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea ++$size; $bytes[] = $buffer[$buf_pos++]; } - $need = $this->_charReader->validateByteSequence( + $need = $this->charReader->validateByteSequence( $bytes, $size); if ($need > 0) { if ($buf_len - $buf_pos < $need) { - $new = $this->_reloadBuffer($fp, $need); + $new = $this->reloadBuffer($fp, $need); if ($new) { $buffer = array_merge($buffer, $new); @@ -233,8 +233,8 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea $bytes[] = $buffer[$buf_pos++]; } } - $this->_array[] = $bytes; - ++$this->_array_size; + $this->array[] = $bytes; + ++$this->array_size; } } while ($has_datas); @@ -248,12 +248,12 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function setPointer($charOffset) { - if ($charOffset > $this->_array_size) { - $charOffset = $this->_array_size; + if ($charOffset > $this->array_size) { + $charOffset = $this->array_size; } elseif ($charOffset < 0) { $charOffset = 0; } - $this->_offset = $charOffset; + $this->offset = $charOffset; } /** @@ -261,17 +261,17 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea */ public function flushContents() { - $this->_offset = 0; - $this->_array = array(); - $this->_array_size = 0; + $this->offset = 0; + $this->array = array(); + $this->array_size = 0; } - private function _reloadBuffer($fp, $len) + private function reloadBuffer($fp, $len) { if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) { $buf = array(); for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { - $buf[] = self::$_byteMap[$bytes[$i]]; + $buf[] = self::$byteMap[$bytes[$i]]; } return $buf; @@ -280,14 +280,14 @@ class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStrea return false; } - private static function _initializeMaps() + private static function initializeMaps() { - if (!isset(self::$_charMap)) { - self::$_charMap = array(); + if (!isset(self::$charMap)) { + self::$charMap = array(); for ($byte = 0; $byte < 256; ++$byte) { - self::$_charMap[$byte] = chr($byte); + self::$charMap[$byte] = chr($byte); } - self::$_byteMap = array_flip(self::$_charMap); + self::$byteMap = array_flip(self::$charMap); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php index 1e8e2899672..0d62fa68ff3 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -11,7 +11,7 @@ /** * A CharacterStream implementation which stores characters in an internal array. * - * @author Xavier De Cock + * @author Xavier De Cock */ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream { @@ -20,63 +20,63 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream * * @var Swift_CharacterReader */ - private $_charReader; + private $charReader; /** * A factory for creating CharacterReader instances. * * @var Swift_CharacterReaderFactory */ - private $_charReaderFactory; + private $charReaderFactory; /** * The character set this stream is using. * * @var string */ - private $_charset; + private $charset; /** * The data's stored as-is. * * @var string */ - private $_datas = ''; + private $datas = ''; /** * Number of bytes in the stream. * * @var int */ - private $_datasSize = 0; + private $datasSize = 0; /** * Map. * * @var mixed */ - private $_map; + private $map; /** * Map Type. * * @var int */ - private $_mapType = 0; + private $mapType = 0; /** * Number of characters in the stream. * * @var int */ - private $_charCount = 0; + private $charCount = 0; /** * Position in the stream. * * @var int */ - private $_currentPos = 0; + private $currentPos = 0; /** * Constructor. @@ -99,9 +99,9 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream */ public function setCharacterSet($charset) { - $this->_charset = $charset; - $this->_charReader = null; - $this->_mapType = 0; + $this->charset = $charset; + $this->charReader = null; + $this->mapType = 0; } /** @@ -111,7 +111,7 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream */ public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) { - $this->_charReaderFactory = $factory; + $this->charReaderFactory = $factory; } /** @@ -119,11 +119,11 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream */ public function flushContents() { - $this->_datas = null; - $this->_map = null; - $this->_charCount = 0; - $this->_currentPos = 0; - $this->_datasSize = 0; + $this->datas = null; + $this->map = null; + $this->charCount = 0; + $this->currentPos = 0; + $this->datasSize = 0; } /** @@ -161,49 +161,49 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream */ public function read($length) { - if ($this->_currentPos >= $this->_charCount) { + if ($this->currentPos >= $this->charCount) { return false; } $ret = false; - $length = $this->_currentPos + $length > $this->_charCount ? $this->_charCount - $this->_currentPos : $length; - switch ($this->_mapType) { + $length = ($this->currentPos + $length > $this->charCount) ? $this->charCount - $this->currentPos : $length; + switch ($this->mapType) { case Swift_CharacterReader::MAP_TYPE_FIXED_LEN: - $len = $length * $this->_map; - $ret = substr($this->_datas, - $this->_currentPos * $this->_map, + $len = $length * $this->map; + $ret = substr($this->datas, + $this->currentPos * $this->map, $len); - $this->_currentPos += $length; + $this->currentPos += $length; break; case Swift_CharacterReader::MAP_TYPE_INVALID: $ret = ''; - for (; $this->_currentPos < $length; ++$this->_currentPos) { - if (isset($this->_map[$this->_currentPos])) { + for (; $this->currentPos < $length; ++$this->currentPos) { + if (isset($this->map[$this->currentPos])) { $ret .= '?'; } else { - $ret .= $this->_datas[$this->_currentPos]; + $ret .= $this->datas[$this->currentPos]; } } break; case Swift_CharacterReader::MAP_TYPE_POSITIONS: - $end = $this->_currentPos + $length; - $end = $end > $this->_charCount ? $this->_charCount : $end; + $end = $this->currentPos + $length; + $end = $end > $this->charCount ? $this->charCount : $end; $ret = ''; $start = 0; - if ($this->_currentPos > 0) { - $start = $this->_map['p'][$this->_currentPos - 1]; + if ($this->currentPos > 0) { + $start = $this->map['p'][$this->currentPos - 1]; } $to = $start; - for (; $this->_currentPos < $end; ++$this->_currentPos) { - if (isset($this->_map['i'][$this->_currentPos])) { - $ret .= substr($this->_datas, $start, $to - $start).'?'; - $start = $this->_map['p'][$this->_currentPos]; + for (; $this->currentPos < $end; ++$this->currentPos) { + if (isset($this->map['i'][$this->currentPos])) { + $ret .= substr($this->datas, $start, $to - $start).'?'; + $start = $this->map['p'][$this->currentPos]; } else { - $to = $this->_map['p'][$this->_currentPos]; + $to = $this->map['p'][$this->currentPos]; } } - $ret .= substr($this->_datas, $start, $to - $start); + $ret .= substr($this->datas, $start, $to - $start); break; } @@ -215,7 +215,7 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream * * @param int $length * - * @return integer[] + * @return int[] */ public function readBytes($length) { @@ -236,10 +236,10 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream */ public function setPointer($charOffset) { - if ($this->_charCount < $charOffset) { - $charOffset = $this->_charCount; + if ($this->charCount < $charOffset) { + $charOffset = $this->charCount; } - $this->_currentPos = $charOffset; + $this->currentPos = $charOffset; } /** @@ -249,19 +249,19 @@ class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream */ public function write($chars) { - if (!isset($this->_charReader)) { - $this->_charReader = $this->_charReaderFactory->getReaderFor( - $this->_charset); - $this->_map = array(); - $this->_mapType = $this->_charReader->getMapType(); + if (!isset($this->charReader)) { + $this->charReader = $this->charReaderFactory->getReaderFor( + $this->charset); + $this->map = array(); + $this->mapType = $this->charReader->getMapType(); } $ignored = ''; - $this->_datas .= $chars; - $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored); + $this->datas .= $chars; + $this->charCount += $this->charReader->getCharPositions(substr($this->datas, $this->datasSize), $this->datasSize, $this->map, $ignored); if ($ignored !== false) { - $this->_datasSize = strlen($this->_datas) - strlen($ignored); + $this->datasSize = strlen($this->datas) - strlen($ignored); } else { - $this->_datasSize = strlen($this->_datas); + $this->datasSize = strlen($this->datas); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/ConfigurableSpool.php b/htdocs/includes/swiftmailer/lib/classes/Swift/ConfigurableSpool.php index 4ae5bacfc40..a711bac478f 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/ConfigurableSpool.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -16,10 +16,10 @@ abstract class Swift_ConfigurableSpool implements Swift_Spool { /** The maximum number of messages to send per flush */ - private $_message_limit; + private $message_limit; /** The time limit per flush */ - private $_time_limit; + private $time_limit; /** * Sets the maximum number of messages to send per flush. @@ -28,7 +28,7 @@ abstract class Swift_ConfigurableSpool implements Swift_Spool */ public function setMessageLimit($limit) { - $this->_message_limit = (int) $limit; + $this->message_limit = (int) $limit; } /** @@ -38,7 +38,7 @@ abstract class Swift_ConfigurableSpool implements Swift_Spool */ public function getMessageLimit() { - return $this->_message_limit; + return $this->message_limit; } /** @@ -48,7 +48,7 @@ abstract class Swift_ConfigurableSpool implements Swift_Spool */ public function setTimeLimit($limit) { - $this->_time_limit = (int) $limit; + $this->time_limit = (int) $limit; } /** @@ -58,6 +58,6 @@ abstract class Swift_ConfigurableSpool implements Swift_Spool */ public function getTimeLimit() { - return $this->_time_limit; + return $this->time_limit; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/DependencyContainer.php b/htdocs/includes/swiftmailer/lib/classes/Swift/DependencyContainer.php index 8c1074a3a37..b1bc3306f22 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/DependencyContainer.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/DependencyContainer.php @@ -11,7 +11,7 @@ /** * Dependency Injection container. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_DependencyContainer { @@ -28,13 +28,13 @@ class Swift_DependencyContainer const TYPE_ALIAS = 0x1000; /** Singleton instance */ - private static $_instance = null; + private static $instance = null; /** The data container */ - private $_store = array(); + private $store = array(); /** The current endpoint in the data container */ - private $_endPoint; + private $endPoint; /** * Constructor should not be used. @@ -48,15 +48,15 @@ class Swift_DependencyContainer /** * Returns a singleton of the DependencyContainer. * - * @return Swift_DependencyContainer + * @return self */ public static function getInstance() { - if (!isset(self::$_instance)) { - self::$_instance = new self(); + if (!isset(self::$instance)) { + self::$instance = new self(); } - return self::$_instance; + return self::$instance; } /** @@ -66,7 +66,7 @@ class Swift_DependencyContainer */ public function listItems() { - return array_keys($this->_store); + return array_keys($this->store); } /** @@ -80,8 +80,8 @@ class Swift_DependencyContainer */ public function has($itemName) { - return array_key_exists($itemName, $this->_store) - && isset($this->_store[$itemName]['lookupType']); + return array_key_exists($itemName, $this->store) + && isset($this->store[$itemName]['lookupType']); } /** @@ -91,9 +91,9 @@ class Swift_DependencyContainer * * @param string $itemName * - * @throws Swift_DependencyException If the dependency is not found - * * @return mixed + * + * @throws Swift_DependencyException If the dependency is not found */ public function lookup($itemName) { @@ -103,15 +103,15 @@ class Swift_DependencyContainer ); } - switch ($this->_store[$itemName]['lookupType']) { + switch ($this->store[$itemName]['lookupType']) { case self::TYPE_ALIAS: - return $this->_createAlias($itemName); + return $this->createAlias($itemName); case self::TYPE_VALUE: - return $this->_getValue($itemName); + return $this->getValue($itemName); case self::TYPE_INSTANCE: - return $this->_createNewInstance($itemName); + return $this->createNewInstance($itemName); case self::TYPE_SHARED: - return $this->_createSharedInstance($itemName); + return $this->createSharedInstance($itemName); } } @@ -125,8 +125,8 @@ class Swift_DependencyContainer public function createDependenciesFor($itemName) { $args = array(); - if (isset($this->_store[$itemName]['args'])) { - $args = $this->_resolveArgs($this->_store[$itemName]['args']); + if (isset($this->store[$itemName]['args'])) { + $args = $this->resolveArgs($this->store[$itemName]['args']); } return $args; @@ -143,12 +143,12 @@ class Swift_DependencyContainer * * @param string $itemName * - * @return Swift_DependencyContainer + * @return $this */ public function register($itemName) { - $this->_store[$itemName] = array(); - $this->_endPoint = &$this->_store[$itemName]; + $this->store[$itemName] = array(); + $this->endPoint = &$this->store[$itemName]; return $this; } @@ -160,11 +160,11 @@ class Swift_DependencyContainer * * @param mixed $value * - * @return Swift_DependencyContainer + * @return $this */ public function asValue($value) { - $endPoint = &$this->_getEndPoint(); + $endPoint = &$this->getEndPoint(); $endPoint['lookupType'] = self::TYPE_VALUE; $endPoint['value'] = $value; @@ -176,11 +176,11 @@ class Swift_DependencyContainer * * @param string $lookup * - * @return Swift_DependencyContainer + * @return $this */ public function asAliasOf($lookup) { - $endPoint = &$this->_getEndPoint(); + $endPoint = &$this->getEndPoint(); $endPoint['lookupType'] = self::TYPE_ALIAS; $endPoint['ref'] = $lookup; @@ -198,11 +198,11 @@ class Swift_DependencyContainer * * @param string $className * - * @return Swift_DependencyContainer + * @return $this */ public function asNewInstanceOf($className) { - $endPoint = &$this->_getEndPoint(); + $endPoint = &$this->getEndPoint(); $endPoint['lookupType'] = self::TYPE_INSTANCE; $endPoint['className'] = $className; @@ -216,11 +216,11 @@ class Swift_DependencyContainer * * @param string $className * - * @return Swift_DependencyContainer + * @return $this */ public function asSharedInstanceOf($className) { - $endPoint = &$this->_getEndPoint(); + $endPoint = &$this->getEndPoint(); $endPoint['lookupType'] = self::TYPE_SHARED; $endPoint['className'] = $className; @@ -236,11 +236,11 @@ class Swift_DependencyContainer * * @param array $lookups * - * @return Swift_DependencyContainer + * @return $this */ public function withDependencies(array $lookups) { - $endPoint = &$this->_getEndPoint(); + $endPoint = &$this->getEndPoint(); $endPoint['args'] = array(); foreach ($lookups as $lookup) { $this->addConstructorLookup($lookup); @@ -257,11 +257,11 @@ class Swift_DependencyContainer * * @param mixed $value * - * @return Swift_DependencyContainer + * @return $this */ public function addConstructorValue($value) { - $endPoint = &$this->_getEndPoint(); + $endPoint = &$this->getEndPoint(); if (!isset($endPoint['args'])) { $endPoint['args'] = array(); } @@ -278,12 +278,12 @@ class Swift_DependencyContainer * * @param string $lookup * - * @return Swift_DependencyContainer + * @return $this */ public function addConstructorLookup($lookup) { - $endPoint = &$this->_getEndPoint(); - if (!isset($this->_endPoint['args'])) { + $endPoint = &$this->getEndPoint(); + if (!isset($this->endPoint['args'])) { $endPoint['args'] = array(); } $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup); @@ -292,21 +292,21 @@ class Swift_DependencyContainer } /** Get the literal value with $itemName */ - private function _getValue($itemName) + private function getValue($itemName) { - return $this->_store[$itemName]['value']; + return $this->store[$itemName]['value']; } /** Resolve an alias to another item */ - private function _createAlias($itemName) + private function createAlias($itemName) { - return $this->lookup($this->_store[$itemName]['ref']); + return $this->lookup($this->store[$itemName]['ref']); } /** Create a fresh instance of $itemName */ - private function _createNewInstance($itemName) + private function createNewInstance($itemName) { - $reflector = new ReflectionClass($this->_store[$itemName]['className']); + $reflector = new ReflectionClass($this->store[$itemName]['className']); if ($reflector->getConstructor()) { return $reflector->newInstanceArgs( $this->createDependenciesFor($itemName) @@ -317,35 +317,35 @@ class Swift_DependencyContainer } /** Create and register a shared instance of $itemName */ - private function _createSharedInstance($itemName) + private function createSharedInstance($itemName) { - if (!isset($this->_store[$itemName]['instance'])) { - $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName); + if (!isset($this->store[$itemName]['instance'])) { + $this->store[$itemName]['instance'] = $this->createNewInstance($itemName); } - return $this->_store[$itemName]['instance']; + return $this->store[$itemName]['instance']; } /** Get the current endpoint in the store */ - private function &_getEndPoint() + private function &getEndPoint() { - if (!isset($this->_endPoint)) { + if (!isset($this->endPoint)) { throw new BadMethodCallException( 'Component must first be registered by calling register()' ); } - return $this->_endPoint; + return $this->endPoint; } /** Get an argument list with dependencies resolved */ - private function _resolveArgs(array $args) + private function resolveArgs(array $args) { $resolved = array(); foreach ($args as $argDefinition) { switch ($argDefinition['type']) { case 'lookup': - $resolved[] = $this->_lookupRecursive($argDefinition['item']); + $resolved[] = $this->lookupRecursive($argDefinition['item']); break; case 'value': $resolved[] = $argDefinition['item']; @@ -357,12 +357,12 @@ class Swift_DependencyContainer } /** Resolve a single dependency with an collections */ - private function _lookupRecursive($item) + private function lookupRecursive($item) { if (is_array($item)) { $collection = array(); foreach ($item as $k => $v) { - $collection[$k] = $this->_lookupRecursive($v); + $collection[$k] = $this->lookupRecursive($v); } return $collection; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/EmbeddedFile.php b/htdocs/includes/swiftmailer/lib/classes/Swift/EmbeddedFile.php index d8c72ad4dfb..9755a88e176 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/EmbeddedFile.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/EmbeddedFile.php @@ -39,20 +39,6 @@ class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile } } - /** - * Create a new EmbeddedFile. - * - * @param string|Swift_OutputByteStream $data - * @param string $filename - * @param string $contentType - * - * @return Swift_Mime_EmbeddedFile - */ - public static function newInstance($data = null, $filename = null, $contentType = null) - { - return new self($data, $filename, $contentType); - } - /** * Create a new EmbeddedFile from a filesystem path. * @@ -62,8 +48,6 @@ class Swift_EmbeddedFile extends Swift_Mime_EmbeddedFile */ public static function fromPath($path) { - return self::newInstance()->setFile( - new Swift_ByteStream_FileByteStream($path) - ); + return (new self())->setFile(new Swift_ByteStream_FileByteStream($path)); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php index 8a81fe39709..b23b07702ef 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -13,7 +13,7 @@ * * Possibly the most accurate RFC 2045 QP implementation found in PHP. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_Encoder_QpEncoder implements Swift_Encoder { @@ -22,21 +22,21 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder * * @var Swift_CharacterStream */ - protected $_charStream; + protected $charStream; /** * A filter used if input should be canonicalized. * * @var Swift_StreamFilter */ - protected $_filter; + protected $filter; /** * Pre-computed QP for HUGE optimization. * * @var string[] */ - protected static $_qpMap = array( + protected static $qpMap = array( 0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', @@ -91,14 +91,14 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder 255 => '=FF', ); - protected static $_safeMapShare = array(); + protected static $safeMapShare = array(); /** * A map of non-encoded ascii characters. * * @var string[] */ - protected $_safeMap = array(); + protected $safeMap = array(); /** * Creates a new QpEncoder for the given CharacterStream. @@ -108,28 +108,28 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder */ public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null) { - $this->_charStream = $charStream; - if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->charStream = $charStream; + if (!isset(self::$safeMapShare[$this->getSafeMapShareId()])) { $this->initSafeMap(); - self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + self::$safeMapShare[$this->getSafeMapShareId()] = $this->safeMap; } else { - $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + $this->safeMap = self::$safeMapShare[$this->getSafeMapShareId()]; } - $this->_filter = $filter; + $this->filter = $filter; } public function __sleep() { - return array('_charStream', '_filter'); + return array('charStream', 'filter'); } public function __wakeup() { - if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + if (!isset(self::$safeMapShare[$this->getSafeMapShareId()])) { $this->initSafeMap(); - self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + self::$safeMapShare[$this->getSafeMapShareId()] = $this->safeMap; } else { - $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + $this->safeMap = self::$safeMapShare[$this->getSafeMapShareId()]; } } @@ -142,7 +142,7 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder { foreach (array_merge( array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) { - $this->_safeMap[$byte] = chr($byte); + $this->safeMap[$byte] = chr($byte); } } @@ -173,19 +173,19 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder $currentLine = &$lines[$lNo++]; $size = $lineLen = 0; - $this->_charStream->flushContents(); - $this->_charStream->importString($string); + $this->charStream->flushContents(); + $this->charStream->importString($string); // Fetching more than 4 chars at one is slower, as is fetching fewer bytes // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes - while (false !== $bytes = $this->_nextSequence()) { + while (false !== $bytes = $this->nextSequence()) { // If we're filtering the input - if (isset($this->_filter)) { + if (isset($this->filter)) { // If we can't filter because we need more bytes - while ($this->_filter->shouldBuffer($bytes)) { + while ($this->filter->shouldBuffer($bytes)) { // Then collect bytes into the buffer - if (false === $moreBytes = $this->_nextSequence(1)) { + if (false === $moreBytes = $this->nextSequence(1)) { break; } @@ -194,10 +194,10 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder } } // And filter them - $bytes = $this->_filter->filter($bytes); + $bytes = $this->filter->filter($bytes); } - $enc = $this->_encodeByteSequence($bytes, $size); + $enc = $this->encodeByteSequence($bytes, $size); $i = strpos($enc, '=0D=0A'); $newLineLength = $lineLen + ($i === false ? $size : $i); @@ -219,7 +219,7 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder } } - return $this->_standardize(implode("=\r\n", $lines)); + return $this->standardize(implode("=\r\n", $lines)); } /** @@ -229,27 +229,27 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder */ public function charsetChanged($charset) { - $this->_charStream->setCharacterSet($charset); + $this->charStream->setCharacterSet($charset); } /** * Encode the given byte array into a verbatim QP form. * - * @param integer[] $bytes - * @param int $size + * @param int[] $bytes + * @param int $size * * @return string */ - protected function _encodeByteSequence(array $bytes, &$size) + protected function encodeByteSequence(array $bytes, &$size) { $ret = ''; $size = 0; foreach ($bytes as $b) { - if (isset($this->_safeMap[$b])) { - $ret .= $this->_safeMap[$b]; + if (isset($this->safeMap[$b])) { + $ret .= $this->safeMap[$b]; ++$size; } else { - $ret .= self::$_qpMap[$b]; + $ret .= self::$qpMap[$b]; $size += 3; } } @@ -262,11 +262,11 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder * * @param int $size number of bytes to read * - * @return integer[] + * @return int[] */ - protected function _nextSequence($size = 4) + protected function nextSequence($size = 4) { - return $this->_charStream->readBytes($size); + return $this->charStream->readBytes($size); } /** @@ -276,7 +276,7 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder * * @return string */ - protected function _standardize($string) + protected function standardize($string) { $string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'), array("=09\r\n", "=20\r\n", "\r\n"), $string @@ -284,7 +284,7 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder switch ($end = ord(substr($string, -1))) { case 0x09: case 0x20: - $string = substr_replace($string, self::$_qpMap[$end], -1); + $string = substr_replace($string, self::$qpMap[$end], -1); } return $string; @@ -295,6 +295,6 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder */ public function __clone() { - $this->_charStream = clone $this->_charStream; + $this->charStream = clone $this->charStream; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php index b0215e88380..bebb13494bd 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -20,7 +20,7 @@ class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder * * @var Swift_CharacterStream */ - private $_charStream; + private $charStream; /** * Creates a new Rfc2231Encoder using the given character stream instance. @@ -29,7 +29,7 @@ class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder */ public function __construct(Swift_CharacterStream $charStream) { - $this->_charStream = $charStream; + $this->charStream = $charStream; } /** @@ -53,12 +53,12 @@ class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder $maxLineLength = 75; } - $this->_charStream->flushContents(); - $this->_charStream->importString($string); + $this->charStream->flushContents(); + $this->charStream->importString($string); $thisLineLength = $maxLineLength - $firstLineOffset; - while (false !== $char = $this->_charStream->read(4)) { + while (false !== $char = $this->charStream->read(4)) { $encodedChar = rawurlencode($char); if (0 != strlen($currentLine) && strlen($currentLine.$encodedChar) > $thisLineLength) { @@ -79,7 +79,7 @@ class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder */ public function charsetChanged($charset) { - $this->_charStream->setCharacterSet($charset); + $this->charStream->setCharacterSet($charset); } /** @@ -87,6 +87,6 @@ class Swift_Encoder_Rfc2231Encoder implements Swift_Encoder */ public function __clone() { - $this->_charStream = clone $this->_charStream; + $this->charStream = clone $this->charStream; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Encoding.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Encoding.php deleted file mode 100644 index 253977b608e..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Encoding.php +++ /dev/null @@ -1,64 +0,0 @@ -lookup($key); - } -} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/CommandEvent.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/CommandEvent.php index 7dc381d9844..9834aa95883 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/CommandEvent.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -20,14 +20,14 @@ class Swift_Events_CommandEvent extends Swift_Events_EventObject * * @var string */ - private $_command; + private $command; /** * An array of codes which a successful response will contain. * - * @var integer[] + * @var int[] */ - private $_successCodes = array(); + private $successCodes = array(); /** * Create a new CommandEvent for $source with $command. @@ -39,8 +39,8 @@ class Swift_Events_CommandEvent extends Swift_Events_EventObject public function __construct(Swift_Transport $source, $command, $successCodes = array()) { parent::__construct($source); - $this->_command = $command; - $this->_successCodes = $successCodes; + $this->command = $command; + $this->successCodes = $successCodes; } /** @@ -50,16 +50,16 @@ class Swift_Events_CommandEvent extends Swift_Events_EventObject */ public function getCommand() { - return $this->_command; + return $this->command; } /** * Get the numeric response codes which indicate success for this command. * - * @return integer[] + * @return int[] */ public function getSuccessCodes() { - return $this->_successCodes; + return $this->successCodes; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php index aac36aaa768..f602608eeb6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php @@ -19,11 +19,11 @@ interface Swift_Events_EventDispatcher * Create a new SendEvent for $source and $message. * * @param Swift_Transport $source - * @param Swift_Mime_Message + * @param Swift_Mime_SimpleMessage * * @return Swift_Events_SendEvent */ - public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message); + public function createSendEvent(Swift_Transport $source, Swift_Mime_SimpleMessage $message); /** * Create a new CommandEvent for $source and $command. diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventObject.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventObject.php index 90694a9a600..21e7c8127d6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventObject.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/EventObject.php @@ -16,10 +16,10 @@ class Swift_Events_EventObject implements Swift_Events_Event { /** The source of this Event */ - private $_source; + private $source; /** The state of this Event (should it bubble up the stack?) */ - private $_bubbleCancelled = false; + private $bubbleCancelled = false; /** * Create a new EventObject originating at $source. @@ -28,7 +28,7 @@ class Swift_Events_EventObject implements Swift_Events_Event */ public function __construct($source) { - $this->_source = $source; + $this->source = $source; } /** @@ -38,7 +38,7 @@ class Swift_Events_EventObject implements Swift_Events_Event */ public function getSource() { - return $this->_source; + return $this->source; } /** @@ -48,7 +48,7 @@ class Swift_Events_EventObject implements Swift_Events_Event */ public function cancelBubble($cancel = true) { - $this->_bubbleCancelled = $cancel; + $this->bubbleCancelled = $cancel; } /** @@ -58,6 +58,6 @@ class Swift_Events_EventObject implements Swift_Events_Event */ public function bubbleCancelled() { - return $this->_bubbleCancelled; + return $this->bubbleCancelled; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php index 2e92ba9404f..5ae6c04b627 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -20,14 +20,14 @@ class Swift_Events_ResponseEvent extends Swift_Events_EventObject * * @var bool */ - private $_valid; + private $valid; /** * The response received from the server. * * @var string */ - private $_response; + private $response; /** * Create a new ResponseEvent for $source and $response. @@ -39,8 +39,8 @@ class Swift_Events_ResponseEvent extends Swift_Events_EventObject public function __construct(Swift_Transport $source, $response, $valid = false) { parent::__construct($source); - $this->_response = $response; - $this->_valid = $valid; + $this->response = $response; + $this->valid = $valid; } /** @@ -50,7 +50,7 @@ class Swift_Events_ResponseEvent extends Swift_Events_EventObject */ public function getResponse() { - return $this->_response; + return $this->response; } /** @@ -60,6 +60,6 @@ class Swift_Events_ResponseEvent extends Swift_Events_EventObject */ public function isValid() { - return $this->_valid; + return $this->valid; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SendEvent.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SendEvent.php index 10da8080f09..5dc8efb1fd6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SendEvent.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SendEvent.php @@ -33,35 +33,35 @@ class Swift_Events_SendEvent extends Swift_Events_EventObject /** * The Message being sent. * - * @var Swift_Mime_Message + * @var Swift_Mime_SimpleMessage */ - private $_message; + private $message; /** * Any recipients which failed after sending. * * @var string[] */ - private $_failedRecipients = array(); + private $failedRecipients = array(); /** * The overall result as a bitmask from the class constants. * * @var int */ - private $_result; + private $result; /** * Create a new SendEvent for $source and $message. * - * @param Swift_Transport $source - * @param Swift_Mime_Message $message + * @param Swift_Transport $source + * @param Swift_Mime_SimpleMessage $message */ - public function __construct(Swift_Transport $source, Swift_Mime_Message $message) + public function __construct(Swift_Transport $source, Swift_Mime_SimpleMessage $message) { parent::__construct($source); - $this->_message = $message; - $this->_result = self::RESULT_PENDING; + $this->message = $message; + $this->result = self::RESULT_PENDING; } /** @@ -77,11 +77,11 @@ class Swift_Events_SendEvent extends Swift_Events_EventObject /** * Get the Message being sent. * - * @return Swift_Mime_Message + * @return Swift_Mime_SimpleMessage */ public function getMessage() { - return $this->_message; + return $this->message; } /** @@ -91,7 +91,7 @@ class Swift_Events_SendEvent extends Swift_Events_EventObject */ public function setFailedRecipients($recipients) { - $this->_failedRecipients = $recipients; + $this->failedRecipients = $recipients; } /** @@ -101,7 +101,7 @@ class Swift_Events_SendEvent extends Swift_Events_EventObject */ public function getFailedRecipients() { - return $this->_failedRecipients; + return $this->failedRecipients; } /** @@ -111,7 +111,7 @@ class Swift_Events_SendEvent extends Swift_Events_EventObject */ public function setResult($result) { - $this->_result = $result; + $this->result = $result; } /** @@ -124,6 +124,6 @@ class Swift_Events_SendEvent extends Swift_Events_EventObject */ public function getResult() { - return $this->_result; + return $this->result; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php index e8aca752f0d..815fa4d922b 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php @@ -16,20 +16,20 @@ class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher { /** A map of event types to their associated listener types */ - private $_eventMap = array(); + private $eventMap = array(); /** Event listeners bound to this dispatcher */ - private $_listeners = array(); + private $listeners = array(); /** Listeners queued to have an Event bubbled up the stack to them */ - private $_bubbleQueue = array(); + private $bubbleQueue = array(); /** * Create a new EventDispatcher. */ public function __construct() { - $this->_eventMap = array( + $this->eventMap = array( 'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener', 'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener', 'Swift_Events_SendEvent' => 'Swift_Events_SendListener', @@ -42,11 +42,11 @@ class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher * Create a new SendEvent for $source and $message. * * @param Swift_Transport $source - * @param Swift_Mime_Message + * @param Swift_Mime_SimpleMessage * * @return Swift_Events_SendEvent */ - public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message) + public function createSendEvent(Swift_Transport $source, Swift_Mime_SimpleMessage $message) { return new Swift_Events_SendEvent($source, $message); } @@ -111,13 +111,13 @@ class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher */ public function bindEventListener(Swift_Events_EventListener $listener) { - foreach ($this->_listeners as $l) { + foreach ($this->listeners as $l) { // Already loaded if ($l === $listener) { return; } } - $this->_listeners[] = $listener; + $this->listeners[] = $listener; } /** @@ -128,29 +128,29 @@ class Swift_Events_SimpleEventDispatcher implements Swift_Events_EventDispatcher */ public function dispatchEvent(Swift_Events_EventObject $evt, $target) { - $this->_prepareBubbleQueue($evt); - $this->_bubble($evt, $target); + $this->prepareBubbleQueue($evt); + $this->bubble($evt, $target); } /** Queue listeners on a stack ready for $evt to be bubbled up it */ - private function _prepareBubbleQueue(Swift_Events_EventObject $evt) + private function prepareBubbleQueue(Swift_Events_EventObject $evt) { - $this->_bubbleQueue = array(); + $this->bubbleQueue = array(); $evtClass = get_class($evt); - foreach ($this->_listeners as $listener) { - if (array_key_exists($evtClass, $this->_eventMap) - && ($listener instanceof $this->_eventMap[$evtClass])) { - $this->_bubbleQueue[] = $listener; + foreach ($this->listeners as $listener) { + if (array_key_exists($evtClass, $this->eventMap) + && ($listener instanceof $this->eventMap[$evtClass])) { + $this->bubbleQueue[] = $listener; } } } /** Bubble $evt up the stack calling $target() on each listener */ - private function _bubble(Swift_Events_EventObject $evt, $target) + private function bubble(Swift_Events_EventObject $evt, $target) { - if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) { + if (!$evt->bubbleCancelled() && $listener = array_shift($this->bubbleQueue)) { $listener->$target($evt); - $this->_bubble($evt, $target); + $this->bubble($evt, $target); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php index f87154fb9b0..77534e3e033 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php @@ -20,7 +20,7 @@ class Swift_Events_TransportExceptionEvent extends Swift_Events_EventObject * * @var Swift_TransportException */ - private $_exception; + private $exception; /** * Create a new TransportExceptionEvent for $transport. @@ -31,7 +31,7 @@ class Swift_Events_TransportExceptionEvent extends Swift_Events_EventObject public function __construct(Swift_Transport $transport, Swift_TransportException $ex) { parent::__construct($transport); - $this->_exception = $ex; + $this->exception = $ex; } /** @@ -41,6 +41,6 @@ class Swift_Events_TransportExceptionEvent extends Swift_Events_EventObject */ public function getException() { - return $this->_exception; + return $this->exception; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/FailoverTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/FailoverTransport.php index 53f277da0a2..dc2859f74a0 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/FailoverTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/FailoverTransport.php @@ -30,16 +30,4 @@ class Swift_FailoverTransport extends Swift_Transport_FailoverTransport $this->setTransports($transports); } - - /** - * Create a new FailoverTransport instance. - * - * @param Swift_Transport[] $transports - * - * @return Swift_FailoverTransport - */ - public static function newInstance($transports = array()) - { - return new self($transports); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/FileSpool.php b/htdocs/includes/swiftmailer/lib/classes/Swift/FileSpool.php index 220853989d3..b0b9eb064c5 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/FileSpool.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/FileSpool.php @@ -17,14 +17,14 @@ class Swift_FileSpool extends Swift_ConfigurableSpool { /** The spool directory */ - private $_path; + private $path; /** * File WriteRetry Limit. * * @var int */ - private $_retryLimit = 10; + private $retryLimit = 10; /** * Create a new FileSpool. @@ -35,11 +35,11 @@ class Swift_FileSpool extends Swift_ConfigurableSpool */ public function __construct($path) { - $this->_path = $path; + $this->path = $path; - if (!file_exists($this->_path)) { - if (!mkdir($this->_path, 0777, true)) { - throw new Swift_IoException('Unable to create Path ['.$this->_path.']'); + if (!file_exists($this->path)) { + if (!mkdir($this->path, 0777, true)) { + throw new Swift_IoException(sprintf('Unable to create path "%s".', $this->path)); } } } @@ -77,25 +77,25 @@ class Swift_FileSpool extends Swift_ConfigurableSpool */ public function setRetryLimit($limit) { - $this->_retryLimit = $limit; + $this->retryLimit = $limit; } /** * Queues a message. * - * @param Swift_Mime_Message $message The message to store + * @param Swift_Mime_SimpleMessage $message The message to store * * @throws Swift_IoException * * @return bool */ - public function queueMessage(Swift_Mime_Message $message) + public function queueMessage(Swift_Mime_SimpleMessage $message) { $ser = serialize($message); - $fileName = $this->_path.'/'.$this->getRandomString(10); - for ($i = 0; $i < $this->_retryLimit; ++$i) { + $fileName = $this->path.'/'.$this->getRandomString(10); + for ($i = 0; $i < $this->retryLimit; ++$i) { /* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */ - $fp = @fopen($fileName.'.message', 'x'); + $fp = @fopen($fileName.'.message', 'xb'); if (false !== $fp) { if (false === fwrite($fp, $ser)) { return false; @@ -108,7 +108,7 @@ class Swift_FileSpool extends Swift_ConfigurableSpool } } - throw new Swift_IoException('Unable to create a file for enqueuing Message'); + throw new Swift_IoException(sprintf('Unable to create a file for enqueuing Message in "%s".', $this->path)); } /** @@ -118,7 +118,7 @@ class Swift_FileSpool extends Swift_ConfigurableSpool */ public function recover($timeout = 900) { - foreach (new DirectoryIterator($this->_path) as $file) { + foreach (new DirectoryIterator($this->path) as $file) { $file = $file->getRealPath(); if (substr($file, -16) == '.message.sending') { @@ -140,7 +140,7 @@ class Swift_FileSpool extends Swift_ConfigurableSpool */ public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) { - $directoryIterator = new DirectoryIterator($this->_path); + $directoryIterator = new DirectoryIterator($this->path); /* Start the transport only if there are queued files to send */ if (!$transport->isStarted()) { @@ -200,7 +200,7 @@ class Swift_FileSpool extends Swift_ConfigurableSpool $ret = ''; $strlen = strlen($base); for ($i = 0; $i < $count; ++$i) { - $ret .= $base[((int) rand(0, $strlen - 1))]; + $ret .= $base[random_int(0, $strlen - 1)]; } return $ret; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/IdGenerator.php b/htdocs/includes/swiftmailer/lib/classes/Swift/IdGenerator.php new file mode 100644 index 00000000000..c845d85e3bd --- /dev/null +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/IdGenerator.php @@ -0,0 +1,22 @@ +setFile( - new Swift_ByteStream_FileByteStream($path) - ); - - return $image; + return (new self())->setFile(new Swift_ByteStream_FileByteStream($path)); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php b/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php index b37f07f74bd..cdd08503004 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php @@ -20,14 +20,14 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache * * @var array */ - private $_contents = array(); + private $contents = array(); /** * An InputStream for cloning. * * @var Swift_KeyCache_KeyCacheInputStream */ - private $_stream; + private $stream; /** * Create a new ArrayKeyCache with the given $stream for cloning to make @@ -37,7 +37,7 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function __construct(Swift_KeyCache_KeyCacheInputStream $stream) { - $this->_stream = $stream; + $this->stream = $stream; } /** @@ -52,16 +52,16 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function setString($nsKey, $itemKey, $string, $mode) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); switch ($mode) { case self::MODE_WRITE: - $this->_contents[$nsKey][$itemKey] = $string; + $this->contents[$nsKey][$itemKey] = $string; break; case self::MODE_APPEND: if (!$this->hasKey($nsKey, $itemKey)) { - $this->_contents[$nsKey][$itemKey] = ''; + $this->contents[$nsKey][$itemKey] = ''; } - $this->_contents[$nsKey][$itemKey] .= $string; + $this->contents[$nsKey][$itemKey] .= $string; break; default: throw new Swift_SwiftException( @@ -83,16 +83,16 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); switch ($mode) { case self::MODE_WRITE: $this->clearKey($nsKey, $itemKey); case self::MODE_APPEND: if (!$this->hasKey($nsKey, $itemKey)) { - $this->_contents[$nsKey][$itemKey] = ''; + $this->contents[$nsKey][$itemKey] = ''; } while (false !== $bytes = $os->read(8192)) { - $this->_contents[$nsKey][$itemKey] .= $bytes; + $this->contents[$nsKey][$itemKey] .= $bytes; } break; default: @@ -116,7 +116,7 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) { - $is = clone $this->_stream; + $is = clone $this->stream; $is->setKeyCache($this); $is->setNsKey($nsKey); $is->setItemKey($itemKey); @@ -137,9 +137,9 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function getString($nsKey, $itemKey) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); if ($this->hasKey($nsKey, $itemKey)) { - return $this->_contents[$nsKey][$itemKey]; + return $this->contents[$nsKey][$itemKey]; } } @@ -152,7 +152,7 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); $is->write($this->getString($nsKey, $itemKey)); } @@ -166,9 +166,9 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function hasKey($nsKey, $itemKey) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); - return array_key_exists($itemKey, $this->_contents[$nsKey]); + return array_key_exists($itemKey, $this->contents[$nsKey]); } /** @@ -179,7 +179,7 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function clearKey($nsKey, $itemKey) { - unset($this->_contents[$nsKey][$itemKey]); + unset($this->contents[$nsKey][$itemKey]); } /** @@ -189,7 +189,7 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache */ public function clearAll($nsKey) { - unset($this->_contents[$nsKey]); + unset($this->contents[$nsKey]); } /** @@ -197,10 +197,10 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache * * @param string $nsKey */ - private function _prepareCache($nsKey) + private function prepareCache($nsKey) { - if (!array_key_exists($nsKey, $this->_contents)) { - $this->_contents[$nsKey] = array(); + if (!array_key_exists($nsKey, $this->contents)) { + $this->contents[$nsKey] = array(); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php b/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php index 453f50a15db..d6a633a1512 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -29,28 +29,21 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache * * @var Swift_KeyCache_KeyCacheInputStream */ - private $_stream; + private $stream; /** * A path to write to. * * @var string */ - private $_path; + private $path; /** * Stored keys. * * @var array */ - private $_keys = array(); - - /** - * Will be true if magic_quotes_runtime is turned on. - * - * @var bool - */ - private $_quotes = false; + private $keys = array(); /** * Create a new DiskKeyCache with the given $stream for cloning to make @@ -61,12 +54,8 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function __construct(Swift_KeyCache_KeyCacheInputStream $stream, $path) { - $this->_stream = $stream; - $this->_path = $path; - - if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { - $this->_quotes = true; - } + $this->stream = $stream; + $this->path = $path; } /** @@ -83,13 +72,13 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function setString($nsKey, $itemKey, $string, $mode) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); switch ($mode) { case self::MODE_WRITE: - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START); break; case self::MODE_APPEND: - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_END); break; default: throw new Swift_SwiftException( @@ -99,7 +88,7 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache break; } fwrite($fp, $string); - $this->_freeHandle($nsKey, $itemKey); + $this->freeHandle($nsKey, $itemKey); } /** @@ -116,13 +105,13 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); switch ($mode) { case self::MODE_WRITE: - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START); break; case self::MODE_APPEND: - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_END); break; default: throw new Swift_SwiftException( @@ -134,7 +123,7 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache while (false !== $bytes = $os->read(8192)) { fwrite($fp, $bytes); } - $this->_freeHandle($nsKey, $itemKey); + $this->freeHandle($nsKey, $itemKey); } /** @@ -150,7 +139,7 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) { - $is = clone $this->_stream; + $is = clone $this->stream; $is->setKeyCache($this); $is->setNsKey($nsKey); $is->setItemKey($itemKey); @@ -173,20 +162,14 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function getString($nsKey, $itemKey) { - $this->_prepareCache($nsKey); + $this->prepareCache($nsKey); if ($this->hasKey($nsKey, $itemKey)) { - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); - if ($this->_quotes) { - ini_set('magic_quotes_runtime', 0); - } + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START); $str = ''; while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { $str .= $bytes; } - if ($this->_quotes) { - ini_set('magic_quotes_runtime', 1); - } - $this->_freeHandle($nsKey, $itemKey); + $this->freeHandle($nsKey, $itemKey); return $str; } @@ -202,17 +185,11 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) { if ($this->hasKey($nsKey, $itemKey)) { - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); - if ($this->_quotes) { - ini_set('magic_quotes_runtime', 0); - } + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_START); while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { $is->write($bytes); } - if ($this->_quotes) { - ini_set('magic_quotes_runtime', 1); - } - $this->_freeHandle($nsKey, $itemKey); + $this->freeHandle($nsKey, $itemKey); } } @@ -226,7 +203,7 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function hasKey($nsKey, $itemKey) { - return is_file($this->_path.'/'.$nsKey.'/'.$itemKey); + return is_file($this->path.'/'.$nsKey.'/'.$itemKey); } /** @@ -238,8 +215,8 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache public function clearKey($nsKey, $itemKey) { if ($this->hasKey($nsKey, $itemKey)) { - $this->_freeHandle($nsKey, $itemKey); - unlink($this->_path.'/'.$nsKey.'/'.$itemKey); + $this->freeHandle($nsKey, $itemKey); + unlink($this->path.'/'.$nsKey.'/'.$itemKey); } } @@ -250,14 +227,14 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function clearAll($nsKey) { - if (array_key_exists($nsKey, $this->_keys)) { - foreach ($this->_keys[$nsKey] as $itemKey => $null) { + if (array_key_exists($nsKey, $this->keys)) { + foreach ($this->keys[$nsKey] as $itemKey => $null) { $this->clearKey($nsKey, $itemKey); } - if (is_dir($this->_path.'/'.$nsKey)) { - rmdir($this->_path.'/'.$nsKey); + if (is_dir($this->path.'/'.$nsKey)) { + rmdir($this->path.'/'.$nsKey); } - unset($this->_keys[$nsKey]); + unset($this->keys[$nsKey]); } } @@ -266,14 +243,14 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache * * @param string $nsKey */ - private function _prepareCache($nsKey) + private function prepareCache($nsKey) { - $cacheDir = $this->_path.'/'.$nsKey; + $cacheDir = $this->path.'/'.$nsKey; if (!is_dir($cacheDir)) { if (!mkdir($cacheDir)) { throw new Swift_IoException('Failed to create cache directory '.$cacheDir); } - $this->_keys[$nsKey] = array(); + $this->keys[$nsKey] = array(); } } @@ -286,27 +263,27 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache * * @return resource */ - private function _getHandle($nsKey, $itemKey, $position) + private function getHandle($nsKey, $itemKey, $position) { - if (!isset($this->_keys[$nsKey][$itemKey])) { + if (!isset($this->keys[$nsKey][$itemKey])) { $openMode = $this->hasKey($nsKey, $itemKey) ? 'r+b' : 'w+b'; - $fp = fopen($this->_path.'/'.$nsKey.'/'.$itemKey, $openMode); - $this->_keys[$nsKey][$itemKey] = $fp; + $fp = fopen($this->path.'/'.$nsKey.'/'.$itemKey, $openMode); + $this->keys[$nsKey][$itemKey] = $fp; } if (self::POSITION_START == $position) { - fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET); + fseek($this->keys[$nsKey][$itemKey], 0, SEEK_SET); } elseif (self::POSITION_END == $position) { - fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END); + fseek($this->keys[$nsKey][$itemKey], 0, SEEK_END); } - return $this->_keys[$nsKey][$itemKey]; + return $this->keys[$nsKey][$itemKey]; } - private function _freeHandle($nsKey, $itemKey) + private function freeHandle($nsKey, $itemKey) { - $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT); + $fp = $this->getHandle($nsKey, $itemKey, self::POSITION_CURRENT); fclose($fp); - $this->_keys[$nsKey][$itemKey] = null; + $this->keys[$nsKey][$itemKey] = null; } /** @@ -314,7 +291,7 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache */ public function __destruct() { - foreach ($this->_keys as $nsKey => $null) { + foreach ($this->keys as $nsKey => $null) { $this->clearAll($nsKey); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php b/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php index b00d458a6de..a74df434c46 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php @@ -16,16 +16,16 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCacheInputStream { /** The KeyCache being written to */ - private $_keyCache; + private $keyCache; /** The nsKey of the KeyCache being written to */ - private $_nsKey; + private $nsKey; /** The itemKey of the KeyCache being written to */ - private $_itemKey; + private $itemKey; /** A stream to write through on each write() */ - private $_writeThrough = null; + private $writeThrough = null; /** * Set the KeyCache to wrap. @@ -34,7 +34,7 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function setKeyCache(Swift_KeyCache $keyCache) { - $this->_keyCache = $keyCache; + $this->keyCache = $keyCache; } /** @@ -44,7 +44,7 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function setWriteThroughStream(Swift_InputByteStream $is) { - $this->_writeThrough = $is; + $this->writeThrough = $is; } /** @@ -55,14 +55,14 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function write($bytes, Swift_InputByteStream $is = null) { - $this->_keyCache->setString( - $this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND + $this->keyCache->setString( + $this->nsKey, $this->itemKey, $bytes, Swift_KeyCache::MODE_APPEND ); if (isset($is)) { $is->write($bytes); } - if (isset($this->_writeThrough)) { - $this->_writeThrough->write($bytes); + if (isset($this->writeThrough)) { + $this->writeThrough->write($bytes); } } @@ -93,7 +93,7 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function flushBuffers() { - $this->_keyCache->clearKey($this->_nsKey, $this->_itemKey); + $this->keyCache->clearKey($this->nsKey, $this->itemKey); } /** @@ -103,7 +103,7 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function setNsKey($nsKey) { - $this->_nsKey = $nsKey; + $this->nsKey = $nsKey; } /** @@ -113,7 +113,7 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function setItemKey($itemKey) { - $this->_itemKey = $itemKey; + $this->itemKey = $itemKey; } /** @@ -122,6 +122,6 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach */ public function __clone() { - $this->_writeThrough = null; + $this->writeThrough = null; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php index fdba9df50dc..03106e55d4e 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -30,16 +30,4 @@ class Swift_LoadBalancedTransport extends Swift_Transport_LoadBalancedTransport $this->setTransports($transports); } - - /** - * Create a new LoadBalancedTransport instance. - * - * @param array $transports - * - * @return Swift_LoadBalancedTransport - */ - public static function newInstance($transports = array()) - { - return new self($transports); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/MailTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/MailTransport.php deleted file mode 100644 index 858ca814792..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/MailTransport.php +++ /dev/null @@ -1,45 +0,0 @@ -createDependenciesFor('transport.mail') - ); - - $this->setExtraParams($extraParams); - } - - /** - * Create a new MailTransport instance. - * - * @param string $extraParams To be passed to mail() - * - * @return Swift_MailTransport - */ - public static function newInstance($extraParams = '-f%s') - { - return new self($extraParams); - } -} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer.php index 34a78d47b54..3561ec6e68e 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer.php @@ -16,7 +16,7 @@ class Swift_Mailer { /** The Transport used to send messages */ - private $_transport; + private $transport; /** * Create a new Mailer using $transport for delivery. @@ -25,19 +25,7 @@ class Swift_Mailer */ public function __construct(Swift_Transport $transport) { - $this->_transport = $transport; - } - - /** - * Create a new Mailer instance. - * - * @param Swift_Transport $transport - * - * @return Swift_Mailer - */ - public static function newInstance(Swift_Transport $transport) - { - return new self($transport); + $this->transport = $transport; } /** @@ -66,23 +54,23 @@ class Swift_Mailer * The return value is the number of recipients who were accepted for * delivery. * - * @param Swift_Mime_Message $message - * @param array $failedRecipients An array of failures by-reference + * @param Swift_Mime_SimpleMessage $message + * @param array $failedRecipients An array of failures by-reference * - * @return int + * @return int The number of successful recipients. Can be 0 which indicates failure */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { $failedRecipients = (array) $failedRecipients; - if (!$this->_transport->isStarted()) { - $this->_transport->start(); + if (!$this->transport->isStarted()) { + $this->transport->start(); } $sent = 0; try { - $sent = $this->_transport->send($message, $failedRecipients); + $sent = $this->transport->send($message, $failedRecipients); } catch (Swift_RfcComplianceException $e) { foreach ($message->getTo() as $address => $name) { $failedRecipients[] = $address; @@ -99,7 +87,7 @@ class Swift_Mailer */ public function registerPlugin(Swift_Events_EventListener $plugin) { - $this->_transport->registerPlugin($plugin); + $this->transport->registerPlugin($plugin); } /** @@ -109,6 +97,6 @@ class Swift_Mailer */ public function getTransport() { - return $this->_transport; + return $this->transport; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php index e3e6cad05bd..84dacb5f185 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -20,7 +20,7 @@ class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientItera * * @var array */ - private $_recipients = array(); + private $recipients = array(); /** * Create a new ArrayRecipientIterator from $recipients. @@ -29,7 +29,7 @@ class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientItera */ public function __construct(array $recipients) { - $this->_recipients = $recipients; + $this->recipients = $recipients; } /** @@ -39,7 +39,7 @@ class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientItera */ public function hasNext() { - return !empty($this->_recipients); + return !empty($this->recipients); } /** @@ -50,6 +50,6 @@ class Swift_Mailer_ArrayRecipientIterator implements Swift_Mailer_RecipientItera */ public function nextRecipient() { - return array_splice($this->_recipients, 0, 1); + return array_splice($this->recipients, 0, 1); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/MemorySpool.php b/htdocs/includes/swiftmailer/lib/classes/Swift/MemorySpool.php index 5b239694d81..9aa62835b32 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/MemorySpool.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/MemorySpool.php @@ -16,6 +16,7 @@ class Swift_MemorySpool implements Swift_Spool { protected $messages = array(); + private $flushRetries = 3; /** * Tests if this Transport mechanism has started. @@ -41,14 +42,22 @@ class Swift_MemorySpool implements Swift_Spool { } + /** + * @param int $retries + */ + public function setFlushRetries($retries) + { + $this->flushRetries = $retries; + } + /** * Stores a message in the queue. * - * @param Swift_Mime_Message $message The message to store + * @param Swift_Mime_SimpleMessage $message The message to store * * @return bool Whether the operation has succeeded */ - public function queueMessage(Swift_Mime_Message $message) + public function queueMessage(Swift_Mime_SimpleMessage $message) { //clone the message to make sure it is not changed while in the queue $this->messages[] = clone $message; @@ -75,8 +84,25 @@ class Swift_MemorySpool implements Swift_Spool } $count = 0; - while ($message = array_pop($this->messages)) { - $count += $transport->send($message, $failedRecipients); + $retries = $this->flushRetries; + while ($retries--) { + try { + while ($message = array_pop($this->messages)) { + $count += $transport->send($message, $failedRecipients); + } + } catch (Swift_TransportException $exception) { + if ($retries) { + // re-queue the message at the end of the queue to give a chance + // to the other messages to be sent, in case the failure was due to + // this message and not just the transport failing + array_unshift($this->messages, $message); + + // wait half a second before we try again + usleep(500000); + } else { + throw $exception; + } + } } return $count; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Message.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Message.php index 11aa5a9a06a..00f6e160027 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Message.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Message.php @@ -60,21 +60,6 @@ class Swift_Message extends Swift_Mime_SimpleMessage } } - /** - * Create a new Message. - * - * @param string $subject - * @param string $body - * @param string $contentType - * @param string $charset - * - * @return Swift_Message - */ - public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) - { - return new self($subject, $body, $contentType, $charset); - } - /** * Add a MimePart to this Message. * @@ -82,21 +67,19 @@ class Swift_Message extends Swift_Mime_SimpleMessage * @param string $contentType * @param string $charset * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function addPart($body, $contentType = null, $charset = null) { - return $this->attach(Swift_MimePart::newInstance( - $body, $contentType, $charset - )); + return $this->attach((new Swift_MimePart($body, $contentType, $charset))->setEncoder($this->getEncoder())); } /** - * Attach a new signature handler to the message. + * Detach a signature handler from a message. * * @param Swift_Signer $signer * - * @return Swift_Message + * @return $this */ public function attachSigner(Swift_Signer $signer) { @@ -114,7 +97,7 @@ class Swift_Message extends Swift_Mime_SimpleMessage * * @param Swift_Signer $signer * - * @return Swift_Message + * @return $this */ public function detachSigner(Swift_Signer $signer) { @@ -207,7 +190,7 @@ class Swift_Message extends Swift_Mime_SimpleMessage $signer->setHeaders($this->getHeaders()); $signer->startBody(); - $this->_bodyToByteStream($signer); + $this->bodyToByteStream($signer); $signer->endBody(); $signer->addSignature($this->getHeaders()); @@ -223,7 +206,7 @@ class Swift_Message extends Swift_Mime_SimpleMessage $this->savedMessage['body'] = $this->getBody(); $this->savedMessage['children'] = $this->getChildren(); if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') { - $this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children'])); + $this->setChildren(array_merge(array($this->becomeMimePart()), $this->savedMessage['children'])); $this->setBody(''); } } @@ -281,11 +264,11 @@ class Swift_Message extends Swift_Mime_SimpleMessage { parent::__clone(); foreach ($this->bodySigners as $key => $bodySigner) { - $this->bodySigners[$key] = clone($bodySigner); + $this->bodySigners[$key] = clone $bodySigner; } foreach ($this->headerSigners as $key => $headerSigner) { - $this->headerSigners[$key] = clone($headerSigner); + $this->headerSigners[$key] = clone $headerSigner; } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Attachment.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Attachment.php index 46a5e8da946..a36ce7237cf 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Attachment.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -16,23 +16,23 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity { /** Recognized MIME types */ - private $_mimeTypes = array(); + private $mimeTypes = array(); /** * Create a new Attachment with $headers, $encoder and $cache. * - * @param Swift_Mime_HeaderSet $headers - * @param Swift_Mime_ContentEncoder $encoder - * @param Swift_KeyCache $cache - * @param Swift_Mime_Grammar $grammar - * @param array $mimeTypes optional + * @param Swift_Mime_SimpleHeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_IdGenerator $idGenerator + * @param array $mimeTypes */ - public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $mimeTypes = array()) + public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $mimeTypes = array()) { - parent::__construct($headers, $encoder, $cache, $grammar); + parent::__construct($headers, $encoder, $cache, $idGenerator); $this->setDisposition('attachment'); $this->setContentType('application/octet-stream'); - $this->_mimeTypes = $mimeTypes; + $this->mimeTypes = $mimeTypes; } /** @@ -56,7 +56,7 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity */ public function getDisposition() { - return $this->_getHeaderFieldModel('Content-Disposition'); + return $this->getHeaderFieldModel('Content-Disposition'); } /** @@ -64,11 +64,11 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity * * @param string $disposition * - * @return Swift_Mime_Attachment + * @return $this */ public function setDisposition($disposition) { - if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) { + if (!$this->setHeaderFieldModel('Content-Disposition', $disposition)) { $this->getHeaders()->addParameterizedHeader('Content-Disposition', $disposition); } @@ -82,7 +82,7 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity */ public function getFilename() { - return $this->_getHeaderParameter('Content-Disposition', 'filename'); + return $this->getHeaderParameter('Content-Disposition', 'filename'); } /** @@ -90,12 +90,12 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity * * @param string $filename * - * @return Swift_Mime_Attachment + * @return $this */ public function setFilename($filename) { - $this->_setHeaderParameter('Content-Disposition', 'filename', $filename); - $this->_setHeaderParameter('Content-Type', 'name', $filename); + $this->setHeaderParameter('Content-Disposition', 'filename', $filename); + $this->setHeaderParameter('Content-Type', 'name', $filename); return $this; } @@ -107,7 +107,7 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity */ public function getSize() { - return $this->_getHeaderParameter('Content-Disposition', 'size'); + return $this->getHeaderParameter('Content-Disposition', 'size'); } /** @@ -115,11 +115,11 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity * * @param int $size * - * @return Swift_Mime_Attachment + * @return $this */ public function setSize($size) { - $this->_setHeaderParameter('Content-Disposition', 'size', $size); + $this->setHeaderParameter('Content-Disposition', 'size', $size); return $this; } @@ -130,7 +130,7 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity * @param Swift_FileStream $file * @param string $contentType optional * - * @return Swift_Mime_Attachment + * @return $this */ public function setFile(Swift_FileStream $file, $contentType = null) { @@ -139,8 +139,8 @@ class Swift_Mime_Attachment extends Swift_Mime_SimpleMimeEntity if (!isset($contentType)) { $extension = strtolower(substr($file->getPath(), strrpos($file->getPath(), '.') + 1)); - if (array_key_exists($extension, $this->_mimeTypes)) { - $this->setContentType($this->_mimeTypes[$extension]); + if (array_key_exists($extension, $this->mimeTypes)) { + $this->setContentType($this->mimeTypes[$extension]); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php index 710b5ac9edc..34de4ef39a4 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php @@ -92,7 +92,7 @@ class Swift_Mime_ContentEncoder_NativeQpContentEncoder implements Swift_Mime_Con sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); } - return $this->_standardize(quoted_printable_encode($string)); + return $this->standardize(quoted_printable_encode($string)); } /** @@ -102,7 +102,7 @@ class Swift_Mime_ContentEncoder_NativeQpContentEncoder implements Swift_Mime_Con * * @return string */ - protected function _standardize($string) + protected function standardize($string) { // transform CR or LF to CRLF $string = preg_replace('~=0D(?!=0A)|(?_name = $name; - $this->_canonical = $canonical; + $this->name = $name; + $this->canonical = $canonical; } /** @@ -52,11 +52,11 @@ class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_Conten */ public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) { - if ($this->_canonical) { - $string = $this->_canonicalize($string); + if ($this->canonical) { + $string = $this->canonicalize($string); } - return $this->_safeWordWrap($string, $maxLineLength, "\r\n"); + return $this->safeWordwrap($string, $maxLineLength, "\r\n"); } /** @@ -72,10 +72,10 @@ class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_Conten $leftOver = ''; while (false !== $bytes = $os->read(8192)) { $toencode = $leftOver.$bytes; - if ($this->_canonical) { - $toencode = $this->_canonicalize($toencode); + if ($this->canonical) { + $toencode = $this->canonicalize($toencode); } - $wrapped = $this->_safeWordWrap($toencode, $maxLineLength, "\r\n"); + $wrapped = $this->safeWordwrap($toencode, $maxLineLength, "\r\n"); $lastLinePos = strrpos($wrapped, "\r\n"); $leftOver = substr($wrapped, $lastLinePos); $wrapped = substr($wrapped, 0, $lastLinePos); @@ -94,7 +94,7 @@ class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_Conten */ public function getName() { - return $this->_name; + return $this->name; } /** @@ -113,7 +113,7 @@ class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_Conten * * @return string */ - private function _safeWordwrap($string, $length = 75, $le = "\r\n") + private function safeWordwrap($string, $length = 75, $le = "\r\n") { if (0 >= $length) { return $string; @@ -151,7 +151,7 @@ class Swift_Mime_ContentEncoder_PlainContentEncoder implements Swift_Mime_Conten * * @return string */ - private function _canonicalize($string) + private function canonicalize($string) { return str_replace( array("\r\n", "\r", "\n"), diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php index 5cc907b8e7e..b3577dbe61f 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -11,11 +11,11 @@ /** * Handles Quoted Printable (QP) Transfer Encoding in Swift Mailer. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder implements Swift_Mime_ContentEncoder { - protected $_dotEscape; + protected $dotEscape; /** * Creates a new QpContentEncoder for the given CharacterStream. @@ -26,26 +26,26 @@ class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder */ public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null, $dotEscape = false) { - $this->_dotEscape = $dotEscape; + $this->dotEscape = $dotEscape; parent::__construct($charStream, $filter); } public function __sleep() { - return array('_charStream', '_filter', '_dotEscape'); + return array('charStream', 'filter', 'dotEscape'); } protected function getSafeMapShareId() { - return get_class($this).($this->_dotEscape ? '.dotEscape' : ''); + return get_class($this).($this->dotEscape ? '.dotEscape' : ''); } protected function initSafeMap() { parent::initSafeMap(); - if ($this->_dotEscape) { + if ($this->dotEscape) { /* Encode . as =2e for buggy remote servers */ - unset($this->_safeMap[0x2e]); + unset($this->safeMap[0x2e]); } } @@ -69,20 +69,20 @@ class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder $thisLineLength = $maxLineLength - $firstLineOffset; - $this->_charStream->flushContents(); - $this->_charStream->importByteStream($os); + $this->charStream->flushContents(); + $this->charStream->importByteStream($os); $currentLine = ''; $prepend = ''; $size = $lineLen = 0; - while (false !== $bytes = $this->_nextSequence()) { + while (false !== $bytes = $this->nextSequence()) { // If we're filtering the input - if (isset($this->_filter)) { + if (isset($this->filter)) { // If we can't filter because we need more bytes - while ($this->_filter->shouldBuffer($bytes)) { + while ($this->filter->shouldBuffer($bytes)) { // Then collect bytes into the buffer - if (false === $moreBytes = $this->_nextSequence(1)) { + if (false === $moreBytes = $this->nextSequence(1)) { break; } @@ -91,16 +91,16 @@ class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder } } // And filter them - $bytes = $this->_filter->filter($bytes); + $bytes = $this->filter->filter($bytes); } - $enc = $this->_encodeByteSequence($bytes, $size); + $enc = $this->encodeByteSequence($bytes, $size); $i = strpos($enc, '=0D=0A'); $newLineLength = $lineLen + ($i === false ? $size : $i); if ($currentLine && $newLineLength >= $thisLineLength) { - $is->write($prepend.$this->_standardize($currentLine)); + $is->write($prepend.$this->standardize($currentLine)); $currentLine = ''; $prepend = "=\r\n"; $thisLineLength = $maxLineLength; @@ -117,7 +117,7 @@ class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder } } if (strlen($currentLine)) { - $is->write($prepend.$this->_standardize($currentLine)); + $is->write($prepend.$this->standardize($currentLine)); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php index 6af757124d3..7835a492873 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -18,15 +18,15 @@ class Swift_Mime_EmbeddedFile extends Swift_Mime_Attachment /** * Creates a new Attachment with $headers and $encoder. * - * @param Swift_Mime_HeaderSet $headers - * @param Swift_Mime_ContentEncoder $encoder - * @param Swift_KeyCache $cache - * @param Swift_Mime_Grammar $grammar - * @param array $mimeTypes optional + * @param Swift_Mime_SimpleHeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_IdGenerator $idGenerator + * @param array $mimeTypes optional */ - public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $mimeTypes = array()) + public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $mimeTypes = array()) { - parent::__construct($headers, $encoder, $cache, $grammar, $mimeTypes); + parent::__construct($headers, $encoder, $cache, $idGenerator, $mimeTypes); $this->setDisposition('inline'); $this->setId($this->getId()); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Grammar.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Grammar.php deleted file mode 100644 index a09f338315c..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Grammar.php +++ /dev/null @@ -1,176 +0,0 @@ -init(); - } - - public function __wakeup() - { - $this->init(); - } - - protected function init() - { - if (count(self::$_specials) > 0) { - return; - } - - self::$_specials = array( - '(', ')', '<', '>', '[', ']', - ':', ';', '@', ',', '.', '"', - ); - - /*** Refer to RFC 2822 for ABNF grammar ***/ - - // All basic building blocks - self::$_grammar['NO-WS-CTL'] = '[\x01-\x08\x0B\x0C\x0E-\x19\x7F]'; - self::$_grammar['WSP'] = '[ \t]'; - self::$_grammar['CRLF'] = '(?:\r\n)'; - self::$_grammar['FWS'] = '(?:(?:'.self::$_grammar['WSP'].'*'. - self::$_grammar['CRLF'].')?'.self::$_grammar['WSP'].')'; - self::$_grammar['text'] = '[\x00-\x08\x0B\x0C\x0E-\x7F]'; - self::$_grammar['quoted-pair'] = '(?:\\\\'.self::$_grammar['text'].')'; - self::$_grammar['ctext'] = '(?:'.self::$_grammar['NO-WS-CTL']. - '|[\x21-\x27\x2A-\x5B\x5D-\x7E])'; - // Uses recursive PCRE (?1) -- could be a weak point?? - self::$_grammar['ccontent'] = '(?:'.self::$_grammar['ctext'].'|'. - self::$_grammar['quoted-pair'].'|(?1))'; - self::$_grammar['comment'] = '(\((?:'.self::$_grammar['FWS'].'|'. - self::$_grammar['ccontent'].')*'.self::$_grammar['FWS'].'?\))'; - self::$_grammar['CFWS'] = '(?:(?:'.self::$_grammar['FWS'].'?'. - self::$_grammar['comment'].')*(?:(?:'.self::$_grammar['FWS'].'?'. - self::$_grammar['comment'].')|'.self::$_grammar['FWS'].'))'; - self::$_grammar['qtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. - '|[\x21\x23-\x5B\x5D-\x7E])'; - self::$_grammar['qcontent'] = '(?:'.self::$_grammar['qtext'].'|'. - self::$_grammar['quoted-pair'].')'; - self::$_grammar['quoted-string'] = '(?:'.self::$_grammar['CFWS'].'?"'. - '('.self::$_grammar['FWS'].'?'.self::$_grammar['qcontent'].')*'. - self::$_grammar['FWS'].'?"'.self::$_grammar['CFWS'].'?)'; - self::$_grammar['atext'] = '[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]'; - self::$_grammar['atom'] = '(?:'.self::$_grammar['CFWS'].'?'. - self::$_grammar['atext'].'+'.self::$_grammar['CFWS'].'?)'; - self::$_grammar['dot-atom-text'] = '(?:'.self::$_grammar['atext'].'+'. - '(\.'.self::$_grammar['atext'].'+)*)'; - self::$_grammar['dot-atom'] = '(?:'.self::$_grammar['CFWS'].'?'. - self::$_grammar['dot-atom-text'].'+'.self::$_grammar['CFWS'].'?)'; - self::$_grammar['word'] = '(?:'.self::$_grammar['atom'].'|'. - self::$_grammar['quoted-string'].')'; - self::$_grammar['phrase'] = '(?:'.self::$_grammar['word'].'+?)'; - self::$_grammar['no-fold-quote'] = '(?:"(?:'.self::$_grammar['qtext']. - '|'.self::$_grammar['quoted-pair'].')*")'; - self::$_grammar['dtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. - '|[\x21-\x5A\x5E-\x7E])'; - self::$_grammar['no-fold-literal'] = '(?:\[(?:'.self::$_grammar['dtext']. - '|'.self::$_grammar['quoted-pair'].')*\])'; - - // Message IDs - self::$_grammar['id-left'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. - self::$_grammar['no-fold-quote'].')'; - self::$_grammar['id-right'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. - self::$_grammar['no-fold-literal'].')'; - - // Addresses, mailboxes and paths - self::$_grammar['local-part'] = '(?:'.self::$_grammar['dot-atom'].'|'. - self::$_grammar['quoted-string'].')'; - self::$_grammar['dcontent'] = '(?:'.self::$_grammar['dtext'].'|'. - self::$_grammar['quoted-pair'].')'; - self::$_grammar['domain-literal'] = '(?:'.self::$_grammar['CFWS'].'?\[('. - self::$_grammar['FWS'].'?'.self::$_grammar['dcontent'].')*?'. - self::$_grammar['FWS'].'?\]'.self::$_grammar['CFWS'].'?)'; - self::$_grammar['domain'] = '(?:'.self::$_grammar['dot-atom'].'|'. - self::$_grammar['domain-literal'].')'; - self::$_grammar['addr-spec'] = '(?:'.self::$_grammar['local-part'].'@'. - self::$_grammar['domain'].')'; - } - - /** - * Get the grammar defined for $name token. - * - * @param string $name exactly as written in the RFC - * - * @return string - */ - public function getDefinition($name) - { - if (array_key_exists($name, self::$_grammar)) { - return self::$_grammar[$name]; - } - - throw new Swift_RfcComplianceException( - "No such grammar '".$name."' defined." - ); - } - - /** - * Returns the tokens defined in RFC 2822 (and some related RFCs). - * - * @return array - */ - public function getGrammarDefinitions() - { - return self::$_grammar; - } - - /** - * Returns the current special characters used in the syntax which need to be escaped. - * - * @return array - */ - public function getSpecials() - { - return self::$_specials; - } - - /** - * Escape special characters in a string (convert to quoted-pairs). - * - * @param string $token - * @param string[] $include additional chars to escape - * @param string[] $exclude chars from escaping - * - * @return string - */ - public function escapeSpecials($token, $include = array(), $exclude = array()) - { - foreach (array_merge(array('\\'), array_diff(self::$_specials, $exclude), $include) as $char) { - $token = str_replace($char, '\\'.$char, $token); - } - - return $token; - } -} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php index 510dd6637b3..062be7d7967 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -31,7 +31,7 @@ class Swift_Mime_HeaderEncoder_QpHeaderEncoder extends Swift_Encoder_QpEncoder i range(0x61, 0x7A), range(0x41, 0x5A), range(0x30, 0x39), array(0x20, 0x21, 0x2A, 0x2B, 0x2D, 0x2F) ) as $byte) { - $this->_safeMap[$byte] = chr($byte); + $this->safeMap[$byte] = chr($byte); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php deleted file mode 100644 index c65f26d7213..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php +++ /dev/null @@ -1,78 +0,0 @@ -setGrammar($grammar); - } + private $cachedValue = null; /** * Set the character set used in this Header. @@ -81,10 +66,10 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function setCharset($charset) { - $this->clearCachedValueIf($charset != $this->_charset); - $this->_charset = $charset; - if (isset($this->_encoder)) { - $this->_encoder->charsetChanged($charset); + $this->clearCachedValueIf($charset != $this->charset); + $this->charset = $charset; + if (isset($this->encoder)) { + $this->encoder->charsetChanged($charset); } } @@ -95,7 +80,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function getCharset() { - return $this->_charset; + return $this->charset; } /** @@ -108,8 +93,8 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function setLanguage($lang) { - $this->clearCachedValueIf($this->_lang != $lang); - $this->_lang = $lang; + $this->clearCachedValueIf($this->lang != $lang); + $this->lang = $lang; } /** @@ -119,7 +104,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function getLanguage() { - return $this->_lang; + return $this->lang; } /** @@ -129,7 +114,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function setEncoder(Swift_Mime_HeaderEncoder $encoder) { - $this->_encoder = $encoder; + $this->encoder = $encoder; $this->setCachedValue(null); } @@ -140,28 +125,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function getEncoder() { - return $this->_encoder; - } - - /** - * Set the grammar used for the header. - * - * @param Swift_Mime_Grammar $grammar - */ - public function setGrammar(Swift_Mime_Grammar $grammar) - { - $this->_grammar = $grammar; - $this->setCachedValue(null); - } - - /** - * Get the grammar used for this Header. - * - * @return Swift_Mime_Grammar - */ - public function getGrammar() - { - return $this->_grammar; + return $this->encoder; } /** @@ -171,7 +135,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function getFieldName() { - return $this->_name; + return $this->name; } /** @@ -181,8 +145,8 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function setMaxLineLength($lineLength) { - $this->clearCachedValueIf($this->_lineLength != $lineLength); - $this->_lineLength = $lineLength; + $this->clearCachedValueIf($this->lineLength != $lineLength); + $this->lineLength = $lineLength; } /** @@ -192,19 +156,19 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ public function getMaxLineLength() { - return $this->_lineLength; + return $this->lineLength; } /** * Get this Header rendered as a RFC 2822 compliant string. * - * @throws Swift_RfcComplianceException - * * @return string + * + * @throws Swift_RfcComplianceException */ public function toString() { - return $this->_tokensToString($this->toTokens()); + return $this->tokensToString($this->toTokens()); } /** @@ -219,8 +183,6 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header return $this->toString(); } - // -- Points of extension - /** * Set the name of this Header field. * @@ -228,7 +190,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ protected function setFieldName($name) { - $this->_name = $name; + $this->name = $name; } /** @@ -247,13 +209,12 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header // Treat token as exactly what was given $phraseStr = $string; // If it's not valid - if (!preg_match('/^'.$this->getGrammar()->getDefinition('phrase').'$/D', $phraseStr)) { + + if (!preg_match('/^'.self::PHRASE_PATTERN.'$/D', $phraseStr)) { // .. but it is just ascii text, try escaping some characters // and make it a quoted-string - if (preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $phraseStr)) { - $phraseStr = $this->getGrammar()->escapeSpecials( - $phraseStr, array('"'), $this->getGrammar()->getSpecials() - ); + if (preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $phraseStr)) { + $phraseStr = $this->escapeSpecials($phraseStr, array('"')); $phraseStr = '"'.$phraseStr.'"'; } else { // ... otherwise it needs encoding @@ -270,6 +231,23 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header return $phraseStr; } + /** + * Escape special characters in a string (convert to quoted-pairs). + * + * @param string $token + * @param string[] $include additional chars to escape + * + * @return string + */ + private function escapeSpecials($token, $include = array()) + { + foreach (array_merge(array('\\'), $include) as $char) { + $token = str_replace($char, '\\'.$char, $token); + } + + return $token; + } + /** * Encode needed word tokens within a string of input. * @@ -365,12 +343,12 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header protected function getTokenAsEncodedWord($token, $firstLineOffset = 0) { // Adjust $firstLineOffset to account for space needed for syntax - $charsetDecl = $this->_charset; - if (isset($this->_lang)) { - $charsetDecl .= '*'.$this->_lang; + $charsetDecl = $this->charset; + if (isset($this->lang)) { + $charsetDecl .= '*'.$this->lang; } $encodingWrapperLength = strlen( - '=?'.$charsetDecl.'?'.$this->_encoder->getName().'??=' + '=?'.$charsetDecl.'?'.$this->encoder->getName().'??=' ); if ($firstLineOffset >= 75) { @@ -379,16 +357,16 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header } $encodedTextLines = explode("\r\n", - $this->_encoder->encodeString( - $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset + $this->encoder->encodeString( + $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->charset ) ); - if (strtolower($this->_charset) !== 'iso-2022-jp') { + if (strtolower($this->charset) !== 'iso-2022-jp') { // special encoding for iso-2022-jp using mb_encode_mimeheader foreach ($encodedTextLines as $lineNum => $line) { $encodedTextLines[$lineNum] = '=?'.$charsetDecl. - '?'.$this->_encoder->getName(). + '?'.$this->encoder->getName(). '?'.$line.'?='; } } @@ -415,7 +393,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ protected function setCachedValue($value) { - $this->_cachedValue = $value; + $this->cachedValue = $value; } /** @@ -425,7 +403,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ protected function getCachedValue() { - return $this->_cachedValue; + return $this->cachedValue; } /** @@ -449,7 +427,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header */ protected function toTokens($string = null) { - if (is_null($string)) { + if (null === $string) { $string = $this->getFieldBody(); } @@ -474,18 +452,18 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header * * @return string */ - private function _tokensToString(array $tokens) + private function tokensToString(array $tokens) { $lineCount = 0; $headerLines = array(); - $headerLines[] = $this->_name.': '; + $headerLines[] = $this->name.': '; $currentLine = &$headerLines[$lineCount++]; // Build all tokens back into compliant header foreach ($tokens as $i => $token) { // Line longer than specified maximum or token was just a new line if (("\r\n" == $token) || - ($i > 0 && strlen($currentLine.$token) > $this->_lineLength) + ($i > 0 && strlen($currentLine.$token) > $this->lineLength) && 0 < strlen($currentLine)) { $headerLines[] = ''; $currentLine = &$headerLines[$lineCount++]; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php index 4fd6674296e..6dcc92bfdd6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -16,29 +16,20 @@ class Swift_Mime_Headers_DateHeader extends Swift_Mime_Headers_AbstractHeader { /** - * The UNIX timestamp value of this Header. + * Date-time value of this Header. * - * @var int + * @var DateTimeImmutable */ - private $_timestamp; + private $dateTime; /** - * Creates a new DateHeader with $name and $timestamp. + * Creates a new DateHeader with $name. * - * Example: - * - * - * - * - * @param string $name of Header - * @param Swift_Mime_Grammar $grammar + * @param string $name of Header */ - public function __construct($name, Swift_Mime_Grammar $grammar) + public function __construct($name) { $this->setFieldName($name); - parent::__construct($grammar); } /** @@ -57,49 +48,48 @@ class Swift_Mime_Headers_DateHeader extends Swift_Mime_Headers_AbstractHeader /** * Set the model for the field body. * - * This method takes a UNIX timestamp. - * - * @param int $model + * @param DateTimeInterface $model */ public function setFieldBodyModel($model) { - $this->setTimestamp($model); + $this->setDateTime($model); } /** * Get the model for the field body. * - * This method returns a UNIX timestamp. - * - * @return mixed + * @return DateTimeImmutable */ public function getFieldBodyModel() { - return $this->getTimestamp(); + return $this->getDateTime(); } /** - * Get the UNIX timestamp of the Date in this Header. + * Get the date-time representing the Date in this Header. * - * @return int + * @return DateTimeImmutable */ - public function getTimestamp() + public function getDateTime() { - return $this->_timestamp; + return $this->dateTime; } /** - * Set the UNIX timestamp of the Date in this Header. + * Set the date-time of the Date in this Header. * - * @param int $timestamp + * If a DateTime instance is provided, it is converted to DateTimeImmutable. + * + * @param DateTimeInterface $dateTime */ - public function setTimestamp($timestamp) + public function setDateTime(DateTimeInterface $dateTime) { - if (!is_null($timestamp)) { - $timestamp = (int) $timestamp; + $this->clearCachedValueIf($this->getCachedValue() != $dateTime->format(DateTime::RFC2822)); + if ($dateTime instanceof DateTime) { + $immutable = new DateTimeImmutable('@'.$dateTime->getTimestamp()); + $dateTime = $immutable->setTimezone($dateTime->getTimezone()); } - $this->clearCachedValueIf($this->_timestamp != $timestamp); - $this->_timestamp = $timestamp; + $this->dateTime = $dateTime; } /** @@ -115,8 +105,8 @@ class Swift_Mime_Headers_DateHeader extends Swift_Mime_Headers_AbstractHeader public function getFieldBody() { if (!$this->getCachedValue()) { - if (isset($this->_timestamp)) { - $this->setCachedValue(date('r', $this->_timestamp)); + if (isset($this->dateTime)) { + $this->setCachedValue($this->dateTime->format(DateTime::RFC2822)); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php index b114506b4a4..acf794479b6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -8,6 +8,9 @@ * file that was distributed with this source code. */ +use Egulias\EmailValidator\EmailValidator; +use Egulias\EmailValidator\Validation\RFCValidation; + /** * An ID MIME Header for something like Message-ID or Content-ID. * @@ -22,18 +25,25 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac * * @var string[] */ - private $_ids = array(); + private $ids = array(); + + /** + * The strict EmailValidator. + * + * @var EmailValidator + */ + private $emailValidator; /** * Creates a new IdentificationHeader with the given $name and $id. * - * @param string $name - * @param Swift_Mime_Grammar $grammar + * @param string $name + * @param EmailValidator $emailValidator */ - public function __construct($name, Swift_Mime_Grammar $grammar) + public function __construct($name, EmailValidator $emailValidator) { $this->setFieldName($name); - parent::__construct($grammar); + $this->emailValidator = $emailValidator; } /** @@ -96,8 +106,8 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac */ public function getId() { - if (count($this->_ids) > 0) { - return $this->_ids[0]; + if (count($this->ids) > 0) { + return $this->ids[0]; } } @@ -113,12 +123,12 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac $actualIds = array(); foreach ($ids as $id) { - $this->_assertValidId($id); + $this->assertValidId($id); $actualIds[] = $id; } - $this->clearCachedValueIf($this->_ids != $actualIds); - $this->_ids = $actualIds; + $this->clearCachedValueIf($this->ids != $actualIds); + $this->ids = $actualIds; } /** @@ -128,7 +138,7 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac */ public function getIds() { - return $this->_ids; + return $this->ids; } /** @@ -148,7 +158,7 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac if (!$this->getCachedValue()) { $angleAddrs = array(); - foreach ($this->_ids as $id) { + foreach ($this->ids as $id) { $angleAddrs[] = '<'.$id.'>'; } @@ -165,16 +175,10 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac * * @throws Swift_RfcComplianceException */ - private function _assertValidId($id) + private function assertValidId($id) { - if (!preg_match( - '/^'.$this->getGrammar()->getDefinition('id-left').'@'. - $this->getGrammar()->getDefinition('id-right').'$/D', - $id - )) { - throw new Swift_RfcComplianceException( - 'Invalid ID given <'.$id.'>' - ); + if (!$this->emailValidator->isValid($id, new RFCValidation())) { + throw new Swift_RfcComplianceException('Invalid ID given <'.$id.'>'); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php index 798e7f42ace..450e0f25b7e 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -8,6 +8,9 @@ * file that was distributed with this source code. */ +use Egulias\EmailValidator\EmailValidator; +use Egulias\EmailValidator\Validation\RFCValidation; + /** * A Mailbox Address MIME Header for something like From or Sender. * @@ -20,20 +23,27 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader * * @var string[] */ - private $_mailboxes = array(); + private $mailboxes = array(); + + /** + * The strict EmailValidator. + * + * @var EmailValidator + */ + private $emailValidator; /** * Creates a new MailboxHeader with $name. * - * @param string $name of Header + * @param string $name of Header * @param Swift_Mime_HeaderEncoder $encoder - * @param Swift_Mime_Grammar $grammar + * @param EmailValidator $emailValidator */ - public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Mime_Grammar $grammar) + public function __construct($name, Swift_Mime_HeaderEncoder $encoder, EmailValidator $emailValidator) { $this->setFieldName($name); $this->setEncoder($encoder); - parent::__construct($grammar); + $this->emailValidator = $emailValidator; } /** @@ -103,7 +113,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ public function setNameAddresses($mailboxes) { - $this->_mailboxes = $this->normalizeMailboxes((array) $mailboxes); + $this->mailboxes = $this->normalizeMailboxes((array) $mailboxes); $this->setCachedValue(null); //Clear any cached value } @@ -134,7 +144,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ public function getNameAddressStrings() { - return $this->_createNameAddressStrings($this->getNameAddresses()); + return $this->createNameAddressStrings($this->getNameAddresses()); } /** @@ -163,7 +173,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ public function getNameAddresses() { - return $this->_mailboxes; + return $this->mailboxes; } /** @@ -200,7 +210,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ public function getAddresses() { - return array_keys($this->_mailboxes); + return array_keys($this->mailboxes); } /** @@ -212,7 +222,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader { $this->setCachedValue(null); foreach ((array) $addresses as $address) { - unset($this->_mailboxes[$address]); + unset($this->mailboxes[$address]); } } @@ -231,15 +241,13 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader public function getFieldBody() { // Compute the string value of the header only if needed - if (is_null($this->getCachedValue())) { - $this->setCachedValue($this->createMailboxListString($this->_mailboxes)); + if (null === $this->getCachedValue()) { + $this->setCachedValue($this->createMailboxListString($this->mailboxes)); } return $this->getCachedValue(); } - // -- Points of extension - /** * Normalizes a user-input list of mailboxes into consistent key=>value pairs. * @@ -260,7 +268,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader $address = $value; $name = null; } - $this->_assertValidAddress($address); + $this->assertValidAddress($address); $actualMailboxes[$address] = $name; } @@ -277,9 +285,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ protected function createDisplayNameString($displayName, $shorten = false) { - return $this->createPhrase($this, $displayName, - $this->getCharset(), $this->getEncoder(), $shorten - ); + return $this->createPhrase($this, $displayName, $this->getCharset(), $this->getEncoder(), $shorten); } /** @@ -293,14 +299,15 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ protected function createMailboxListString(array $mailboxes) { - return implode(', ', $this->_createNameAddressStrings($mailboxes)); + return implode(', ', $this->createNameAddressStrings($mailboxes)); } /** * Redefine the encoding requirements for mailboxes. * - * Commas and semicolons are used to separate - * multiple addresses, and should therefore be encoded + * All "specials" must be encoded as the full header value will not be quoted + * + * @see RFC 2822 3.2.1 * * @param string $token * @@ -308,7 +315,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader */ protected function tokenNeedsEncoding($token) { - return preg_match('/[,;]/', $token) || parent::tokenNeedsEncoding($token); + return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token); } /** @@ -318,13 +325,13 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader * * @return string[] */ - private function _createNameAddressStrings(array $mailboxes) + private function createNameAddressStrings(array $mailboxes) { $strings = array(); foreach ($mailboxes as $email => $name) { $mailboxStr = $email; - if (!is_null($name)) { + if (null !== $name) { $nameStr = $this->createDisplayNameString($name, empty($strings)); $mailboxStr = $nameStr.' <'.$mailboxStr.'>'; } @@ -341,14 +348,12 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader * * @throws Swift_RfcComplianceException If invalid. */ - private function _assertValidAddress($address) + private function assertValidAddress($address) { - if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', - $address)) { + if (!$this->emailValidator->isValid($address, new RFCValidation())) { throw new Swift_RfcComplianceException( - 'Address in mailbox given ['.$address. - '] does not comply with RFC 2822, 3.6.2.' - ); + 'Address in mailbox given ['.$address.'] does not comply with RFC 2822, 3.6.2.' + ); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php index b52b964b773..29277994c0a 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php @@ -20,25 +20,21 @@ class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header * * @var string */ - private $_value; + private $value; /** * The name of this Header. * * @var string */ - private $_fieldName; + private $fieldName; /** - * Creates a new SimpleHeader with $name. - * - * @param string $name - * @param Swift_Mime_HeaderEncoder $encoder - * @param Swift_Mime_Grammar $grammar + * @param string $name */ public function __construct($name) { - $this->_fieldName = $name; + $this->fieldName = $name; } /** @@ -85,7 +81,7 @@ class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header */ public function getValue() { - return $this->_value; + return $this->value; } /** @@ -95,7 +91,7 @@ class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header */ public function setValue($value) { - $this->_value = $value; + $this->value = $value; } /** @@ -105,7 +101,7 @@ class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header */ public function getFieldBody() { - return $this->_value; + return $this->value; } /** @@ -115,7 +111,7 @@ class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header */ public function toString() { - return $this->_fieldName.': '.$this->_value; + return $this->fieldName.': '.$this->value; } /** @@ -125,7 +121,7 @@ class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header */ public function getFieldName() { - return $this->_fieldName; + return $this->fieldName; } /** diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php index c506daec146..b4a0ddf1224 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -13,7 +13,7 @@ * * @author Chris Corbyn */ -class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_UnstructuredHeader implements Swift_Mime_ParameterizedHeader +class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_UnstructuredHeader { /** * RFC 2231's definition of a token. @@ -27,14 +27,14 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct * * @var Swift_Encoder */ - private $_paramEncoder; + private $paramEncoder; /** * The parameters as an associative array. * * @var string[] */ - private $_params = array(); + private $params = array(); /** * Creates a new ParameterizedHeader with $name. @@ -42,12 +42,11 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct * @param string $name * @param Swift_Mime_HeaderEncoder $encoder * @param Swift_Encoder $paramEncoder, optional - * @param Swift_Mime_Grammar $grammar */ - public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder = null, Swift_Mime_Grammar $grammar) + public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder = null) { - parent::__construct($name, $encoder, $grammar); - $this->_paramEncoder = $paramEncoder; + parent::__construct($name, $encoder); + $this->paramEncoder = $paramEncoder; } /** @@ -71,8 +70,8 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct public function setCharset($charset) { parent::setCharset($charset); - if (isset($this->_paramEncoder)) { - $this->_paramEncoder->charsetChanged($charset); + if (isset($this->paramEncoder)) { + $this->paramEncoder->charsetChanged($charset); } } @@ -98,7 +97,7 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct { $params = $this->getParameters(); - return array_key_exists($parameter, $params) ? $params[$parameter] : null; + return $params[$parameter] ?? null; } /** @@ -108,8 +107,8 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct */ public function setParameters(array $parameters) { - $this->clearCachedValueIf($this->_params != $parameters); - $this->_params = $parameters; + $this->clearCachedValueIf($this->params != $parameters); + $this->params = $parameters; } /** @@ -119,7 +118,7 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct */ public function getParameters() { - return $this->_params; + return $this->params; } /** @@ -130,10 +129,10 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct public function getFieldBody() //TODO: Check caching here { $body = parent::getFieldBody(); - foreach ($this->_params as $name => $value) { - if (!is_null($value)) { + foreach ($this->params as $name => $value) { + if (null !== $value) { // Add the parameter - $body .= '; '.$this->_createParameter($name, $value); + $body .= '; '.$this->createParameter($name, $value); } } @@ -155,12 +154,12 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct $tokens = parent::toTokens(parent::getFieldBody()); // Try creating any parameters - foreach ($this->_params as $name => $value) { - if (!is_null($value)) { + foreach ($this->params as $name => $value) { + if (null !== $value) { // Add the semi-colon separator $tokens[count($tokens) - 1] .= ';'; $tokens = array_merge($tokens, $this->generateTokenLines( - ' '.$this->_createParameter($name, $value) + ' '.$this->createParameter($name, $value) )); } } @@ -176,7 +175,7 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct * * @return string */ - private function _createParameter($name, $value) + private function createParameter($name, $value) { $origValue = $value; @@ -189,7 +188,7 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { // TODO: text, or something else?? // ... and it's not ascii - if (!preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $value)) { + if (!preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $value)) { $encoded = true; // Allow space for the indices, charset and language $maxValueLength = $this->getMaxLineLength() - strlen($name.'*N*="";') - 1; @@ -201,8 +200,8 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct // Encode if we need to if ($encoded || strlen($value) > $maxValueLength) { - if (isset($this->_paramEncoder)) { - $value = $this->_paramEncoder->encodeString( + if (isset($this->paramEncoder)) { + $value = $this->paramEncoder->encodeString( $origValue, $firstLineOffset, $maxValueLength, $this->getCharset() ); } else { @@ -212,19 +211,19 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct } } - $valueLines = isset($this->_paramEncoder) ? explode("\r\n", $value) : array($value); + $valueLines = isset($this->paramEncoder) ? explode("\r\n", $value) : array($value); // Need to add indices if (count($valueLines) > 1) { $paramLines = array(); foreach ($valueLines as $i => $line) { $paramLines[] = $name.'*'.$i. - $this->_getEndOfParameterValue($line, true, $i == 0); + $this->getEndOfParameterValue($line, true, $i == 0); } return implode(";\r\n ", $paramLines); } else { - return $name.$this->_getEndOfParameterValue( + return $name.$this->getEndOfParameterValue( $valueLines[0], $encoded, true ); } @@ -239,7 +238,7 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct * * @return string */ - private function _getEndOfParameterValue($value, $encoded = false, $firstLine = false) + private function getEndOfParameterValue($value, $encoded = false, $firstLine = false) { if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { $value = '"'.$value.'"'; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php index 2fffc7b4aac..f79e19fd8b3 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php @@ -8,6 +8,9 @@ * file that was distributed with this source code. */ +use Egulias\EmailValidator\EmailValidator; +use Egulias\EmailValidator\Validation\RFCValidation; + /** * A Path Header in Swift Mailer, such a Return-Path. * @@ -20,18 +23,25 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader * * @var string */ - private $_address; + private $address; + + /** + * The strict EmailValidator. + * + * @var EmailValidator + */ + private $emailValidator; /** * Creates a new PathHeader with the given $name. * - * @param string $name - * @param Swift_Mime_Grammar $grammar + * @param string $name + * @param EmailValidator $emailValidator */ - public function __construct($name, Swift_Mime_Grammar $grammar) + public function __construct($name, EmailValidator $emailValidator) { $this->setFieldName($name); - parent::__construct($grammar); + $this->emailValidator = $emailValidator; } /** @@ -80,13 +90,13 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader */ public function setAddress($address) { - if (is_null($address)) { - $this->_address = null; + if (null === $address) { + $this->address = null; } elseif ('' == $address) { - $this->_address = ''; + $this->address = ''; } else { - $this->_assertValidAddress($address); - $this->_address = $address; + $this->assertValidAddress($address); + $this->address = $address; } $this->setCachedValue(null); } @@ -100,7 +110,7 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader */ public function getAddress() { - return $this->_address; + return $this->address; } /** @@ -116,8 +126,8 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader public function getFieldBody() { if (!$this->getCachedValue()) { - if (isset($this->_address)) { - $this->setCachedValue('<'.$this->_address.'>'); + if (isset($this->address)) { + $this->setCachedValue('<'.$this->address.'>'); } } @@ -131,13 +141,12 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader * * @throws Swift_RfcComplianceException If address is invalid */ - private function _assertValidAddress($address) + private function assertValidAddress($address) { - if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', - $address)) { + if (!$this->emailValidator->isValid($address, new RFCValidation())) { throw new Swift_RfcComplianceException( 'Address set in PathHeader does not comply with addr-spec of RFC 2822.' - ); + ); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php index 86177f14a1f..d9d9f5e25d3 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php @@ -20,20 +20,18 @@ class Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_AbstractH * * @var string */ - private $_value; + private $value; /** * Creates a new SimpleHeader with $name. * * @param string $name * @param Swift_Mime_HeaderEncoder $encoder - * @param Swift_Mime_Grammar $grammar */ - public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Mime_Grammar $grammar) + public function __construct($name, Swift_Mime_HeaderEncoder $encoder) { $this->setFieldName($name); $this->setEncoder($encoder); - parent::__construct($grammar); } /** @@ -80,7 +78,7 @@ class Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_AbstractH */ public function getValue() { - return $this->_value; + return $this->value; } /** @@ -90,8 +88,8 @@ class Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_AbstractH */ public function setValue($value) { - $this->clearCachedValueIf($this->_value != $value); - $this->_value = $value; + $this->clearCachedValueIf($this->value != $value); + $this->value = $value; } /** @@ -103,7 +101,7 @@ class Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_AbstractH { if (!$this->getCachedValue()) { $this->setCachedValue( - $this->encodeWords($this, $this->_value) + $this->encodeWords($this, $this->value) ); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/IdGenerator.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/IdGenerator.php new file mode 100644 index 00000000000..6e98ee8975d --- /dev/null +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/IdGenerator.php @@ -0,0 +1,53 @@ +idRight = $idRight; + } + + /** + * Returns the right-hand side of the "@" used in all generated IDs. + * + * @return string + */ + public function getIdRight() + { + return $this->idRight; + } + + /** + * Sets the right-hand side of the "@" to use in all generated IDs. + * + * @param string $idRight + */ + public function setIdRight($idRight) + { + $this->idRight = $idRight; + } + + /** + * @return string + */ + public function generateId() + { + $idLeft = bin2hex(random_bytes(16)); // set 32 hex values + + return $idLeft.'@'.$this->idRight; + } +} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Message.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Message.php deleted file mode 100644 index 9b36d216271..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/Message.php +++ /dev/null @@ -1,223 +0,0 @@ - 'Real Name'). - * - * If the second parameter is provided and the first is a string, then $name - * is associated with the address. - * - * @param mixed $address - * @param string $name optional - */ - public function setSender($address, $name = null); - - /** - * Get the sender address for this message. - * - * This has a higher significance than the From address. - * - * @return string - */ - public function getSender(); - - /** - * Set the From address of this message. - * - * It is permissible for multiple From addresses to be set using an array. - * - * If multiple From addresses are used, you SHOULD set the Sender address and - * according to RFC 2822, MUST set the sender address. - * - * An array can be used if display names are to be provided: i.e. - * array('email@address.com' => 'Real Name'). - * - * If the second parameter is provided and the first is a string, then $name - * is associated with the address. - * - * @param mixed $addresses - * @param string $name optional - */ - public function setFrom($addresses, $name = null); - - /** - * Get the From address(es) of this message. - * - * This method always returns an associative array where the keys are the - * addresses. - * - * @return string[] - */ - public function getFrom(); - - /** - * Set the Reply-To address(es). - * - * Any replies from the receiver will be sent to this address. - * - * It is permissible for multiple reply-to addresses to be set using an array. - * - * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. - * - * If the second parameter is provided and the first is a string, then $name - * is associated with the address. - * - * @param mixed $addresses - * @param string $name optional - */ - public function setReplyTo($addresses, $name = null); - - /** - * Get the Reply-To addresses for this message. - * - * This method always returns an associative array where the keys provide the - * email addresses. - * - * @return string[] - */ - public function getReplyTo(); - - /** - * Set the To address(es). - * - * Recipients set in this field will receive a copy of this message. - * - * This method has the same synopsis as {@link setFrom()} and {@link setCc()}. - * - * If the second parameter is provided and the first is a string, then $name - * is associated with the address. - * - * @param mixed $addresses - * @param string $name optional - */ - public function setTo($addresses, $name = null); - - /** - * Get the To addresses for this message. - * - * This method always returns an associative array, whereby the keys provide - * the actual email addresses. - * - * @return string[] - */ - public function getTo(); - - /** - * Set the Cc address(es). - * - * Recipients set in this field will receive a 'carbon-copy' of this message. - * - * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. - * - * @param mixed $addresses - * @param string $name optional - */ - public function setCc($addresses, $name = null); - - /** - * Get the Cc addresses for this message. - * - * This method always returns an associative array, whereby the keys provide - * the actual email addresses. - * - * @return string[] - */ - public function getCc(); - - /** - * Set the Bcc address(es). - * - * Recipients set in this field will receive a 'blind-carbon-copy' of this - * message. - * - * In other words, they will get the message, but any other recipients of the - * message will have no such knowledge of their receipt of it. - * - * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. - * - * @param mixed $addresses - * @param string $name optional - */ - public function setBcc($addresses, $name = null); - - /** - * Get the Bcc addresses for this message. - * - * This method always returns an associative array, whereby the keys provide - * the actual email addresses. - * - * @return string[] - */ - public function getBcc(); -} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php deleted file mode 100644 index 30f460cdcdb..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php +++ /dev/null @@ -1,117 +0,0 @@ -setContentType('text/plain'); - if (!is_null($charset)) { + if (null !== $charset) { $this->setCharset($charset); } } @@ -53,14 +53,14 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity * @param string $contentType optional * @param string $charset optional * - * @return Swift_Mime_MimePart + * @return $this */ public function setBody($body, $contentType = null, $charset = null) { if (isset($charset)) { $this->setCharset($charset); } - $body = $this->_convertString($body); + $body = $this->convertString($body); parent::setBody($body, $contentType); @@ -74,7 +74,7 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity */ public function getCharset() { - return $this->_getHeaderParameter('Content-Type', 'charset'); + return $this->getHeaderParameter('Content-Type', 'charset'); } /** @@ -82,15 +82,15 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity * * @param string $charset * - * @return Swift_Mime_MimePart + * @return $this */ public function setCharset($charset) { - $this->_setHeaderParameter('Content-Type', 'charset', $charset); - if ($charset !== $this->_userCharset) { - $this->_clearCache(); + $this->setHeaderParameter('Content-Type', 'charset', $charset); + if ($charset !== $this->userCharset) { + $this->clearCache(); } - $this->_userCharset = $charset; + $this->userCharset = $charset; parent::charsetChanged($charset); return $this; @@ -103,7 +103,7 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity */ public function getFormat() { - return $this->_getHeaderParameter('Content-Type', 'format'); + return $this->getHeaderParameter('Content-Type', 'format'); } /** @@ -111,12 +111,12 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity * * @param string $format * - * @return Swift_Mime_MimePart + * @return $this */ public function setFormat($format) { - $this->_setHeaderParameter('Content-Type', 'format', $format); - $this->_userFormat = $format; + $this->setHeaderParameter('Content-Type', 'format', $format); + $this->userFormat = $format; return $this; } @@ -128,7 +128,7 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity */ public function getDelSp() { - return 'yes' == $this->_getHeaderParameter('Content-Type', 'delsp') ? true : false; + return 'yes' === $this->getHeaderParameter('Content-Type', 'delsp'); } /** @@ -136,12 +136,12 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity * * @param bool $delsp * - * @return Swift_Mime_MimePart + * @return $this */ public function setDelSp($delsp = true) { - $this->_setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null); - $this->_userDelSp = $delsp; + $this->setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null); + $this->userDelSp = $delsp; return $this; } @@ -155,7 +155,7 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity */ public function getNestingLevel() { - return $this->_nestingLevel; + return $this->nestingLevel; } /** @@ -170,31 +170,31 @@ class Swift_Mime_MimePart extends Swift_Mime_SimpleMimeEntity } /** Fix the content-type and encoding of this entity */ - protected function _fixHeaders() + protected function fixHeaders() { - parent::_fixHeaders(); + parent::fixHeaders(); if (count($this->getChildren())) { - $this->_setHeaderParameter('Content-Type', 'charset', null); - $this->_setHeaderParameter('Content-Type', 'format', null); - $this->_setHeaderParameter('Content-Type', 'delsp', null); + $this->setHeaderParameter('Content-Type', 'charset', null); + $this->setHeaderParameter('Content-Type', 'format', null); + $this->setHeaderParameter('Content-Type', 'delsp', null); } else { - $this->setCharset($this->_userCharset); - $this->setFormat($this->_userFormat); - $this->setDelSp($this->_userDelSp); + $this->setCharset($this->userCharset); + $this->setFormat($this->userFormat); + $this->setDelSp($this->userDelSp); } } /** Set the nesting level of this entity */ - protected function _setNestingLevel($level) + protected function setNestingLevel($level) { - $this->_nestingLevel = $level; + $this->nestingLevel = $level; } /** Encode charset when charset is not utf-8 */ - protected function _convertString($string) + protected function convertString($string) { $charset = strtolower($this->getCharset()); - if (!in_array($charset, array('utf-8', 'iso-8859-1', ''))) { + if (!in_array($charset, array('utf-8', 'iso-8859-1', 'iso-8859-15', ''))) { // mb_convert_encoding must be the first one to check, since iconv cannot convert some words. if (function_exists('mb_convert_encoding')) { $string = mb_convert_encoding($string, $charset, 'utf-8'); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php deleted file mode 100644 index e15c6ef95b1..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php +++ /dev/null @@ -1,34 +0,0 @@ -_encoder = $encoder; - $this->_paramEncoder = $paramEncoder; - $this->_grammar = $grammar; - $this->_charset = $charset; + $this->encoder = $encoder; + $this->paramEncoder = $paramEncoder; + $this->emailValidator = $emailValidator; + $this->charset = $charset; } /** @@ -53,30 +58,30 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory */ public function createMailboxHeader($name, $addresses = null) { - $header = new Swift_Mime_Headers_MailboxHeader($name, $this->_encoder, $this->_grammar); + $header = new Swift_Mime_Headers_MailboxHeader($name, $this->encoder, $this->emailValidator); if (isset($addresses)) { $header->setFieldBodyModel($addresses); } - $this->_setHeaderCharset($header); + $this->setHeaderCharset($header); return $header; } /** - * Create a new Date header using $timestamp (UNIX time). + * Create a new Date header using $dateTime. * - * @param string $name - * @param int|null $timestamp + * @param string $name + * @param DateTimeInterface|null $dateTime * * @return Swift_Mime_Header */ - public function createDateHeader($name, $timestamp = null) + public function createDateHeader($name, DateTimeInterface $dateTime = null) { - $header = new Swift_Mime_Headers_DateHeader($name, $this->_grammar); - if (isset($timestamp)) { - $header->setFieldBodyModel($timestamp); + $header = new Swift_Mime_Headers_DateHeader($name); + if (isset($dateTime)) { + $header->setFieldBodyModel($dateTime); } - $this->_setHeaderCharset($header); + $this->setHeaderCharset($header); return $header; } @@ -91,11 +96,11 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory */ public function createTextHeader($name, $value = null) { - $header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->_encoder, $this->_grammar); + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->encoder); if (isset($value)) { $header->setFieldBodyModel($value); } - $this->_setHeaderCharset($header); + $this->setHeaderCharset($header); return $header; } @@ -107,19 +112,18 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory * @param string $value * @param array $params * - * @return Swift_Mime_ParameterizedHeader + * @return Swift_Mime_Headers_ParameterizedHeader */ - public function createParameterizedHeader($name, $value = null, - $params = array()) + public function createParameterizedHeader($name, $value = null, $params = array()) { - $header = new Swift_Mime_Headers_ParameterizedHeader($name, $this->_encoder, strtolower($name) == 'content-disposition' ? $this->_paramEncoder : null, $this->_grammar); + $header = new Swift_Mime_Headers_ParameterizedHeader($name, $this->encoder, (strtolower($name) == 'content-disposition') ? $this->paramEncoder : null); if (isset($value)) { $header->setFieldBodyModel($value); } foreach ($params as $k => $v) { $header->setParameter($k, $v); } - $this->_setHeaderCharset($header); + $this->setHeaderCharset($header); return $header; } @@ -134,11 +138,11 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory */ public function createIdHeader($name, $ids = null) { - $header = new Swift_Mime_Headers_IdentificationHeader($name, $this->_grammar); + $header = new Swift_Mime_Headers_IdentificationHeader($name, $this->emailValidator); if (isset($ids)) { $header->setFieldBodyModel($ids); } - $this->_setHeaderCharset($header); + $this->setHeaderCharset($header); return $header; } @@ -153,11 +157,11 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory */ public function createPathHeader($name, $path = null) { - $header = new Swift_Mime_Headers_PathHeader($name, $this->_grammar); + $header = new Swift_Mime_Headers_PathHeader($name, $this->emailValidator); if (isset($path)) { $header->setFieldBodyModel($path); } - $this->_setHeaderCharset($header); + $this->setHeaderCharset($header); return $header; } @@ -169,9 +173,9 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory */ public function charsetChanged($charset) { - $this->_charset = $charset; - $this->_encoder->charsetChanged($charset); - $this->_paramEncoder->charsetChanged($charset); + $this->charset = $charset; + $this->encoder->charsetChanged($charset); + $this->paramEncoder->charsetChanged($charset); } /** @@ -179,15 +183,15 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_HeaderFactory */ public function __clone() { - $this->_encoder = clone $this->_encoder; - $this->_paramEncoder = clone $this->_paramEncoder; + $this->encoder = clone $this->encoder; + $this->paramEncoder = clone $this->paramEncoder; } /** Apply the charset to the Header */ - private function _setHeaderCharset(Swift_Mime_Header $header) + private function setHeaderCharset(Swift_Mime_Header $header) { - if (isset($this->_charset)) { - $header->setCharset($this->_charset); + if (isset($this->charset)) { + $header->setCharset($this->charset); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php index cf8bf14a750..6f71fcac132 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php @@ -13,37 +13,42 @@ * * @author Chris Corbyn */ -class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet +class Swift_Mime_SimpleHeaderSet implements Swift_Mime_CharsetObserver { /** HeaderFactory */ - private $_factory; + private $factory; /** Collection of set Headers */ - private $_headers = array(); + private $headers = array(); /** Field ordering details */ - private $_order = array(); + private $order = array(); /** List of fields which are required to be displayed */ - private $_required = array(); + private $required = array(); /** The charset used by Headers */ - private $_charset; + private $charset; /** * Create a new SimpleHeaderSet with the given $factory. * - * @param Swift_Mime_HeaderFactory $factory - * @param string $charset + * @param Swift_Mime_SimpleHeaderFactory $factory + * @param string $charset */ - public function __construct(Swift_Mime_HeaderFactory $factory, $charset = null) + public function __construct(Swift_Mime_SimpleHeaderFactory $factory, $charset = null) { - $this->_factory = $factory; + $this->factory = $factory; if (isset($charset)) { $this->setCharset($charset); } } + public function newInstance() + { + return new self($this->factory); + } + /** * Set the charset used by these headers. * @@ -51,9 +56,9 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function setCharset($charset) { - $this->_charset = $charset; - $this->_factory->charsetChanged($charset); - $this->_notifyHeadersOfCharset($charset); + $this->charset = $charset; + $this->factory->charsetChanged($charset); + $this->notifyHeadersOfCharset($charset); } /** @@ -64,20 +69,20 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function addMailboxHeader($name, $addresses = null) { - $this->_storeHeader($name, - $this->_factory->createMailboxHeader($name, $addresses)); + $this->storeHeader($name, + $this->factory->createMailboxHeader($name, $addresses)); } /** - * Add a new Date header using $timestamp (UNIX time). + * Add a new Date header using $dateTime. * - * @param string $name - * @param int $timestamp + * @param string $name + * @param DateTimeInterface $dateTime */ - public function addDateHeader($name, $timestamp = null) + public function addDateHeader($name, DateTimeInterface $dateTime = null) { - $this->_storeHeader($name, - $this->_factory->createDateHeader($name, $timestamp)); + $this->storeHeader($name, + $this->factory->createDateHeader($name, $dateTime)); } /** @@ -88,8 +93,8 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function addTextHeader($name, $value = null) { - $this->_storeHeader($name, - $this->_factory->createTextHeader($name, $value)); + $this->storeHeader($name, + $this->factory->createTextHeader($name, $value)); } /** @@ -101,7 +106,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function addParameterizedHeader($name, $value = null, $params = array()) { - $this->_storeHeader($name, $this->_factory->createParameterizedHeader($name, $value, $params)); + $this->storeHeader($name, $this->factory->createParameterizedHeader($name, $value, $params)); } /** @@ -112,7 +117,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function addIdHeader($name, $ids = null) { - $this->_storeHeader($name, $this->_factory->createIdHeader($name, $ids)); + $this->storeHeader($name, $this->factory->createIdHeader($name, $ids)); } /** @@ -123,7 +128,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function addPathHeader($name, $path = null) { - $this->_storeHeader($name, $this->_factory->createPathHeader($name, $path)); + $this->storeHeader($name, $this->factory->createPathHeader($name, $path)); } /** @@ -140,7 +145,16 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet { $lowerName = strtolower($name); - return array_key_exists($lowerName, $this->_headers) && array_key_exists($index, $this->_headers[$lowerName]); + if (!array_key_exists($lowerName, $this->headers)) { + return false; + } + + if (func_num_args() < 2) { + // index was not specified, so we only need to check that there is at least one header value set + return (bool) count($this->headers[$lowerName]); + } + + return array_key_exists($index, $this->headers[$lowerName]); } /** @@ -157,7 +171,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function set(Swift_Mime_Header $header, $index = 0) { - $this->_storeHeader($header->getFieldName(), $header, $index); + $this->storeHeader($header->getFieldName(), $header, $index); } /** @@ -173,10 +187,18 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function get($name, $index = 0) { - if ($this->has($name, $index)) { - $lowerName = strtolower($name); + $name = strtolower($name); - return $this->_headers[$lowerName][$index]; + if (func_num_args() < 2) { + if ($this->has($name)) { + $values = array_values($this->headers[$name]); + + return array_shift($values); + } + } else { + if ($this->has($name, $index)) { + return $this->headers[$name][$index]; + } } } @@ -191,7 +213,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet { if (!isset($name)) { $headers = array(); - foreach ($this->_headers as $collection) { + foreach ($this->headers as $collection) { $headers = array_merge($headers, $collection); } @@ -199,11 +221,11 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet } $lowerName = strtolower($name); - if (!array_key_exists($lowerName, $this->_headers)) { + if (!array_key_exists($lowerName, $this->headers)) { return array(); } - return $this->_headers[$lowerName]; + return $this->headers[$lowerName]; } /** @@ -213,9 +235,9 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function listAll() { - $headers = $this->_headers; - if ($this->_canSort()) { - uksort($headers, array($this, '_sortHeaders')); + $headers = $this->headers; + if ($this->canSort()) { + uksort($headers, array($this, 'sortHeaders')); } return array_keys($headers); @@ -232,7 +254,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet public function remove($name, $index = 0) { $lowerName = strtolower($name); - unset($this->_headers[$lowerName][$index]); + unset($this->headers[$lowerName][$index]); } /** @@ -243,17 +265,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet public function removeAll($name) { $lowerName = strtolower($name); - unset($this->_headers[$lowerName]); - } - - /** - * Create a new instance of this HeaderSet. - * - * @return Swift_Mime_HeaderSet - */ - public function newInstance() - { - return new self($this->_factory); + unset($this->headers[$lowerName]); } /** @@ -265,7 +277,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function defineOrdering(array $sequence) { - $this->_order = array_flip(array_map('strtolower', $sequence)); + $this->order = array_flip(array_map('strtolower', $sequence)); } /** @@ -277,7 +289,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function setAlwaysDisplayed(array $names) { - $this->_required = array_flip(array_map('strtolower', $names)); + $this->required = array_flip(array_map('strtolower', $names)); } /** @@ -298,13 +310,13 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet public function toString() { $string = ''; - $headers = $this->_headers; - if ($this->_canSort()) { - uksort($headers, array($this, '_sortHeaders')); + $headers = $this->headers; + if ($this->canSort()) { + uksort($headers, array($this, 'sortHeaders')); } foreach ($headers as $collection) { foreach ($collection as $header) { - if ($this->_isDisplayed($header) || $header->getFieldBody() != '') { + if ($this->isDisplayed($header) || $header->getFieldBody() != '') { $string .= $header->toString(); } } @@ -326,31 +338,31 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet } /** Save a Header to the internal collection */ - private function _storeHeader($name, Swift_Mime_Header $header, $offset = null) + private function storeHeader($name, Swift_Mime_Header $header, $offset = null) { - if (!isset($this->_headers[strtolower($name)])) { - $this->_headers[strtolower($name)] = array(); + if (!isset($this->headers[strtolower($name)])) { + $this->headers[strtolower($name)] = array(); } if (!isset($offset)) { - $this->_headers[strtolower($name)][] = $header; + $this->headers[strtolower($name)][] = $header; } else { - $this->_headers[strtolower($name)][$offset] = $header; + $this->headers[strtolower($name)][$offset] = $header; } } /** Test if the headers can be sorted */ - private function _canSort() + private function canSort() { - return count($this->_order) > 0; + return count($this->order) > 0; } /** uksort() algorithm for Header ordering */ - private function _sortHeaders($a, $b) + private function sortHeaders($a, $b) { $lowerA = strtolower($a); $lowerB = strtolower($b); - $aPos = array_key_exists($lowerA, $this->_order) ? $this->_order[$lowerA] : -1; - $bPos = array_key_exists($lowerB, $this->_order) ? $this->_order[$lowerB] : -1; + $aPos = array_key_exists($lowerA, $this->order) ? $this->order[$lowerA] : -1; + $bPos = array_key_exists($lowerB, $this->order) ? $this->order[$lowerB] : -1; if (-1 === $aPos && -1 === $bPos) { // just be sure to be determinist here @@ -367,15 +379,15 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet } /** Test if the given Header is always displayed */ - private function _isDisplayed(Swift_Mime_Header $header) + private function isDisplayed(Swift_Mime_Header $header) { - return array_key_exists(strtolower($header->getFieldName()), $this->_required); + return array_key_exists(strtolower($header->getFieldName()), $this->required); } /** Notify all Headers of the new charset */ - private function _notifyHeadersOfCharset($charset) + private function notifyHeadersOfCharset($charset) { - foreach ($this->_headers as $headerGroup) { + foreach ($this->headers as $headerGroup) { foreach ($headerGroup as $header) { $header->setCharset($charset); } @@ -387,10 +399,10 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_HeaderSet */ public function __clone() { - $this->_factory = clone $this->_factory; - foreach ($this->_headers as $groupKey => $headerGroup) { + $this->factory = clone $this->factory; + foreach ($this->headers as $groupKey => $headerGroup) { foreach ($headerGroup as $key => $header) { - $this->_headers[$groupKey][$key] = clone $header; + $this->headers[$groupKey][$key] = clone $header; } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php index 124644b5a93..768de07f812 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php @@ -13,20 +13,26 @@ * * @author Chris Corbyn */ -class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime_Message +class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart { + const PRIORITY_HIGHEST = 1; + const PRIORITY_HIGH = 2; + const PRIORITY_NORMAL = 3; + const PRIORITY_LOW = 4; + const PRIORITY_LOWEST = 5; + /** * Create a new SimpleMessage with $headers, $encoder and $cache. * - * @param Swift_Mime_HeaderSet $headers - * @param Swift_Mime_ContentEncoder $encoder - * @param Swift_KeyCache $cache - * @param Swift_Mime_Grammar $grammar - * @param string $charset + * @param Swift_Mime_SimpleHeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_IdGenerator $idGenerator + * @param string $charset */ - public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar, $charset = null) + public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator, $charset = null) { - parent::__construct($headers, $encoder, $cache, $grammar, $charset); + parent::__construct($headers, $encoder, $cache, $idGenerator, $charset); $this->getHeaders()->defineOrdering(array( 'Return-Path', 'Received', @@ -47,7 +53,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime )); $this->getHeaders()->setAlwaysDisplayed(array('Date', 'Message-ID', 'From')); $this->getHeaders()->addTextHeader('MIME-Version', '1.0'); - $this->setDate(time()); + $this->setDate(new DateTimeImmutable()); $this->setId($this->getId()); $this->getHeaders()->addMailboxHeader('From'); } @@ -67,11 +73,11 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * * @param string $subject * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setSubject($subject) { - if (!$this->_setHeaderFieldModel('Subject', $subject)) { + if (!$this->setHeaderFieldModel('Subject', $subject)) { $this->getHeaders()->addTextHeader('Subject', $subject); } @@ -85,20 +91,20 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getSubject() { - return $this->_getHeaderFieldModel('Subject'); + return $this->getHeaderFieldModel('Subject'); } /** * Set the date at which this message was created. * - * @param int $date + * @param DateTimeInterface $dateTime * - * @return Swift_Mime_SimpleMessage + * @return $this */ - public function setDate($date) + public function setDate(DateTimeInterface $dateTime) { - if (!$this->_setHeaderFieldModel('Date', $date)) { - $this->getHeaders()->addDateHeader('Date', $date); + if (!$this->setHeaderFieldModel('Date', $dateTime)) { + $this->getHeaders()->addDateHeader('Date', $dateTime); } return $this; @@ -107,11 +113,11 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime /** * Get the date at which this message was created. * - * @return int + * @return DateTimeInterface */ public function getDate() { - return $this->_getHeaderFieldModel('Date'); + return $this->getHeaderFieldModel('Date'); } /** @@ -119,11 +125,11 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * * @param string $address * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setReturnPath($address) { - if (!$this->_setHeaderFieldModel('Return-Path', $address)) { + if (!$this->setHeaderFieldModel('Return-Path', $address)) { $this->getHeaders()->addPathHeader('Return-Path', $address); } @@ -137,7 +143,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getReturnPath() { - return $this->_getHeaderFieldModel('Return-Path'); + return $this->getHeaderFieldModel('Return-Path'); } /** @@ -148,7 +154,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string $address * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setSender($address, $name = null) { @@ -156,7 +162,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime $address = array($address => $name); } - if (!$this->_setHeaderFieldModel('Sender', (array) $address)) { + if (!$this->setHeaderFieldModel('Sender', (array) $address)) { $this->getHeaders()->addMailboxHeader('Sender', (array) $address); } @@ -170,7 +176,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getSender() { - return $this->_getHeaderFieldModel('Sender'); + return $this->getHeaderFieldModel('Sender'); } /** @@ -181,7 +187,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string $address * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function addFrom($address, $name = null) { @@ -202,7 +208,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string|array $addresses * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setFrom($addresses, $name = null) { @@ -210,7 +216,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime $addresses = array($addresses => $name); } - if (!$this->_setHeaderFieldModel('From', (array) $addresses)) { + if (!$this->setHeaderFieldModel('From', (array) $addresses)) { $this->getHeaders()->addMailboxHeader('From', (array) $addresses); } @@ -224,7 +230,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getFrom() { - return $this->_getHeaderFieldModel('From'); + return $this->getHeaderFieldModel('From'); } /** @@ -235,7 +241,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string $address * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function addReplyTo($address, $name = null) { @@ -256,7 +262,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param mixed $addresses * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setReplyTo($addresses, $name = null) { @@ -264,7 +270,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime $addresses = array($addresses => $name); } - if (!$this->_setHeaderFieldModel('Reply-To', (array) $addresses)) { + if (!$this->setHeaderFieldModel('Reply-To', (array) $addresses)) { $this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses); } @@ -278,7 +284,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getReplyTo() { - return $this->_getHeaderFieldModel('Reply-To'); + return $this->getHeaderFieldModel('Reply-To'); } /** @@ -289,7 +295,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string $address * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function addTo($address, $name = null) { @@ -311,7 +317,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param mixed $addresses * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setTo($addresses, $name = null) { @@ -319,7 +325,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime $addresses = array($addresses => $name); } - if (!$this->_setHeaderFieldModel('To', (array) $addresses)) { + if (!$this->setHeaderFieldModel('To', (array) $addresses)) { $this->getHeaders()->addMailboxHeader('To', (array) $addresses); } @@ -333,7 +339,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getTo() { - return $this->_getHeaderFieldModel('To'); + return $this->getHeaderFieldModel('To'); } /** @@ -344,7 +350,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string $address * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function addCc($address, $name = null) { @@ -363,7 +369,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param mixed $addresses * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setCc($addresses, $name = null) { @@ -371,7 +377,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime $addresses = array($addresses => $name); } - if (!$this->_setHeaderFieldModel('Cc', (array) $addresses)) { + if (!$this->setHeaderFieldModel('Cc', (array) $addresses)) { $this->getHeaders()->addMailboxHeader('Cc', (array) $addresses); } @@ -385,7 +391,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getCc() { - return $this->_getHeaderFieldModel('Cc'); + return $this->getHeaderFieldModel('Cc'); } /** @@ -396,7 +402,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param string $address * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function addBcc($address, $name = null) { @@ -415,7 +421,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * @param mixed $addresses * @param string $name optional * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setBcc($addresses, $name = null) { @@ -423,7 +429,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime $addresses = array($addresses => $name); } - if (!$this->_setHeaderFieldModel('Bcc', (array) $addresses)) { + if (!$this->setHeaderFieldModel('Bcc', (array) $addresses)) { $this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses); } @@ -437,7 +443,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getBcc() { - return $this->_getHeaderFieldModel('Bcc'); + return $this->getHeaderFieldModel('Bcc'); } /** @@ -447,16 +453,16 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * * @param int $priority * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setPriority($priority) { $priorityMap = array( - 1 => 'Highest', - 2 => 'High', - 3 => 'Normal', - 4 => 'Low', - 5 => 'Lowest', + self::PRIORITY_HIGHEST => 'Highest', + self::PRIORITY_HIGH => 'High', + self::PRIORITY_NORMAL => 'Normal', + self::PRIORITY_LOW => 'Low', + self::PRIORITY_LOWEST => 'Lowest', ); $pMapKeys = array_keys($priorityMap); if ($priority > max($pMapKeys)) { @@ -464,7 +470,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime } elseif ($priority < min($pMapKeys)) { $priority = min($pMapKeys); } - if (!$this->_setHeaderFieldModel('X-Priority', + if (!$this->setHeaderFieldModel('X-Priority', sprintf('%d (%s)', $priority, $priorityMap[$priority]))) { $this->getHeaders()->addTextHeader('X-Priority', sprintf('%d (%s)', $priority, $priorityMap[$priority])); @@ -483,11 +489,11 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getPriority() { - list($priority) = sscanf($this->_getHeaderFieldModel('X-Priority'), + list($priority) = sscanf($this->getHeaderFieldModel('X-Priority'), '%[1-5]' ); - return isset($priority) ? $priority : 3; + return $priority ?? 3; } /** @@ -495,11 +501,11 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime * * @param array $addresses * - * @return Swift_Mime_SimpleMessage + * @return $this */ public function setReadReceiptTo($addresses) { - if (!$this->_setHeaderFieldModel('Disposition-Notification-To', $addresses)) { + if (!$this->setHeaderFieldModel('Disposition-Notification-To', $addresses)) { $this->getHeaders() ->addMailboxHeader('Disposition-Notification-To', $addresses); } @@ -514,17 +520,17 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime */ public function getReadReceiptTo() { - return $this->_getHeaderFieldModel('Disposition-Notification-To'); + return $this->getHeaderFieldModel('Disposition-Notification-To'); } /** - * Attach a {@link Swift_Mime_MimeEntity} such as an Attachment or MimePart. + * Attach a {@link Swift_Mime_SimpleMimeEntity} such as an Attachment or MimePart. * - * @param Swift_Mime_MimeEntity $entity + * @param Swift_Mime_SimpleMimeEntity $entity * - * @return Swift_Mime_SimpleMessage + * @return $this */ - public function attach(Swift_Mime_MimeEntity $entity) + public function attach(Swift_Mime_SimpleMimeEntity $entity) { $this->setChildren(array_merge($this->getChildren(), array($entity))); @@ -534,11 +540,11 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime /** * Remove an already attached entity. * - * @param Swift_Mime_MimeEntity $entity + * @param Swift_Mime_SimpleMimeEntity $entity * - * @return Swift_Mime_SimpleMessage + * @return $this */ - public function detach(Swift_Mime_MimeEntity $entity) + public function detach(Swift_Mime_SimpleMimeEntity $entity) { $newChildren = array(); foreach ($this->getChildren() as $child) { @@ -552,14 +558,14 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime } /** - * Attach a {@link Swift_Mime_MimeEntity} and return it's CID source. + * Attach a {@link Swift_Mime_SimpleMimeEntity} and return it's CID source. * This method should be used when embedding images or other data in a message. * - * @param Swift_Mime_MimeEntity $entity + * @param Swift_Mime_SimpleMimeEntity $entity * * @return string */ - public function embed(Swift_Mime_MimeEntity $entity) + public function embed(Swift_Mime_SimpleMimeEntity $entity) { $this->attach($entity); @@ -574,7 +580,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime public function toString() { if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { - $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + $this->setChildren(array_merge(array($this->becomeMimePart()), $children)); $string = parent::toString(); $this->setChildren($children); } else { @@ -604,7 +610,7 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime public function toByteStream(Swift_InputByteStream $is) { if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { - $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + $this->setChildren(array_merge(array($this->becomeMimePart()), $children)); parent::toByteStream($is); $this->setChildren($children); } else { @@ -612,29 +618,29 @@ class Swift_Mime_SimpleMessage extends Swift_Mime_MimePart implements Swift_Mime } } - /** @see Swift_Mime_SimpleMimeEntity::_getIdField() */ - protected function _getIdField() + /** @see Swift_Mime_SimpleMimeEntity::getIdField() */ + protected function getIdField() { return 'Message-ID'; } /** Turn the body of this message into a child of itself if needed */ - protected function _becomeMimePart() + protected function becomeMimePart() { $part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(), - $this->_getCache(), $this->_getGrammar(), $this->_userCharset + $this->getCache(), $this->getIdGenerator(), $this->userCharset ); - $part->setContentType($this->_userContentType); + $part->setContentType($this->userContentType); $part->setBody($this->getBody()); - $part->setFormat($this->_userFormat); - $part->setDelSp($this->_userDelSp); - $part->_setNestingLevel($this->_getTopNestingLevel()); + $part->setFormat($this->userFormat); + $part->setDelSp($this->userDelSp); + $part->setNestingLevel($this->getTopNestingLevel()); return $part; } /** Get the highest nesting level nested inside this message */ - private function _getTopNestingLevel() + private function getTopNestingLevel() { $highestLevel = $this->getNestingLevel(); foreach ($this->getChildren() as $child) { diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php index 6b91718fb85..2d1a9b47d56 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php @@ -13,79 +13,91 @@ * * @author Chris Corbyn */ -class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity +class Swift_Mime_SimpleMimeEntity implements Swift_Mime_CharsetObserver, Swift_Mime_EncodingObserver { + /** Main message document; there can only be one of these */ + const LEVEL_TOP = 16; + + /** An entity which nests with the same precedence as an attachment */ + const LEVEL_MIXED = 256; + + /** An entity which nests with the same precedence as a mime part */ + const LEVEL_ALTERNATIVE = 4096; + + /** An entity which nests with the same precedence as embedded content */ + const LEVEL_RELATED = 65536; + /** A collection of Headers for this mime entity */ - private $_headers; + private $headers; /** The body as a string, or a stream */ - private $_body; + private $body; /** The encoder that encodes the body into a streamable format */ - private $_encoder; + private $encoder; - /** The grammar to use for id validation */ - private $_grammar; + /** Message ID generator */ + private $idGenerator; /** A mime boundary, if any is used */ - private $_boundary; + private $boundary; /** Mime types to be used based on the nesting level */ - private $_compositeRanges = array( + private $compositeRanges = array( 'multipart/mixed' => array(self::LEVEL_TOP, self::LEVEL_MIXED), 'multipart/alternative' => array(self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE), 'multipart/related' => array(self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED), ); /** A set of filter rules to define what level an entity should be nested at */ - private $_compoundLevelFilters = array(); + private $compoundLevelFilters = array(); /** The nesting level of this entity */ - private $_nestingLevel = self::LEVEL_ALTERNATIVE; + private $nestingLevel = self::LEVEL_ALTERNATIVE; /** A KeyCache instance used during encoding and streaming */ - private $_cache; + private $cache; /** Direct descendants of this entity */ - private $_immediateChildren = array(); + private $immediateChildren = array(); /** All descendants of this entity */ - private $_children = array(); + private $children = array(); /** The maximum line length of the body of this entity */ - private $_maxLineLength = 78; + private $maxLineLength = 78; /** The order in which alternative mime types should appear */ - private $_alternativePartOrder = array( + private $alternativePartOrder = array( 'text/plain' => 1, 'text/html' => 2, 'multipart/related' => 3, ); /** The CID of this entity */ - private $_id; + private $id; /** The key used for accessing the cache */ - private $_cacheKey; + private $cacheKey; - protected $_userContentType; + protected $userContentType; /** * Create a new SimpleMimeEntity with $headers, $encoder and $cache. * - * @param Swift_Mime_HeaderSet $headers - * @param Swift_Mime_ContentEncoder $encoder - * @param Swift_KeyCache $cache - * @param Swift_Mime_Grammar $grammar + * @param Swift_Mime_SimpleHeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_IdGenerator $idGenerator */ - public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar) + public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator) { - $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); - $this->_cache = $cache; - $this->_headers = $headers; - $this->_grammar = $grammar; + $this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values + $this->cache = $cache; + $this->headers = $headers; + $this->idGenerator = $idGenerator; $this->setEncoder($encoder); - $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding')); + $this->headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding')); // This array specifies that, when the entire MIME document contains // $compoundLevel, then for each child within $level, if its Content-Type @@ -100,7 +112,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity // ) // ) - $this->_compoundLevelFilters = array( + $this->compoundLevelFilters = array( (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array( self::LEVEL_ALTERNATIVE => array( 'text/plain' => self::LEVEL_ALTERNATIVE, @@ -109,7 +121,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity ), ); - $this->_id = $this->getRandomId(); + $this->id = $this->idGenerator->generateId(); } /** @@ -119,19 +131,19 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function generateId() { - $this->setId($this->getRandomId()); + $this->setId($this->idGenerator->generateId()); - return $this->_id; + return $this->id; } /** - * Get the {@link Swift_Mime_HeaderSet} for this entity. + * Get the {@link Swift_Mime_SimpleHeaderSet} for this entity. * - * @return Swift_Mime_HeaderSet + * @return Swift_Mime_SimpleHeaderSet */ public function getHeaders() { - return $this->_headers; + return $this->headers; } /** @@ -143,7 +155,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getNestingLevel() { - return $this->_nestingLevel; + return $this->nestingLevel; } /** @@ -153,7 +165,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getContentType() { - return $this->_getHeaderFieldModel('Content-Type'); + return $this->getHeaderFieldModel('Content-Type'); } /** @@ -161,14 +173,14 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @param string $type * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setContentType($type) { - $this->_setContentTypeInHeaders($type); + $this->setContentTypeInHeaders($type); // Keep track of the value so that if the content-type changes automatically // due to added child entities, it can be restored if they are later removed - $this->_userContentType = $type; + $this->userContentType = $type; return $this; } @@ -182,9 +194,9 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getId() { - $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField()); + $tmp = (array) $this->getHeaderFieldModel($this->getIdField()); - return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id; + return $this->headers->has($this->getIdField()) ? current($tmp) : $this->id; } /** @@ -192,14 +204,14 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @param string $id * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setId($id) { - if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) { - $this->_headers->addIdHeader($this->_getIdField(), $id); + if (!$this->setHeaderFieldModel($this->getIdField(), $id)) { + $this->headers->addIdHeader($this->getIdField(), $id); } - $this->_id = $id; + $this->id = $id; return $this; } @@ -213,7 +225,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getDescription() { - return $this->_getHeaderFieldModel('Content-Description'); + return $this->getHeaderFieldModel('Content-Description'); } /** @@ -223,12 +235,12 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @param string $description * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setDescription($description) { - if (!$this->_setHeaderFieldModel('Content-Description', $description)) { - $this->_headers->addTextHeader('Content-Description', $description); + if (!$this->setHeaderFieldModel('Content-Description', $description)) { + $this->headers->addTextHeader('Content-Description', $description); } return $this; @@ -241,7 +253,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getMaxLineLength() { - return $this->_maxLineLength; + return $this->maxLineLength; } /** @@ -251,11 +263,11 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @param int $length * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setMaxLineLength($length) { - $this->_maxLineLength = $length; + $this->maxLineLength = $length; return $this; } @@ -263,37 +275,36 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity /** * Get all children added to this entity. * - * @return Swift_Mime_MimeEntity[] + * @return Swift_Mime_SimpleMimeEntity[] */ public function getChildren() { - return $this->_children; + return $this->children; } /** * Set all children of this entity. * - * @param Swift_Mime_MimeEntity[] $children - * @param int $compoundLevel For internal use only + * @param Swift_Mime_SimpleMimeEntity[] $children + * @param int $compoundLevel For internal use only * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setChildren(array $children, $compoundLevel = null) { // TODO: Try to refactor this logic - - $compoundLevel = isset($compoundLevel) ? $compoundLevel : $this->_getCompoundLevel($children); + $compoundLevel = $compoundLevel ?? $this->getCompoundLevel($children); $immediateChildren = array(); $grandchildren = array(); - $newContentType = $this->_userContentType; + $newContentType = $this->userContentType; foreach ($children as $child) { - $level = $this->_getNeededChildLevel($child, $compoundLevel); + $level = $this->getNeededChildLevel($child, $compoundLevel); if (empty($immediateChildren)) { //first iteration $immediateChildren = array($child); } else { - $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + $nextLevel = $this->getNeededChildLevel($immediateChildren[0], $compoundLevel); if ($nextLevel == $level) { $immediateChildren[] = $child; } elseif ($level < $nextLevel) { @@ -308,11 +319,11 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity } if ($immediateChildren) { - $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + $lowestLevel = $this->getNeededChildLevel($immediateChildren[0], $compoundLevel); // Determine which composite media type is needed to accommodate the // immediate children - foreach ($this->_compositeRanges as $mediaType => $range) { + foreach ($this->compositeRanges as $mediaType => $range) { if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) { $newContentType = $mediaType; @@ -322,18 +333,18 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity // Put any grandchildren in a subpart if (!empty($grandchildren)) { - $subentity = $this->_createChild(); - $subentity->_setNestingLevel($lowestLevel); + $subentity = $this->createChild(); + $subentity->setNestingLevel($lowestLevel); $subentity->setChildren($grandchildren, $compoundLevel); array_unshift($immediateChildren, $subentity); } } - $this->_immediateChildren = $immediateChildren; - $this->_children = $children; - $this->_setContentTypeInHeaders($newContentType); - $this->_fixHeaders(); - $this->_sortChildren(); + $this->immediateChildren = $immediateChildren; + $this->children = $children; + $this->setContentTypeInHeaders($newContentType); + $this->fixHeaders(); + $this->sortChildren(); return $this; } @@ -345,7 +356,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getBody() { - return $this->_body instanceof Swift_OutputByteStream ? $this->_readStream($this->_body) : $this->_body; + return $this->body instanceof Swift_OutputByteStream ? $this->readStream($this->body) : $this->body; } /** @@ -355,15 +366,15 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * @param mixed $body * @param string $contentType optional * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setBody($body, $contentType = null) { - if ($body !== $this->_body) { - $this->_clearCache(); + if ($body !== $this->body) { + $this->clearCache(); } - $this->_body = $body; + $this->body = $body; if (isset($contentType)) { $this->setContentType($contentType); } @@ -378,7 +389,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getEncoder() { - return $this->_encoder; + return $this->encoder; } /** @@ -386,17 +397,17 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @param Swift_Mime_ContentEncoder $encoder * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setEncoder(Swift_Mime_ContentEncoder $encoder) { - if ($encoder !== $this->_encoder) { - $this->_clearCache(); + if ($encoder !== $this->encoder) { + $this->clearCache(); } - $this->_encoder = $encoder; - $this->_setEncoding($encoder->getName()); - $this->_notifyEncoderChanged($encoder); + $this->encoder = $encoder; + $this->setEncoding($encoder->getName()); + $this->notifyEncoderChanged($encoder); return $this; } @@ -408,11 +419,11 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function getBoundary() { - if (!isset($this->_boundary)) { - $this->_boundary = '_=_swift_v4_'.time().'_'.md5(getmypid().mt_rand().uniqid('', true)).'_=_'; + if (!isset($this->boundary)) { + $this->boundary = '_=_swift_'.time().'_'.bin2hex(random_bytes(16)).'_=_'; } - return $this->_boundary; + return $this->boundary; } /** @@ -422,12 +433,12 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @throws Swift_RfcComplianceException * - * @return Swift_Mime_SimpleMimeEntity + * @return $this */ public function setBoundary($boundary) { - $this->_assertValidBoundary($boundary); - $this->_boundary = $boundary; + $this->assertValidBoundary($boundary); + $this->boundary = $boundary; return $this; } @@ -440,7 +451,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function charsetChanged($charset) { - $this->_notifyCharsetChanged($charset); + $this->notifyCharsetChanged($charset); } /** @@ -451,7 +462,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function encoderChanged(Swift_Mime_ContentEncoder $encoder) { - $this->_notifyEncoderChanged($encoder); + $this->notifyEncoderChanged($encoder); } /** @@ -461,8 +472,8 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function toString() { - $string = $this->_headers->toString(); - $string .= $this->_bodyToString(); + $string = $this->headers->toString(); + $string .= $this->bodyToString(); return $string; } @@ -472,22 +483,22 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @return string */ - protected function _bodyToString() + protected function bodyToString() { $string = ''; - if (isset($this->_body) && empty($this->_immediateChildren)) { - if ($this->_cache->hasKey($this->_cacheKey, 'body')) { - $body = $this->_cache->getString($this->_cacheKey, 'body'); + if (isset($this->body) && empty($this->immediateChildren)) { + if ($this->cache->hasKey($this->cacheKey, 'body')) { + $body = $this->cache->getString($this->cacheKey, 'body'); } else { - $body = "\r\n".$this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()); - $this->_cache->setString($this->_cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE); + $body = "\r\n".$this->encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()); + $this->cache->setString($this->cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE); } $string .= $body; } - if (!empty($this->_immediateChildren)) { - foreach ($this->_immediateChildren as $child) { + if (!empty($this->immediateChildren)) { + foreach ($this->immediateChildren as $child) { $string .= "\r\n\r\n--".$this->getBoundary()."\r\n"; $string .= $child->toString(); } @@ -516,10 +527,10 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function toByteStream(Swift_InputByteStream $is) { - $is->write($this->_headers->toString()); + $is->write($this->headers->toString()); $is->commit(); - $this->_bodyToByteStream($is); + $this->bodyToByteStream($is); } /** @@ -527,26 +538,26 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @param Swift_InputByteStream */ - protected function _bodyToByteStream(Swift_InputByteStream $is) + protected function bodyToByteStream(Swift_InputByteStream $is) { - if (empty($this->_immediateChildren)) { - if (isset($this->_body)) { - if ($this->_cache->hasKey($this->_cacheKey, 'body')) { - $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is); + if (empty($this->immediateChildren)) { + if (isset($this->body)) { + if ($this->cache->hasKey($this->cacheKey, 'body')) { + $this->cache->exportToByteStream($this->cacheKey, 'body', $is); } else { - $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body'); + $cacheIs = $this->cache->getInputByteStream($this->cacheKey, 'body'); if ($cacheIs) { $is->bind($cacheIs); } $is->write("\r\n"); - if ($this->_body instanceof Swift_OutputByteStream) { - $this->_body->setReadPointer(0); + if ($this->body instanceof Swift_OutputByteStream) { + $this->body->setReadPointer(0); - $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength()); + $this->encoder->encodeByteStream($this->body, $is, 0, $this->getMaxLineLength()); } else { - $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength())); + $is->write($this->encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength())); } if ($cacheIs) { @@ -556,8 +567,8 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity } } - if (!empty($this->_immediateChildren)) { - foreach ($this->_immediateChildren as $child) { + if (!empty($this->immediateChildren)) { + foreach ($this->immediateChildren as $child) { $is->write("\r\n\r\n--".$this->getBoundary()."\r\n"); $child->toByteStream($is); } @@ -568,7 +579,7 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity /** * Get the name of the header that provides the ID of this entity. */ - protected function _getIdField() + protected function getIdField() { return 'Content-ID'; } @@ -576,20 +587,20 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity /** * Get the model data (usually an array or a string) for $field. */ - protected function _getHeaderFieldModel($field) + protected function getHeaderFieldModel($field) { - if ($this->_headers->has($field)) { - return $this->_headers->get($field)->getFieldBodyModel(); + if ($this->headers->has($field)) { + return $this->headers->get($field)->getFieldBodyModel(); } } /** * Set the model data for $field. */ - protected function _setHeaderFieldModel($field, $model) + protected function setHeaderFieldModel($field, $model) { - if ($this->_headers->has($field)) { - $this->_headers->get($field)->setFieldBodyModel($model); + if ($this->headers->has($field)) { + $this->headers->get($field)->setFieldBodyModel($model); return true; } @@ -600,20 +611,20 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity /** * Get the parameter value of $parameter on $field header. */ - protected function _getHeaderParameter($field, $parameter) + protected function getHeaderParameter($field, $parameter) { - if ($this->_headers->has($field)) { - return $this->_headers->get($field)->getParameter($parameter); + if ($this->headers->has($field)) { + return $this->headers->get($field)->getParameter($parameter); } } /** * Set the parameter value of $parameter on $field header. */ - protected function _setHeaderParameter($field, $parameter, $value) + protected function setHeaderParameter($field, $parameter, $value) { - if ($this->_headers->has($field)) { - $this->_headers->get($field)->setParameter($parameter, $value); + if ($this->headers->has($field)) { + $this->headers->get($field)->setParameter($parameter, $value); return true; } @@ -624,16 +635,16 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity /** * Re-evaluate what content type and encoding should be used on this entity. */ - protected function _fixHeaders() + protected function fixHeaders() { - if (count($this->_immediateChildren)) { - $this->_setHeaderParameter('Content-Type', 'boundary', + if (count($this->immediateChildren)) { + $this->setHeaderParameter('Content-Type', 'boundary', $this->getBoundary() ); - $this->_headers->remove('Content-Transfer-Encoding'); + $this->headers->remove('Content-Transfer-Encoding'); } else { - $this->_setHeaderParameter('Content-Type', 'boundary', null); - $this->_setEncoding($this->_encoder->getName()); + $this->setHeaderParameter('Content-Type', 'boundary', null); + $this->setEncoding($this->encoder->getName()); } } @@ -642,50 +653,30 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity * * @return Swift_KeyCache */ - protected function _getCache() + protected function getCache() { - return $this->_cache; + return $this->cache; } /** - * Get the grammar used for validation. + * Get the ID generator. * - * @return Swift_Mime_Grammar + * @return Swift_IdGenerator */ - protected function _getGrammar() + protected function getIdGenerator() { - return $this->_grammar; + return $this->idGenerator; } /** * Empty the KeyCache for this entity. */ - protected function _clearCache() + protected function clearCache() { - $this->_cache->clearKey($this->_cacheKey, 'body'); + $this->cache->clearKey($this->cacheKey, 'body'); } - /** - * Returns a random Content-ID or Message-ID. - * - * @return string - */ - protected function getRandomId() - { - $idLeft = md5(getmypid().'.'.time().'.'.uniqid(mt_rand(), true)); - $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated'; - $id = $idLeft.'@'.$idRight; - - try { - $this->_assertValidId($id); - } catch (Swift_RfcComplianceException $e) { - $id = $idLeft.'@swift.generated'; - } - - return $id; - } - - private function _readStream(Swift_OutputByteStream $os) + private function readStream(Swift_OutputByteStream $os) { $string = ''; while (false !== $bytes = $os->read(8192)) { @@ -697,33 +688,33 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity return $string; } - private function _setEncoding($encoding) + private function setEncoding($encoding) { - if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) { - $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding); + if (!$this->setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) { + $this->headers->addTextHeader('Content-Transfer-Encoding', $encoding); } } - private function _assertValidBoundary($boundary) + private function assertValidBoundary($boundary) { if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) { throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.'); } } - private function _setContentTypeInHeaders($type) + private function setContentTypeInHeaders($type) { - if (!$this->_setHeaderFieldModel('Content-Type', $type)) { - $this->_headers->addParameterizedHeader('Content-Type', $type); + if (!$this->setHeaderFieldModel('Content-Type', $type)) { + $this->headers->addParameterizedHeader('Content-Type', $type); } } - private function _setNestingLevel($level) + private function setNestingLevel($level) { - $this->_nestingLevel = $level; + $this->nestingLevel = $level; } - private function _getCompoundLevel($children) + private function getCompoundLevel($children) { $level = 0; foreach ($children as $child) { @@ -733,10 +724,10 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity return $level; } - private function _getNeededChildLevel($child, $compoundLevel) + private function getNeededChildLevel($child, $compoundLevel) { $filter = array(); - foreach ($this->_compoundLevelFilters as $bitmask => $rules) { + foreach ($this->compoundLevelFilters as $bitmask => $rules) { if (($compoundLevel & $bitmask) === $bitmask) { $filter = $rules + $filter; } @@ -752,31 +743,31 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity return $realLevel; } - private function _createChild() + private function createChild() { - return new self($this->_headers->newInstance(), $this->_encoder, $this->_cache, $this->_grammar); + return new self($this->headers->newInstance(), $this->encoder, $this->cache, $this->idGenerator); } - private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder) + private function notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder) { - foreach ($this->_immediateChildren as $child) { + foreach ($this->immediateChildren as $child) { $child->encoderChanged($encoder); } } - private function _notifyCharsetChanged($charset) + private function notifyCharsetChanged($charset) { - $this->_encoder->charsetChanged($charset); - $this->_headers->charsetChanged($charset); - foreach ($this->_immediateChildren as $child) { + $this->encoder->charsetChanged($charset); + $this->headers->charsetChanged($charset); + foreach ($this->immediateChildren as $child) { $child->charsetChanged($charset); } } - private function _sortChildren() + private function sortChildren() { $shouldSort = false; - foreach ($this->_immediateChildren as $child) { + foreach ($this->immediateChildren as $child) { // NOTE: This include alternative parts moved into a related part if ($child->getNestingLevel() == self::LEVEL_ALTERNATIVE) { $shouldSort = true; @@ -786,43 +777,32 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity // Sort in order of preference, if there is one if ($shouldSort) { - usort($this->_immediateChildren, array($this, '_childSortAlgorithm')); + // Group the messages by order of preference + $sorted = array(); + foreach ($this->immediateChildren as $child) { + $type = $child->getContentType(); + $level = array_key_exists($type, $this->alternativePartOrder) ? $this->alternativePartOrder[$type] : max($this->alternativePartOrder) + 1; + + if (empty($sorted[$level])) { + $sorted[$level] = array(); + } + + $sorted[$level][] = $child; + } + + ksort($sorted); + + $this->immediateChildren = array_reduce($sorted, 'array_merge', array()); } } - private function _childSortAlgorithm($a, $b) - { - $typePrefs = array(); - $types = array(strtolower($a->getContentType()), strtolower($b->getContentType())); - - foreach ($types as $type) { - $typePrefs[] = array_key_exists($type, $this->_alternativePartOrder) ? $this->_alternativePartOrder[$type] : max($this->_alternativePartOrder) + 1; - } - - return $typePrefs[0] >= $typePrefs[1] ? 1 : -1; - } - - // -- Destructor - /** * Empties it's own contents from the cache. */ public function __destruct() { - $this->_cache->clearAll($this->_cacheKey); - } - - /** - * Throws an Exception if the id passed does not comply with RFC 2822. - * - * @param string $id - * - * @throws Swift_RfcComplianceException - */ - private function _assertValidId($id) - { - if (!preg_match('/^'.$this->_grammar->getDefinition('id-left').'@'.$this->_grammar->getDefinition('id-right').'$/D', $id)) { - throw new Swift_RfcComplianceException('Invalid ID given <'.$id.'>'); + if ($this->cache instanceof Swift_KeyCache) { + $this->cache->clearAll($this->cacheKey); } } @@ -831,11 +811,11 @@ class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity */ public function __clone() { - $this->_headers = clone $this->_headers; - $this->_encoder = clone $this->_encoder; - $this->_cacheKey = uniqid(); + $this->headers = clone $this->headers; + $this->encoder = clone $this->encoder; + $this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values $children = array(); - foreach ($this->_children as $pos => $child) { + foreach ($this->children as $pos => $child) { $children[$pos] = clone $child; } $this->setChildren($children); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/MimePart.php b/htdocs/includes/swiftmailer/lib/classes/Swift/MimePart.php index 215f8db348a..8fa0cbebcd2 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/MimePart.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/MimePart.php @@ -42,18 +42,4 @@ class Swift_MimePart extends Swift_Mime_MimePart $this->setContentType($contentType); } } - - /** - * Create a new MimePart. - * - * @param string $body - * @param string $contentType - * @param string $charset - * - * @return Swift_Mime_MimePart - */ - public static function newInstance($body = null, $contentType = null, $charset = null) - { - return new self($body, $contentType, $charset); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/NullTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/NullTransport.php index b38e1cf7697..a580dfbefb6 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/NullTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/NullTransport.php @@ -15,9 +15,6 @@ */ class Swift_NullTransport extends Swift_Transport_NullTransport { - /** - * Create a new NullTransport. - */ public function __construct() { call_user_func_array( @@ -26,14 +23,4 @@ class Swift_NullTransport extends Swift_Transport_NullTransport ->createDependenciesFor('transport.null') ); } - - /** - * Create a new NullTransport instance. - * - * @return Swift_NullTransport - */ - public static function newInstance() - { - return new self(); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php index a2ec2abcd50..06b3109e62c 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php @@ -20,28 +20,28 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ * * @var int */ - private $_threshold; + private $threshold; /** * The number of seconds to sleep for during a restart. * * @var int */ - private $_sleep; + private $sleep; /** * The internal counter. * * @var int */ - private $_counter = 0; + private $counter = 0; /** * The Sleeper instance for sleeping. * * @var Swift_Plugins_Sleeper */ - private $_sleeper; + private $sleeper; /** * Create a new AntiFloodPlugin with $threshold and $sleep time. @@ -54,7 +54,7 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ { $this->setThreshold($threshold); $this->setSleepTime($sleep); - $this->_sleeper = $sleeper; + $this->sleeper = $sleeper; } /** @@ -64,7 +64,7 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ */ public function setThreshold($threshold) { - $this->_threshold = $threshold; + $this->threshold = $threshold; } /** @@ -74,7 +74,7 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ */ public function getThreshold() { - return $this->_threshold; + return $this->threshold; } /** @@ -84,7 +84,7 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ */ public function setSleepTime($sleep) { - $this->_sleep = $sleep; + $this->sleep = $sleep; } /** @@ -94,7 +94,7 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ */ public function getSleepTime() { - return $this->_sleep; + return $this->sleep; } /** @@ -113,15 +113,15 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ */ public function sendPerformed(Swift_Events_SendEvent $evt) { - ++$this->_counter; - if ($this->_counter >= $this->_threshold) { + ++$this->counter; + if ($this->counter >= $this->threshold) { $transport = $evt->getTransport(); $transport->stop(); - if ($this->_sleep) { - $this->sleep($this->_sleep); + if ($this->sleep) { + $this->sleep($this->sleep); } $transport->start(); - $this->_counter = 0; + $this->counter = 0; } } @@ -132,8 +132,8 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_ */ public function sleep($seconds) { - if (isset($this->_sleeper)) { - $this->_sleeper->sleep($seconds); + if (isset($this->sleeper)) { + $this->sleeper->sleep($seconds); } else { sleep($seconds); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php index f7e18d0ebea..af060a99cc3 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php @@ -20,17 +20,17 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, * * @var int */ - private $_out = 0; + private $out = 0; /** * The incoming traffic counter. * * @var int */ - private $_in = 0; + private $in = 0; /** Bound byte streams */ - private $_mirrors = array(); + private $mirrors = array(); /** * Not used. @@ -58,7 +58,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, public function commandSent(Swift_Events_CommandEvent $evt) { $command = $evt->getCommand(); - $this->_out += strlen($command); + $this->out += strlen($command); } /** @@ -69,7 +69,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, public function responseReceived(Swift_Events_ResponseEvent $evt) { $response = $evt->getResponse(); - $this->_in += strlen($response); + $this->in += strlen($response); } /** @@ -79,8 +79,8 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function write($bytes) { - $this->_out += strlen($bytes); - foreach ($this->_mirrors as $stream) { + $this->out += strlen($bytes); + foreach ($this->mirrors as $stream) { $stream->write($bytes); } } @@ -102,7 +102,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function bind(Swift_InputByteStream $is) { - $this->_mirrors[] = $is; + $this->mirrors[] = $is; } /** @@ -116,9 +116,9 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function unbind(Swift_InputByteStream $is) { - foreach ($this->_mirrors as $k => $stream) { + foreach ($this->mirrors as $k => $stream) { if ($is === $stream) { - unset($this->_mirrors[$k]); + unset($this->mirrors[$k]); } } } @@ -128,7 +128,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function flushBuffers() { - foreach ($this->_mirrors as $stream) { + foreach ($this->mirrors as $stream) { $stream->flushBuffers(); } } @@ -140,7 +140,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function getBytesOut() { - return $this->_out; + return $this->out; } /** @@ -150,7 +150,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function getBytesIn() { - return $this->_in; + return $this->in; } /** @@ -158,7 +158,7 @@ class Swift_Plugins_BandwidthMonitorPlugin implements Swift_Events_SendListener, */ public function reset() { - $this->_out = 0; - $this->_in = 0; + $this->out = 0; + $this->in = 0; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php index 0762b36d218..9d5feefb92c 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php @@ -17,19 +17,19 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_Plugins_Decorator_Replacements { /** The replacement map */ - private $_replacements; + private $replacements; /** The body as it was before replacements */ - private $_originalBody; + private $originalBody; /** The original headers of the message, before replacements */ - private $_originalHeaders = array(); + private $originalHeaders = array(); /** Bodies of children before they are replaced */ - private $_originalChildBodies = array(); + private $originalChildBodies = array(); /** The Message that was last replaced */ - private $_lastMessage; + private $lastMessage; /** * Create a new DecoratorPlugin with $replacements. @@ -66,9 +66,9 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ public function setReplacements($replacements) { if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { - $this->_replacements = (array) $replacements; + $this->replacements = (array) $replacements; } else { - $this->_replacements = $replacements; + $this->replacements = $replacements; } } @@ -80,7 +80,7 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ public function beforeSendPerformed(Swift_Events_SendEvent $evt) { $message = $evt->getMessage(); - $this->_restoreMessage($message); + $this->restoreMessage($message); $to = array_keys($message->getTo()); $address = array_shift($to); if ($replacements = $this->getReplacementsFor($address)) { @@ -91,7 +91,7 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ $search, $replace, $body ); if ($body != $bodyReplaced) { - $this->_originalBody = $body; + $this->originalBody = $body; $message->setBody($bodyReplaced); } @@ -111,12 +111,12 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ $count = 1; } } - } else { + } elseif (is_string($body)) { $bodyReplaced = str_replace($search, $replace, $body, $count); } if ($count) { - $this->_originalHeaders[$header->getFieldName()] = $body; + $this->originalHeaders[$header->getFieldName()] = $body; $header->setFieldBodyModel($bodyReplaced); } } @@ -131,11 +131,11 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ ); if ($body != $bodyReplaced) { $child->setBody($bodyReplaced); - $this->_originalChildBodies[$child->getId()] = $body; + $this->originalChildBodies[$child->getId()] = $body; } } } - $this->_lastMessage = $message; + $this->lastMessage = $message; } } @@ -155,11 +155,11 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ */ public function getReplacementsFor($address) { - if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements) { - return $this->_replacements->getReplacementsFor($address); + if ($this->replacements instanceof Swift_Plugins_Decorator_Replacements) { + return $this->replacements->getReplacementsFor($address); } - return isset($this->_replacements[$address]) ? $this->_replacements[$address] : null; + return $this->replacements[$address] ?? null; } /** @@ -169,36 +169,36 @@ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_ */ public function sendPerformed(Swift_Events_SendEvent $evt) { - $this->_restoreMessage($evt->getMessage()); + $this->restoreMessage($evt->getMessage()); } /** Restore a changed message back to its original state */ - private function _restoreMessage(Swift_Mime_Message $message) + private function restoreMessage(Swift_Mime_SimpleMessage $message) { - if ($this->_lastMessage === $message) { - if (isset($this->_originalBody)) { - $message->setBody($this->_originalBody); - $this->_originalBody = null; + if ($this->lastMessage === $message) { + if (isset($this->originalBody)) { + $message->setBody($this->originalBody); + $this->originalBody = null; } - if (!empty($this->_originalHeaders)) { + if (!empty($this->originalHeaders)) { foreach ($message->getHeaders()->getAll() as $header) { - if (array_key_exists($header->getFieldName(), $this->_originalHeaders)) { - $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]); + if (array_key_exists($header->getFieldName(), $this->originalHeaders)) { + $header->setFieldBodyModel($this->originalHeaders[$header->getFieldName()]); } } - $this->_originalHeaders = array(); + $this->originalHeaders = array(); } - if (!empty($this->_originalChildBodies)) { + if (!empty($this->originalChildBodies)) { $children = (array) $message->getChildren(); foreach ($children as $child) { $id = $child->getId(); - if (array_key_exists($id, $this->_originalChildBodies)) { - $child->setBody($this->_originalChildBodies[$id]); + if (array_key_exists($id, $this->originalChildBodies)) { + $child->setBody($this->originalChildBodies[$id]); } } - $this->_originalChildBodies = array(); + $this->originalChildBodies = array(); } - $this->_lastMessage = null; + $this->lastMessage = null; } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php index 7552b67a2f7..b975d36b858 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php @@ -18,9 +18,9 @@ class Swift_Plugins_ImpersonatePlugin implements Swift_Events_SendListener /** * The sender to impersonate. * - * @var String + * @var string */ - private $_sender; + private $sender; /** * Create a new ImpersonatePlugin to impersonate $sender. @@ -29,7 +29,7 @@ class Swift_Plugins_ImpersonatePlugin implements Swift_Events_SendListener */ public function __construct($sender) { - $this->_sender = $sender; + $this->sender = $sender; } /** @@ -46,7 +46,7 @@ class Swift_Plugins_ImpersonatePlugin implements Swift_Events_SendListener $headers->addPathHeader('X-Swift-Return-Path', $message->getReturnPath()); // replace them with the one to send to - $message->setReturnPath($this->_sender); + $message->setReturnPath($this->sender); } /** diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php index 64db4384648..7ddf1fd16db 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php @@ -11,12 +11,12 @@ /** * Does real time logging of Transport level information. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_Events_ResponseListener, Swift_Events_TransportChangeListener, Swift_Events_TransportExceptionListener, Swift_Plugins_Logger { /** The logger which is delegated to */ - private $_logger; + private $logger; /** * Create a new LoggerPlugin using $logger. @@ -25,7 +25,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ */ public function __construct(Swift_Plugins_Logger $logger) { - $this->_logger = $logger; + $this->logger = $logger; } /** @@ -35,7 +35,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ */ public function add($entry) { - $this->_logger->add($entry); + $this->logger->add($entry); } /** @@ -43,7 +43,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ */ public function clear() { - $this->_logger->clear(); + $this->logger->clear(); } /** @@ -53,7 +53,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ */ public function dump() { - return $this->_logger->dump(); + return $this->logger->dump(); } /** @@ -64,7 +64,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ public function commandSent(Swift_Events_CommandEvent $evt) { $command = $evt->getCommand(); - $this->_logger->add(sprintf('>> %s', $command)); + $this->logger->add(sprintf('>> %s', $command)); } /** @@ -75,7 +75,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ public function responseReceived(Swift_Events_ResponseEvent $evt) { $response = $evt->getResponse(); - $this->_logger->add(sprintf('<< %s', $response)); + $this->logger->add(sprintf('<< %s', $response)); } /** @@ -86,7 +86,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) { $transportName = get_class($evt->getSource()); - $this->_logger->add(sprintf('++ Starting %s', $transportName)); + $this->logger->add(sprintf('++ Starting %s', $transportName)); } /** @@ -97,7 +97,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ public function transportStarted(Swift_Events_TransportChangeEvent $evt) { $transportName = get_class($evt->getSource()); - $this->_logger->add(sprintf('++ %s started', $transportName)); + $this->logger->add(sprintf('++ %s started', $transportName)); } /** @@ -108,7 +108,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) { $transportName = get_class($evt->getSource()); - $this->_logger->add(sprintf('++ Stopping %s', $transportName)); + $this->logger->add(sprintf('++ Stopping %s', $transportName)); } /** @@ -119,7 +119,7 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ public function transportStopped(Swift_Events_TransportChangeEvent $evt) { $transportName = get_class($evt->getSource()); - $this->_logger->add(sprintf('++ %s stopped', $transportName)); + $this->logger->add(sprintf('++ %s stopped', $transportName)); } /** @@ -132,10 +132,10 @@ class Swift_Plugins_LoggerPlugin implements Swift_Events_CommandListener, Swift_ $e = $evt->getException(); $message = $e->getMessage(); $code = $e->getCode(); - $this->_logger->add(sprintf('!! %s (code: %s)', $message, $code)); + $this->logger->add(sprintf('!! %s (code: %s)', $message, $code)); $message .= PHP_EOL; $message .= 'Log data:'.PHP_EOL; - $message .= $this->_logger->dump(); + $message .= $this->logger->dump(); $evt->cancelBubble(); throw new Swift_TransportException($message, $code, $e->getPrevious()); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php index 865bb0aa3b9..186b6b41bcd 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php @@ -20,14 +20,14 @@ class Swift_Plugins_Loggers_ArrayLogger implements Swift_Plugins_Logger * * @var array */ - private $_log = array(); + private $log = array(); /** * Max size of the log. * * @var int */ - private $_size = 0; + private $size = 0; /** * Create a new ArrayLogger with a maximum of $size entries. @@ -36,7 +36,7 @@ class Swift_Plugins_Loggers_ArrayLogger implements Swift_Plugins_Logger */ public function __construct($size = 50) { - $this->_size = $size; + $this->size = $size; } /** @@ -46,9 +46,9 @@ class Swift_Plugins_Loggers_ArrayLogger implements Swift_Plugins_Logger */ public function add($entry) { - $this->_log[] = $entry; - while (count($this->_log) > $this->_size) { - array_shift($this->_log); + $this->log[] = $entry; + while (count($this->log) > $this->size) { + array_shift($this->log); } } @@ -57,7 +57,7 @@ class Swift_Plugins_Loggers_ArrayLogger implements Swift_Plugins_Logger */ public function clear() { - $this->_log = array(); + $this->log = array(); } /** @@ -67,6 +67,6 @@ class Swift_Plugins_Loggers_ArrayLogger implements Swift_Plugins_Logger */ public function dump() { - return implode(PHP_EOL, $this->_log); + return implode(PHP_EOL, $this->log); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php index 3583297ab10..40a53d2bba2 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php @@ -16,7 +16,7 @@ class Swift_Plugins_Loggers_EchoLogger implements Swift_Plugins_Logger { /** Whether or not HTML should be output */ - private $_isHtml; + private $isHtml; /** * Create a new EchoLogger. @@ -25,7 +25,7 @@ class Swift_Plugins_Loggers_EchoLogger implements Swift_Plugins_Logger */ public function __construct($isHtml = true) { - $this->_isHtml = $isHtml; + $this->isHtml = $isHtml; } /** @@ -35,7 +35,7 @@ class Swift_Plugins_Loggers_EchoLogger implements Swift_Plugins_Logger */ public function add($entry) { - if ($this->_isHtml) { + if ($this->isHtml) { printf('%s%s%s', htmlspecialchars($entry, ENT_QUOTES), '
    ', PHP_EOL); } else { printf('%s%s', $entry, PHP_EOL); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php index e622cb37d16..5ff1d9321c0 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php @@ -16,7 +16,7 @@ class Swift_Plugins_MessageLogger implements Swift_Events_SendListener { /** - * @var array + * @var Swift_Mime_Message[] */ private $messages; @@ -28,7 +28,7 @@ class Swift_Plugins_MessageLogger implements Swift_Events_SendListener /** * Get the message list. * - * @return array + * @return Swift_Mime_Message[] */ public function getMessages() { diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php index 18abb770632..c19403aec61 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php @@ -11,36 +11,36 @@ /** * Makes sure a connection to a POP3 host has been established prior to connecting to SMTP. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeListener, Swift_Plugins_Pop_Pop3Connection { /** A delegate connection to use (mostly a test hook) */ - private $_connection; + private $connection; /** Hostname of the POP3 server */ - private $_host; + private $host; /** Port number to connect on */ - private $_port; + private $port; /** Encryption type to use (if any) */ - private $_crypto; + private $crypto; /** Username to use (if any) */ - private $_username; + private $username; /** Password to use (if any) */ - private $_password; + private $password; /** Established connection via TCP socket */ - private $_socket; + private $socket; /** Connect timeout in seconds */ - private $_timeout = 10; + private $timeout = 10; /** SMTP Transport to bind to */ - private $_transport; + private $transport; /** * Create a new PopBeforeSmtpPlugin for $host and $port. @@ -51,23 +51,9 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL */ public function __construct($host, $port = 110, $crypto = null) { - $this->_host = $host; - $this->_port = $port; - $this->_crypto = $crypto; - } - - /** - * Create a new PopBeforeSmtpPlugin for $host and $port. - * - * @param string $host - * @param int $port - * @param string $crypto as "tls" or "ssl" - * - * @return Swift_Plugins_PopBeforeSmtpPlugin - */ - public static function newInstance($host, $port = 110, $crypto = null) - { - return new self($host, $port, $crypto); + $this->host = $host; + $this->port = $port; + $this->crypto = $crypto; } /** @@ -75,11 +61,11 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL * * @param Swift_Plugins_Pop_Pop3Connection $connection * - * @return Swift_Plugins_PopBeforeSmtpPlugin + * @return $this */ public function setConnection(Swift_Plugins_Pop_Pop3Connection $connection) { - $this->_connection = $connection; + $this->connection = $connection; return $this; } @@ -91,7 +77,7 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL */ public function bindSmtp(Swift_Transport $smtp) { - $this->_transport = $smtp; + $this->transport = $smtp; } /** @@ -99,11 +85,11 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL * * @param int $timeout * - * @return Swift_Plugins_PopBeforeSmtpPlugin + * @return $this */ public function setTimeout($timeout) { - $this->_timeout = (int) $timeout; + $this->timeout = (int) $timeout; return $this; } @@ -113,11 +99,11 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL * * @param string $username * - * @return Swift_Plugins_PopBeforeSmtpPlugin + * @return $this */ public function setUsername($username) { - $this->_username = $username; + $this->username = $username; return $this; } @@ -127,11 +113,11 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL * * @param string $password * - * @return Swift_Plugins_PopBeforeSmtpPlugin + * @return $this */ public function setPassword($password) { - $this->_password = $password; + $this->password = $password; return $this; } @@ -143,29 +129,29 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL */ public function connect() { - if (isset($this->_connection)) { - $this->_connection->connect(); + if (isset($this->connection)) { + $this->connection->connect(); } else { - if (!isset($this->_socket)) { + if (!isset($this->socket)) { if (!$socket = fsockopen( - $this->_getHostString(), $this->_port, $errno, $errstr, $this->_timeout)) { + $this->getHostString(), $this->port, $errno, $errstr, $this->timeout)) { throw new Swift_Plugins_Pop_Pop3Exception( - sprintf('Failed to connect to POP3 host [%s]: %s', $this->_host, $errstr) + sprintf('Failed to connect to POP3 host [%s]: %s', $this->host, $errstr) ); } - $this->_socket = $socket; + $this->socket = $socket; - if (false === $greeting = fgets($this->_socket)) { + if (false === $greeting = fgets($this->socket)) { throw new Swift_Plugins_Pop_Pop3Exception( sprintf('Failed to connect to POP3 host [%s]', trim($greeting)) ); } - $this->_assertOk($greeting); + $this->assertOk($greeting); - if ($this->_username) { - $this->_command(sprintf("USER %s\r\n", $this->_username)); - $this->_command(sprintf("PASS %s\r\n", $this->_password)); + if ($this->username) { + $this->command(sprintf("USER %s\r\n", $this->username)); + $this->command(sprintf("PASS %s\r\n", $this->password)); } } } @@ -176,16 +162,16 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL */ public function disconnect() { - if (isset($this->_connection)) { - $this->_connection->disconnect(); + if (isset($this->connection)) { + $this->connection->disconnect(); } else { - $this->_command("QUIT\r\n"); - if (!fclose($this->_socket)) { + $this->command("QUIT\r\n"); + if (!fclose($this->socket)) { throw new Swift_Plugins_Pop_Pop3Exception( - sprintf('POP3 host [%s] connection could not be stopped', $this->_host) + sprintf('POP3 host [%s] connection could not be stopped', $this->host) ); } - $this->_socket = null; + $this->socket = null; } } @@ -196,8 +182,8 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL */ public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) { - if (isset($this->_transport)) { - if ($this->_transport !== $evt->getTransport()) { + if (isset($this->transport)) { + if ($this->transport !== $evt->getTransport()) { return; } } @@ -227,26 +213,26 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL { } - private function _command($command) + private function command($command) { - if (!fwrite($this->_socket, $command)) { + if (!fwrite($this->socket, $command)) { throw new Swift_Plugins_Pop_Pop3Exception( sprintf('Failed to write command [%s] to POP3 host', trim($command)) ); } - if (false === $response = fgets($this->_socket)) { + if (false === $response = fgets($this->socket)) { throw new Swift_Plugins_Pop_Pop3Exception( sprintf('Failed to read from POP3 host after command [%s]', trim($command)) ); } - $this->_assertOk($response); + $this->assertOk($response); return $response; } - private function _assertOk($response) + private function assertOk($response) { if (substr($response, 0, 3) != '+OK') { throw new Swift_Plugins_Pop_Pop3Exception( @@ -255,10 +241,10 @@ class Swift_Plugins_PopBeforeSmtpPlugin implements Swift_Events_TransportChangeL } } - private function _getHostString() + private function getHostString() { - $host = $this->_host; - switch (strtolower($this->_crypto)) { + $host = $this->host; + switch (strtolower($this->crypto)) { case 'ssl': $host = 'ssl://'.$host; break; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php index c3a1f868544..8d797dd117b 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php @@ -11,7 +11,7 @@ /** * Redirects all email to a single recipient. * - * @author Fabien Potencier + * @author Fabien Potencier */ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener { @@ -20,14 +20,14 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener * * @var mixed */ - private $_recipient; + private $recipient; /** * List of regular expression for recipient whitelisting. * * @var array */ - private $_whitelist = array(); + private $whitelist = array(); /** * Create a new RedirectingPlugin. @@ -37,8 +37,8 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener */ public function __construct($recipient, array $whitelist = array()) { - $this->_recipient = $recipient; - $this->_whitelist = $whitelist; + $this->recipient = $recipient; + $this->whitelist = $whitelist; } /** @@ -48,7 +48,7 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener */ public function setRecipient($recipient) { - $this->_recipient = $recipient; + $this->recipient = $recipient; } /** @@ -58,7 +58,7 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener */ public function getRecipient() { - return $this->_recipient; + return $this->recipient; } /** @@ -68,7 +68,7 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener */ public function setWhitelist(array $whitelist) { - $this->_whitelist = $whitelist; + $this->whitelist = $whitelist; } /** @@ -78,7 +78,7 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener */ public function getWhitelist() { - return $this->_whitelist; + return $this->whitelist; } /** @@ -106,9 +106,9 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener } // Filter remaining headers against whitelist - $this->_filterHeaderSet($headers, 'To'); - $this->_filterHeaderSet($headers, 'Cc'); - $this->_filterHeaderSet($headers, 'Bcc'); + $this->filterHeaderSet($headers, 'To'); + $this->filterHeaderSet($headers, 'Cc'); + $this->filterHeaderSet($headers, 'Bcc'); // Add each hard coded recipient $to = $message->getTo(); @@ -116,7 +116,7 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener $to = array(); } - foreach ((array) $this->_recipient as $recipient) { + foreach ((array) $this->recipient as $recipient) { if (!array_key_exists($recipient, $to)) { $message->addTo($recipient); } @@ -126,13 +126,13 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener /** * Filter header set against a whitelist of regular expressions. * - * @param Swift_Mime_HeaderSet $headerSet - * @param string $type + * @param Swift_Mime_SimpleHeaderSet $headerSet + * @param string $type */ - private function _filterHeaderSet(Swift_Mime_HeaderSet $headerSet, $type) + private function filterHeaderSet(Swift_Mime_SimpleHeaderSet $headerSet, $type) { foreach ($headerSet->getAll($type) as $headers) { - $headers->setNameAddresses($this->_filterNameAddresses($headers->getNameAddresses())); + $headers->setNameAddresses($this->filterNameAddresses($headers->getNameAddresses())); } } @@ -143,12 +143,12 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener * * @return array */ - private function _filterNameAddresses(array $recipients) + private function filterNameAddresses(array $recipients) { $filtered = array(); foreach ($recipients as $address => $name) { - if ($this->_isWhitelisted($address)) { + if ($this->isWhitelisted($address)) { $filtered[$address] = $name; } } @@ -163,13 +163,13 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener * * @return bool */ - protected function _isWhitelisted($recipient) + protected function isWhitelisted($recipient) { - if (in_array($recipient, (array) $this->_recipient)) { + if (in_array($recipient, (array) $this->recipient)) { return true; } - foreach ($this->_whitelist as $pattern) { + foreach ($this->whitelist as $pattern) { if (preg_match($pattern, $recipient)) { return true; } @@ -185,10 +185,10 @@ class Swift_Plugins_RedirectingPlugin implements Swift_Events_SendListener */ public function sendPerformed(Swift_Events_SendEvent $evt) { - $this->_restoreMessage($evt->getMessage()); + $this->restoreMessage($evt->getMessage()); } - private function _restoreMessage(Swift_Mime_Message $message) + private function restoreMessage(Swift_Mime_SimpleMessage $message) { // restore original headers $headers = $message->getHeaders(); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporter.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporter.php index 0f21b7d6052..3a2b6657c09 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporter.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporter.php @@ -24,9 +24,9 @@ interface Swift_Plugins_Reporter /** * Notifies this ReportNotifier that $address failed or succeeded. * - * @param Swift_Mime_Message $message - * @param string $address - * @param int $result from {@link RESULT_PASS, RESULT_FAIL} + * @param Swift_Mime_SimpleMessage $message + * @param string $address + * @param int $result from {@link RESULT_PASS, RESULT_FAIL} */ - public function notify(Swift_Mime_Message $message, $address, $result); + public function notify(Swift_Mime_SimpleMessage $message, $address, $result); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php index a37901ff0d4..5ffca24c61c 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php @@ -20,7 +20,7 @@ class Swift_Plugins_ReporterPlugin implements Swift_Events_SendListener * * @var Swift_Plugins_Reporter */ - private $_reporter; + private $reporter; /** * Create a new ReporterPlugin using $reporter. @@ -29,7 +29,7 @@ class Swift_Plugins_ReporterPlugin implements Swift_Events_SendListener */ public function __construct(Swift_Plugins_Reporter $reporter) { - $this->_reporter = $reporter; + $this->reporter = $reporter; } /** @@ -49,13 +49,13 @@ class Swift_Plugins_ReporterPlugin implements Swift_Events_SendListener $message = $evt->getMessage(); $failures = array_flip($evt->getFailedRecipients()); foreach ((array) $message->getTo() as $address => $null) { - $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + $this->reporter->notify($message, $address, (array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS)); } foreach ((array) $message->getCc() as $address => $null) { - $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + $this->reporter->notify($message, $address, (array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS)); } foreach ((array) $message->getBcc() as $address => $null) { - $this->_reporter->notify($message, $address, array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS); + $this->reporter->notify($message, $address, (array_key_exists($address, $failures) ? Swift_Plugins_Reporter::RESULT_FAIL : Swift_Plugins_Reporter::RESULT_PASS)); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php index cad9d168ef2..67c117ac7c2 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php @@ -20,22 +20,22 @@ class Swift_Plugins_Reporters_HitReporter implements Swift_Plugins_Reporter * * @var array */ - private $_failures = array(); + private $failures = array(); - private $_failures_cache = array(); + private $failures_cache = array(); /** * Notifies this ReportNotifier that $address failed or succeeded. * - * @param Swift_Mime_Message $message - * @param string $address - * @param int $result from {@link RESULT_PASS, RESULT_FAIL} + * @param Swift_Mime_SimpleMessage $message + * @param string $address + * @param int $result from {@link RESULT_PASS, RESULT_FAIL} */ - public function notify(Swift_Mime_Message $message, $address, $result) + public function notify(Swift_Mime_SimpleMessage $message, $address, $result) { - if (self::RESULT_FAIL == $result && !isset($this->_failures_cache[$address])) { - $this->_failures[] = $address; - $this->_failures_cache[$address] = true; + if (self::RESULT_FAIL == $result && !isset($this->failures_cache[$address])) { + $this->failures[] = $address; + $this->failures_cache[$address] = true; } } @@ -46,7 +46,7 @@ class Swift_Plugins_Reporters_HitReporter implements Swift_Plugins_Reporter */ public function getFailedRecipients() { - return $this->_failures; + return $this->failures; } /** @@ -54,6 +54,6 @@ class Swift_Plugins_Reporters_HitReporter implements Swift_Plugins_Reporter */ public function clear() { - $this->_failures = $this->_failures_cache = array(); + $this->failures = $this->failures_cache = array(); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php index c62593557e3..525eef570a9 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php @@ -18,11 +18,11 @@ class Swift_Plugins_Reporters_HtmlReporter implements Swift_Plugins_Reporter /** * Notifies this ReportNotifier that $address failed or succeeded. * - * @param Swift_Mime_Message $message - * @param string $address - * @param int $result from {@see RESULT_PASS, RESULT_FAIL} + * @param Swift_Mime_SimpleMessage $message + * @param string $address + * @param int $result from {@see RESULT_PASS, RESULT_FAIL} */ - public function notify(Swift_Mime_Message $message, $address, $result) + public function notify(Swift_Mime_SimpleMessage $message, $address, $result) { if (self::RESULT_PASS == $result) { echo '
    '.PHP_EOL; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php index 24bc929ee6b..77c2e8db6ec 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php @@ -29,28 +29,28 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin * * @var Swift_Plugins_Sleeper */ - private $_sleeper; + private $sleeper; /** * The Timer instance which provides the timestamp. * * @var Swift_Plugins_Timer */ - private $_timer; + private $timer; /** * The time at which the first email was sent. * * @var int */ - private $_start; + private $start; /** * The rate at which messages should be sent. * * @var int */ - private $_rate; + private $rate; /** * The mode for throttling. @@ -59,14 +59,14 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin * * @var int */ - private $_mode; + private $mode; /** * An internal counter of the number of messages sent. * * @var int */ - private $_messages = 0; + private $messages = 0; /** * Create a new ThrottlerPlugin. @@ -78,10 +78,10 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin */ public function __construct($rate, $mode = self::BYTES_PER_MINUTE, Swift_Plugins_Sleeper $sleeper = null, Swift_Plugins_Timer $timer = null) { - $this->_rate = $rate; - $this->_mode = $mode; - $this->_sleeper = $sleeper; - $this->_timer = $timer; + $this->rate = $rate; + $this->mode = $mode; + $this->sleeper = $sleeper; + $this->timer = $timer; } /** @@ -92,22 +92,22 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin public function beforeSendPerformed(Swift_Events_SendEvent $evt) { $time = $this->getTimestamp(); - if (!isset($this->_start)) { - $this->_start = $time; + if (!isset($this->start)) { + $this->start = $time; } - $duration = $time - $this->_start; + $duration = $time - $this->start; - switch ($this->_mode) { - case self::BYTES_PER_MINUTE : - $sleep = $this->_throttleBytesPerMinute($duration); + switch ($this->mode) { + case self::BYTES_PER_MINUTE: + $sleep = $this->throttleBytesPerMinute($duration); break; - case self::MESSAGES_PER_SECOND : - $sleep = $this->_throttleMessagesPerSecond($duration); + case self::MESSAGES_PER_SECOND: + $sleep = $this->throttleMessagesPerSecond($duration); break; - case self::MESSAGES_PER_MINUTE : - $sleep = $this->_throttleMessagesPerMinute($duration); + case self::MESSAGES_PER_MINUTE: + $sleep = $this->throttleMessagesPerMinute($duration); break; - default : + default: $sleep = 0; break; } @@ -125,7 +125,7 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin public function sendPerformed(Swift_Events_SendEvent $evt) { parent::sendPerformed($evt); - ++$this->_messages; + ++$this->messages; } /** @@ -135,8 +135,8 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin */ public function sleep($seconds) { - if (isset($this->_sleeper)) { - $this->_sleeper->sleep($seconds); + if (isset($this->sleeper)) { + $this->sleeper->sleep($seconds); } else { sleep($seconds); } @@ -149,8 +149,8 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin */ public function getTimestamp() { - if (isset($this->_timer)) { - return $this->_timer->getTimestamp(); + if (isset($this->timer)) { + return $this->timer->getTimestamp(); } return time(); @@ -163,9 +163,9 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin * * @return int */ - private function _throttleBytesPerMinute($timePassed) + private function throttleBytesPerMinute($timePassed) { - $expectedDuration = $this->getBytesOut() / ($this->_rate / 60); + $expectedDuration = $this->getBytesOut() / ($this->rate / 60); return (int) ceil($expectedDuration - $timePassed); } @@ -177,9 +177,9 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin * * @return int */ - private function _throttleMessagesPerSecond($timePassed) + private function throttleMessagesPerSecond($timePassed) { - $expectedDuration = $this->_messages / ($this->_rate); + $expectedDuration = $this->messages / $this->rate; return (int) ceil($expectedDuration - $timePassed); } @@ -191,9 +191,9 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin * * @return int */ - private function _throttleMessagesPerMinute($timePassed) + private function throttleMessagesPerMinute($timePassed) { - $expectedDuration = $this->_messages / ($this->_rate / 60); + $expectedDuration = $this->messages / ($this->rate / 60); return (int) ceil($expectedDuration - $timePassed); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Preferences.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Preferences.php index 503db84d883..79eafde9ad9 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Preferences.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Preferences.php @@ -16,7 +16,7 @@ class Swift_Preferences { /** Singleton instance */ - private static $_instance = null; + private static $instance = null; /** Constructor not to be used */ private function __construct() @@ -26,15 +26,15 @@ class Swift_Preferences /** * Gets the instance of Preferences. * - * @return Swift_Preferences + * @return self */ public static function getInstance() { - if (!isset(self::$_instance)) { - self::$_instance = new self(); + if (!isset(self::$instance)) { + self::$instance = new self(); } - return self::$_instance; + return self::$instance; } /** @@ -42,12 +42,11 @@ class Swift_Preferences * * @param string $charset * - * @return Swift_Preferences + * @return $this */ public function setCharset($charset) { - Swift_DependencyContainer::getInstance() - ->register('properties.charset')->asValue($charset); + Swift_DependencyContainer::getInstance()->register('properties.charset')->asValue($charset); return $this; } @@ -57,12 +56,11 @@ class Swift_Preferences * * @param string $dir * - * @return Swift_Preferences + * @return $this */ public function setTempDir($dir) { - Swift_DependencyContainer::getInstance() - ->register('tempdir')->asValue($dir); + Swift_DependencyContainer::getInstance()->register('tempdir')->asValue($dir); return $this; } @@ -72,12 +70,11 @@ class Swift_Preferences * * @param string $type * - * @return Swift_Preferences + * @return $this */ public function setCacheType($type) { - Swift_DependencyContainer::getInstance() - ->register('cache')->asAliasOf(sprintf('cache.%s', $type)); + Swift_DependencyContainer::getInstance()->register('cache')->asAliasOf(sprintf('cache.%s', $type)); return $this; } @@ -87,7 +84,7 @@ class Swift_Preferences * * @param bool $dotEscape * - * @return Swift_Preferences + * @return $this */ public function setQPDotEscape($dotEscape) { diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/SendmailTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/SendmailTransport.php index 974b24f2701..c129281b347 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/SendmailTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/SendmailTransport.php @@ -30,16 +30,4 @@ class Swift_SendmailTransport extends Swift_Transport_SendmailTransport $this->setCommand($command); } - - /** - * Create a new SendmailTransport instance. - * - * @param string $command - * - * @return Swift_SendmailTransport - */ - public static function newInstance($command = '/usr/sbin/sendmail -bs') - { - return new self($command); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/SignedMessage.php b/htdocs/includes/swiftmailer/lib/classes/Swift/SignedMessage.php deleted file mode 100644 index 2e7a8726d21..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/SignedMessage.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * @deprecated - */ -class Swift_SignedMessage extends Swift_Message -{ -} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/BodySigner.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/BodySigner.php index 9ffcef39c9f..8e66e18f4e0 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/BodySigner.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/BodySigner.php @@ -20,7 +20,7 @@ interface Swift_Signers_BodySigner extends Swift_Signer * * @param Swift_Message $message * - * @return Swift_Signers_BodySigner + * @return self */ public function signMessage(Swift_Message $message); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php index 6040b2856f2..a88daa19815 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php @@ -11,7 +11,7 @@ /** * DKIM Signer used to apply DKIM Signature to a message. * - * @author Xavier De Cock + * @author Xavier De Cock */ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner { @@ -20,99 +20,103 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @var string */ - protected $_privateKey; + protected $privateKey; /** * DomainName. * * @var string */ - protected $_domainName; + protected $domainName; /** * Selector. * * @var string */ - protected $_selector; + protected $selector; + + private $passphrase = ''; /** * Hash algorithm used. * + * @see RFC6376 3.3: Signers MUST implement and SHOULD sign using rsa-sha256. + * * @var string */ - protected $_hashAlgorithm = 'rsa-sha1'; + protected $hashAlgorithm = 'rsa-sha256'; /** * Body canon method. * * @var string */ - protected $_bodyCanon = 'simple'; + protected $bodyCanon = 'simple'; /** * Header canon method. * * @var string */ - protected $_headerCanon = 'simple'; + protected $headerCanon = 'simple'; /** * Headers not being signed. * * @var array */ - protected $_ignoredHeaders = array('return-path' => true); + protected $ignoredHeaders = array('return-path' => true); /** * Signer identity. * * @var string */ - protected $_signerIdentity; + protected $signerIdentity; /** * BodyLength. * * @var int */ - protected $_bodyLen = 0; + protected $bodyLen = 0; /** * Maximum signedLen. * * @var int */ - protected $_maxLen = PHP_INT_MAX; + protected $maxLen = PHP_INT_MAX; /** * Embbed bodyLen in signature. * * @var bool */ - protected $_showLen = false; + protected $showLen = false; /** * When the signature has been applied (true means time()), false means not embedded. * * @var mixed */ - protected $_signatureTimestamp = true; + protected $signatureTimestamp = true; /** * When will the signature expires false means not embedded, if sigTimestamp is auto - * Expiration is relative, otherwhise it's absolute. + * Expiration is relative, otherwise it's absolute. * * @var int */ - protected $_signatureExpiration = false; + protected $signatureExpiration = false; /** * Must we embed signed headers? * * @var bool */ - protected $_debugHeaders = false; + protected $debugHeaders = false; // work variables /** @@ -120,46 +124,46 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @var array */ - protected $_signedHeaders = array(); + protected $signedHeaders = array(); /** - * If debugHeaders is set store debugDatas here. + * If debugHeaders is set store debugData here. * * @var string */ - private $_debugHeadersData = ''; + private $debugHeadersData = ''; /** * Stores the bodyHash. * * @var string */ - private $_bodyHash = ''; + private $bodyHash = ''; /** * Stores the signature header. * * @var Swift_Mime_Headers_ParameterizedHeader */ - protected $_dkimHeader; + protected $dkimHeader; - private $_bodyHashHandler; + private $bodyHashHandler; - private $_headerHash; + private $headerHash; - private $_headerCanonData = ''; + private $headerCanonData = ''; - private $_bodyCanonEmptyCounter = 0; + private $bodyCanonEmptyCounter = 0; - private $_bodyCanonIgnoreStart = 2; + private $bodyCanonIgnoreStart = 2; - private $_bodyCanonSpace = false; + private $bodyCanonSpace = false; - private $_bodyCanonLastChar = null; + private $bodyCanonLastChar = null; - private $_bodyCanonLine = ''; + private $bodyCanonLine = ''; - private $_bound = array(); + private $bound = array(); /** * Constructor. @@ -167,27 +171,15 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * @param string $privateKey * @param string $domainName * @param string $selector + * @param string $passphrase */ - public function __construct($privateKey, $domainName, $selector) + public function __construct($privateKey, $domainName, $selector, $passphrase = '') { - $this->_privateKey = $privateKey; - $this->_domainName = $domainName; - $this->_signerIdentity = '@'.$domainName; - $this->_selector = $selector; - } - - /** - * Instanciate DKIMSigner. - * - * @param string $privateKey - * @param string $domainName - * @param string $selector - * - * @return Swift_Signers_DKIMSigner - */ - public static function newInstance($privateKey, $domainName, $selector) - { - return new static($privateKey, $domainName, $selector); + $this->privateKey = $privateKey; + $this->domainName = $domainName; + $this->signerIdentity = '@'.$domainName; + $this->selector = $selector; + $this->passphrase = $passphrase; } /** @@ -197,14 +189,14 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner */ public function reset() { - $this->_headerHash = null; - $this->_signedHeaders = array(); - $this->_bodyHash = null; - $this->_bodyHashHandler = null; - $this->_bodyCanonIgnoreStart = 2; - $this->_bodyCanonEmptyCounter = 0; - $this->_bodyCanonLastChar = null; - $this->_bodyCanonSpace = false; + $this->headerHash = null; + $this->signedHeaders = array(); + $this->bodyHash = null; + $this->bodyHashHandler = null; + $this->bodyCanonIgnoreStart = 2; + $this->bodyCanonEmptyCounter = 0; + $this->bodyCanonLastChar = null; + $this->bodyCanonSpace = false; } /** @@ -219,14 +211,15 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param string $bytes * - * @throws Swift_IoException - * * @return int + * + * @throws Swift_IoException */ + // TODO fix return public function write($bytes) { - $this->_canonicalizeBody($bytes); - foreach ($this->_bound as $is) { + $this->canonicalizeBody($bytes); + foreach ($this->bound as $is) { $is->write($bytes); } } @@ -234,8 +227,6 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner /** * For any bytes that are currently buffered inside the stream, force them * off the buffer. - * - * @throws Swift_IoException */ public function commit() { @@ -253,7 +244,7 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner public function bind(Swift_InputByteStream $is) { // Don't have to mirror anything - $this->_bound[] = $is; + $this->bound[] = $is; return; } @@ -269,15 +260,13 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner public function unbind(Swift_InputByteStream $is) { // Don't have to mirror anything - foreach ($this->_bound as $k => $stream) { + foreach ($this->bound as $k => $stream) { if ($stream === $is) { - unset($this->_bound[$k]); + unset($this->bound[$k]); return; } } - - return; } /** @@ -292,19 +281,28 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner } /** - * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1. * - * @param string $hash + * @param string $hash 'rsa-sha1' or 'rsa-sha256' * - * @return Swift_Signers_DKIMSigner + * @throws Swift_SwiftException + * + * @return $this */ public function setHashAlgorithm($hash) { - // Unable to sign with rsa-sha256 - if ($hash == 'rsa-sha1') { - $this->_hashAlgorithm = 'rsa-sha1'; - } else { - $this->_hashAlgorithm = 'rsa-sha256'; + switch ($hash) { + case 'rsa-sha1': + $this->hashAlgorithm = 'rsa-sha1'; + break; + case 'rsa-sha256': + $this->hashAlgorithm = 'rsa-sha256'; + if (!defined('OPENSSL_ALGO_SHA256')) { + throw new Swift_SwiftException('Unable to set sha256 as it is not supported by OpenSSL.'); + } + break; + default: + throw new Swift_SwiftException('Unable to set the hash algorithm, must be one of rsa-sha1 or rsa-sha256 (%s given).', $hash); } return $this; @@ -315,14 +313,14 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param string $canon * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setBodyCanon($canon) { if ($canon == 'relaxed') { - $this->_bodyCanon = 'relaxed'; + $this->bodyCanon = 'relaxed'; } else { - $this->_bodyCanon = 'simple'; + $this->bodyCanon = 'simple'; } return $this; @@ -333,14 +331,14 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param string $canon * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setHeaderCanon($canon) { if ($canon == 'relaxed') { - $this->_headerCanon = 'relaxed'; + $this->headerCanon = 'relaxed'; } else { - $this->_headerCanon = 'simple'; + $this->headerCanon = 'simple'; } return $this; @@ -351,11 +349,11 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param string $identity * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setSignerIdentity($identity) { - $this->_signerIdentity = $identity; + $this->signerIdentity = $identity; return $this; } @@ -365,19 +363,19 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param mixed $len (bool or int) * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setBodySignedLen($len) { if ($len === true) { - $this->_showLen = true; - $this->_maxLen = PHP_INT_MAX; + $this->showLen = true; + $this->maxLen = PHP_INT_MAX; } elseif ($len === false) { $this->showLen = false; - $this->_maxLen = PHP_INT_MAX; + $this->maxLen = PHP_INT_MAX; } else { - $this->_showLen = true; - $this->_maxLen = (int) $len; + $this->showLen = true; + $this->maxLen = (int) $len; } return $this; @@ -388,11 +386,11 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param int $time A timestamp * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setSignatureTimestamp($time) { - $this->_signatureTimestamp = $time; + $this->signatureTimestamp = $time; return $this; } @@ -402,11 +400,11 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @param int $time A timestamp * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setSignatureExpiration($time) { - $this->_signatureExpiration = $time; + $this->signatureExpiration = $time; return $this; } @@ -420,7 +418,7 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner */ public function setDebugHeaders($debug) { - $this->_debugHeaders = (bool) $debug; + $this->debugHeaders = (bool) $debug; return $this; } @@ -431,15 +429,15 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner public function startBody() { // Init - switch ($this->_hashAlgorithm) { - case 'rsa-sha256' : - $this->_bodyHashHandler = hash_init('sha256'); + switch ($this->hashAlgorithm) { + case 'rsa-sha256': + $this->bodyHashHandler = hash_init('sha256'); break; - case 'rsa-sha1' : - $this->_bodyHashHandler = hash_init('sha1'); + case 'rsa-sha1': + $this->bodyHashHandler = hash_init('sha1'); break; } - $this->_bodyCanonLine = ''; + $this->bodyCanonLine = ''; } /** @@ -447,7 +445,7 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner */ public function endBody() { - $this->_endOfBody(); + $this->endOfBody(); } /** @@ -457,7 +455,7 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner */ public function getAlteredHeaders() { - if ($this->_debugHeaders) { + if ($this->debugHeaders) { return array('DKIM-Signature', 'X-DebugHash'); } else { return array('DKIM-Signature'); @@ -473,7 +471,7 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner */ public function ignoreHeader($header_name) { - $this->_ignoredHeaders[strtolower($header_name)] = true; + $this->ignoredHeaders[strtolower($header_name)] = true; return $this; } @@ -481,24 +479,24 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner /** * Set the headers to sign. * - * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_SimpleHeaderSet $headers * * @return Swift_Signers_DKIMSigner */ - public function setHeaders(Swift_Mime_HeaderSet $headers) + public function setHeaders(Swift_Mime_SimpleHeaderSet $headers) { - $this->_headerCanonData = ''; + $this->headerCanonData = ''; // Loop through Headers $listHeaders = $headers->listAll(); foreach ($listHeaders as $hName) { // Check if we need to ignore Header - if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if (!isset($this->ignoredHeaders[strtolower($hName)])) { if ($headers->has($hName)) { $tmp = $headers->getAll($hName); foreach ($tmp as $header) { if ($header->getFieldBody() != '') { - $this->_addHeader($header->toString()); - $this->_signedHeaders[] = $header->getFieldName(); + $this->addHeader($header->toString()); + $this->signedHeaders[] = $header->getFieldName(); } } } @@ -511,37 +509,37 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner /** * Add the signature to the given Headers. * - * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_SimpleHeaderSet $headers * * @return Swift_Signers_DKIMSigner */ - public function addSignature(Swift_Mime_HeaderSet $headers) + public function addSignature(Swift_Mime_SimpleHeaderSet $headers) { // Prepare the DKIM-Signature - $params = array('v' => '1', 'a' => $this->_hashAlgorithm, 'bh' => base64_encode($this->_bodyHash), 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'i' => $this->_signerIdentity, 's' => $this->_selector); - if ($this->_bodyCanon != 'simple') { - $params['c'] = $this->_headerCanon.'/'.$this->_bodyCanon; - } elseif ($this->_headerCanon != 'simple') { - $params['c'] = $this->_headerCanon; + $params = array('v' => '1', 'a' => $this->hashAlgorithm, 'bh' => base64_encode($this->bodyHash), 'd' => $this->domainName, 'h' => implode(': ', $this->signedHeaders), 'i' => $this->signerIdentity, 's' => $this->selector); + if ($this->bodyCanon != 'simple') { + $params['c'] = $this->headerCanon.'/'.$this->bodyCanon; + } elseif ($this->headerCanon != 'simple') { + $params['c'] = $this->headerCanon; } - if ($this->_showLen) { - $params['l'] = $this->_bodyLen; + if ($this->showLen) { + $params['l'] = $this->bodyLen; } - if ($this->_signatureTimestamp === true) { + if ($this->signatureTimestamp === true) { $params['t'] = time(); - if ($this->_signatureExpiration !== false) { - $params['x'] = $params['t'] + $this->_signatureExpiration; + if ($this->signatureExpiration !== false) { + $params['x'] = $params['t'] + $this->signatureExpiration; } } else { - if ($this->_signatureTimestamp !== false) { - $params['t'] = $this->_signatureTimestamp; + if ($this->signatureTimestamp !== false) { + $params['t'] = $this->signatureTimestamp; } - if ($this->_signatureExpiration !== false) { - $params['x'] = $this->_signatureExpiration; + if ($this->signatureExpiration !== false) { + $params['x'] = $this->signatureExpiration; } } - if ($this->_debugHeaders) { - $params['z'] = implode('|', $this->_debugHeadersData); + if ($this->debugHeaders) { + $params['z'] = implode('|', $this->debugHeadersData); } $string = ''; foreach ($params as $k => $v) { @@ -551,67 +549,57 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner $headers->addTextHeader('DKIM-Signature', $string); // Add the last DKIM-Signature $tmp = $headers->getAll('DKIM-Signature'); - $this->_dkimHeader = end($tmp); - $this->_addHeader(trim($this->_dkimHeader->toString())."\r\n b=", true); - $this->_endOfHeaders(); - if ($this->_debugHeaders) { - $headers->addTextHeader('X-DebugHash', base64_encode($this->_headerHash)); + $this->dkimHeader = end($tmp); + $this->addHeader(trim($this->dkimHeader->toString())."\r\n b=", true); + if ($this->debugHeaders) { + $headers->addTextHeader('X-DebugHash', base64_encode($this->headerHash)); } - $this->_dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '))); + $this->dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->getEncryptedHash()), 73, ' '))); return $this; } /* Private helpers */ - protected function _addHeader($header, $is_sig = false) + protected function addHeader($header, $is_sig = false) { - switch ($this->_headerCanon) { - case 'relaxed' : + switch ($this->headerCanon) { + case 'relaxed': // Prepare Header and cascade $exploded = explode(':', $header, 2); $name = strtolower(trim($exploded[0])); $value = str_replace("\r\n", '', $exploded[1]); $value = preg_replace("/[ \t][ \t]+/", ' ', $value); $header = $name.':'.trim($value).($is_sig ? '' : "\r\n"); - case 'simple' : + case 'simple': // Nothing to do } - $this->_addToHeaderHash($header); + $this->addToHeaderHash($header); } - /** - * @deprecated This method is currently useless in this class but it must be - * kept for BC reasons due to its "protected" scope. This method - * might be overriden by custom client code. - */ - protected function _endOfHeaders() - { - } - - protected function _canonicalizeBody($string) + protected function canonicalizeBody($string) { $len = strlen($string); $canon = ''; - $method = ($this->_bodyCanon == 'relaxed'); + $method = ($this->bodyCanon == 'relaxed'); for ($i = 0; $i < $len; ++$i) { - if ($this->_bodyCanonIgnoreStart > 0) { - --$this->_bodyCanonIgnoreStart; + if ($this->bodyCanonIgnoreStart > 0) { + --$this->bodyCanonIgnoreStart; continue; } switch ($string[$i]) { - case "\r" : - $this->_bodyCanonLastChar = "\r"; + case "\r": + $this->bodyCanonLastChar = "\r"; break; - case "\n" : - if ($this->_bodyCanonLastChar == "\r") { + case "\n": + if ($this->bodyCanonLastChar == "\r") { if ($method) { - $this->_bodyCanonSpace = false; + $this->bodyCanonSpace = false; } - if ($this->_bodyCanonLine == '') { - ++$this->_bodyCanonEmptyCounter; + if ($this->bodyCanonLine == '') { + ++$this->bodyCanonEmptyCounter; } else { - $this->_bodyCanonLine = ''; + $this->bodyCanonLine = ''; $canon .= "\r\n"; } } else { @@ -619,55 +607,55 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner // todo handle it but should never happen } break; - case ' ' : - case "\t" : + case ' ': + case "\t": if ($method) { - $this->_bodyCanonSpace = true; + $this->bodyCanonSpace = true; break; } - default : - if ($this->_bodyCanonEmptyCounter > 0) { - $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); - $this->_bodyCanonEmptyCounter = 0; + default: + if ($this->bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->bodyCanonEmptyCounter); + $this->bodyCanonEmptyCounter = 0; } - if ($this->_bodyCanonSpace) { - $this->_bodyCanonLine .= ' '; + if ($this->bodyCanonSpace) { + $this->bodyCanonLine .= ' '; $canon .= ' '; - $this->_bodyCanonSpace = false; + $this->bodyCanonSpace = false; } - $this->_bodyCanonLine .= $string[$i]; + $this->bodyCanonLine .= $string[$i]; $canon .= $string[$i]; } } - $this->_addToBodyHash($canon); + $this->addToBodyHash($canon); } - protected function _endOfBody() + protected function endOfBody() { // Add trailing Line return if last line is non empty - if (strlen($this->_bodyCanonLine) > 0) { - $this->_addToBodyHash("\r\n"); + if (strlen($this->bodyCanonLine) > 0) { + $this->addToBodyHash("\r\n"); } - $this->_bodyHash = hash_final($this->_bodyHashHandler, true); + $this->bodyHash = hash_final($this->bodyHashHandler, true); } - private function _addToBodyHash($string) + private function addToBodyHash($string) { $len = strlen($string); - if ($len > ($new_len = ($this->_maxLen - $this->_bodyLen))) { + if ($len > ($new_len = ($this->maxLen - $this->bodyLen))) { $string = substr($string, 0, $new_len); $len = $new_len; } - hash_update($this->_bodyHashHandler, $string); - $this->_bodyLen += $len; + hash_update($this->bodyHashHandler, $string); + $this->bodyLen += $len; } - private function _addToHeaderHash($header) + private function addToHeaderHash($header) { - if ($this->_debugHeaders) { - $this->_debugHeadersData[] = trim($header); + if ($this->debugHeaders) { + $this->debugHeadersData[] = trim($header); } - $this->_headerCanonData .= $header; + $this->headerCanonData .= $header; } /** @@ -675,10 +663,10 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner * * @return string */ - private function _getEncryptedHash() + private function getEncryptedHash() { $signature = ''; - switch ($this->_hashAlgorithm) { + switch ($this->hashAlgorithm) { case 'rsa-sha1': $algorithm = OPENSSL_ALGO_SHA1; break; @@ -686,11 +674,11 @@ class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner $algorithm = OPENSSL_ALGO_SHA256; break; } - $pkeyId = openssl_get_privatekey($this->_privateKey); + $pkeyId = openssl_get_privatekey($this->privateKey, $this->passphrase); if (!$pkeyId) { throw new Swift_SwiftException('Unable to load DKIM Private Key ['.openssl_error_string().']'); } - if (openssl_sign($this->_headerCanonData, $signature, $pkeyId, $algorithm)) { + if (openssl_sign($this->headerCanonData, $signature, $pkeyId, $algorithm)) { return $signature; } throw new Swift_SwiftException('Unable to sign DKIM Hash ['.openssl_error_string().']'); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php index 3f42d3f6e90..40654987f9b 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php @@ -11,7 +11,7 @@ /** * DomainKey Signer used to apply DomainKeys Signature to a message. * - * @author Xavier De Cock + * @author Xavier De Cock */ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner { @@ -20,56 +20,56 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @var string */ - protected $_privateKey; + protected $privateKey; /** * DomainName. * * @var string */ - protected $_domainName; + protected $domainName; /** * Selector. * * @var string */ - protected $_selector; + protected $selector; /** * Hash algorithm used. * * @var string */ - protected $_hashAlgorithm = 'rsa-sha1'; + protected $hashAlgorithm = 'rsa-sha1'; /** * Canonisation method. * * @var string */ - protected $_canon = 'simple'; + protected $canon = 'simple'; /** * Headers not being signed. * * @var array */ - protected $_ignoredHeaders = array(); + protected $ignoredHeaders = array(); /** * Signer identity. * * @var string */ - protected $_signerIdentity; + protected $signerIdentity; /** * Must we embed signed headers? * * @var bool */ - protected $_debugHeaders = false; + protected $debugHeaders = false; // work variables /** @@ -77,37 +77,37 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @var array */ - private $_signedHeaders = array(); + private $signedHeaders = array(); /** * Stores the signature header. * * @var Swift_Mime_Headers_ParameterizedHeader */ - protected $_domainKeyHeader; + protected $domainKeyHeader; /** * Hash Handler. * * @var resource|null */ - private $_hashHandler; + private $hashHandler; - private $_hash; + private $hash; - private $_canonData = ''; + private $canonData = ''; - private $_bodyCanonEmptyCounter = 0; + private $bodyCanonEmptyCounter = 0; - private $_bodyCanonIgnoreStart = 2; + private $bodyCanonIgnoreStart = 2; - private $_bodyCanonSpace = false; + private $bodyCanonSpace = false; - private $_bodyCanonLastChar = null; + private $bodyCanonLastChar = null; - private $_bodyCanonLine = ''; + private $bodyCanonLine = ''; - private $_bound = array(); + private $bound = array(); /** * Constructor. @@ -118,39 +118,25 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner */ public function __construct($privateKey, $domainName, $selector) { - $this->_privateKey = $privateKey; - $this->_domainName = $domainName; - $this->_signerIdentity = '@'.$domainName; - $this->_selector = $selector; - } - - /** - * Instanciate DomainKeySigner. - * - * @param string $privateKey - * @param string $domainName - * @param string $selector - * - * @return Swift_Signers_DomainKeySigner - */ - public static function newInstance($privateKey, $domainName, $selector) - { - return new static($privateKey, $domainName, $selector); + $this->privateKey = $privateKey; + $this->domainName = $domainName; + $this->signerIdentity = '@'.$domainName; + $this->selector = $selector; } /** * Resets internal states. * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function reset() { - $this->_hash = null; - $this->_hashHandler = null; - $this->_bodyCanonIgnoreStart = 2; - $this->_bodyCanonEmptyCounter = 0; - $this->_bodyCanonLastChar = null; - $this->_bodyCanonSpace = false; + $this->hash = null; + $this->hashHandler = null; + $this->bodyCanonIgnoreStart = 2; + $this->bodyCanonEmptyCounter = 0; + $this->bodyCanonLastChar = null; + $this->bodyCanonSpace = false; return $this; } @@ -167,15 +153,16 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param string $bytes * + * @return int + * * @throws Swift_IoException * - * @return int - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function write($bytes) { - $this->_canonicalizeBody($bytes); - foreach ($this->_bound as $is) { + $this->canonicalizeBody($bytes); + foreach ($this->bound as $is) { $is->write($bytes); } @@ -188,7 +175,7 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @throws Swift_IoException * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function commit() { @@ -203,12 +190,12 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param Swift_InputByteStream $is * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function bind(Swift_InputByteStream $is) { // Don't have to mirror anything - $this->_bound[] = $is; + $this->bound[] = $is; return $this; } @@ -221,16 +208,16 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param Swift_InputByteStream $is * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function unbind(Swift_InputByteStream $is) { // Don't have to mirror anything - foreach ($this->_bound as $k => $stream) { + foreach ($this->bound as $k => $stream) { if ($stream === $is) { - unset($this->_bound[$k]); + unset($this->bound[$k]); - return; + break; } } @@ -243,7 +230,7 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @throws Swift_IoException * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function flushBuffers() { @@ -257,11 +244,11 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param string $hash * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function setHashAlgorithm($hash) { - $this->_hashAlgorithm = 'rsa-sha1'; + $this->hashAlgorithm = 'rsa-sha1'; return $this; } @@ -271,14 +258,14 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param string $canon simple | nofws defaults to simple * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function setCanon($canon) { if ($canon == 'nofws') { - $this->_canon = 'nofws'; + $this->canon = 'nofws'; } else { - $this->_canon = 'simple'; + $this->canon = 'simple'; } return $this; @@ -289,11 +276,11 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param string $identity * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function setSignerIdentity($identity) { - $this->_signerIdentity = $identity; + $this->signerIdentity = $identity; return $this; } @@ -303,11 +290,11 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param bool $debug * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function setDebugHeaders($debug) { - $this->_debugHeaders = (bool) $debug; + $this->debugHeaders = (bool) $debug; return $this; } @@ -324,7 +311,7 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner */ public function endBody() { - $this->_endOfBody(); + $this->endOfBody(); } /** @@ -334,7 +321,7 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner */ public function getAlteredHeaders() { - if ($this->_debugHeaders) { + if ($this->debugHeaders) { return array('DomainKey-Signature', 'X-DebugHash'); } @@ -346,11 +333,11 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @param string $header_name * - * @return Swift_Signers_DomainKeySigner + * @return $this */ public function ignoreHeader($header_name) { - $this->_ignoredHeaders[strtolower($header_name)] = true; + $this->ignoredHeaders[strtolower($header_name)] = true; return $this; } @@ -358,31 +345,31 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner /** * Set the headers to sign. * - * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_SimpleHeaderSet $headers * - * @return Swift_Signers_DomainKeySigner + * @return $this */ - public function setHeaders(Swift_Mime_HeaderSet $headers) + public function setHeaders(Swift_Mime_SimpleHeaderSet $headers) { - $this->_startHash(); - $this->_canonData = ''; + $this->startHash(); + $this->canonData = ''; // Loop through Headers $listHeaders = $headers->listAll(); foreach ($listHeaders as $hName) { // Check if we need to ignore Header - if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if (!isset($this->ignoredHeaders[strtolower($hName)])) { if ($headers->has($hName)) { $tmp = $headers->getAll($hName); foreach ($tmp as $header) { if ($header->getFieldBody() != '') { - $this->_addHeader($header->toString()); - $this->_signedHeaders[] = $header->getFieldName(); + $this->addHeader($header->toString()); + $this->signedHeaders[] = $header->getFieldName(); } } } } } - $this->_endOfHeaders(); + $this->endOfHeaders(); return $this; } @@ -390,14 +377,14 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner /** * Add the signature to the given Headers. * - * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_SimpleHeaderSet $headers * - * @return Swift_Signers_DomainKeySigner + * @return $this */ - public function addSignature(Swift_Mime_HeaderSet $headers) + public function addSignature(Swift_Mime_SimpleHeaderSet $headers) { // Prepare the DomainKey-Signature Header - $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector); + $params = array('a' => $this->hashAlgorithm, 'b' => chunk_split(base64_encode($this->getEncryptedHash()), 73, ' '), 'c' => $this->canon, 'd' => $this->domainName, 'h' => implode(': ', $this->signedHeaders), 'q' => 'dns', 's' => $this->selector); $string = ''; foreach ($params as $k => $v) { $string .= $k.'='.$v.'; '; @@ -410,50 +397,50 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner /* Private helpers */ - protected function _addHeader($header) + protected function addHeader($header) { - switch ($this->_canon) { - case 'nofws' : + switch ($this->canon) { + case 'nofws': // Prepare Header and cascade $exploded = explode(':', $header, 2); $name = strtolower(trim($exploded[0])); $value = str_replace("\r\n", '', $exploded[1]); $value = preg_replace("/[ \t][ \t]+/", ' ', $value); $header = $name.':'.trim($value)."\r\n"; - case 'simple' : + case 'simple': // Nothing to do } - $this->_addToHash($header); + $this->addToHash($header); } - protected function _endOfHeaders() + protected function endOfHeaders() { - $this->_bodyCanonEmptyCounter = 1; + $this->bodyCanonEmptyCounter = 1; } - protected function _canonicalizeBody($string) + protected function canonicalizeBody($string) { $len = strlen($string); $canon = ''; - $nofws = ($this->_canon == 'nofws'); + $nofws = ($this->canon == 'nofws'); for ($i = 0; $i < $len; ++$i) { - if ($this->_bodyCanonIgnoreStart > 0) { - --$this->_bodyCanonIgnoreStart; + if ($this->bodyCanonIgnoreStart > 0) { + --$this->bodyCanonIgnoreStart; continue; } switch ($string[$i]) { - case "\r" : - $this->_bodyCanonLastChar = "\r"; + case "\r": + $this->bodyCanonLastChar = "\r"; break; - case "\n" : - if ($this->_bodyCanonLastChar == "\r") { + case "\n": + if ($this->bodyCanonLastChar == "\r") { if ($nofws) { - $this->_bodyCanonSpace = false; + $this->bodyCanonSpace = false; } - if ($this->_bodyCanonLine == '') { - ++$this->_bodyCanonEmptyCounter; + if ($this->bodyCanonLine == '') { + ++$this->bodyCanonEmptyCounter; } else { - $this->_bodyCanonLine = ''; + $this->bodyCanonLine = ''; $canon .= "\r\n"; } } else { @@ -461,48 +448,48 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r'); } break; - case ' ' : - case "\t" : + case ' ': + case "\t": case "\x09": //HTAB if ($nofws) { - $this->_bodyCanonSpace = true; + $this->bodyCanonSpace = true; break; } - default : - if ($this->_bodyCanonEmptyCounter > 0) { - $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); - $this->_bodyCanonEmptyCounter = 0; + default: + if ($this->bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->bodyCanonEmptyCounter); + $this->bodyCanonEmptyCounter = 0; } - $this->_bodyCanonLine .= $string[$i]; + $this->bodyCanonLine .= $string[$i]; $canon .= $string[$i]; } } - $this->_addToHash($canon); + $this->addToHash($canon); } - protected function _endOfBody() + protected function endOfBody() { - if (strlen($this->_bodyCanonLine) > 0) { - $this->_addToHash("\r\n"); + if (strlen($this->bodyCanonLine) > 0) { + $this->addToHash("\r\n"); } - $this->_hash = hash_final($this->_hashHandler, true); + $this->hash = hash_final($this->hashHandler, true); } - private function _addToHash($string) + private function addToHash($string) { - $this->_canonData .= $string; - hash_update($this->_hashHandler, $string); + $this->canonData .= $string; + hash_update($this->hashHandler, $string); } - private function _startHash() + private function startHash() { // Init - switch ($this->_hashAlgorithm) { - case 'rsa-sha1' : - $this->_hashHandler = hash_init('sha1'); + switch ($this->hashAlgorithm) { + case 'rsa-sha1': + $this->hashHandler = hash_init('sha1'); break; } - $this->_canonLine = ''; + $this->bodyCanonLine = ''; } /** @@ -510,14 +497,14 @@ class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner * * @return string */ - private function _getEncryptedHash() + private function getEncryptedHash() { $signature = ''; - $pkeyId = openssl_get_privatekey($this->_privateKey); + $pkeyId = openssl_get_privatekey($this->privateKey); if (!$pkeyId) { throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']'); } - if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) { + if (openssl_sign($this->canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) { return $signature; } throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']'); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php index c75cb08a518..6104e34f3f5 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php @@ -20,21 +20,21 @@ interface Swift_Signers_HeaderSigner extends Swift_Signer, Swift_InputByteStream * * @param string $header_name * - * @return Swift_Signers_HeaderSigner + * @return self */ public function ignoreHeader($header_name); /** * Prepare the Signer to get a new Body. * - * @return Swift_Signers_HeaderSigner + * @return self */ public function startBody(); /** * Give the signal that the body has finished streaming. * - * @return Swift_Signers_HeaderSigner + * @return self */ public function endBody(); @@ -43,18 +43,18 @@ interface Swift_Signers_HeaderSigner extends Swift_Signer, Swift_InputByteStream * * @param Swift_Mime_SimpleHeaderSet $headers * - * @return Swift_Signers_HeaderSigner + * @return self */ - public function setHeaders(Swift_Mime_HeaderSet $headers); + public function setHeaders(Swift_Mime_SimpleHeaderSet $headers); /** * Add the header(s) to the headerSet. * - * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_SimpleHeaderSet $headers * - * @return Swift_Signers_HeaderSigner + * @return self */ - public function addSignature(Swift_Mime_HeaderSet $headers); + public function addSignature(Swift_Mime_SimpleHeaderSet $headers); /** * Return the list of header a signer might tamper. diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php index 3a35ad55d07..deb29f575f2 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php @@ -12,13 +12,13 @@ * DKIM Signer used to apply DKIM Signature to a message * Takes advantage of pecl extension. * - * @author Xavier De Cock + * @author Xavier De Cock */ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner { - private $_peclLoaded = false; + private $peclLoaded = false; - private $_dkimHandler = null; + private $dkimHandler = null; private $dropFirstLF = true; @@ -33,22 +33,17 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner throw new Swift_SwiftException('php-opendkim extension not found'); } - $this->_peclLoaded = true; + $this->peclLoaded = true; parent::__construct($privateKey, $domainName, $selector); } - public static function newInstance($privateKey, $domainName, $selector) - { - return new static($privateKey, $domainName, $selector); - } - - public function addSignature(Swift_Mime_HeaderSet $headers) + public function addSignature(Swift_Mime_SimpleHeaderSet $headers) { $header = new Swift_Mime_Headers_OpenDKIMHeader('DKIM-Signature'); - $headerVal = $this->_dkimHandler->getSignatureHeader(); + $headerVal = $this->dkimHandler->getSignatureHeader(); if (!$headerVal) { - throw new Swift_SwiftException('OpenDKIM Error: '.$this->_dkimHandler->getError()); + throw new Swift_SwiftException('OpenDKIM Error: '.$this->dkimHandler->getError()); } $header->setValue($headerVal); $headers->set($header); @@ -56,40 +51,40 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner return $this; } - public function setHeaders(Swift_Mime_HeaderSet $headers) + public function setHeaders(Swift_Mime_SimpleHeaderSet $headers) { - $bodyLen = $this->_bodyLen; + $bodyLen = $this->bodyLen; if (is_bool($bodyLen)) { $bodyLen = -1; } - $hash = $this->_hashAlgorithm == 'rsa-sha1' ? OpenDKIMSign::ALG_RSASHA1 : OpenDKIMSign::ALG_RSASHA256; - $bodyCanon = $this->_bodyCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; - $headerCanon = $this->_headerCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; - $this->_dkimHandler = new OpenDKIMSign($this->_privateKey, $this->_selector, $this->_domainName, $headerCanon, $bodyCanon, $hash, $bodyLen); + $hash = $this->hashAlgorithm == 'rsa-sha1' ? OpenDKIMSign::ALG_RSASHA1 : OpenDKIMSign::ALG_RSASHA256; + $bodyCanon = $this->bodyCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $headerCanon = $this->headerCanon == 'simple' ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $this->dkimHandler = new OpenDKIMSign($this->privateKey, $this->selector, $this->domainName, $headerCanon, $bodyCanon, $hash, $bodyLen); // Hardcode signature Margin for now - $this->_dkimHandler->setMargin(78); + $this->dkimHandler->setMargin(78); - if (!is_numeric($this->_signatureTimestamp)) { + if (!is_numeric($this->signatureTimestamp)) { OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, time()); } else { - if (!OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, $this->_signatureTimestamp)) { + if (!OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, $this->signatureTimestamp)) { throw new Swift_SwiftException('Unable to force signature timestamp ['.openssl_error_string().']'); } } - if (isset($this->_signerIdentity)) { - $this->_dkimHandler->setSigner($this->_signerIdentity); + if (isset($this->signerIdentity)) { + $this->dkimHandler->setSigner($this->signerIdentity); } $listHeaders = $headers->listAll(); foreach ($listHeaders as $hName) { // Check if we need to ignore Header - if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if (!isset($this->ignoredHeaders[strtolower($hName)])) { $tmp = $headers->getAll($hName); if ($headers->has($hName)) { foreach ($tmp as $header) { if ($header->getFieldBody() != '') { $htosign = $header->toString(); - $this->_dkimHandler->header($htosign); - $this->_signedHeaders[] = $header->getFieldName(); + $this->dkimHandler->header($htosign); + $this->signedHeaders[] = $header->getFieldName(); } } } @@ -101,28 +96,28 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner public function startBody() { - if (!$this->_peclLoaded) { + if (!$this->peclLoaded) { return parent::startBody(); } $this->dropFirstLF = true; - $this->_dkimHandler->eoh(); + $this->dkimHandler->eoh(); return $this; } public function endBody() { - if (!$this->_peclLoaded) { + if (!$this->peclLoaded) { return parent::endBody(); } - $this->_dkimHandler->eom(); + $this->dkimHandler->eom(); return $this; } public function reset() { - $this->_dkimHandler = null; + $this->dkimHandler = null; parent::reset(); return $this; @@ -133,11 +128,11 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner * * @param int $time * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setSignatureTimestamp($time) { - $this->_signatureTimestamp = $time; + $this->signatureTimestamp = $time; return $this; } @@ -147,11 +142,11 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner * * @param int $time * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setSignatureExpiration($time) { - $this->_signatureExpiration = $time; + $this->signatureExpiration = $time; return $this; } @@ -161,21 +156,21 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner * * @param bool $debug * - * @return Swift_Signers_DKIMSigner + * @return $this */ public function setDebugHeaders($debug) { - $this->_debugHeaders = (bool) $debug; + $this->debugHeaders = (bool) $debug; return $this; } // Protected - protected function _canonicalizeBody($string) + protected function canonicalizeBody($string) { - if (!$this->_peclLoaded) { - return parent::_canonicalizeBody($string); + if (!$this->peclLoaded) { + return parent::canonicalizeBody($string); } if (false && $this->dropFirstLF === true) { if ($string[0] == "\r" && $string[1] == "\n") { @@ -184,7 +179,7 @@ class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner } $this->dropFirstLF = false; if (strlen($string)) { - $this->_dkimHandler->body($string); + $this->dkimHandler->body($string); } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php index b267099a860..eea2648c9e9 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php @@ -34,7 +34,7 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner protected $replacementFactory; /** - * @var Swift_Mime_HeaderFactory + * @var Swift_Mime_SimpleHeaderFactory */ protected $headerFactory; @@ -59,39 +59,20 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner ->lookup('transport.replacementfactory'); $this->signOptions = PKCS7_DETACHED; - - // Supported since php5.4 - if (defined('OPENSSL_CIPHER_AES_128_CBC')) { - $this->encryptCipher = OPENSSL_CIPHER_AES_128_CBC; - } else { - $this->encryptCipher = OPENSSL_CIPHER_RC2_128; - } - } - - /** - * Returns an new Swift_Signers_SMimeSigner instance. - * - * @param string $certificate - * @param string $privateKey - * - * @return Swift_Signers_SMimeSigner - */ - public static function newInstance($certificate = null, $privateKey = null) - { - return new self($certificate, $privateKey); + $this->encryptCipher = OPENSSL_CIPHER_AES_128_CBC; } /** * Set the certificate location to use for signing. * - * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * @see http://www.php.net/manual/en/openssl.pkcs7.flags.php * * @param string $certificate * @param string|array $privateKey If the key needs an passphrase use array('file-location', 'passphrase') instead * @param int $signOptions Bitwise operator options for openssl_pkcs7_sign() * @param string $extraCerts A file containing intermediate certificates needed by the signing certificate * - * @return Swift_Signers_SMimeSigner + * @return $this */ public function setSignCertificate($certificate, $privateKey = null, $signOptions = PKCS7_DETACHED, $extraCerts = null) { @@ -117,13 +98,13 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner /** * Set the certificate location to use for encryption. * - * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php - * @link http://nl3.php.net/manual/en/openssl.ciphers.php + * @see http://www.php.net/manual/en/openssl.pkcs7.flags.php + * @see http://nl3.php.net/manual/en/openssl.ciphers.php * * @param string|array $recipientCerts Either an single X.509 certificate, or an assoc array of X.509 certificates. * @param int $cipher * - * @return Swift_Signers_SMimeSigner + * @return $this */ public function setEncryptCertificate($recipientCerts, $cipher = null) { @@ -169,7 +150,7 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner * * @param bool $signThenEncrypt * - * @return Swift_Signers_SMimeSigner + * @return $this */ public function setSignThenEncrypt($signThenEncrypt = true) { @@ -189,7 +170,7 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner /** * Resets internal states. * - * @return Swift_Signers_SMimeSigner + * @return $this */ public function reset() { @@ -201,7 +182,7 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner * * @param Swift_Message $message * - * @return Swift_Signers_SMimeSigner + * @return $this */ public function signMessage(Swift_Message $message) { @@ -297,7 +278,7 @@ class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner $args[] = $this->extraCerts; } - if (!call_user_func_array('openssl_pkcs7_sign', $args)) { + if (!openssl_pkcs7_sign(...$args)) { throw new Swift_IoException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/SmtpTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/SmtpTransport.php index 62516114019..011c03ab188 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/SmtpTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/SmtpTransport.php @@ -11,7 +11,7 @@ /** * Sends Messages over SMTP with ESMTP support. * - * @author Chris Corbyn + * @author Chris Corbyn * * @method Swift_SmtpTransport setUsername(string $username) Set the username to authenticate with. * @method string getUsername() Get the username to authenticate with. @@ -41,18 +41,4 @@ class Swift_SmtpTransport extends Swift_Transport_EsmtpTransport $this->setPort($port); $this->setEncryption($security); } - - /** - * Create a new SmtpTransport instance. - * - * @param string $host - * @param int $port - * @param string $security - * - * @return Swift_SmtpTransport - */ - public static function newInstance($host = 'localhost', $port = 25, $security = null) - { - return new self($host, $port, $security); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Spool.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Spool.php index c16ab4b38eb..9d0e8fee2be 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Spool.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Spool.php @@ -35,11 +35,11 @@ interface Swift_Spool /** * Queues a message. * - * @param Swift_Mime_Message $message The message to store + * @param Swift_Mime_SimpleMessage $message The message to store * * @return bool Whether the operation has succeeded */ - public function queueMessage(Swift_Mime_Message $message); + public function queueMessage(Swift_Mime_SimpleMessage $message); /** * Sends messages using the given transport instance. diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/SpoolTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/SpoolTransport.php index cf9bf78fb8f..f92567ba021 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/SpoolTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/SpoolTransport.php @@ -32,16 +32,4 @@ class Swift_SpoolTransport extends Swift_Transport_SpoolTransport $arguments ); } - - /** - * Create a new SpoolTransport instance. - * - * @param Swift_Spool $spool - * - * @return Swift_SpoolTransport - */ - public static function newInstance(Swift_Spool $spool) - { - return new self($spool); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php b/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php index d5735667b94..ba1f6d323fc 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php @@ -13,26 +13,26 @@ * * This stream filter deals with Byte arrays rather than simple strings. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilter { /** The needle(s) to search for */ - private $_search; + private $search; /** The replacement(s) to make */ - private $_replace; + private $replace; /** The Index for searching */ - private $_index; + private $index; /** The Search Tree */ - private $_tree = array(); + private $tree = array(); /** Gives the size of the largest search */ - private $_treeMaxLen = 0; + private $treeMaxLen = 0; - private $_repSize; + private $repSize; /** * Create a new ByteArrayReplacementFilter with $search and $replace. @@ -42,11 +42,11 @@ class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilt */ public function __construct($search, $replace) { - $this->_search = $search; - $this->_index = array(); - $this->_tree = array(); - $this->_replace = array(); - $this->_repSize = array(); + $this->search = $search; + $this->index = array(); + $this->tree = array(); + $this->replace = array(); + $this->repSize = array(); $tree = null; $i = null; @@ -56,10 +56,10 @@ class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilt $tree[-1] = min(count($replace) - 1, $i - 1); $tree[-2] = $last_size; } - $tree = &$this->_tree; + $tree = &$this->tree; if (is_array($search_element)) { foreach ($search_element as $k => $char) { - $this->_index[$char] = true; + $this->index[$char] = true; if (!isset($tree[$char])) { $tree[$char] = array(); } @@ -74,23 +74,23 @@ class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilt } $tree = &$tree[$search_element]; $size = max($last_size, $size); - $this->_index[$search_element] = true; + $this->index[$search_element] = true; } } if ($i !== null) { $tree[-1] = min(count($replace) - 1, $i); $tree[-2] = $last_size; - $this->_treeMaxLen = $size; + $this->treeMaxLen = $size; } foreach ($replace as $rep) { if (!is_array($rep)) { $rep = array($rep); } - $this->_replace[] = $rep; + $this->replace[] = $rep; } - for ($i = count($this->_replace) - 1; $i >= 0; --$i) { - $this->_replace[$i] = $rep = $this->filter($this->_replace[$i], $i); - $this->_repSize[$i] = count($rep); + for ($i = count($this->replace) - 1; $i >= 0; --$i) { + $this->replace[$i] = $rep = $this->filter($this->replace[$i], $i); + $this->repSize[$i] = count($rep); } } @@ -105,36 +105,37 @@ class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilt { $endOfBuffer = end($buffer); - return isset($this->_index[$endOfBuffer]); + return isset($this->index[$endOfBuffer]); } /** * Perform the actual replacements on $buffer and return the result. * * @param array $buffer - * @param int $_minReplaces + * @param int $minReplaces * * @return array */ - public function filter($buffer, $_minReplaces = -1) + public function filter($buffer, $minReplaces = -1) { - if ($this->_treeMaxLen == 0) { + if ($this->treeMaxLen == 0) { return $buffer; } $newBuffer = array(); $buf_size = count($buffer); + $last_size = 0; for ($i = 0; $i < $buf_size; ++$i) { - $search_pos = $this->_tree; + $search_pos = $this->tree; $last_found = PHP_INT_MAX; // We try to find if the next byte is part of a search pattern - for ($j = 0; $j <= $this->_treeMaxLen; ++$j) { + for ($j = 0; $j <= $this->treeMaxLen; ++$j) { // We have a new byte for a search pattern - if (isset($buffer [$p = $i + $j]) && isset($search_pos[$buffer[$p]])) { + if (isset($buffer[$p = $i + $j]) && isset($search_pos[$buffer[$p]])) { $search_pos = $search_pos[$buffer[$p]]; // We have a complete pattern, save, in case we don't find a better match later if (isset($search_pos[-1]) && $search_pos[-1] < $last_found - && $search_pos[-1] > $_minReplaces) { + && $search_pos[-1] > $minReplaces) { $last_found = $search_pos[-1]; $last_size = $search_pos[-2]; } @@ -142,9 +143,9 @@ class Swift_StreamFilters_ByteArrayReplacementFilter implements Swift_StreamFilt // We got a complete pattern elseif ($last_found !== PHP_INT_MAX) { // Adding replacement datas to output buffer - $rep_size = $this->_repSize[$last_found]; + $rep_size = $this->repSize[$last_found]; for ($j = 0; $j < $rep_size; ++$j) { - $newBuffer[] = $this->_replace[$last_found][$j]; + $newBuffer[] = $this->replace[$last_found][$j]; } // We Move cursor forward $i += $last_size - 1; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php b/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php index d0db8b96197..50a63f1aaa2 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php @@ -16,10 +16,10 @@ class Swift_StreamFilters_StringReplacementFilter implements Swift_StreamFilter { /** The needle(s) to search for */ - private $_search; + private $search; /** The replacement(s) to make */ - private $_replace; + private $replace; /** * Create a new StringReplacementFilter with $search and $replace. @@ -29,8 +29,8 @@ class Swift_StreamFilters_StringReplacementFilter implements Swift_StreamFilter */ public function __construct($search, $replace) { - $this->_search = $search; - $this->_replace = $replace; + $this->search = $search; + $this->replace = $replace; } /** @@ -42,8 +42,12 @@ class Swift_StreamFilters_StringReplacementFilter implements Swift_StreamFilter */ public function shouldBuffer($buffer) { + if ('' === $buffer) { + return false; + } + $endOfBuffer = substr($buffer, -1); - foreach ((array) $this->_search as $needle) { + foreach ((array) $this->search as $needle) { if (false !== strpos($needle, $endOfBuffer)) { return true; } @@ -61,6 +65,6 @@ class Swift_StreamFilters_StringReplacementFilter implements Swift_StreamFilter */ public function filter($buffer) { - return str_replace($this->_search, $this->_replace, $buffer); + return str_replace($this->search, $this->replace, $buffer); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php b/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php index e98240b5bc5..f60f0cf8218 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php @@ -16,7 +16,7 @@ class Swift_StreamFilters_StringReplacementFilterFactory implements Swift_ReplacementFilterFactory { /** Lazy-loaded filters */ - private $_filters = array(); + private $filters = array(); /** * Create a new StreamFilter to replace $search with $replace in a string. @@ -28,18 +28,18 @@ class Swift_StreamFilters_StringReplacementFilterFactory implements Swift_Replac */ public function createFilter($search, $replace) { - if (!isset($this->_filters[$search][$replace])) { - if (!isset($this->_filters[$search])) { - $this->_filters[$search] = array(); + if (!isset($this->filters[$search][$replace])) { + if (!isset($this->filters[$search])) { + $this->filters[$search] = array(); } - if (!isset($this->_filters[$search][$replace])) { - $this->_filters[$search][$replace] = array(); + if (!isset($this->filters[$search][$replace])) { + $this->filters[$search][$replace] = array(); } - $this->_filters[$search][$replace] = new Swift_StreamFilters_StringReplacementFilter($search, $replace); + $this->filters[$search][$replace] = new Swift_StreamFilters_StringReplacementFilter($search, $replace); } - return $this->_filters[$search][$replace]; + return $this->filters[$search][$replace]; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport.php index 6535eadf253..f6214b7bfc5 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport.php @@ -32,18 +32,41 @@ interface Swift_Transport */ public function stop(); + /** + * Check if this Transport mechanism is alive. + * + * If a Transport mechanism session is no longer functional, the method + * returns FALSE. It is the responsibility of the developer to handle this + * case and restart the Transport mechanism manually. + * + * @example + * + * if (!$transport->ping()) { + * $transport->stop(); + * $transport->start(); + * } + * + * The Transport mechanism will be started, if it is not already. + * + * It is undefined if the Transport mechanism attempts to restart as long as + * the return value reflects whether the mechanism is now functional. + * + * @return bool TRUE if the transport is alive + */ + public function ping(); + /** * Send the given Message. * * Recipient/sender data will be retrieved from the Message API. * The return value is the number of recipients who were accepted for delivery. * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null); + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null); /** * Register a plugin in the Transport. diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php index 72d4ecdff4d..34c3323fdba 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php @@ -16,34 +16,35 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport { /** Input-Output buffer for sending/receiving SMTP commands and responses */ - protected $_buffer; + protected $buffer; /** Connection status */ - protected $_started = false; + protected $started = false; /** The domain name to use in HELO command */ - protected $_domain = '[127.0.0.1]'; + protected $domain = '[127.0.0.1]'; /** The event dispatching layer */ - protected $_eventDispatcher; + protected $eventDispatcher; /** Source Ip */ - protected $_sourceIp; + protected $sourceIp; /** Return an array of params for the Buffer */ - abstract protected function _getBufferParams(); + abstract protected function getBufferParams(); /** * Creates a new EsmtpTransport using the given I/O buffer. * * @param Swift_Transport_IoBuffer $buf * @param Swift_Events_EventDispatcher $dispatcher + * @param string $localDomain */ - public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1') { - $this->_eventDispatcher = $dispatcher; - $this->_buffer = $buf; - $this->_lookupHostname(); + $this->eventDispatcher = $dispatcher; + $this->buffer = $buf; + $this->setLocalDomain($localDomain); } /** @@ -52,16 +53,25 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport * This should be a fully-qualified domain name and should be truly the domain * you're using. * - * If your server doesn't have a domain name, use the IP in square - * brackets (i.e. [127.0.0.1]). + * If your server does not have a domain name, use the IP address. This will + * automatically be wrapped in square brackets as described in RFC 5321, + * section 4.1.3. * * @param string $domain * - * @return Swift_Transport_AbstractSmtpTransport + * @return $this */ public function setLocalDomain($domain) { - $this->_domain = $domain; + if (substr($domain, 0, 1) !== '[') { + if (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + $domain = '['.$domain.']'; + } elseif (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $domain = '[IPv6:'.$domain.']'; + } + } + + $this->domain = $domain; return $this; } @@ -69,11 +79,14 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport /** * Get the name of the domain Swift will identify as. * + * If an IP address was specified, this will be returned wrapped in square + * brackets as described in RFC 5321, section 4.1.3. + * * @return string */ public function getLocalDomain() { - return $this->_domain; + return $this->domain; } /** @@ -83,7 +96,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function setSourceIp($source) { - $this->_sourceIp = $source; + $this->sourceIp = $source; } /** @@ -93,7 +106,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function getSourceIp() { - return $this->_sourceIp; + return $this->sourceIp; } /** @@ -101,27 +114,27 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function start() { - if (!$this->_started) { - if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); + if (!$this->started) { + if ($evt = $this->eventDispatcher->createTransportChangeEvent($this)) { + $this->eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); if ($evt->bubbleCancelled()) { return; } } try { - $this->_buffer->initialize($this->_getBufferParams()); + $this->buffer->initialize($this->getBufferParams()); } catch (Swift_TransportException $e) { - $this->_throwException($e); + $this->throwException($e); } - $this->_readGreeting(); - $this->_doHeloCommand(); + $this->readGreeting(); + $this->doHeloCommand(); if ($evt) { - $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted'); + $this->eventDispatcher->dispatchEvent($evt, 'transportStarted'); } - $this->_started = true; + $this->started = true; } } @@ -132,7 +145,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function isStarted() { - return $this->_started; + return $this->started; } /** @@ -141,25 +154,25 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport * Recipient/sender data will be retrieved from the Message API. * The return value is the number of recipients who were accepted for delivery. * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { $sent = 0; $failedRecipients = (array) $failedRecipients; - if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt = $this->eventDispatcher->createSendEvent($this, $message)) { + $this->eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); if ($evt->bubbleCancelled()) { return 0; } } - if (!$reversePath = $this->_getReversePath($message)) { - $this->_throwException(new Swift_TransportException( + if (!$reversePath = $this->getReversePath($message)) { + $this->throwException(new Swift_TransportException( 'Cannot send message without a sender address' ) ); @@ -173,8 +186,8 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport $message->setBcc(array()); try { - $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients); - $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients); + $sent += $this->sendTo($message, $reversePath, $tos, $failedRecipients); + $sent += $this->sendBcc($message, $reversePath, $bcc, $failedRecipients); } catch (Exception $e) { $message->setBcc($bcc); throw $e; @@ -191,7 +204,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); } $evt->setFailedRecipients($failedRecipients); - $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + $this->eventDispatcher->dispatchEvent($evt, 'sendPerformed'); } $message->generateId(); //Make sure a new Message ID is used @@ -204,9 +217,9 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function stop() { - if ($this->_started) { - if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); + if ($this->started) { + if ($evt = $this->eventDispatcher->createTransportChangeEvent($this)) { + $this->eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); if ($evt->bubbleCancelled()) { return; } @@ -218,16 +231,39 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } try { - $this->_buffer->terminate(); + $this->buffer->terminate(); if ($evt) { - $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped'); + $this->eventDispatcher->dispatchEvent($evt, 'transportStopped'); } } catch (Swift_TransportException $e) { - $this->_throwException($e); + $this->throwException($e); } } - $this->_started = false; + $this->started = false; + } + + /** + * {@inheritdoc} + */ + public function ping() + { + try { + if (!$this->isStarted()) { + $this->start(); + } + + $this->executeCommand("NOOP\r\n", array(250)); + } catch (Swift_TransportException $e) { + try { + $this->stop(); + } catch (Swift_TransportException $e) { + } + + return false; + } + + return true; } /** @@ -237,7 +273,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function registerPlugin(Swift_Events_EventListener $plugin) { - $this->_eventDispatcher->bindEventListener($plugin); + $this->eventDispatcher->bindEventListener($plugin); } /** @@ -255,7 +291,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport */ public function getBuffer() { - return $this->_buffer; + return $this->buffer; } /** @@ -273,32 +309,32 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport public function executeCommand($command, $codes = array(), &$failures = null) { $failures = (array) $failures; - $seq = $this->_buffer->write($command); - $response = $this->_getFullResponse($seq); - if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) { - $this->_eventDispatcher->dispatchEvent($evt, 'commandSent'); + $seq = $this->buffer->write($command); + $response = $this->getFullResponse($seq); + if ($evt = $this->eventDispatcher->createCommandEvent($this, $command, $codes)) { + $this->eventDispatcher->dispatchEvent($evt, 'commandSent'); } - $this->_assertResponseCode($response, $codes); + $this->assertResponseCode($response, $codes); return $response; } /** Read the opening SMTP greeting */ - protected function _readGreeting() + protected function readGreeting() { - $this->_assertResponseCode($this->_getFullResponse(0), array(220)); + $this->assertResponseCode($this->getFullResponse(0), array(220)); } /** Send the HELO welcome */ - protected function _doHeloCommand() + protected function doHeloCommand() { $this->executeCommand( - sprintf("HELO %s\r\n", $this->_domain), array(250) + sprintf("HELO %s\r\n", $this->domain), array(250) ); } /** Send the MAIL FROM command */ - protected function _doMailFromCommand($address) + protected function doMailFromCommand($address) { $this->executeCommand( sprintf("MAIL FROM:<%s>\r\n", $address), array(250) @@ -306,7 +342,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Send the RCPT TO command */ - protected function _doRcptToCommand($address) + protected function doRcptToCommand($address) { $this->executeCommand( sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252) @@ -314,27 +350,27 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Send the DATA command */ - protected function _doDataCommand() + protected function doDataCommand() { $this->executeCommand("DATA\r\n", array(354)); } /** Stream the contents of the message over the buffer */ - protected function _streamMessage(Swift_Mime_Message $message) + protected function streamMessage(Swift_Mime_SimpleMessage $message) { - $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n..")); + $this->buffer->setWriteTranslations(array("\r\n." => "\r\n..")); try { - $message->toByteStream($this->_buffer); - $this->_buffer->flushBuffers(); + $message->toByteStream($this->buffer); + $this->buffer->flushBuffers(); } catch (Swift_TransportException $e) { - $this->_throwException($e); + $this->throwException($e); } - $this->_buffer->setWriteTranslations(array()); + $this->buffer->setWriteTranslations(array()); $this->executeCommand("\r\n.\r\n", array(250)); } /** Determine the best-use reverse path for this message */ - protected function _getReversePath(Swift_Mime_Message $message) + protected function getReversePath(Swift_Mime_SimpleMessage $message) { $return = $message->getReturnPath(); $sender = $message->getSender(); @@ -355,10 +391,10 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Throw a TransportException, first sending it to any listeners */ - protected function _throwException(Swift_TransportException $e) + protected function throwException(Swift_TransportException $e) { - if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { - $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if ($evt = $this->eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); if (!$evt->bubbleCancelled()) { throw $e; } @@ -368,18 +404,18 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Throws an Exception if a response code is incorrect */ - protected function _assertResponseCode($response, $wanted) + protected function assertResponseCode($response, $wanted) { list($code) = sscanf($response, '%3d'); $valid = (empty($wanted) || in_array($code, $wanted)); - if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response, + if ($evt = $this->eventDispatcher->createResponseEvent($this, $response, $valid)) { - $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived'); + $this->eventDispatcher->dispatchEvent($evt, 'responseReceived'); } if (!$valid) { - $this->_throwException( + $this->throwException( new Swift_TransportException( 'Expected response code '.implode('/', $wanted).' but got code '. '"'.$code.'", with message "'.$response.'"', @@ -389,18 +425,18 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Get an entire multi-line response using its sequence number */ - protected function _getFullResponse($seq) + protected function getFullResponse($seq) { $response = ''; try { do { - $line = $this->_buffer->readLine($seq); + $line = $this->buffer->readLine($seq); $response .= $line; - } while (null !== $line && false !== $line && ' ' != $line{3}); + } while (null !== $line && false !== $line && ' ' != $line[3]); } catch (Swift_TransportException $e) { - $this->_throwException($e); + $this->throwException($e); } catch (Swift_IoException $e) { - $this->_throwException( + $this->throwException( new Swift_TransportException( $e->getMessage()) ); @@ -410,13 +446,13 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Send an email to the given recipients from the given reverse path */ - private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients) + private function doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients) { $sent = 0; - $this->_doMailFromCommand($reversePath); + $this->doMailFromCommand($reversePath); foreach ($recipients as $forwardPath) { try { - $this->_doRcptToCommand($forwardPath); + $this->doRcptToCommand($forwardPath); ++$sent; } catch (Swift_TransportException $e) { $failedRecipients[] = $forwardPath; @@ -424,8 +460,8 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } if ($sent != 0) { - $this->_doDataCommand(); - $this->_streamMessage($message); + $this->doDataCommand(); + $this->streamMessage($message); } else { $this->reset(); } @@ -434,23 +470,23 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport } /** Send a message to the given To: recipients */ - private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients) + private function sendTo(Swift_Mime_SimpleMessage $message, $reversePath, array $to, array &$failedRecipients) { if (empty($to)) { return 0; } - return $this->_doMailTransaction($message, $reversePath, array_keys($to), + return $this->doMailTransaction($message, $reversePath, array_keys($to), $failedRecipients); } /** Send a message to all Bcc: recipients */ - private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients) + private function sendBcc(Swift_Mime_SimpleMessage $message, $reversePath, array $bcc, array &$failedRecipients) { $sent = 0; foreach ($bcc as $forwardPath => $name) { $message->setBcc(array($forwardPath => $name)); - $sent += $this->_doMailTransaction( + $sent += $this->doMailTransaction( $message, $reversePath, array($forwardPath), $failedRecipients ); } @@ -458,33 +494,14 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport return $sent; } - /** Try to determine the hostname of the server this is run on */ - private function _lookupHostname() - { - if (!empty($_SERVER['SERVER_NAME']) - && $this->_isFqdn($_SERVER['SERVER_NAME'])) { - $this->_domain = $_SERVER['SERVER_NAME']; - } elseif (!empty($_SERVER['SERVER_ADDR'])) { - $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']); - } - } - - /** Determine is the $hostname is a fully-qualified name */ - private function _isFqdn($hostname) - { - // We could do a really thorough check, but there's really no point - if (false !== $dotPos = strpos($hostname, '.')) { - return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1); - } - - return false; - } - /** * Destructor. */ public function __destruct() { - $this->stop(); + try { + $this->stop(); + } catch (Exception $e) { + } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php index 53f721d03c6..26c2d745a95 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php @@ -40,7 +40,7 @@ class Swift_Transport_Esmtp_Auth_CramMd5Authenticator implements Swift_Transport $challenge = $agent->executeCommand("AUTH CRAM-MD5\r\n", array(334)); $challenge = base64_decode(substr($challenge, 4)); $message = base64_encode( - $username.' '.$this->_getResponse($password, $challenge) + $username.' '.$this->getResponse($password, $challenge) ); $agent->executeCommand(sprintf("%s\r\n", $message), array(235)); @@ -60,7 +60,7 @@ class Swift_Transport_Esmtp_Auth_CramMd5Authenticator implements Swift_Transport * * @return string */ - private function _getResponse($secret, $challenge) + private function getResponse($secret, $challenge) { if (strlen($secret) > 64) { $secret = pack('H32', md5($secret)); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php index d8331316963..fa5d2110041 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php @@ -13,7 +13,7 @@ /** * Handles NTLM authentication. * - * @author Ward Peeters + * @author Ward Peeters */ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator { @@ -38,19 +38,17 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es * @param string $password * * @return bool + * + * @throws \LogicException */ public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) { - if (!function_exists('mcrypt_module_open')) { - throw new LogicException('The mcrypt functions need to be enabled to use the NTLM authenticator.'); - } - - if (!function_exists('openssl_random_pseudo_bytes')) { + if (!function_exists('openssl_encrypt')) { throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.'); } if (!function_exists('bcmul')) { - throw new LogicException('The BCMatch functions must be enabled to use the NTLM authenticator.'); + throw new LogicException('The BCMath functions must be enabled to use the NTLM authenticator.'); } try { @@ -60,7 +58,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es // extra parameters for our unit cases $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000')); - $client = func_num_args() > 4 ? func_get_arg(4) : $this->getRandomBytes(8); + $client = func_num_args() > 4 ? func_get_arg(4) : random_bytes(8); // Message 3 response $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent); @@ -125,10 +123,10 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es $responseHex = bin2hex($response); $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2; $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2; - $challenge = $this->hex2bin(substr($responseHex, 48, 16)); - $context = $this->hex2bin(substr($responseHex, 64, 16)); - $targetInfoH = $this->hex2bin(substr($responseHex, 80, 16)); - $targetName = $this->hex2bin(substr($responseHex, $offset, $length)); + $challenge = hex2bin(substr($responseHex, 48, 16)); + $context = hex2bin(substr($responseHex, 64, 16)); + $targetInfoH = hex2bin(substr($responseHex, 80, 16)); + $targetName = hex2bin(substr($responseHex, $offset, $length)); $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2; $targetInfoBlock = substr($responseHex, $offset); list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock); @@ -142,7 +140,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es $serverName, $DNSDomainName, $DNSServerName, - $this->hex2bin($targetInfoBlock), + hex2bin($targetInfoBlock), $terminatorByte, ); } @@ -165,7 +163,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es while ($offset < $length) { $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256; $offset += 8; - $data[] = $this->hex2bin(substr($block, $offset, $blockLength * 2)); + $data[] = hex2bin(substr($block, $offset, $blockLength * 2)); $offset += $blockLength * 2; } @@ -300,9 +298,14 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es return explode('\\', $name); } - list($user, $domain) = explode('@', $name); + if (false !== strpos($name, '@')) { + list($user, $domain) = explode('@', $name); - return array($domain, $user); + return array($domain, $user); + } + + // no domain passed + return array('', $name); } /** @@ -365,11 +368,9 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es protected function getCorrectTimestamp($time) { // Get our timestamp (tricky!) - bcscale(0); - $time = number_format($time, 0, '.', ''); // save microtime to string - $time = bcadd($time, '11644473600000'); // add epoch time - $time = bcmul($time, 10000); // tenths of a microsecond. + $time = bcadd($time, '11644473600000', 0); // add epoch time + $time = bcmul($time, 10000, 0); // tenths of a microsecond. $binary = $this->si2bin($time, 64); // create 64 bit binary string $timestamp = ''; @@ -459,10 +460,11 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es } } - return $this->hex2bin(implode('', $material)); + return hex2bin(implode('', $material)); } /** HELPER FUNCTIONS */ + /** * Create our security buffer depending on length and offset. * @@ -538,7 +540,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es protected function createByte($input, $bytes = 4, $isHex = true) { if ($isHex) { - $byte = $this->hex2bin(str_pad($input, $bytes * 2, '00')); + $byte = hex2bin(str_pad($input, $bytes * 2, '00')); } else { $byte = str_pad($input, $bytes, "\x00"); } @@ -546,39 +548,19 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es return $byte; } - /** - * Create random bytes. - * - * @param $length - * - * @return string - */ - protected function getRandomBytes($length) - { - $bytes = openssl_random_pseudo_bytes($length, $strong); - - if (false !== $bytes && true === $strong) { - return $bytes; - } - - throw new RuntimeException('OpenSSL did not produce a secure random number.'); - } - /** ENCRYPTION ALGORITHMS */ + /** * DES Encryption. * - * @param string $value + * @param string $value An 8-byte string * @param string $key * * @return string */ protected function desEncrypt($value, $key) { - $cipher = mcrypt_module_open(MCRYPT_DES, '', 'ecb', ''); - mcrypt_generic_init($cipher, $key, mcrypt_create_iv(mcrypt_enc_get_iv_size($cipher), MCRYPT_DEV_RANDOM)); - - return mcrypt_generic($cipher, $value); + return substr(openssl_encrypt($value, 'DES-ECB', $key, \OPENSSL_RAW_DATA), 0, 8); } /** @@ -616,7 +598,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es { $input = $this->convertTo16bit($input); - return function_exists('hash') ? $this->hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); + return function_exists('hash') ? hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); } /** @@ -631,22 +613,6 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es return iconv('UTF-8', 'UTF-16LE', $input); } - /** - * Hex2bin replacement for < PHP 5.4. - * - * @param string $hex - * - * @return string Binary - */ - protected function hex2bin($hex) - { - if (function_exists('hex2bin')) { - return hex2bin($hex); - } else { - return pack('H*', $hex); - } - } - /** * @param string $message */ @@ -671,7 +637,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es 'Target Information Terminator', ); - $data = $this->parseMessage2($this->hex2bin($message)); + $data = $this->parseMessage2(hex2bin($message)); foreach ($map as $key => $value) { echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."
    \n"; @@ -717,7 +683,7 @@ class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Es ); foreach ($map as $key => $value) { - echo $data[$key].' - '.$this->hex2bin($data[$key]).' ||| '.$value."
    \n"; + echo $data[$key].' - '.hex2bin($data[$key]).' ||| '.$value."
    \n"; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php index ca35e7b83ad..d1a4cc3d774 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php @@ -13,7 +13,7 @@ * * Example: * - * $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls') + * $transport = (new Swift_SmtpTransport('smtp.gmail.com', 587, 'tls')) * ->setAuthMode('XOAUTH2') * ->setUsername('YOUR_EMAIL_ADDRESS') * ->setPassword('YOUR_ACCESS_TOKEN'); diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php index cb36133c94d..94387f01a2e 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php @@ -20,35 +20,35 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler * * @var Swift_Transport_Esmtp_Authenticator[] */ - private $_authenticators = array(); + private $authenticators = array(); /** * The username for authentication. * * @var string */ - private $_username; + private $username; /** * The password for authentication. * * @var string */ - private $_password; + private $password; /** * The auth mode for authentication. * * @var string */ - private $_auth_mode; + private $auth_mode; /** * The ESMTP AUTH parameters available. * * @var string[] */ - private $_esmtpParams = array(); + private $esmtpParams = array(); /** * Create a new AuthHandler with $authenticators for support. @@ -67,7 +67,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function setAuthenticators(array $authenticators) { - $this->_authenticators = $authenticators; + $this->authenticators = $authenticators; } /** @@ -77,7 +77,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function getAuthenticators() { - return $this->_authenticators; + return $this->authenticators; } /** @@ -87,7 +87,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function setUsername($username) { - $this->_username = $username; + $this->username = $username; } /** @@ -97,7 +97,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function getUsername() { - return $this->_username; + return $this->username; } /** @@ -107,7 +107,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function setPassword($password) { - $this->_password = $password; + $this->password = $password; } /** @@ -117,7 +117,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function getPassword() { - return $this->_password; + return $this->password; } /** @@ -127,7 +127,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function setAuthMode($mode) { - $this->_auth_mode = $mode; + $this->auth_mode = $mode; } /** @@ -137,7 +137,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function getAuthMode() { - return $this->_auth_mode; + return $this->auth_mode; } /** @@ -157,7 +157,7 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function setKeywordParams(array $parameters) { - $this->_esmtpParams = $parameters; + $this->esmtpParams = $parameters; } /** @@ -167,20 +167,20 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler */ public function afterEhlo(Swift_Transport_SmtpAgent $agent) { - if ($this->_username) { + if ($this->username) { $count = 0; - foreach ($this->_getAuthenticatorsForAgent() as $authenticator) { + foreach ($this->getAuthenticatorsForAgent() as $authenticator) { if (in_array(strtolower($authenticator->getAuthKeyword()), - array_map('strtolower', $this->_esmtpParams))) { + array_map('strtolower', $this->esmtpParams))) { ++$count; - if ($authenticator->authenticate($agent, $this->_username, $this->_password)) { + if ($authenticator->authenticate($agent, $this->username, $this->password)) { return; } } } throw new Swift_TransportException( 'Failed to authenticate on SMTP server with username "'. - $this->_username.'" using '.$count.' possible authenticators' + $this->username.'" using '.$count.' possible authenticators' ); } } @@ -246,13 +246,13 @@ class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler * * @return array */ - protected function _getAuthenticatorsForAgent() + protected function getAuthenticatorsForAgent() { - if (!$mode = strtolower($this->_auth_mode)) { - return $this->_authenticators; + if (!$mode = strtolower($this->auth_mode)) { + return $this->authenticators; } - foreach ($this->_authenticators as $authenticator) { + foreach ($this->authenticators as $authenticator) { if (strtolower($authenticator->getAuthKeyword()) == $mode) { return array($authenticator); } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php index a1cd0dcd1de..7eb7ef33086 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php @@ -20,21 +20,21 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @var Swift_Transport_EsmtpHandler[] */ - private $_handlers = array(); + private $handlers = array(); /** * ESMTP capabilities. * * @var string[] */ - private $_capabilities = array(); + private $capabilities = array(); /** * Connection buffer parameters. * * @var array */ - private $_params = array( + private $params = array( 'protocol' => 'tcp', 'host' => 'localhost', 'port' => 25, @@ -51,10 +51,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * @param Swift_Transport_IoBuffer $buf * @param Swift_Transport_EsmtpHandler[] $extensionHandlers * @param Swift_Events_EventDispatcher $dispatcher + * @param string $localDomain */ - public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher) + public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1') { - parent::__construct($buf, $dispatcher); + parent::__construct($buf, $dispatcher, $localDomain); $this->setExtensionHandlers($extensionHandlers); } @@ -63,11 +64,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param string $host * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setHost($host) { - $this->_params['host'] = $host; + $this->params['host'] = $host; return $this; } @@ -79,7 +80,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getHost() { - return $this->_params['host']; + return $this->params['host']; } /** @@ -87,11 +88,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param int $port * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setPort($port) { - $this->_params['port'] = (int) $port; + $this->params['port'] = (int) $port; return $this; } @@ -103,7 +104,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getPort() { - return $this->_params['port']; + return $this->params['port']; } /** @@ -111,12 +112,12 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param int $timeout seconds * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setTimeout($timeout) { - $this->_params['timeout'] = (int) $timeout; - $this->_buffer->setParam('timeout', (int) $timeout); + $this->params['timeout'] = (int) $timeout; + $this->buffer->setParam('timeout', (int) $timeout); return $this; } @@ -128,7 +129,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getTimeout() { - return $this->_params['timeout']; + return $this->params['timeout']; } /** @@ -136,16 +137,17 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param string $encryption * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setEncryption($encryption) { + $encryption = strtolower($encryption); if ('tls' == $encryption) { - $this->_params['protocol'] = 'tcp'; - $this->_params['tls'] = true; + $this->params['protocol'] = 'tcp'; + $this->params['tls'] = true; } else { - $this->_params['protocol'] = $encryption; - $this->_params['tls'] = false; + $this->params['protocol'] = $encryption; + $this->params['tls'] = false; } return $this; @@ -158,7 +160,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getEncryption() { - return $this->_params['tls'] ? 'tls' : $this->_params['protocol']; + return $this->params['tls'] ? 'tls' : $this->params['protocol']; } /** @@ -166,11 +168,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param array $options * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setStreamOptions($options) { - $this->_params['stream_context_options'] = $options; + $this->params['stream_context_options'] = $options; return $this; } @@ -182,7 +184,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getStreamOptions() { - return $this->_params['stream_context_options']; + return $this->params['stream_context_options']; } /** @@ -190,11 +192,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param string $source * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setSourceIp($source) { - $this->_params['sourceIp'] = $source; + $this->params['sourceIp'] = $source; return $this; } @@ -206,7 +208,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getSourceIp() { - return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null; + return $this->params['sourceIp'] ?? null; } /** @@ -214,7 +216,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo * * @param Swift_Transport_EsmtpHandler[] $handlers * - * @return Swift_Transport_EsmtpTransport + * @return $this */ public function setExtensionHandlers(array $handlers) { @@ -222,10 +224,11 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo foreach ($handlers as $handler) { $assoc[$handler->getHandledKeyword()] = $handler; } - - @uasort($assoc, array($this, '_sortHandlers')); - $this->_handlers = $assoc; - $this->_setHandlerParams(); + uasort($assoc, function ($a, $b) { + return $a->getPriorityOver($b->getHandledKeyword()); + }); + $this->handlers = $assoc; + $this->setHandlerParams(); return $this; } @@ -237,7 +240,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo */ public function getExtensionHandlers() { - return array_values($this->_handlers); + return array_values($this->handlers); } /** @@ -257,7 +260,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo $failures = (array) $failures; $stopSignal = false; $response = null; - foreach ($this->_getActiveHandlers() as $handler) { + foreach ($this->getActiveHandlers() as $handler) { $response = $handler->onCommand( $this, $command, $codes, $failures, $stopSignal ); @@ -269,18 +272,16 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo return parent::executeCommand($command, $codes, $failures); } - // -- Mixin invocation code - /** Mixin handling method for ESMTP handlers */ public function __call($method, $args) { - foreach ($this->_handlers as $handler) { + foreach ($this->handlers as $handler) { if (in_array(strtolower($method), array_map('strtolower', (array) $handler->exposeMixinMethods()) )) { $return = call_user_func_array(array($handler, $method), $args); // Allow fluid method calls - if (is_null($return) && substr($method, 0, 3) == 'set') { + if (null === $return && substr($method, 0, 3) == 'set') { return $this; } else { return $return; @@ -291,53 +292,53 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo } /** Get the params to initialize the buffer */ - protected function _getBufferParams() + protected function getBufferParams() { - return $this->_params; + return $this->params; } /** Overridden to perform EHLO instead */ - protected function _doHeloCommand() + protected function doHeloCommand() { try { $response = $this->executeCommand( - sprintf("EHLO %s\r\n", $this->_domain), array(250) + sprintf("EHLO %s\r\n", $this->domain), array(250) ); } catch (Swift_TransportException $e) { - return parent::_doHeloCommand(); + return parent::doHeloCommand(); } - if ($this->_params['tls']) { + if ($this->params['tls']) { try { $this->executeCommand("STARTTLS\r\n", array(220)); - if (!$this->_buffer->startTLS()) { + if (!$this->buffer->startTLS()) { throw new Swift_TransportException('Unable to connect with TLS encryption'); } try { $response = $this->executeCommand( - sprintf("EHLO %s\r\n", $this->_domain), array(250) + sprintf("EHLO %s\r\n", $this->domain), array(250) ); } catch (Swift_TransportException $e) { - return parent::_doHeloCommand(); + return parent::doHeloCommand(); } } catch (Swift_TransportException $e) { - $this->_throwException($e); + $this->throwException($e); } } - $this->_capabilities = $this->_getCapabilities($response); - $this->_setHandlerParams(); - foreach ($this->_getActiveHandlers() as $handler) { + $this->capabilities = $this->getCapabilities($response); + $this->setHandlerParams(); + foreach ($this->getActiveHandlers() as $handler) { $handler->afterEhlo($this); } } /** Overridden to add Extension support */ - protected function _doMailFromCommand($address) + protected function doMailFromCommand($address) { - $handlers = $this->_getActiveHandlers(); + $handlers = $this->getActiveHandlers(); $params = array(); foreach ($handlers as $handler) { $params = array_merge($params, (array) $handler->getMailParams()); @@ -349,9 +350,9 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo } /** Overridden to add Extension support */ - protected function _doRcptToCommand($address) + protected function doRcptToCommand($address) { - $handlers = $this->_getActiveHandlers(); + $handlers = $this->getActiveHandlers(); $params = array(); foreach ($handlers as $handler) { $params = array_merge($params, (array) $handler->getRcptParams()); @@ -363,7 +364,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo } /** Determine ESMTP capabilities by function group */ - private function _getCapabilities($ehloResponse) + private function getCapabilities($ehloResponse) { $capabilities = array(); $ehloResponse = trim($ehloResponse); @@ -382,31 +383,25 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo } /** Set parameters which are used by each extension handler */ - private function _setHandlerParams() + private function setHandlerParams() { - foreach ($this->_handlers as $keyword => $handler) { - if (array_key_exists($keyword, $this->_capabilities)) { - $handler->setKeywordParams($this->_capabilities[$keyword]); + foreach ($this->handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->capabilities)) { + $handler->setKeywordParams($this->capabilities[$keyword]); } } } /** Get ESMTP handlers which are currently ok to use */ - private function _getActiveHandlers() + private function getActiveHandlers() { $handlers = array(); - foreach ($this->_handlers as $keyword => $handler) { - if (array_key_exists($keyword, $this->_capabilities)) { + foreach ($this->handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->capabilities)) { $handlers[] = $handler; } } return $handlers; } - - /** Custom sort for extension handler ordering */ - private function _sortHandlers($a, $b) - { - return $a->getPriorityOver($b->getHandledKeyword()); - } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php index c039f3beccf..94151f89fef 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php @@ -20,7 +20,31 @@ class Swift_Transport_FailoverTransport extends Swift_Transport_LoadBalancedTran * * @var Swift_Transport */ - private $_currentTransport; + private $currentTransport; + + // needed as __construct is called from elsewhere explicitly + public function __construct() + { + parent::__construct(); + } + + /** + * {@inheritdoc} + */ + public function ping() + { + $maxTransports = count($this->transports); + for ($i = 0; $i < $maxTransports + && $transport = $this->getNextTransport(); ++$i) { + if ($transport->ping()) { + return true; + } else { + $this->killCurrentTransport(); + } + } + + return count($this->transports) > 0; + } /** * Send the given Message. @@ -28,35 +52,35 @@ class Swift_Transport_FailoverTransport extends Swift_Transport_LoadBalancedTran * Recipient/sender data will be retrieved from the Message API. * The return value is the number of recipients who were accepted for delivery. * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { - $maxTransports = count($this->_transports); + $maxTransports = count($this->transports); $sent = 0; - $this->_lastUsedTransport = null; + $this->lastUsedTransport = null; for ($i = 0; $i < $maxTransports - && $transport = $this->_getNextTransport(); ++$i) { + && $transport = $this->getNextTransport(); ++$i) { try { if (!$transport->isStarted()) { $transport->start(); } if ($sent = $transport->send($message, $failedRecipients)) { - $this->_lastUsedTransport = $transport; + $this->lastUsedTransport = $transport; return $sent; } } catch (Swift_TransportException $e) { - $this->_killCurrentTransport(); + $this->killCurrentTransport(); } } - if (count($this->_transports) == 0) { + if (count($this->transports) == 0) { throw new Swift_TransportException( 'All Transports in FailoverTransport failed, or no Transports available' ); @@ -65,18 +89,18 @@ class Swift_Transport_FailoverTransport extends Swift_Transport_LoadBalancedTran return $sent; } - protected function _getNextTransport() + protected function getNextTransport() { - if (!isset($this->_currentTransport)) { - $this->_currentTransport = parent::_getNextTransport(); + if (!isset($this->currentTransport)) { + $this->currentTransport = parent::getNextTransport(); } - return $this->_currentTransport; + return $this->currentTransport; } - protected function _killCurrentTransport() + protected function killCurrentTransport() { - $this->_currentTransport = null; - parent::_killCurrentTransport(); + $this->currentTransport = null; + parent::killCurrentTransport(); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php index fbf0b2602e4..dc6a2149bab 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php @@ -20,21 +20,26 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport * * @var Swift_Transport[] */ - private $_deadTransports = array(); + private $deadTransports = array(); /** * The Transports which are used in rotation. * * @var Swift_Transport[] */ - protected $_transports = array(); + protected $transports = array(); /** * The Transport used in the last successful send operation. * * @var Swift_Transport */ - protected $_lastUsedTransport = null; + protected $lastUsedTransport = null; + + // needed as __construct is called from elsewhere explicitly + public function __construct() + { + } /** * Set $transports to delegate to. @@ -43,8 +48,8 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function setTransports(array $transports) { - $this->_transports = $transports; - $this->_deadTransports = array(); + $this->transports = $transports; + $this->deadTransports = array(); } /** @@ -54,7 +59,7 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function getTransports() { - return array_merge($this->_transports, $this->_deadTransports); + return array_merge($this->transports, $this->deadTransports); } /** @@ -64,7 +69,7 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function getLastUsedTransport() { - return $this->_lastUsedTransport; + return $this->lastUsedTransport; } /** @@ -74,7 +79,7 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function isStarted() { - return count($this->_transports) > 0; + return count($this->transports) > 0; } /** @@ -82,7 +87,7 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function start() { - $this->_transports = array_merge($this->_transports, $this->_deadTransports); + $this->transports = array_merge($this->transports, $this->deadTransports); } /** @@ -90,44 +95,58 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function stop() { - foreach ($this->_transports as $transport) { + foreach ($this->transports as $transport) { $transport->stop(); } } + /** + * {@inheritdoc} + */ + public function ping() + { + foreach ($this->transports as $transport) { + if (!$transport->ping()) { + $this->killCurrentTransport(); + } + } + + return count($this->transports) > 0; + } + /** * Send the given Message. * * Recipient/sender data will be retrieved from the Message API. * The return value is the number of recipients who were accepted for delivery. * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { - $maxTransports = count($this->_transports); + $maxTransports = count($this->transports); $sent = 0; - $this->_lastUsedTransport = null; + $this->lastUsedTransport = null; for ($i = 0; $i < $maxTransports - && $transport = $this->_getNextTransport(); ++$i) { + && $transport = $this->getNextTransport(); ++$i) { try { if (!$transport->isStarted()) { $transport->start(); } if ($sent = $transport->send($message, $failedRecipients)) { - $this->_lastUsedTransport = $transport; + $this->lastUsedTransport = $transport; break; } } catch (Swift_TransportException $e) { - $this->_killCurrentTransport(); + $this->killCurrentTransport(); } } - if (count($this->_transports) == 0) { + if (count($this->transports) == 0) { throw new Swift_TransportException( 'All Transports in LoadBalancedTransport failed, or no Transports available' ); @@ -143,7 +162,7 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport */ public function registerPlugin(Swift_Events_EventListener $plugin) { - foreach ($this->_transports as $transport) { + foreach ($this->transports as $transport) { $transport->registerPlugin($plugin); } } @@ -153,10 +172,10 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport * * @return Swift_Transport */ - protected function _getNextTransport() + protected function getNextTransport() { - if ($next = array_shift($this->_transports)) { - $this->_transports[] = $next; + if ($next = array_shift($this->transports)) { + $this->transports[] = $next; } return $next; @@ -165,14 +184,14 @@ class Swift_Transport_LoadBalancedTransport implements Swift_Transport /** * Tag the currently used (top of stack) transport as dead/useless. */ - protected function _killCurrentTransport() + protected function killCurrentTransport() { - if ($transport = array_pop($this->_transports)) { + if ($transport = array_pop($this->transports)) { try { $transport->stop(); } catch (Exception $e) { } - $this->_deadTransports[] = $transport; + $this->deadTransports[] = $transport; } } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php deleted file mode 100644 index 77489cedc68..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php +++ /dev/null @@ -1,32 +0,0 @@ -_invoker = $invoker; - $this->_eventDispatcher = $eventDispatcher; - } - - /** - * Not used. - */ - public function isStarted() - { - return false; - } - - /** - * Not used. - */ - public function start() - { - } - - /** - * Not used. - */ - public function stop() - { - } - - /** - * Set the additional parameters used on the mail() function. - * - * This string is formatted for sprintf() where %s is the sender address. - * - * @param string $params - * - * @return Swift_Transport_MailTransport - */ - public function setExtraParams($params) - { - $this->_extraParams = $params; - - return $this; - } - - /** - * Get the additional parameters used on the mail() function. - * - * This string is formatted for sprintf() where %s is the sender address. - * - * @return string - */ - public function getExtraParams() - { - return $this->_extraParams; - } - - /** - * Send the given Message. - * - * Recipient/sender data will be retrieved from the Message API. - * The return value is the number of recipients who were accepted for delivery. - * - * @param Swift_Mime_Message $message - * @param string[] $failedRecipients An array of failures by-reference - * - * @return int - */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) - { - $failedRecipients = (array) $failedRecipients; - - if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); - if ($evt->bubbleCancelled()) { - return 0; - } - } - - $count = ( - count((array) $message->getTo()) - + count((array) $message->getCc()) - + count((array) $message->getBcc()) - ); - - $toHeader = $message->getHeaders()->get('To'); - $subjectHeader = $message->getHeaders()->get('Subject'); - - if (!$toHeader) { - $this->_throwException(new Swift_TransportException('Cannot send message without a recipient')); - } - $to = $toHeader->getFieldBody(); - $subject = $subjectHeader ? $subjectHeader->getFieldBody() : ''; - - $reversePath = $this->_getReversePath($message); - - // Remove headers that would otherwise be duplicated - $message->getHeaders()->remove('To'); - $message->getHeaders()->remove('Subject'); - - $messageStr = $message->toString(); - - $message->getHeaders()->set($toHeader); - $message->getHeaders()->set($subjectHeader); - - // Separate headers from body - if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) { - $headers = substr($messageStr, 0, $endHeaders)."\r\n"; //Keep last EOL - $body = substr($messageStr, $endHeaders + 4); - } else { - $headers = $messageStr."\r\n"; - $body = ''; - } - - unset($messageStr); - - if ("\r\n" != PHP_EOL) { - // Non-windows (not using SMTP) - $headers = str_replace("\r\n", PHP_EOL, $headers); - $subject = str_replace("\r\n", PHP_EOL, $subject); - $body = str_replace("\r\n", PHP_EOL, $body); - } else { - // Windows, using SMTP - $headers = str_replace("\r\n.", "\r\n..", $headers); - $subject = str_replace("\r\n.", "\r\n..", $subject); - $body = str_replace("\r\n.", "\r\n..", $body); - } - - if ($this->_invoker->mail($to, $subject, $body, $headers, - sprintf($this->_extraParams, escapeshellarg($reversePath)))) { - if ($evt) { - $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); - $evt->setFailedRecipients($failedRecipients); - $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); - } - } else { - $failedRecipients = array_merge( - $failedRecipients, - array_keys((array) $message->getTo()), - array_keys((array) $message->getCc()), - array_keys((array) $message->getBcc()) - ); - - if ($evt) { - $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); - $evt->setFailedRecipients($failedRecipients); - $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); - } - - $message->generateId(); - - $count = 0; - } - - return $count; - } - - /** - * Register a plugin. - * - * @param Swift_Events_EventListener $plugin - */ - public function registerPlugin(Swift_Events_EventListener $plugin) - { - $this->_eventDispatcher->bindEventListener($plugin); - } - - /** Throw a TransportException, first sending it to any listeners */ - protected function _throwException(Swift_TransportException $e) - { - if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { - $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); - if (!$evt->bubbleCancelled()) { - throw $e; - } - } else { - throw $e; - } - } - - /** Determine the best-use reverse path for this message */ - private function _getReversePath(Swift_Mime_Message $message) - { - $return = $message->getReturnPath(); - $sender = $message->getSender(); - $from = $message->getFrom(); - $path = null; - if (!empty($return)) { - $path = $return; - } elseif (!empty($sender)) { - $keys = array_keys($sender); - $path = array_shift($keys); - } elseif (!empty($from)) { - $keys = array_keys($from); - $path = array_shift($keys); - } - - return $path; - } -} diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/NullTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/NullTransport.php index ad20e0e535f..dc38078c3c3 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/NullTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/NullTransport.php @@ -16,14 +16,14 @@ class Swift_Transport_NullTransport implements Swift_Transport { /** The event dispatcher from the plugin API */ - private $_eventDispatcher; + private $eventDispatcher; /** * Constructor. */ public function __construct(Swift_Events_EventDispatcher $eventDispatcher) { - $this->_eventDispatcher = $eventDispatcher; + $this->eventDispatcher = $eventDispatcher; } /** @@ -50,18 +50,26 @@ class Swift_Transport_NullTransport implements Swift_Transport { } + /** + * {@inheritdoc} + */ + public function ping() + { + return true; + } + /** * Sends the given message. * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int The number of sent emails */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { - if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt = $this->eventDispatcher->createSendEvent($this, $message)) { + $this->eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); if ($evt->bubbleCancelled()) { return 0; } @@ -69,7 +77,7 @@ class Swift_Transport_NullTransport implements Swift_Transport if ($evt) { $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); - $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + $this->eventDispatcher->dispatchEvent($evt, 'sendPerformed'); } $count = ( @@ -88,6 +96,6 @@ class Swift_Transport_NullTransport implements Swift_Transport */ public function registerPlugin(Swift_Events_EventListener $plugin) { - $this->_eventDispatcher->bindEventListener($plugin); + $this->eventDispatcher->bindEventListener($plugin); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php index 34ac4ce3af8..807538505bf 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php @@ -24,7 +24,7 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran * * @var array */ - private $_params = array( + private $params = array( 'timeout' => 30, 'blocking' => 1, 'command' => '/usr/sbin/sendmail -bs', @@ -36,10 +36,11 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran * * @param Swift_Transport_IoBuffer $buf * @param Swift_Events_EventDispatcher $dispatcher + * @param string $localDomain */ - public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1') { - parent::__construct($buf, $dispatcher); + parent::__construct($buf, $dispatcher, $localDomain); } /** @@ -64,11 +65,11 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran * * @param string $command * - * @return Swift_Transport_SendmailTransport + * @return $this */ public function setCommand($command) { - $this->_params['command'] = $command; + $this->params['command'] = $command; return $this; } @@ -80,7 +81,7 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran */ public function getCommand() { - return $this->_params['command']; + return $this->params['command']; } /** @@ -92,12 +93,12 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran * NOTE: If using 'sendmail -t' you will not be aware of any failures until * they bounce (i.e. send() will always return 100% success). * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { $failedRecipients = (array) $failedRecipients; $command = $this->getCommand(); @@ -105,18 +106,18 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran $count = 0; if (false !== strpos($command, ' -t')) { - if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt = $this->eventDispatcher->createSendEvent($this, $message)) { + $this->eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); if ($evt->bubbleCancelled()) { return 0; } } if (false === strpos($command, ' -f')) { - $command .= ' -f'.escapeshellarg($this->_getReversePath($message)); + $command .= ' -f'.escapeshellarg($this->getReversePath($message)); } - $buffer->initialize(array_merge($this->_params, array('command' => $command))); + $buffer->initialize(array_merge($this->params, array('command' => $command))); if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) { $buffer->setWriteTranslations(array("\r\n" => "\n", "\n." => "\n..")); @@ -136,14 +137,14 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran if ($evt) { $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); $evt->setFailedRecipients($failedRecipients); - $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + $this->eventDispatcher->dispatchEvent($evt, 'sendPerformed'); } $message->generateId(); } elseif (false !== strpos($command, ' -bs')) { $count = parent::send($message, $failedRecipients); } else { - $this->_throwException(new Swift_TransportException( + $this->throwException(new Swift_TransportException( 'Unsupported sendmail command flags ['.$command.']. '. 'Must be one of "-bs" or "-t" but can include additional flags.' )); @@ -153,8 +154,8 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran } /** Get the params to initialize the buffer */ - protected function _getBufferParams() + protected function getBufferParams() { - return $this->_params; + return $this->params; } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php deleted file mode 100644 index 4cab66bd6bf..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php +++ /dev/null @@ -1,39 +0,0 @@ -_eventDispatcher = $eventDispatcher; - $this->_spool = $spool; + $this->eventDispatcher = $eventDispatcher; + $this->spool = $spool; } /** @@ -35,11 +35,11 @@ class Swift_Transport_SpoolTransport implements Swift_Transport * * @param Swift_Spool $spool * - * @return Swift_Transport_SpoolTransport + * @return $this */ public function setSpool(Swift_Spool $spool) { - $this->_spool = $spool; + $this->spool = $spool; return $this; } @@ -51,7 +51,7 @@ class Swift_Transport_SpoolTransport implements Swift_Transport */ public function getSpool() { - return $this->_spool; + return $this->spool; } /** @@ -78,28 +78,36 @@ class Swift_Transport_SpoolTransport implements Swift_Transport { } + /** + * {@inheritdoc} + */ + public function ping() + { + return true; + } + /** * Sends the given message. * - * @param Swift_Mime_Message $message + * @param Swift_Mime_SimpleMessage $message * @param string[] $failedRecipients An array of failures by-reference * * @return int The number of sent e-mail's */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) + public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) { - if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { - $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt = $this->eventDispatcher->createSendEvent($this, $message)) { + $this->eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); if ($evt->bubbleCancelled()) { return 0; } } - $success = $this->_spool->queueMessage($message); + $success = $this->spool->queueMessage($message); if ($evt) { $evt->setResult($success ? Swift_Events_SendEvent::RESULT_SPOOLED : Swift_Events_SendEvent::RESULT_FAILED); - $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + $this->eventDispatcher->dispatchEvent($evt, 'sendPerformed'); } return 1; @@ -112,6 +120,6 @@ class Swift_Transport_SpoolTransport implements Swift_Transport */ public function registerPlugin(Swift_Events_EventListener $plugin) { - $this->_eventDispatcher->bindEventListener($plugin); + $this->eventDispatcher->bindEventListener($plugin); } } diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php index 5134ea48a45..bc6ed5a34b4 100644 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php +++ b/htdocs/includes/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php @@ -11,27 +11,27 @@ /** * A generic IoBuffer implementation supporting remote sockets and local processes. * - * @author Chris Corbyn + * @author Chris Corbyn */ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_Transport_IoBuffer { /** A primary socket */ - private $_stream; + private $stream; /** The input stream */ - private $_in; + private $in; /** The output stream */ - private $_out; + private $out; /** Buffer initialization parameters */ - private $_params = array(); + private $params = array(); /** The ReplacementFilterFactory */ - private $_replacementFactory; + private $replacementFactory; /** Translations performed on data being streamed into the buffer */ - private $_translations = array(); + private $translations = array(); /** * Create a new StreamBuffer using $replacementFactory for transformations. @@ -40,7 +40,7 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn */ public function __construct(Swift_ReplacementFilterFactory $replacementFactory) { - $this->_replacementFactory = $replacementFactory; + $this->replacementFactory = $replacementFactory; } /** @@ -52,14 +52,14 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn */ public function initialize(array $params) { - $this->_params = $params; + $this->params = $params; switch ($params['type']) { case self::TYPE_PROCESS: - $this->_establishProcessConnection(); + $this->establishProcessConnection(); break; case self::TYPE_SOCKET: default: - $this->_establishSocketConnection(); + $this->establishSocketConnection(); break; } } @@ -72,27 +72,26 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn */ public function setParam($param, $value) { - if (isset($this->_stream)) { + if (isset($this->stream)) { switch ($param) { case 'timeout': - if ($this->_stream) { - stream_set_timeout($this->_stream, $value); + if ($this->stream) { + stream_set_timeout($this->stream, $value); } break; case 'blocking': - if ($this->_stream) { - stream_set_blocking($this->_stream, 1); + if ($this->stream) { + stream_set_blocking($this->stream, 1); } - } } - $this->_params[$param] = $value; + $this->params[$param] = $value; } public function startTLS() { - return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + return stream_socket_enable_crypto($this->stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); } /** @@ -100,22 +99,22 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn */ public function terminate() { - if (isset($this->_stream)) { - switch ($this->_params['type']) { + if (isset($this->stream)) { + switch ($this->params['type']) { case self::TYPE_PROCESS: - fclose($this->_in); - fclose($this->_out); - proc_close($this->_stream); + fclose($this->in); + fclose($this->out); + proc_close($this->stream); break; case self::TYPE_SOCKET: default: - fclose($this->_stream); + fclose($this->stream); break; } } - $this->_stream = null; - $this->_out = null; - $this->_in = null; + $this->stream = null; + $this->out = null; + $this->in = null; } /** @@ -128,19 +127,19 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn */ public function setWriteTranslations(array $replacements) { - foreach ($this->_translations as $search => $replace) { + foreach ($this->translations as $search => $replace) { if (!isset($replacements[$search])) { $this->removeFilter($search); - unset($this->_translations[$search]); + unset($this->translations[$search]); } } foreach ($replacements as $search => $replace) { - if (!isset($this->_translations[$search])) { + if (!isset($this->translations[$search])) { $this->addFilter( - $this->_replacementFactory->createFilter($search, $replace), $search + $this->replacementFactory->createFilter($search, $replace), $search ); - $this->_translations[$search] = true; + $this->translations[$search] = true; } } } @@ -153,20 +152,20 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn * * @param int $sequence of last write to scan from * - * @throws Swift_IoException - * * @return string + * + * @throws Swift_IoException */ public function readLine($sequence) { - if (isset($this->_out) && !feof($this->_out)) { - $line = fgets($this->_out); + if (isset($this->out) && !feof($this->out)) { + $line = fgets($this->out); if (strlen($line) == 0) { - $metas = stream_get_meta_data($this->_out); + $metas = stream_get_meta_data($this->out); if ($metas['timed_out']) { throw new Swift_IoException( 'Connection to '. - $this->_getReadConnectionDescription(). + $this->getReadConnectionDescription(). ' Timed Out' ); } @@ -185,20 +184,20 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn * * @param int $length * - * @throws Swift_IoException - * * @return string|bool + * + * @throws Swift_IoException */ public function read($length) { - if (isset($this->_out) && !feof($this->_out)) { - $ret = fread($this->_out, $length); + if (isset($this->out) && !feof($this->out)) { + $ret = fread($this->out, $length); if (strlen($ret) == 0) { - $metas = stream_get_meta_data($this->_out); + $metas = stream_get_meta_data($this->out); if ($metas['timed_out']) { throw new Swift_IoException( 'Connection to '. - $this->_getReadConnectionDescription(). + $this->getReadConnectionDescription(). ' Timed Out' ); } @@ -214,22 +213,22 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn } /** Flush the stream contents */ - protected function _flush() + protected function flush() { - if (isset($this->_in)) { - fflush($this->_in); + if (isset($this->in)) { + fflush($this->in); } } /** Write this bytes to the stream */ - protected function _commit($bytes) + protected function doCommit($bytes) { - if (isset($this->_in)) { + if (isset($this->in)) { $bytesToWrite = strlen($bytes); $totalBytesWritten = 0; while ($totalBytesWritten < $bytesToWrite) { - $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten)); + $bytesWritten = fwrite($this->in, substr($bytes, $totalBytesWritten)); if (false === $bytesWritten || 0 === $bytesWritten) { break; } @@ -238,7 +237,7 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn } if ($totalBytesWritten > 0) { - return ++$this->_sequence; + return ++$this->sequence; } } } @@ -246,77 +245,79 @@ class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableIn /** * Establishes a connection to a remote server. */ - private function _establishSocketConnection() + private function establishSocketConnection() { - $host = $this->_params['host']; - if (!empty($this->_params['protocol'])) { - $host = $this->_params['protocol'].'://'.$host; + $host = $this->params['host']; + if (!empty($this->params['protocol'])) { + $host = $this->params['protocol'].'://'.$host; } $timeout = 15; - if (!empty($this->_params['timeout'])) { - $timeout = $this->_params['timeout']; + if (!empty($this->params['timeout'])) { + $timeout = $this->params['timeout']; } $options = array(); - if (!empty($this->_params['sourceIp'])) { - $options['socket']['bindto'] = $this->_params['sourceIp'].':0'; + if (!empty($this->params['sourceIp'])) { + $options['socket']['bindto'] = $this->params['sourceIp'].':0'; } - if (isset($this->_params['stream_context_options'])) { - $options = array_merge($options, $this->_params['stream_context_options']); + + if (isset($this->params['stream_context_options'])) { + $options = array_merge($options, $this->params['stream_context_options']); } $streamContext = stream_context_create($options); - $this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext); - if (false === $this->_stream) { + $this->stream = @stream_socket_client($host.':'.$this->params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext); + if (false === $this->stream) { throw new Swift_TransportException( - 'Connection could not be established with host '.$this->_params['host']. + 'Connection could not be established with host '.$this->params['host']. ' ['.$errstr.' #'.$errno.']' ); } - if (!empty($this->_params['blocking'])) { - stream_set_blocking($this->_stream, 1); + if (!empty($this->params['blocking'])) { + stream_set_blocking($this->stream, 1); } else { - stream_set_blocking($this->_stream, 0); + stream_set_blocking($this->stream, 0); } - stream_set_timeout($this->_stream, $timeout); - $this->_in = &$this->_stream; - $this->_out = &$this->_stream; + stream_set_timeout($this->stream, $timeout); + $this->in = &$this->stream; + $this->out = &$this->stream; } /** * Opens a process for input/output. */ - private function _establishProcessConnection() + private function establishProcessConnection() { - $command = $this->_params['command']; + $command = $this->params['command']; $descriptorSpec = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'), ); - $this->_stream = proc_open($command, $descriptorSpec, $pipes); + $pipes = array(); + $this->stream = proc_open($command, $descriptorSpec, $pipes); stream_set_blocking($pipes[2], 0); if ($err = stream_get_contents($pipes[2])) { throw new Swift_TransportException( 'Process could not be started ['.$err.']' ); } - $this->_in = &$pipes[0]; - $this->_out = &$pipes[1]; + $this->in = &$pipes[0]; + $this->out = &$pipes[1]; } - private function _getReadConnectionDescription() + private function getReadConnectionDescription() { - switch ($this->_params['type']) { + switch ($this->params['type']) { case self::TYPE_PROCESS: - return 'Process '.$this->_params['command']; + return 'Process '.$this->params['command']; break; case self::TYPE_SOCKET: default: - $host = $this->_params['host']; - if (!empty($this->_params['protocol'])) { - $host = $this->_params['protocol'].'://'.$host; + $host = $this->params['host']; + if (!empty($this->params['protocol'])) { + $host = $this->params['protocol'].'://'.$host; } - $host .= ':'.$this->_params['port']; + $host .= ':'.$this->params['port']; return $host; break; diff --git a/htdocs/includes/swiftmailer/lib/classes/Swift/Validate.php b/htdocs/includes/swiftmailer/lib/classes/Swift/Validate.php deleted file mode 100644 index e16c212c49f..00000000000 --- a/htdocs/includes/swiftmailer/lib/classes/Swift/Validate.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class Swift_Validate -{ - /** - * Grammar Object. - * - * @var Swift_Mime_Grammar - */ - private static $grammar = null; - - /** - * Checks if an e-mail address matches the current grammars. - * - * @param string $email - * - * @return bool - */ - public static function email($email) - { - if (self::$grammar === null) { - self::$grammar = Swift_DependencyContainer::getInstance() - ->lookup('mime.grammar'); - } - - return (bool) preg_match( - '/^'.self::$grammar->getDefinition('addr-spec').'$/D', - $email - ); - } -} diff --git a/htdocs/includes/swiftmailer/lib/dependency_maps/mime_deps.php b/htdocs/includes/swiftmailer/lib/dependency_maps/mime_deps.php index 04f394b3267..4ce9c7cbbc5 100644 --- a/htdocs/includes/swiftmailer/lib/dependency_maps/mime_deps.php +++ b/htdocs/includes/swiftmailer/lib/dependency_maps/mime_deps.php @@ -1,13 +1,25 @@ register('properties.charset') ->asValue('utf-8') - ->register('mime.grammar') - ->asSharedInstanceOf('Swift_Mime_Grammar') + ->register('email.validator') + ->asSharedInstanceOf('Egulias\EmailValidator\EmailValidator') + + ->register('mime.idgenerator.idright') + // As SERVER_NAME can come from the user in certain configurations, check that + // it does not contain forbidden characters (see RFC 952 and RFC 2181). Use + // preg_replace() instead of preg_match() to prevent DoS attacks with long host names. + ->asValue(!empty($_SERVER['SERVER_NAME']) && preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $_SERVER['SERVER_NAME']) === '' ? $_SERVER['SERVER_NAME'] : 'swift.generated') + + ->register('mime.idgenerator') + ->asSharedInstanceOf('Swift_Mime_IdGenerator') + ->withDependencies(array( + 'mime.idgenerator.idright', + )) ->register('mime.message') ->asNewInstanceOf('Swift_Mime_SimpleMessage') @@ -15,7 +27,7 @@ Swift_DependencyContainer::getInstance() 'mime.headerset', 'mime.qpcontentencoder', 'cache', - 'mime.grammar', + 'mime.idgenerator', 'properties.charset', )) @@ -25,7 +37,7 @@ Swift_DependencyContainer::getInstance() 'mime.headerset', 'mime.qpcontentencoder', 'cache', - 'mime.grammar', + 'mime.idgenerator', 'properties.charset', )) @@ -35,7 +47,7 @@ Swift_DependencyContainer::getInstance() 'mime.headerset', 'mime.base64contentencoder', 'cache', - 'mime.grammar', + 'mime.idgenerator', )) ->addConstructorValue($swift_mime_types) @@ -45,7 +57,7 @@ Swift_DependencyContainer::getInstance() 'mime.headerset', 'mime.base64contentencoder', 'cache', - 'mime.grammar', + 'mime.idgenerator', )) ->addConstructorValue($swift_mime_types) @@ -54,7 +66,7 @@ Swift_DependencyContainer::getInstance() ->withDependencies(array( 'mime.qpheaderencoder', 'mime.rfc2231encoder', - 'mime.grammar', + 'email.validator', 'properties.charset', )) @@ -93,7 +105,7 @@ Swift_DependencyContainer::getInstance() ->withDependencies(array('properties.charset')) ->asNewInstanceOf('Swift_Mime_ContentEncoder_NativeQpContentEncoder') - ->register('mime.qpcontentencoderproxy') + ->register('mime.qpcontentencoder') ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoderProxy') ->withDependencies(array('mime.safeqpcontentencoder', 'mime.nativeqpcontentencoder', 'properties.charset')) @@ -113,11 +125,6 @@ Swift_DependencyContainer::getInstance() ->register('mime.rfc2231encoder') ->asNewInstanceOf('Swift_Encoder_Rfc2231Encoder') ->withDependencies(array('mime.charstream')) - - // As of PHP 5.4.7, the quoted_printable_encode() function behaves correctly. - // see https://github.com/php/php-src/commit/18bb426587d62f93c54c40bf8535eb8416603629 - ->register('mime.qpcontentencoder') - ->asAliasOf(version_compare(phpversion(), '5.4.7', '>=') ? 'mime.qpcontentencoderproxy' : 'mime.safeqpcontentencoder') ; unset($swift_mime_types); diff --git a/htdocs/includes/swiftmailer/lib/dependency_maps/transport_deps.php b/htdocs/includes/swiftmailer/lib/dependency_maps/transport_deps.php index 77e432cfac0..15772abb255 100644 --- a/htdocs/includes/swiftmailer/lib/dependency_maps/transport_deps.php +++ b/htdocs/includes/swiftmailer/lib/dependency_maps/transport_deps.php @@ -1,12 +1,19 @@ register('transport.localdomain') + // As SERVER_NAME can come from the user in certain configurations, check that + // it does not contain forbidden characters (see RFC 952 and RFC 2181). Use + // preg_replace() instead of preg_match() to prevent DoS attacks with long host names. + ->asValue(!empty($_SERVER['SERVER_NAME']) && preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $_SERVER['SERVER_NAME']) === '' ? trim($_SERVER['SERVER_NAME'], '[]') : '127.0.0.1') + ->register('transport.smtp') ->asNewInstanceOf('Swift_Transport_EsmtpTransport') ->withDependencies(array( 'transport.buffer', array('transport.authhandler'), 'transport.eventdispatcher', + 'transport.localdomain', )) ->register('transport.sendmail') @@ -14,12 +21,9 @@ Swift_DependencyContainer::getInstance() ->withDependencies(array( 'transport.buffer', 'transport.eventdispatcher', + 'transport.localdomain', )) - ->register('transport.mail') - ->asNewInstanceOf('Swift_Transport_MailTransport') - ->withDependencies(array('transport.mailinvoker', 'transport.eventdispatcher')) - ->register('transport.loadbalanced') ->asNewInstanceOf('Swift_Transport_LoadBalancedTransport') @@ -34,9 +38,6 @@ Swift_DependencyContainer::getInstance() ->asNewInstanceOf('Swift_Transport_NullTransport') ->withDependencies(array('transport.eventdispatcher')) - ->register('transport.mailinvoker') - ->asSharedInstanceOf('Swift_Transport_SimpleMailInvoker') - ->register('transport.buffer') ->asNewInstanceOf('Swift_Transport_StreamBuffer') ->withDependencies(array('transport.replacementfactory')) diff --git a/htdocs/includes/swiftmailer/lib/mime_types.php b/htdocs/includes/swiftmailer/lib/mime_types.php index 2d7b98dc1aa..b42c1cc1a3e 100644 --- a/htdocs/includes/swiftmailer/lib/mime_types.php +++ b/htdocs/includes/swiftmailer/lib/mime_types.php @@ -7,7 +7,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * - * autogenerated using http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + * autogenerated using https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types * and https://raw.github.com/minad/mimemagic/master/script/freedesktop.org.xml */ diff --git a/htdocs/includes/swiftmailer/lib/preferences.php b/htdocs/includes/swiftmailer/lib/preferences.php index e5195014824..27b7065dbee 100644 --- a/htdocs/includes/swiftmailer/lib/preferences.php +++ b/htdocs/includes/swiftmailer/lib/preferences.php @@ -17,9 +17,3 @@ $preferences->setCharset('utf-8'); if (@is_writable($tmpDir = sys_get_temp_dir())) { $preferences->setTempDir($tmpDir)->setCacheType('disk'); } - -// this should only be done when Swiftmailer won't use the native QP content encoder -// see mime_deps.php -if (version_compare(phpversion(), '5.4.7', '<')) { - $preferences->setQPDotEscape(false); -} diff --git a/htdocs/includes/swiftmailer/lib/swift_init.php b/htdocs/includes/swiftmailer/lib/swift_init.php deleted file mode 100644 index 5c4bae4f4f1..00000000000 --- a/htdocs/includes/swiftmailer/lib/swift_init.php +++ /dev/null @@ -1,28 +0,0 @@ - 'audio/wav', 'wma' => 'audio/x-ms-wma', 'wmv' => 'audio/x-ms-wmv', - 'xls' => 'application/excel', + 'xls' => 'application/vnd.ms-excel', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xml' => 'application/xml', ); diff --git a/htdocs/includes/tecnickcom/tcpdf/CHANGELOG.TXT b/htdocs/includes/tecnickcom/tcpdf/CHANGELOG.TXT index 31b587b2842..d6e4cf05e91 100644 --- a/htdocs/includes/tecnickcom/tcpdf/CHANGELOG.TXT +++ b/htdocs/includes/tecnickcom/tcpdf/CHANGELOG.TXT @@ -1,3 +1,9 @@ +Unreleased + - fix Undesired mouseover effect on links in PDF on Chrome Pdf Viewer + +6.2.13 (2016-06-10) + - IMPORTANT: A new version of this library is under development at https://github.com/tecnickcom/tc-lib-pdf and as a consequence this version will not receive any additional development or support. This version should be considered obsolete, new projects should use the new version as soon it will become stable. + 6.2.12 (2015-09-12) - fix composer package name to tecnickcom/tcpdf diff --git a/htdocs/includes/tecnickcom/tcpdf/README.TXT b/htdocs/includes/tecnickcom/tcpdf/README.TXT deleted file mode 100644 index 2061fa7a16b..00000000000 --- a/htdocs/includes/tecnickcom/tcpdf/README.TXT +++ /dev/null @@ -1,117 +0,0 @@ -TCPDF - README -============================================================ - -I WISH TO IMPROVE AND EXPAND TCPDF BUT I NEED YOUR SUPPORT. -PLEASE MAKE A DONATION: -http://sourceforge.net/donate/index.php?group_id=128076 -or via PayPal at paypal@tecnick.com - ------------------------------------------------------------- - -Name: TCPDF -Version: 6.2.12 -Release date: 2015-09-12 -Author: Nicola Asuni - -Copyright (c) 2002-2015: - Nicola Asuni - Tecnick.com LTD - www.tecnick.com - -URLs: - http://www.tcpdf.org - http://www.sourceforge.net/projects/tcpdf - https://github.com/tecnickcom/TCPDF - -Description: - TCPDF is a PHP class for generating PDF files on-the-fly without requiring external extensions. - This library includes also a class to extract data from existing PDF documents and - classes to generate 1D and 2D barcodes in various formats. - -Main Features: - * no external libraries are required for the basic functions; - * all standard page formats, custom page formats, custom margins and units of measure; - * UTF-8 Unicode and Right-To-Left languages; - * TrueTypeUnicode, OpenTypeUnicode v1, TrueType, OpenType v1, Type1 and CID-0 fonts; - * font subsetting; - * methods to publish some XHTML + CSS code, Javascript and Forms; - * images, graphic (geometric figures) and transformation methods; - * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/script/formats.php) - * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417; - * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies; - * automatic page header and footer management; - * document encryption up to 256 bit and digital signature certifications; - * transactions to UNDO commands; - * PDF annotations, including links, text and file attachments; - * text rendering modes (fill, stroke and clipping); - * multiple columns mode; - * no-write page regions; - * bookmarks, named destinations and table of content; - * text hyphenation; - * text stretching and spacing (tracking); - * automatic page break, line break and text alignments including justification; - * automatic page numbering and page groups; - * move and delete pages; - * page compression (requires php-zlib extension); - * XOBject Templates; - * Layers and object visibility. - * PDF/A-1b support. - -Installation (full instructions on http: www.tcpdf.org): - 1. copy the folder on your Web server - 2. set your installation path and other parameters on the config/tcpdf_config.php - 3. call the examples/example_001.php page with your browser to see an example - -Source Code Documentation: - http://www.tcpdf.org - -Additional Documentation: - http://www.tcpdf.org - -License: - Copyright (C) 2002-2014 Nicola Asuni - Tecnick.com LTD - - TCPDF is free software: you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - TCPDF is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Lesser General Public License for more details. - - You should have received a copy of the License - along with TCPDF. If not, see - . - - See LICENSE.TXT file for more information. - -Third party fonts: - - This library may include third party font files released with different licenses. - - All the PHP files on the fonts directory are subject to the general TCPDF license (GNU-LGPLv3), - they do not contain any binary data but just a description of the general properties of a particular font. - These files can be also generated on the fly using the font utilities and TCPDF methods. - - All the original binary TTF font files have been renamed for compatibility with TCPDF and compressed using the gzcompress PHP function that uses the ZLIB data format (.z files). - - The binary files (.z) that begins with the prefix "free" have been extracted from the GNU FreeFont collection (GNU-GPLv3). - The binary files (.z) that begins with the prefix "pdfa" have been derived from the GNU FreeFont, so they are subject to the same license. - For the details of Copyright, License and other information, please check the files inside the directory fonts/freefont-20120503 - Link : http://www.gnu.org/software/freefont/ - - The binary files (.z) that begins with the prefix "dejavu" have been extracted from the DejaVu fonts 2.33 (Bitstream) collection. - For the details of Copyright, License and other information, please check the files inside the directory fonts/dejavu-fonts-ttf-2.33 - Link : http://dejavu-fonts.org - - The binary files (.z) that begins with the prefix "ae" have been extracted from the Arabeyes.org collection (GNU-GPLv2). - Link : http://projects.arabeyes.org/ - -ICC profile: - TCPDF includes the sRGB.icc profile from the icc-profiles-free Debian package: - https://packages.debian.org/source/stable/icc-profiles-free - - -============================================================ diff --git a/htdocs/includes/tecnickcom/tcpdf/README.md b/htdocs/includes/tecnickcom/tcpdf/README.md new file mode 100644 index 00000000000..baa518137cb --- /dev/null +++ b/htdocs/includes/tecnickcom/tcpdf/README.md @@ -0,0 +1,84 @@ +# TCPDF +*PHP PDF Library* + +[![Donate via PayPal](https://img.shields.io/badge/donate-paypal-87ceeb.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20TCPDF%20project) +*Please consider supporting this project by making a donation via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations¤cy_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20TCPDF%20project)* + +* **category** Library +* **author** Nicola Asuni +* **copyright** 2002-2018 Nicola Asuni - Tecnick.com LTD +* **license** http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT) +* **link** http://www.tcpdf.org +* **source** https://github.com/tecnickcom/TCPDF + + +## IMPORTANT +A new version of this library is under development at https://github.com/tecnickcom/tc-lib-pdf and as a consequence this version will not receive any additional development or support. +This version should be considered obsolete, new projects should use the new version as soon it will become stable. + + + +## Description + +PHP library for generating PDF documents on-the-fly. + +### Main Features: +* no external libraries are required for the basic functions; +* all standard page formats, custom page formats, custom margins and units of measure; +* UTF-8 Unicode and Right-To-Left languages; +* TrueTypeUnicode, OpenTypeUnicode v1, TrueType, OpenType v1, Type1 and CID-0 fonts; +* font subsetting; +* methods to publish some XHTML + CSS code, Javascript and Forms; +* images, graphic (geometric figures) and transformation methods; +* supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/script/formats.php) +* 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417; +* JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies; +* automatic page header and footer management; +* document encryption up to 256 bit and digital signature certifications; +* transactions to UNDO commands; +* PDF annotations, including links, text and file attachments; +* text rendering modes (fill, stroke and clipping); +* multiple columns mode; +* no-write page regions; +* bookmarks, named destinations and table of content; +* text hyphenation; +* text stretching and spacing (tracking); +* automatic page break, line break and text alignments including justification; +* automatic page numbering and page groups; +* move and delete pages; +* page compression (requires php-zlib extension); +* XOBject Templates; +* Layers and object visibility. +* PDF/A-1b support. + +### Third party fonts: + +This library may include third party font files released with different licenses. + +All the PHP files on the fonts directory are subject to the general TCPDF license (GNU-LGPLv3), +they do not contain any binary data but just a description of the general properties of a particular font. +These files can be also generated on the fly using the font utilities and TCPDF methods. + +All the original binary TTF font files have been renamed for compatibility with TCPDF and compressed using the gzcompress PHP function that uses the ZLIB data format (.z files). + +The binary files (.z) that begins with the prefix "free" have been extracted from the GNU FreeFont collection (GNU-GPLv3). +The binary files (.z) that begins with the prefix "pdfa" have been derived from the GNU FreeFont, so they are subject to the same license. +For the details of Copyright, License and other information, please check the files inside the directory fonts/freefont-20120503 +Link : http://www.gnu.org/software/freefont/ + +The binary files (.z) that begins with the prefix "dejavu" have been extracted from the DejaVu fonts 2.33 (Bitstream) collection. +For the details of Copyright, License and other information, please check the files inside the directory fonts/dejavu-fonts-ttf-2.33 +Link : http://dejavu-fonts.org + +The binary files (.z) that begins with the prefix "ae" have been extracted from the Arabeyes.org collection (GNU-GPLv2). +Link : http://projects.arabeyes.org/ + +### ICC profile: + +TCPDF includes the sRGB.icc profile from the icc-profiles-free Debian package: +https://packages.debian.org/source/stable/icc-profiles-free + + +## Developer(s) Contact + +* Nicola Asuni diff --git a/htdocs/includes/tecnickcom/tcpdf/composer.json b/htdocs/includes/tecnickcom/tcpdf/composer.json index beab50943a6..83ffd67b8b6 100644 --- a/htdocs/includes/tecnickcom/tcpdf/composer.json +++ b/htdocs/includes/tecnickcom/tcpdf/composer.json @@ -1,40 +1,47 @@ { - "name": "tecnickcom/tcpdf", - "version": "6.2.12", - "homepage": "http://www.tcpdf.org/", - "type": "library", - "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", - "keywords": ["PDF","tcpdf","PDFD32000-2008","qrcode","datamatrix","pdf417","barcodes"], - "license": "LGPLv3", - "authors": [ - { - "name": "Nicola Asuni", - "email": "info@tecnick.com", - "homepage": "http://nicolaasuni.tecnick.com" - } - ], - "require": { - "php": ">=5.3.0" - }, - "autoload": { - "classmap": [ - "fonts", - "config", - "include", - "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", - "tcpdf_barcodes_1d.php", - "tcpdf_barcodes_2d.php", - "include/tcpdf_colors.php", - "include/tcpdf_filters.php", - "include/tcpdf_font_data.php", - "include/tcpdf_fonts.php", - "include/tcpdf_images.php", - "include/tcpdf_static.php", - "include/barcodes/datamatrix.php", - "include/barcodes/pdf417.php", - "include/barcodes/qrcode.php" - ] - } + "name": "tecnickcom/tcpdf", + "version": "6.2.17", + "homepage": "http://www.tcpdf.org/", + "type": "library", + "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", + "keywords": [ + "PDF", + "tcpdf", + "PDFD32000-2008", + "qrcode", + "datamatrix", + "pdf417", + "barcodes" + ], + "license": "LGPL-3.0", + "authors": [ + { + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "role": "lead" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "classmap": [ + "config", + "include", + "tcpdf.php", + "tcpdf_parser.php", + "tcpdf_import.php", + "tcpdf_barcodes_1d.php", + "tcpdf_barcodes_2d.php", + "include/tcpdf_colors.php", + "include/tcpdf_filters.php", + "include/tcpdf_font_data.php", + "include/tcpdf_fonts.php", + "include/tcpdf_images.php", + "include/tcpdf_static.php", + "include/barcodes/datamatrix.php", + "include/barcodes/pdf417.php", + "include/barcodes/qrcode.php" + ] + } } diff --git a/htdocs/includes/tecnickcom/tcpdf/fonts/dejavu-fonts-ttf-2.33/status.txt b/htdocs/includes/tecnickcom/tcpdf/fonts/dejavu-fonts-ttf-2.33/status.txt index e860ef48db6..dd6765ab1a8 100644 --- a/htdocs/includes/tecnickcom/tcpdf/fonts/dejavu-fonts-ttf-2.33/status.txt +++ b/htdocs/includes/tecnickcom/tcpdf/fonts/dejavu-fonts-ttf-2.33/status.txt @@ -1,6657 +1 @@ -This is the status file for DejaVu fonts -($Id: status.txt 2425 2010-08-22 16:07:31Z moyogo $) - -original = present in original Bitstream Vera 1.10 - = added in DejaVu fonts - -U+0020 space original -U+0021 exclam original -U+0022 quotedbl original -U+0023 numbersign original -U+0024 dollar original -U+0025 percent original -U+0026 ampersand original -U+0027 quotesingle original -U+0028 parenleft original -U+0029 parenright original -U+002a asterisk original -U+002b plus original -U+002c comma original -U+002d hyphen original -U+002e period original -U+002f slash original -U+0030 zero original -U+0031 one original -U+0032 two original -U+0033 three original -U+0034 four original -U+0035 five original -U+0036 six original -U+0037 seven original -U+0038 eight original -U+0039 nine original -U+003a colon original -U+003b semicolon original -U+003c less original -U+003d equal original -U+003e greater original -U+003f question original -U+0040 at original -U+0041 A original -U+0042 B original -U+0043 C original -U+0044 D original -U+0045 E original -U+0046 F original -U+0047 G original -U+0048 H original -U+0049 I original -U+004a J original -U+004b K original -U+004c L original -U+004d M original -U+004e N original -U+004f O original -U+0050 P original -U+0051 Q original -U+0052 R original -U+0053 S original -U+0054 T original -U+0055 U original -U+0056 V original -U+0057 W original -U+0058 X original -U+0059 Y original -U+005a Z original -U+005b bracketleft original -U+005c backslash original -U+005d bracketright original -U+005e asciicircum original -U+005f underscore original -U+0060 grave original -U+0061 a original -U+0062 b original -U+0063 c original -U+0064 d original -U+0065 e original -U+0066 f original -U+0067 g original -U+0068 h original -U+0069 i original -U+006a j original -U+006b k original -U+006c l original -U+006d m original -U+006e n original -U+006f o original -U+0070 p original -U+0071 q original -U+0072 r original -U+0073 s original -U+0074 t original -U+0075 u original -U+0076 v original -U+0077 w original -U+0078 x original -U+0079 y original -U+007a z original -U+007b braceleft original -U+007c bar original -U+007d braceright original -U+007e asciitilde original -U+00a0 nonbreakingspace original -U+00a1 exclamdown original -U+00a2 cent original -U+00a3 sterling original -U+00a4 currency original -U+00a5 yen original -U+00a6 brokenbar original -U+00a7 section original -U+00a8 dieresis original -U+00a9 copyright original -U+00aa ordfeminine original -U+00ab guillemotleft original -U+00ac logicalnot original -U+00ad sfthyphen original -U+00ae registered original -U+00af macron original -U+00b0 degree original -U+00b1 plusminus original -U+00b2 twosuperior original -U+00b3 threesuperior original -U+00b4 acute original -U+00b5 mu original -U+00b6 paragraph original -U+00b7 periodcentered original -U+00b8 cedilla original -U+00b9 onesuperior original -U+00ba ordmasculine original -U+00bb guillemotright original -U+00bc onequarter original -U+00bd onehalf original -U+00be threequarters original -U+00bf questiondown original -U+00c0 Agrave original -U+00c1 Aacute original -U+00c2 Acircumflex original -U+00c3 Atilde original -U+00c4 Adieresis original -U+00c5 Aring original -U+00c6 AE original -U+00c7 Ccedilla original -U+00c8 Egrave original -U+00c9 Eacute original -U+00ca Ecircumflex original -U+00cb Edieresis original -U+00cc Igrave original -U+00cd Iacute original -U+00ce Icircumflex original -U+00cf Idieresis original -U+00d0 Eth original -U+00d1 Ntilde original -U+00d2 Ograve original -U+00d3 Oacute original -U+00d4 Ocircumflex original -U+00d5 Otilde original -U+00d6 Odieresis original -U+00d7 multiply original -U+00d8 Oslash original -U+00d9 Ugrave original -U+00da Uacute original -U+00db Ucircumflex original -U+00dc Udieresis original -U+00dd Yacute original -U+00de Thorn original -U+00df germandbls original -U+00e0 agrave original -U+00e1 aacute original -U+00e2 acircumflex original -U+00e3 atilde original -U+00e4 adieresis original -U+00e5 aring original -U+00e6 ae original -U+00e7 ccedilla original -U+00e8 egrave original -U+00e9 eacute original -U+00ea ecircumflex original -U+00eb edieresis original -U+00ec igrave original -U+00ed iacute original -U+00ee icircumflex original -U+00ef idieresis original -U+00f0 eth original -U+00f1 ntilde original -U+00f2 ograve original -U+00f3 oacute original -U+00f4 ocircumflex original -U+00f5 otilde original -U+00f6 odieresis original -U+00f7 divide original -U+00f8 oslash original -U+00f9 ugrave original -U+00fa uacute original -U+00fb ucircumflex original -U+00fc udieresis original -U+00fd yacute original -U+00fe thorn original -U+00ff ydieresis original -U+0100 Amacron 1.5 -U+0101 amacron 1.5 -U+0102 Abreve 1.5 -U+0103 abreve 1.5 -U+0104 Aogonek 1.4 -U+0105 aogonek 1.4 -U+0106 Cacute original -U+0107 cacute original -U+0108 Ccircumflex 1.5 -U+0109 ccircumflex 1.5 -U+010a Cdotaccent 1.5 -U+010b cdotaccent 1.5 -U+010c Ccaron original -U+010d ccaron original -U+010e Dcaron 1.0 -U+010f dcaron 1.0 -U+0110 Dcroat 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0111 dcroat original -U+0112 Emacron 1.5 -U+0113 emacron 1.5 -U+0114 Ebreve 1.5 -U+0115 ebreve 1.5 -U+0116 Edotaccent 1.5 -U+0117 edotaccent 1.5 -U+0118 Eogonek 1.4 -U+0119 eogonek 1.4 -U+011a Ecaron 1.0 -U+011b ecaron 1.0 -U+011c Gcircumflex 1.5 -U+011d gcircumflex 1.5 -U+011e Gbreve original -U+011f gbreve original -U+0120 Gdotaccent 1.5 -U+0121 gdotaccent 1.5 -U+0122 Gcommaaccent 1.11 -U+0123 gcommaaccent 1.11 -U+0124 Hcircumflex 1.5 -U+0125 hcircumflex 1.5 -U+0126 Hbar 1.12 -U+0127 hbar 1.12 -U+0128 Itilde 1.5 -U+0129 itilde 1.5 -U+012a Imacron 1.5 -U+012b imacron 1.5 -U+012c Ibreve 1.5 -U+012d ibreve 1.5 -U+012e Iogonek 1.11 -U+012f iogonek 1.11 -U+0130 Idotaccent original -U+0131 dotlessi original -U+0132 IJ 1.11 -U+0133 ij 1.11 -U+0134 Jcircumflex 1.5 -U+0135 jcircumflex 1.5 -U+0136 Kcommaaccent 1.11 -U+0137 kcommaaccent 1.11 -U+0138 kgreenlandic 1.12 -U+0139 Lacute 1.1 -U+013a lacute 1.1 -U+013b Lcommaaccent 1.11 -U+013c lcommaaccent 1.11 -U+013d Lcaron 1.1 -U+013e lcaron 1.1 -U+013f Ldot 1.2 -U+0140 ldot 1.2 -U+0141 Lslash original -U+0142 lslash original -U+0143 Nacute 1.4 -U+0144 nacute 1.4 -U+0145 Ncommaaccent 1.11 -U+0146 ncommaaccent 1.11 -U+0147 Ncaron 1.0 -U+0148 ncaron 1.0 -U+0149 napostrophe 1.12 -U+014a Eng 1.12 -U+014b eng 1.12 -U+014c Omacron 1.5 -U+014d omacron 1.5 -U+014e Obreve 1.5 -U+014f obreve 1.5 -U+0150 Ohungarumlaut 1.5 -U+0151 ohungarumlaut 1.5 -U+0152 OE original -U+0153 oe original -U+0154 Racute 1.1 -U+0155 racute 1.1 -U+0156 Rcommaaccent 1.11 -U+0157 rcommaaccent 1.11 -U+0158 Rcaron 1.0 -U+0159 rcaron 1.0 -U+015a Sacute 1.4 -U+015b sacute 1.4 -U+015c Scircumflex 1.5 -U+015d scircumflex 1.5 -U+015e Scedilla original -U+015f scedilla original -U+0160 Scaron original -U+0161 scaron original -U+0162 Tcommaaccent 1.5 -U+0163 tcommaaccent 1.5 -U+0164 Tcaron 1.0 -U+0165 tcaron 1.0 -U+0166 Tbar 1.12 -U+0167 tbar 1.12 -U+0168 Utilde 1.5 -U+0169 utilde 1.5 -U+016a Umacron 1.5 -U+016b umacron 1.5 -U+016c Ubreve 1.5 -U+016d ubreve 1.5 -U+016e Uring 1.0 -U+016f uring 1.0 -U+0170 Uhungarumlaut 1.5 -U+0171 uhungarumlaut 1.5 -U+0172 Uogonek 1.11 -U+0173 uogonek 1.11 -U+0174 Wcircumflex 1.2 -U+0175 wcircumflex 1.2 -U+0176 Ycircumflex 1.2 -U+0177 ycircumflex 1.2 -U+0178 Ydieresis original -U+0179 Zacute 1.4 -U+017a zacute 1.4 -U+017b Zdotaccent 1.4 -U+017c zdotaccent 1.4 -U+017d Zcaron original -U+017e zcaron original -U+017f longs 1.12 -U+0180 uni0180 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.12 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0181 uni0181 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0182 uni0182 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0183 uni0183 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0184 uni0184 2.3 -U+0185 uni0185 2.3 -U+0186 uni0186 1.15 -U+0187 uni0187 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0188 uni0188 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0189 uni0189 2.1 -U+018a uni018A 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+018b uni018B 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+018c uni018C 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+018d uni018D 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+018e uni018E 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+018f uni018F 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0190 uni0190 1.15 -U+0191 uni0191 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0192 florin original -U+0193 uni0193 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0194 uni0194 1.14 -U+0195 uni0195 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.6 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0196 uni0196 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0197 uni0197 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0198 uni0198 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0199 uni0199 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+019a uni019A 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+019b uni019B 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+019c uni019C 2.3 -U+019d uni019D 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+019e uni019E 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+019f uni019F 2.3 -U+01a0 Ohorn 2.3 -U+01a1 ohorn 2.3 -U+01a2 uni01A2 2.3 -U+01a3 uni01A3 2.3 -U+01a4 uni01A4 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01a5 uni01A5 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01a6 uni01A6 2.3 -U+01a7 uni01A7 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01a8 uni01A8 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01a9 uni01A9 2.2 -U+01aa uni01AA 2.3 -U+01ab uni01AB 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01ac uni01AC 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01ad uni01AD 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01ae uni01AE 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01af Uhorn 2.3 -U+01b0 uhorn 2.3 -U+01b1 uni01B1 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01b2 uni01B2 2.3 -U+01b3 uni01B3 2.3 -U+01b4 uni01B4 2.3 -U+01b5 uni01B5 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01b6 uni01B6 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01b7 uni01B7 2.3 -U+01b8 uni01B8 2.3 -U+01b9 uni01B9 2.3 -U+01ba uni01BA 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+01bb uni01BB 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01bc uni01BC 2.3 -U+01bd uni01BD 2.3 -U+01be uni01BE 2.3 -U+01bf uni01BF 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+01c0 uni01C0 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01c1 uni01C1 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01c2 uni01C2 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+01c3 uni01C3 2.2 -U+01c4 uni01C4 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01c5 uni01C5 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01c6 uni01C6 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01c7 uni01C7 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01c8 uni01C8 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01c9 uni01C9 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01ca uni01CA 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01cb uni01CB 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01cc uni01CC 1.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01cd uni01CD 1.15 -U+01ce uni01CE 1.15 -U+01cf uni01CF 1.15 -U+01d0 uni01D0 1.15 -U+01d1 uni01D1 1.15 -U+01d2 uni01D2 1.15 -U+01d3 uni01D3 1.15 -U+01d4 uni01D4 1.15 -U+01d5 uni01D5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01d6 uni01D6 1.13 -U+01d7 uni01D7 2.3 -U+01d8 uni01D8 2.3 -U+01d9 uni01D9 2.3 -U+01da uni01DA 2.3 -U+01db uni01DB 2.3 -U+01dc uni01DC 2.3 -U+01dd uni01DD 2.2 -U+01de uni01DE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique) 2.23 (Serif Italic Condensed) -U+01df uni01DF 1.13 -U+01e0 uni01E0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01e1 uni01E1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01e2 uni01E2 1.5 -U+01e3 uni01E3 1.5 -U+01e4 uni01E4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+01e5 uni01E5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+01e6 Gcaron 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01e7 gcaron 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01e8 uni01E8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01e9 uni01E9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01ea uni01EA 1.9 -U+01eb uni01EB 1.9 -U+01ec uni01EC 1.9 -U+01ed uni01ED 1.9 -U+01ee uni01EE 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01ef uni01EF 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01f0 uni01F0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono) 2.22 (Sans Mono Bold) 2.23 (Serif Italic Condensed) -U+01f1 uni01F1 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01f2 uni01F2 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01f3 uni01F3 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01f4 uni01F4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01f5 uni01F5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01f6 uni01F6 2.3 -U+01f7 uni01F7 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+01f8 uni01F8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01f9 uni01F9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01fa Aringacute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01fb aringacute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+01fc AEacute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01fd aeacute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01fe Oslashacute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+01ff oslashacute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0200 uni0200 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0201 uni0201 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0202 uni0202 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0203 uni0203 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0204 uni0204 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0205 uni0205 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0206 uni0206 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0207 uni0207 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0208 uni0208 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0209 uni0209 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+020a uni020A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+020b uni020B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+020c uni020C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+020d uni020D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+020e uni020E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+020f uni020F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0210 uni0210 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0211 uni0211 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0212 uni0212 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0213 uni0213 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0214 uni0214 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0215 uni0215 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0216 uni0216 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0217 uni0217 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0218 Scommaaccent 1.5 -U+0219 scommaaccent 1.5 -U+021a uni021A 1.5 -U+021b uni021B 1.5 -U+021c uni021C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+021d uni021D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+021e uni021E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+021f uni021F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0220 uni0220 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.16 (Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.17 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.18 (Sans Mono, Sans Mono Bold) 2.23 (Serif Italic Condensed) -U+0221 uni0221 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0222 uni0222 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0223 uni0223 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0224 uni0224 2.3 -U+0225 uni0225 2.3 -U+0226 uni0226 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0227 uni0227 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0228 uni0228 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0229 uni0229 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+022a uni022A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+022b uni022B 1.13 -U+022c uni022C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+022d uni022D 1.13 -U+022e uni022E 1.10 -U+022f uni022F 1.10 -U+0230 uni0230 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0231 uni0231 1.13 -U+0232 uni0232 1.5 -U+0233 uni0233 1.5 -U+0234 uni0234 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0235 uni0235 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0236 uni0236 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0237 dotlessj 1.5 -U+0238 uni0238 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0239 uni0239 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+023a uni023A 2.3 -U+023b uni023B 2.3 -U+023c uni023C 2.3 -U+023d uni023D 2.3 -U+023e uni023E 2.3 -U+023f uni023F 2.3 -U+0240 uni0240 2.3 -U+0241 uni0241 2.3 -U+0242 uni0242 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+0243 uni0243 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0244 uni0244 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0245 uni0245 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0246 uni0246 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0247 uni0247 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0248 uni0248 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0249 uni0249 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+024a uni024A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+024b uni024B 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.26 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+024c uni024C 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+024d uni024D 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+024e uni024E 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+024f uni024F 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0250 uni0250 1.14 -U+0251 uni0251 1.14 -U+0252 uni0252 1.14 -U+0253 uni0253 1.14 -U+0254 uni0254 1.14 -U+0255 uni0255 1.14 -U+0256 uni0256 1.14 -U+0257 uni0257 1.14 -U+0258 uni0258 1.14 -U+0259 uni0259 1.14 -U+025a uni025A 1.14 -U+025b uni025B 1.14 -U+025c uni025C 1.14 -U+025d uni025D 1.14 -U+025e uni025E 1.14 -U+025f uni025F 1.14 -U+0260 uni0260 1.14 -U+0261 uni0261 1.14 -U+0262 uni0262 1.14 -U+0263 uni0263 1.14 -U+0264 uni0264 1.14 -U+0265 uni0265 1.14 -U+0266 uni0266 1.14 -U+0267 uni0267 1.14 -U+0268 uni0268 1.14 -U+0269 uni0269 1.14 -U+026a uni026A 1.14 -U+026b uni026B 1.14 -U+026c uni026C 1.14 -U+026d uni026D 1.14 -U+026e uni026E 1.14 -U+026f uni026F 1.14 -U+0270 uni0270 1.14 -U+0271 uni0271 1.14 -U+0272 uni0272 1.14 -U+0273 uni0273 1.14 -U+0274 uni0274 1.14 -U+0275 uni0275 1.14 -U+0276 uni0276 1.14 -U+0277 uni0277 1.14 -U+0278 uni0278 1.14 -U+0279 uni0279 1.14 -U+027a uni027A 1.14 -U+027b uni027B 1.14 -U+027c uni027C 1.14 -U+027d uni027D 1.14 -U+027e uni027E 1.14 -U+027f uni027F 1.14 -U+0280 uni0280 1.14 -U+0281 uni0281 1.14 -U+0282 uni0282 1.14 -U+0283 uni0283 1.14 -U+0284 uni0284 1.14 -U+0285 uni0285 1.14 -U+0286 uni0286 1.14 -U+0287 uni0287 1.14 -U+0288 uni0288 1.14 -U+0289 uni0289 1.14 -U+028a uni028A 1.14 -U+028b uni028B 1.14 -U+028c uni028C 1.14 -U+028d uni028D 1.14 -U+028e uni028E 1.14 -U+028f uni028F 1.14 -U+0290 uni0290 1.14 -U+0291 uni0291 1.14 -U+0292 uni0292 1.14 -U+0293 uni0293 1.14 -U+0294 uni0294 1.14 -U+0295 uni0295 1.14 -U+0296 uni0296 1.14 -U+0297 uni0297 1.14 -U+0298 uni0298 1.14 -U+0299 uni0299 1.14 -U+029a uni029A 1.14 -U+029b uni029B 1.14 -U+029c uni029C 1.14 -U+029d uni029D 1.14 -U+029e uni029E 1.14 -U+029f uni029F 1.14 -U+02a0 uni02A0 1.14 -U+02a1 uni02A1 1.14 -U+02a2 uni02A2 1.14 -U+02a3 uni02A3 1.14 -U+02a4 uni02A4 1.14 -U+02a5 uni02A5 1.14 -U+02a6 uni02A6 1.14 -U+02a7 uni02A7 1.14 -U+02a8 uni02A8 1.14 -U+02a9 uni02A9 1.14 -U+02aa uni02AA 1.14 -U+02ab uni02AB 1.14 -U+02ac uni02AC 1.14 -U+02ad uni02AD 1.14 -U+02ae uni02AE 1.14 -U+02af uni02AF 1.14 -U+02b0 uni02B0 1.14 -U+02b1 uni02B1 1.14 -U+02b2 uni02B2 1.14 -U+02b3 uni02B3 1.14 -U+02b4 uni02B4 1.14 -U+02b5 uni02B5 1.14 -U+02b6 uni02B6 1.14 -U+02b7 uni02B7 1.14 -U+02b8 uni02B8 1.14 -U+02b9 uni02B9 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+02ba uni02BA 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02bb uni02BB 1.5 -U+02bc uni02BC 1.12 -U+02bd uni02BD 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+02be uni02BE 2.2 -U+02bf uni02BF 2.2 -U+02c0 uni02C0 1.14 -U+02c1 uni02C1 1.14 -U+02c2 uni02C2 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02c3 uni02C3 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02c4 uni02C4 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02c5 uni02C5 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02c6 circumflex original -U+02c7 caron original -U+02c8 uni02C8 2.0 -U+02c9 uni02C9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+02ca uni02CA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+02cb uni02CB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+02cc uni02CC 2.0 -U+02cd uni02CD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+02ce uni02CE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+02cf uni02CF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+02d0 uni02D0 1.14 -U+02d1 uni02D1 1.14 -U+02d2 uni02D2 2.0 -U+02d3 uni02D3 2.2 -U+02d4 uni02D4 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02d5 uni02D5 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02d6 uni02D6 2.0 -U+02d7 uni02D7 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+02d8 breve original -U+02d9 dotaccent original -U+02da ring original -U+02db ogonek original -U+02dc tilde original -U+02dd hungarumlaut original -U+02de uni02DE 2.0 -U+02df uni02DF 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02e0 uni02E0 1.14 -U+02e1 uni02E1 1.14 -U+02e2 uni02E2 1.14 -U+02e3 uni02E3 1.14 -U+02e4 uni02E4 1.14 -U+02e5 uni02E5 2.0 -U+02e6 uni02E6 2.0 -U+02e7 uni02E7 2.0 -U+02e8 uni02E8 2.0 -U+02e9 uni02E9 2.0 -U+02ec uni02EC 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02ed uni02ED 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+02ee uni02EE 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+02f3 uni02F3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+02f7 uni02F7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+0300 gravecomb 1.15 -U+0301 acutecomb 1.15 -U+0302 uni0302 1.15 -U+0303 tildecomb 1.15 -U+0304 uni0304 1.15 -U+0305 uni0305 2.0 -U+0306 uni0306 1.15 -U+0307 uni0307 1.15 -U+0308 uni0308 1.15 -U+0309 hookabovecomb 2.1 -U+030a uni030A 1.15 -U+030b uni030B 1.15 -U+030c uni030C 1.15 -U+030d uni030D 2.0 -U+030e uni030E 2.0 -U+030f uni030F 2.0 -U+0310 uni0310 2.0 -U+0311 uni0311 2.0 -U+0312 uni0312 1.11 -U+0313 uni0313 2.1 -U+0314 uni0314 2.1 -U+0315 uni0315 2.0 -U+0316 uni0316 2.0 -U+0317 uni0317 2.0 -U+0318 uni0318 2.0 -U+0319 uni0319 2.0 -U+031a uni031A 2.1 -U+031b uni031B 2.1 -U+031c uni031C 2.0 -U+031d uni031D 2.0 -U+031e uni031E 2.0 -U+031f uni031F 2.0 -U+0320 uni0320 2.0 -U+0321 uni0321 1.15 -U+0322 uni0322 1.15 -U+0323 dotbelowcomb 2.1 -U+0324 uni0324 2.0 -U+0325 uni0325 2.0 -U+0326 uni0326 1.5 -U+0327 uni0327 2.1 -U+0328 uni0328 2.1 -U+0329 uni0329 2.0 -U+032a uni032A 2.0 -U+032b uni032B 2.1 -U+032c uni032C 2.0 -U+032d uni032D 2.0 -U+032e uni032E 2.0 -U+032f uni032F 2.0 -U+0330 uni0330 2.0 -U+0331 uni0331 2.0 -U+0332 uni0332 2.0 -U+0333 uni0333 2.1 -U+0334 uni0334 2.3 -U+0335 uni0335 2.3 -U+0336 uni0336 2.3 -U+0337 uni0337 2.3 -U+0338 uni0338 2.3 -U+0339 uni0339 2.0 -U+033a uni033A 2.0 -U+033b uni033B 2.0 -U+033c uni033C 2.1 -U+033d uni033D 2.0 -U+033e uni033E 2.1 -U+033f uni033F 2.1 -U+0340 uni0340 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0341 uni0341 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0342 uni0342 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0343 uni0343 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0344 uni0344 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0345 uni0345 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0346 uni0346 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0347 uni0347 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0348 uni0348 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0349 uni0349 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+034a uni034A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+034b uni034B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+034c uni034C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+034d uni034D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+034e uni034E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+034f uni034F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0351 uni0351 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0352 uni0352 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique) 2.28 (Sans Condensed Oblique, Sans Oblique) -U+0353 uni0353 2.5 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0357 uni0357 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0358 uni0358 2.3 -U+035a uni035A 2.28 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+035c uni035C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+035d uni035D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+035e uni035E 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+035f uni035F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0360 uni0360 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0361 uni0361 2.0 -U+0362 uni0362 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0370 uni0370 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0371 uni0371 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0372 uni0372 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0373 uni0373 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0374 uni0374 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0375 uni0375 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0376 uni0376 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0377 uni0377 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+037a uni037A 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+037b uni037B 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+037c uni037C 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+037d uni037D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+037e uni037E 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0384 tonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0385 dieresistonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0386 Alphatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0387 anoteleia 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0388 Epsilontonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0389 Etatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+038a Iotatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+038c Omicrontonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+038e Upsilontonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+038f Omegatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0390 iotadieresistonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0391 Alpha 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0392 Beta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0393 Gamma 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0394 uni0394 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0395 Epsilon 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0396 Zeta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0397 Eta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0398 Theta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0399 Iota 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+039a Kappa 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+039b Lambda 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+039c Mu 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+039d Nu 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+039e Xi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+039f Omicron 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a0 Pi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a1 Rho 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a3 Sigma 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a4 Tau 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a5 Upsilon 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a6 Phi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a7 Chi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a8 Psi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03a9 Omega original -U+03aa Iotadieresis 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ab Upsilondieresis 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.1 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ac alphatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ad epsilontonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ae etatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03af iotatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b0 upsilondieresistonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b1 alpha 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b2 beta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b3 gamma 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b4 delta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b5 epsilon 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b6 zeta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b7 eta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b8 theta 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03b9 iota 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ba kappa 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03bb lambda 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03bc uni03BC 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03bd nu 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03be xi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03bf omicron 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c0 pi original -U+03c1 rho 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c2 sigma1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c3 sigma 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c4 tau 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c5 upsilon 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c6 phi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c7 chi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c8 psi 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03c9 omega 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ca iotadieresis 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03cb upsilondieresis 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03cc omicrontonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03cd upsilontonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ce omegatonos 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 1.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.2 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03cf uni03CF 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+03d0 uni03D0 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03d1 theta1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03d2 Upsilon1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03d3 uni03D3 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03d4 uni03D4 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03d5 phi1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.18 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03d6 omega1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+03d7 uni03D7 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03d8 uni03D8 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+03d9 uni03D9 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+03da uni03DA 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03db uni03DB 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03dc uni03DC 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03dd uni03DD 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03de uni03DE 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03df uni03DF 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03e0 uni03E0 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03e1 uni03E1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03e2 uni03E2 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e3 uni03E3 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e4 uni03E4 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e5 uni03E5 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e6 uni03E6 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e7 uni03E7 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e8 uni03E8 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03e9 uni03E9 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03ea uni03EA 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03eb uni03EB 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03ec uni03EC 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03ed uni03ED 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03ee uni03EE 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03ef uni03EF 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+03f0 uni03F0 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03f1 uni03F1 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+03f2 uni03F2 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f3 uni03F3 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f4 uni03F4 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f5 uni03F5 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f6 uni03F6 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f7 uni03F7 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f8 uni03F8 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03f9 uni03F9 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03fa uni03FA 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03fb uni03FB 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+03fc uni03FC 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+03fd uni03FD 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03fe uni03FE 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+03ff uni03FF 1.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.0 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0400 uni0400 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0401 uni0401 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0402 uni0402 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0403 uni0403 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0404 uni0404 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0405 uni0405 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0406 uni0406 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0407 uni0407 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0408 uni0408 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0409 uni0409 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+040a uni040A 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+040b uni040B 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+040c uni040C 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+040d uni040D 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+040e uni040E 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+040f uni040F 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0410 uni0410 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0411 uni0411 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0412 uni0412 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0413 uni0413 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0414 uni0414 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0415 uni0415 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0416 uni0416 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0417 uni0417 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0418 uni0418 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0419 uni0419 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+041a uni041A 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+041b uni041B 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+041c uni041C 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+041d uni041D 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+041e uni041E 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+041f uni041F 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0420 uni0420 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0421 uni0421 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0422 uni0422 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0423 uni0423 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0424 uni0424 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0425 uni0425 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0426 uni0426 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0427 uni0427 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0428 uni0428 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0429 uni0429 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+042a uni042A 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+042b uni042B 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+042c uni042C 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+042d uni042D 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+042e uni042E 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+042f uni042F 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0430 uni0430 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0431 uni0431 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0432 uni0432 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0433 uni0433 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0434 uni0434 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0435 uni0435 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0436 uni0436 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0437 uni0437 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0438 uni0438 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0439 uni0439 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+043a uni043A 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+043b uni043B 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+043c uni043C 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+043d uni043D 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+043e uni043E 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+043f uni043F 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0440 uni0440 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0441 uni0441 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0442 uni0442 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0443 uni0443 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0444 uni0444 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0445 uni0445 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0446 uni0446 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0447 uni0447 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0448 uni0448 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0449 uni0449 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+044a uni044A 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+044b uni044B 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+044c uni044C 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+044d uni044D 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+044e uni044E 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+044f uni044F 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0450 uni0450 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0451 uni0451 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0452 uni0452 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0453 uni0453 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0454 uni0454 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0455 uni0455 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0456 uni0456 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0457 uni0457 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0458 uni0458 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0459 uni0459 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+045a uni045A 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+045b uni045B 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+045c uni045C 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+045d uni045D 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+045e uni045E 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+045f uni045F 1.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 1.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold) 1.6 (Serif Bold Italic, Serif Italic) 1.7 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+0460 uni0460 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0461 uni0461 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) -U+0462 uni0462 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.20 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0463 uni0463 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.20 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0464 uni0464 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0465 uni0465 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0466 uni0466 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0467 uni0467 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0468 uni0468 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0469 uni0469 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+046a uni046A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.21 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+046b uni046B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.21 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+046c uni046C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+046d uni046D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+046e uni046E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+046f uni046F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0470 uni0470 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0471 uni0471 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0472 uni0472 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0473 uni0473 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0474 uni0474 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.12 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0475 uni0475 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.12 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0476 uni0476 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0477 uni0477 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0478 uni0478 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0479 uni0479 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+047a uni047A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+047b uni047B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+047c uni047C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+047d uni047D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+047e uni047E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+047f uni047F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0480 uni0480 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0481 uni0481 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0482 uni0482 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0483 uni0483 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0484 uni0484 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0485 uni0485 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0486 uni0486 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0487 uni0487 2.9 (Sans, Sans Condensed) 2.27 (Sans Bold, Sans Bold Oblique, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0488 uni0488 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0489 uni0489 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+048a uni048A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+048b uni048B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+048c uni048C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+048d uni048D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+048e uni048E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+048f uni048F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0490 uni0490 1.15 -U+0491 uni0491 1.15 -U+0492 uni0492 1.14 -U+0493 uni0493 1.14 -U+0494 uni0494 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0495 uni0495 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+0496 uni0496 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0497 uni0497 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.15 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0498 uni0498 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+0499 uni0499 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+049a uni049A 1.14 -U+049b uni049B 1.14 -U+049c uni049C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+049d uni049D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+049e uni049E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+049f uni049F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04a0 uni04A0 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+04a1 uni04A1 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+04a2 uni04A2 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04a3 uni04A3 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04a4 uni04A4 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+04a5 uni04A5 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+04a6 uni04A6 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04a7 uni04A7 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04a8 uni04A8 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04a9 uni04A9 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04aa uni04AA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04ab uni04AB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04ac uni04AC 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04ad uni04AD 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04ae uni04AE 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04af uni04AF 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04b0 uni04B0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+04b1 uni04B1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+04b2 uni04B2 1.14 -U+04b3 uni04B3 1.14 -U+04b4 uni04B4 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04b5 uni04B5 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04b6 uni04B6 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04b7 uni04B7 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04b8 uni04B8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04b9 uni04B9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04ba uni04BA 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04bb uni04BB 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04bc uni04BC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04bd uni04BD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04be uni04BE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04bf uni04BF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04c0 uni04C0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04c1 uni04C1 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04c2 uni04C2 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04c3 uni04C3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+04c4 uni04C4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+04c5 uni04C5 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04c6 uni04C6 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04c7 uni04C7 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+04c8 uni04C8 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+04c9 uni04C9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04ca uni04CA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04cb uni04CB 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04cc uni04CC 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04cd uni04CD 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04ce uni04CE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04cf uni04CF 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04d0 uni04D0 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d1 uni04D1 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d2 uni04D2 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d3 uni04D3 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d4 uni04D4 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d5 uni04D5 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d6 uni04D6 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d7 uni04D7 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04d8 uni04D8 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04d9 uni04D9 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04da uni04DA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04db uni04DB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04dc uni04DC 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04dd uni04DD 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04de uni04DE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04df uni04DF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04e0 uni04E0 2.3 -U+04e1 uni04E1 2.3 -U+04e2 uni04E2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04e3 uni04E3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04e4 uni04E4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04e5 uni04E5 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04e6 uni04E6 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04e7 uni04E7 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04e8 uni04E8 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+04e9 uni04E9 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+04ea uni04EA 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+04eb uni04EB 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+04ec uni04EC 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04ed uni04ED 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04ee uni04EE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04ef uni04EF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04f0 uni04F0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04f1 uni04F1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04f2 uni04F2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04f3 uni04F3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04f4 uni04F4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04f5 uni04F5 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04f6 uni04F6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04f7 uni04F7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04f8 uni04F8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+04f9 uni04F9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+04fa uni04FA 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04fb uni04FB 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04fc uni04FC 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04fd uni04FD 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+04fe uni04FE 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) -U+04ff uni04FF 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) -U+0500 uni0500 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) -U+0501 uni0501 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) -U+0502 uni0502 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0503 uni0503 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0504 uni0504 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0505 uni0505 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0506 uni0506 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0507 uni0507 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0508 uni0508 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0509 uni0509 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+050a uni050A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+050b uni050B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+050c uni050C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+050d uni050D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+050e uni050E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+050f uni050F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0510 uni0510 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0511 uni0511 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0512 uni0512 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0513 uni0513 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0514 uni0514 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0515 uni0515 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+0516 uni0516 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0517 uni0517 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0518 uni0518 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0519 uni0519 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+051a uni051A 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+051b uni051B 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+051c uni051C 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+051d uni051D 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+051e uni051E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+051f uni051F 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0520 uni0520 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0521 uni0521 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0522 uni0522 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0523 uni0523 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0524 uni0524 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0525 uni0525 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0531 uni0531 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0532 uni0532 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0533 uni0533 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0534 uni0534 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0535 uni0535 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0536 uni0536 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0537 uni0537 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0538 uni0538 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0539 uni0539 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+053a uni053A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+053b uni053B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+053c uni053C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+053d uni053D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+053e uni053E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+053f uni053F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0540 uni0540 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0541 uni0541 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0542 uni0542 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0543 uni0543 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0544 uni0544 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0545 uni0545 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0546 uni0546 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0547 uni0547 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0548 uni0548 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0549 uni0549 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+054a uni054A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+054b uni054B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+054c uni054C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+054d uni054D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+054e uni054E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+054f uni054F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0550 uni0550 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0551 uni0551 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0552 uni0552 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0553 uni0553 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0554 uni0554 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0555 uni0555 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0556 uni0556 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0559 uni0559 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+055a uni055A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+055b uni055B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+055c uni055C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+055d uni055D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+055e uni055E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+055f uni055F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0561 uni0561 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0562 uni0562 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0563 uni0563 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0564 uni0564 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0565 uni0565 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0566 uni0566 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0567 uni0567 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0568 uni0568 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0569 uni0569 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+056a uni056A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+056b uni056B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+056c uni056C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+056d uni056D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+056e uni056E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+056f uni056F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0570 uni0570 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0571 uni0571 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0572 uni0572 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0573 uni0573 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0574 uni0574 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0575 uni0575 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0576 uni0576 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0577 uni0577 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0578 uni0578 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0579 uni0579 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+057a uni057A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+057b uni057B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+057c uni057C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+057d uni057D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+057e uni057E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+057f uni057F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0580 uni0580 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0581 uni0581 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0582 uni0582 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0583 uni0583 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0584 uni0584 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0585 uni0585 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0586 uni0586 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0587 uni0587 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+0589 uni0589 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+058a uni058A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+05b0 uni05B0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b1 uni05B1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b2 uni05B2 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b3 uni05B3 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b4 uni05B4 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b5 uni05B5 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b6 uni05B6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b7 uni05B7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b8 uni05B8 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05b9 uni05B9 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05ba uni05BA 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+05bb uni05BB 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05bc uni05BC 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05bd uni05BD 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05be uni05BE 2.9 (Sans Condensed Oblique, Sans Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique) -U+05bf uni05BF 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05c0 uni05C0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05c1 uni05C1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05c2 uni05C2 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05c3 uni05C3 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05c6 uni05C6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05c7 uni05C7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d0 uni05D0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d1 uni05D1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d2 uni05D2 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d3 uni05D3 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d4 uni05D4 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d5 uni05D5 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d6 uni05D6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d7 uni05D7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d8 uni05D8 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05d9 uni05D9 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05da uni05DA 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05db uni05DB 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05dc uni05DC 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05dd uni05DD 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05de uni05DE 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05df uni05DF 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e0 uni05E0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e1 uni05E1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e2 uni05E2 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e3 uni05E3 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e4 uni05E4 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e5 uni05E5 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e6 uni05E6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e7 uni05E7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e8 uni05E8 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05e9 uni05E9 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05ea uni05EA 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05f0 uni05F0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05f1 uni05F1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05f2 uni05F2 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+05f3 uni05F3 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+05f4 uni05F4 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0606 uni0606 2.26 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold) -U+0607 uni0607 2.26 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold) -U+0609 uni0609 2.26 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold) -U+060a uni060A 2.26 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold) -U+060c uni060C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0615 uni0615 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+061b uni061B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+061f uni061F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0621 uni0621 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0622 uni0622 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0623 uni0623 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0624 uni0624 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0625 uni0625 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0626 uni0626 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0627 uni0627 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0628 uni0628 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0629 uni0629 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+062a uni062A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+062b uni062B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+062c uni062C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+062d uni062D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+062e uni062E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+062f uni062F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0630 uni0630 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0631 uni0631 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0632 uni0632 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0633 uni0633 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0634 uni0634 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0635 uni0635 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0636 uni0636 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0637 uni0637 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0638 uni0638 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0639 uni0639 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+063a uni063A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0640 uni0640 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0641 uni0641 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0642 uni0642 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0643 uni0643 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0644 uni0644 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0645 uni0645 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0646 uni0646 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0647 uni0647 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0648 uni0648 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0649 uni0649 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+064a uni064A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+064b uni064B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+064c uni064C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+064d uni064D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+064e uni064E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+064f uni064F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0650 uni0650 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0651 uni0651 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0652 uni0652 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0653 uni0653 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0654 uni0654 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0655 uni0655 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0657 uni0657 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+065a uni065A 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0660 uni0660 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0661 uni0661 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0662 uni0662 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0663 uni0663 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0664 uni0664 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0665 uni0665 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0666 uni0666 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0667 uni0667 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0668 uni0668 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0669 uni0669 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+066a uni066A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+066b uni066B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+066c uni066C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+066d uni066D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+066e uni066E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+066f uni066F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0670 uni0670 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0674 uni0674 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans ExtraLight) 2.16 (Sans Mono, Sans Mono Bold) -U+0679 uni0679 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+067a uni067A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+067b uni067B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+067c uni067C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+067d uni067D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+067e uni067E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+067f uni067F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0680 uni0680 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0681 uni0681 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0682 uni0682 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0683 uni0683 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0684 uni0684 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0685 uni0685 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0686 uni0686 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0687 uni0687 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0688 uni0688 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0689 uni0689 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+068a uni068A 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+068b uni068B 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+068c uni068C 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+068d uni068D 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+068e uni068E 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+068f uni068F 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0690 uni0690 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0691 uni0691 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0692 uni0692 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0693 uni0693 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0694 uni0694 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0695 uni0695 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0696 uni0696 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0697 uni0697 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0698 uni0698 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+0699 uni0699 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+069a uni069A 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+069b uni069B 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+069c uni069C 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+069d uni069D 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+069e uni069E 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+069f uni069F 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a0 uni06A0 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a1 uni06A1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06a2 uni06A2 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a3 uni06A3 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a4 uni06A4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06a5 uni06A5 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a6 uni06A6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a7 uni06A7 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a8 uni06A8 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06a9 uni06A9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06aa uni06AA 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06ab uni06AB 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06ac uni06AC 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06ad uni06AD 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06ae uni06AE 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06af uni06AF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06b0 uni06B0 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b1 uni06B1 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b2 uni06B2 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b3 uni06B3 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b4 uni06B4 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b5 uni06B5 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06b6 uni06B6 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b7 uni06B7 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b8 uni06B8 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06b9 uni06B9 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06ba uni06BA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06bb uni06BB 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06bc uni06BC 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06bd uni06BD 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06be uni06BE 2.16 (Sans Mono, Sans Mono Bold) 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06bf uni06BF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+06c6 uni06C6 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06cc uni06CC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06ce uni06CE 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06d5 uni06D5 2.10 (Sans, Sans Bold) 2.11 (Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f0 uni06F0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f1 uni06F1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f2 uni06F2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f3 uni06F3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f4 uni06F4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f5 uni06F5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f6 uni06F6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f7 uni06F7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f8 uni06F8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+06f9 uni06F9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+07c0 uni07C0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c1 uni07C1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c2 uni07C2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c3 uni07C3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c4 uni07C4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c5 uni07C5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c6 uni07C6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c7 uni07C7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c8 uni07C8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07c9 uni07C9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07ca uni07CA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07cb uni07CB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07cc uni07CC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07cd uni07CD 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07ce uni07CE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07cf uni07CF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d0 uni07D0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d1 uni07D1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d2 uni07D2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d3 uni07D3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d4 uni07D4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d5 uni07D5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d6 uni07D6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d7 uni07D7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d8 uni07D8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07d9 uni07D9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07da uni07DA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07db uni07DB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07dc uni07DC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07dd uni07DD 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07de uni07DE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07df uni07DF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e0 uni07E0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e1 uni07E1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e2 uni07E2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e3 uni07E3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e4 uni07E4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e5 uni07E5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e6 uni07E6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07e7 uni07E7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07eb uni07EB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07ec uni07EC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07ed uni07ED 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07ee uni07EE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07ef uni07EF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f0 uni07F0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f1 uni07F1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f2 uni07F2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f3 uni07F3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f4 uni07F4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f5 uni07F5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f8 uni07F8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07f9 uni07F9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+07fa uni07FA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+0e3f uni0E3F 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0e81 uni0E81 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e82 uni0E82 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e84 uni0E84 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e87 uni0E87 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e88 uni0E88 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e8a uni0E8A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e8d uni0E8D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e94 uni0E94 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e95 uni0E95 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e96 uni0E96 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e97 uni0E97 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e99 uni0E99 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e9a uni0E9A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e9b uni0E9B 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e9c uni0E9C 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e9d uni0E9D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e9e uni0E9E 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0e9f uni0E9F 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ea1 uni0EA1 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ea2 uni0EA2 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ea3 uni0EA3 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ea5 uni0EA5 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ea7 uni0EA7 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eaa uni0EAA 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eab uni0EAB 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ead uni0EAD 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eae uni0EAE 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eaf uni0EAF 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb0 uni0EB0 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb1 uni0EB1 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb2 uni0EB2 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb3 uni0EB3 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb4 uni0EB4 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb5 uni0EB5 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb6 uni0EB6 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb7 uni0EB7 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb8 uni0EB8 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eb9 uni0EB9 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ebb uni0EBB 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ebc uni0EBC 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ebd uni0EBD 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec0 uni0EC0 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec1 uni0EC1 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec2 uni0EC2 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec3 uni0EC3 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec4 uni0EC4 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec6 uni0EC6 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ec8 uni0EC8 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ec9 uni0EC9 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0eca uni0ECA 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ecb uni0ECB 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ecc uni0ECC 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ecd uni0ECD 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+0ed0 uni0ED0 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed1 uni0ED1 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed2 uni0ED2 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed3 uni0ED3 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed4 uni0ED4 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed5 uni0ED5 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed6 uni0ED6 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed7 uni0ED7 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0ed8 uni0ED8 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+0ed9 uni0ED9 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0edc uni0EDC 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+0edd uni0EDD 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+10a0 uni10A0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a1 uni10A1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a2 uni10A2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a3 uni10A3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a4 uni10A4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a5 uni10A5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a6 uni10A6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a7 uni10A7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a8 uni10A8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10a9 uni10A9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10aa uni10AA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10ab uni10AB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10ac uni10AC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10ad uni10AD 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10ae uni10AE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10af uni10AF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b0 uni10B0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b1 uni10B1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b2 uni10B2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b3 uni10B3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b4 uni10B4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b5 uni10B5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b6 uni10B6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b7 uni10B7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b8 uni10B8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10b9 uni10B9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10ba uni10BA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10bb uni10BB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10bc uni10BC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10bd uni10BD 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10be uni10BE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10bf uni10BF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10c0 uni10C0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10c1 uni10C1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10c2 uni10C2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10c3 uni10C3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10c4 uni10C4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10c5 uni10C5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+10d0 uni10D0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d1 uni10D1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d2 uni10D2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d3 uni10D3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d4 uni10D4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d5 uni10D5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d6 uni10D6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d7 uni10D7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d8 uni10D8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10d9 uni10D9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10da uni10DA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10db uni10DB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10dc uni10DC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10dd uni10DD 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10de uni10DE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10df uni10DF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e0 uni10E0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e1 uni10E1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e2 uni10E2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e3 uni10E3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e4 uni10E4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e5 uni10E5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e6 uni10E6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e7 uni10E7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e8 uni10E8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10e9 uni10E9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10ea uni10EA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10eb uni10EB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10ec uni10EC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10ed uni10ED 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10ee uni10EE 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10ef uni10EF 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f0 uni10F0 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f1 uni10F1 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f2 uni10F2 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f3 uni10F3 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f4 uni10F4 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f5 uni10F5 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f6 uni10F6 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f7 uni10F7 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f8 uni10F8 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10f9 uni10F9 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10fa uni10FA 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10fb uni10FB 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+10fc uni10FC 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Mono, Sans Mono Bold, Serif, Serif Bold, Serif Condensed, Serif Condensed Bold) 2.20 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+1401 uni1401 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1402 uni1402 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1403 uni1403 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1404 uni1404 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1405 uni1405 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1406 uni1406 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1407 uni1407 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1409 uni1409 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+140a uni140A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+140b uni140B 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+140c uni140C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+140d uni140D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+140e uni140E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+140f uni140F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1410 uni1410 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1411 uni1411 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1412 uni1412 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1413 uni1413 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1414 uni1414 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1415 uni1415 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1416 uni1416 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1417 uni1417 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1418 uni1418 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1419 uni1419 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+141a uni141A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+141b uni141B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+141d uni141D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+141e uni141E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+141f uni141F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1420 uni1420 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1421 uni1421 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1422 uni1422 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1423 uni1423 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1424 uni1424 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1425 uni1425 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1426 uni1426 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1427 uni1427 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1428 uni1428 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1429 uni1429 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+142a uni142A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+142b uni142B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+142c uni142C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+142d uni142D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+142e uni142E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+142f uni142F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1430 uni1430 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1431 uni1431 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1432 uni1432 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1433 uni1433 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1434 uni1434 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1435 uni1435 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1437 uni1437 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1438 uni1438 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1439 uni1439 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+143a uni143A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+143b uni143B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+143c uni143C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+143d uni143D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+143e uni143E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+143f uni143F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1440 uni1440 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1441 uni1441 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1442 uni1442 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1443 uni1443 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1444 uni1444 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1445 uni1445 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1446 uni1446 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1447 uni1447 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1448 uni1448 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1449 uni1449 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+144a uni144A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+144c uni144C 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+144d uni144D 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+144e uni144E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+144f uni144F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1450 uni1450 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1451 uni1451 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1452 uni1452 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1454 uni1454 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1455 uni1455 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1456 uni1456 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1457 uni1457 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1458 uni1458 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1459 uni1459 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+145a uni145A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+145b uni145B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+145c uni145C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+145d uni145D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+145e uni145E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+145f uni145F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1460 uni1460 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1461 uni1461 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1462 uni1462 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1463 uni1463 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1464 uni1464 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1465 uni1465 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1466 uni1466 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1467 uni1467 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1468 uni1468 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1469 uni1469 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+146a uni146A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+146b uni146B 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+146c uni146C 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+146d uni146D 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+146e uni146E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+146f uni146F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1470 uni1470 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1471 uni1471 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1472 uni1472 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1473 uni1473 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1474 uni1474 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1475 uni1475 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1476 uni1476 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1477 uni1477 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1478 uni1478 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1479 uni1479 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+147a uni147A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+147b uni147B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+147c uni147C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+147d uni147D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+147e uni147E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+147f uni147F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1480 uni1480 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1481 uni1481 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1482 uni1482 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1483 uni1483 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1484 uni1484 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1485 uni1485 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1486 uni1486 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1487 uni1487 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1488 uni1488 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1489 uni1489 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+148a uni148A 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+148b uni148B 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+148c uni148C 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+148d uni148D 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+148e uni148E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+148f uni148F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1490 uni1490 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1491 uni1491 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1492 uni1492 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1493 uni1493 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1494 uni1494 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1495 uni1495 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1496 uni1496 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1497 uni1497 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1498 uni1498 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1499 uni1499 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+149a uni149A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+149b uni149B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+149c uni149C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+149d uni149D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+149e uni149E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+149f uni149F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a0 uni14A0 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a1 uni14A1 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a2 uni14A2 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a3 uni14A3 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a4 uni14A4 2.13 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Oblique) 2.15 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+14a5 uni14A5 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a6 uni14A6 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a7 uni14A7 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a8 uni14A8 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14a9 uni14A9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14aa uni14AA 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ab uni14AB 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ac uni14AC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ad uni14AD 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ae uni14AE 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14af uni14AF 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b0 uni14B0 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b1 uni14B1 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b2 uni14B2 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b3 uni14B3 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b4 uni14B4 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b5 uni14B5 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b6 uni14B6 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b7 uni14B7 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b8 uni14B8 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14b9 uni14B9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ba uni14BA 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14bb uni14BB 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14bc uni14BC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14bd uni14BD 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c0 uni14C0 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c1 uni14C1 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c2 uni14C2 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c3 uni14C3 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c4 uni14C4 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c5 uni14C5 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c6 uni14C6 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c7 uni14C7 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c8 uni14C8 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14c9 uni14C9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ca uni14CA 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14cb uni14CB 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14cc uni14CC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14cd uni14CD 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ce uni14CE 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14cf uni14CF 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d0 uni14D0 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d1 uni14D1 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d2 uni14D2 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d3 uni14D3 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d4 uni14D4 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d5 uni14D5 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d6 uni14D6 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d7 uni14D7 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d8 uni14D8 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14d9 uni14D9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14da uni14DA 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14db uni14DB 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14dc uni14DC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14dd uni14DD 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14de uni14DE 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14df uni14DF 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e0 uni14E0 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e1 uni14E1 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e2 uni14E2 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e3 uni14E3 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e4 uni14E4 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e5 uni14E5 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e6 uni14E6 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e7 uni14E7 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e8 uni14E8 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14e9 uni14E9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ea uni14EA 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ec uni14EC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ed uni14ED 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ee uni14EE 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ef uni14EF 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f0 uni14F0 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f1 uni14F1 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f2 uni14F2 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f3 uni14F3 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f4 uni14F4 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f5 uni14F5 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f6 uni14F6 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f7 uni14F7 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f8 uni14F8 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14f9 uni14F9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14fa uni14FA 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14fb uni14FB 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14fc uni14FC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14fd uni14FD 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14fe uni14FE 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+14ff uni14FF 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1500 uni1500 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1501 uni1501 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1502 uni1502 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1503 uni1503 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1504 uni1504 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1505 uni1505 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1506 uni1506 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1507 uni1507 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1510 uni1510 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1511 uni1511 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1512 uni1512 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1513 uni1513 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1514 uni1514 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1515 uni1515 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1516 uni1516 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1517 uni1517 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1518 uni1518 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1519 uni1519 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+151a uni151A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+151b uni151B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+151c uni151C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+151d uni151D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+151e uni151E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+151f uni151F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1520 uni1520 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1521 uni1521 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1522 uni1522 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1523 uni1523 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1524 uni1524 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1525 uni1525 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1526 uni1526 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1527 uni1527 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1528 uni1528 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1529 uni1529 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+152a uni152A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+152b uni152B 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+152c uni152C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+152d uni152D 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+152e uni152E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+152f uni152F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1530 uni1530 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1531 uni1531 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1532 uni1532 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1533 uni1533 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1534 uni1534 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1535 uni1535 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1536 uni1536 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1537 uni1537 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1538 uni1538 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1539 uni1539 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+153a uni153A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+153b uni153B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+153c uni153C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+153d uni153D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+153e uni153E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1540 uni1540 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1541 uni1541 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1542 uni1542 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1543 uni1543 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1544 uni1544 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1545 uni1545 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1546 uni1546 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1547 uni1547 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1548 uni1548 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1549 uni1549 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+154a uni154A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+154b uni154B 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+154c uni154C 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+154d uni154D 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+154e uni154E 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+154f uni154F 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1550 uni1550 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1552 uni1552 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1553 uni1553 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1554 uni1554 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1555 uni1555 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1556 uni1556 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1557 uni1557 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1558 uni1558 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1559 uni1559 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+155a uni155A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+155b uni155B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+155c uni155C 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+155d uni155D 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+155e uni155E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+155f uni155F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1560 uni1560 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1561 uni1561 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1562 uni1562 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1563 uni1563 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1564 uni1564 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1565 uni1565 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1566 uni1566 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1567 uni1567 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1568 uni1568 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1569 uni1569 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+156a uni156A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1574 uni1574 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1575 uni1575 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1576 uni1576 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1577 uni1577 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1578 uni1578 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1579 uni1579 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+157a uni157A 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+157b uni157B 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+157c uni157C 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+157d uni157D 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+157e uni157E 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+157f uni157F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1580 uni1580 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1581 uni1581 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1582 uni1582 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1583 uni1583 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1584 uni1584 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1585 uni1585 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+158a uni158A 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+158b uni158B 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+158c uni158C 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+158d uni158D 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+158e uni158E 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+158f uni158F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1590 uni1590 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1591 uni1591 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1592 uni1592 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1593 uni1593 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1594 uni1594 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1595 uni1595 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1596 uni1596 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a0 uni15A0 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a1 uni15A1 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a2 uni15A2 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a3 uni15A3 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a4 uni15A4 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a5 uni15A5 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a6 uni15A6 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a7 uni15A7 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a8 uni15A8 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15a9 uni15A9 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15aa uni15AA 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15ab uni15AB 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15ac uni15AC 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15ad uni15AD 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15ae uni15AE 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15af uni15AF 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15de uni15DE 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+15e1 uni15E1 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1646 uni1646 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1647 uni1647 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+166e uni166E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+166f uni166F 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1670 uni1670 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1671 uni1671 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1672 uni1672 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1673 uni1673 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1674 uni1674 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1675 uni1675 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1676 uni1676 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1680 uni1680 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1681 uni1681 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1682 uni1682 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1683 uni1683 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1684 uni1684 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1685 uni1685 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1686 uni1686 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1687 uni1687 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1688 uni1688 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1689 uni1689 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+168a uni168A 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+168b uni168B 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+168c uni168C 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+168d uni168D 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+168e uni168E 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+168f uni168F 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1690 uni1690 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1691 uni1691 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1692 uni1692 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1693 uni1693 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1694 uni1694 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1695 uni1695 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1696 uni1696 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1697 uni1697 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1698 uni1698 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1699 uni1699 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+169a uni169A 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+169b uni169B 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+169c uni169C 2.22 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.28 (Sans ExtraLight) -U+1d00 uni1D00 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d01 uni1D01 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d02 uni1D02 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1d03 uni1D03 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d04 uni1D04 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d05 uni1D05 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d06 uni1D06 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d07 uni1D07 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d08 uni1D08 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+1d09 uni1D09 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1d0a uni1D0A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d0b uni1D0B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d0c uni1D0C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d0d uni1D0D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d0e uni1D0E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d0f uni1D0F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d10 uni1D10 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d11 uni1D11 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d12 uni1D12 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d13 uni1D13 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d14 uni1D14 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1d15 uni1D15 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d16 uni1D16 2.3 -U+1d17 uni1D17 2.3 -U+1d18 uni1D18 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d19 uni1D19 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d1a uni1D1A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d1b uni1D1B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d1c uni1D1C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d1d uni1D1D 2.3 -U+1d1e uni1D1E 2.3 -U+1d1f uni1D1F 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+1d20 uni1D20 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d21 uni1D21 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d22 uni1D22 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d23 uni1D23 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d24 uni1D24 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d25 uni1D25 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d26 uni1D26 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d27 uni1D27 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d28 uni1D28 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d29 uni1D29 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d2a uni1D2A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d2b uni1D2B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans ExtraLight) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d2c uni1D2C 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d2d uni1D2D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d2e uni1D2E 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d2f uni1D2F 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d30 uni1D30 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d31 uni1D31 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d32 uni1D32 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d33 uni1D33 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d34 uni1D34 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d35 uni1D35 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d36 uni1D36 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d37 uni1D37 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d38 uni1D38 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d39 uni1D39 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d3a uni1D3A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d3b uni1D3B 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d3c uni1D3C 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d3d uni1D3D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d3e uni1D3E 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d3f uni1D3F 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d40 uni1D40 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d41 uni1D41 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d42 uni1D42 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d43 uni1D43 2.3 -U+1d44 uni1D44 2.3 -U+1d45 uni1D45 2.3 -U+1d46 uni1D46 2.3 -U+1d47 uni1D47 2.3 -U+1d48 uni1D48 2.3 -U+1d49 uni1D49 2.3 -U+1d4a uni1D4A 2.3 -U+1d4b uni1D4B 2.3 -U+1d4c uni1D4C 2.3 -U+1d4d uni1D4D 2.3 -U+1d4e uni1D4E 2.3 -U+1d4f uni1D4F 2.3 -U+1d50 uni1D50 2.3 -U+1d51 uni1D51 2.3 -U+1d52 uni1D52 2.3 -U+1d53 uni1D53 2.3 -U+1d54 uni1D54 2.3 -U+1d55 uni1D55 2.3 -U+1d56 uni1D56 2.3 -U+1d57 uni1D57 2.3 -U+1d58 uni1D58 2.3 -U+1d59 uni1D59 2.3 -U+1d5a uni1D5A 2.3 -U+1d5b uni1D5B 2.3 -U+1d5c uni1D5C 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d5d uni1D5D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d5e uni1D5E 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d5f uni1D5F 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d60 uni1D60 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d61 uni1D61 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d62 uni1D62 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d63 uni1D63 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d64 uni1D64 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d65 uni1D65 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d66 uni1D66 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d67 uni1D67 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d68 uni1D68 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d69 uni1D69 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d6a uni1D6A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d6b uni1D6B 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1d77 uni1D77 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1d78 uni1D78 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1d7b uni1D7B 2.3 -U+1d7d uni1D7D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d85 uni1D85 2.3 -U+1d9b uni1D9B 2.3 -U+1d9c uni1D9C 2.3 -U+1d9d uni1D9D 2.3 -U+1d9e uni1D9E 2.3 -U+1d9f uni1D9F 2.3 -U+1da0 uni1DA0 2.3 -U+1da1 uni1DA1 2.3 -U+1da2 uni1DA2 2.3 -U+1da3 uni1DA3 2.3 -U+1da4 uni1DA4 2.3 -U+1da5 uni1DA5 2.3 -U+1da6 uni1DA6 2.3 -U+1da7 uni1DA7 2.3 -U+1da8 uni1DA8 2.3 -U+1da9 uni1DA9 2.3 -U+1daa uni1DAA 2.3 -U+1dab uni1DAB 2.3 -U+1dac uni1DAC 2.3 -U+1dad uni1DAD 2.3 -U+1dae uni1DAE 2.3 -U+1daf uni1DAF 2.3 -U+1db0 uni1DB0 2.3 -U+1db1 uni1DB1 2.3 -U+1db2 uni1DB2 2.3 -U+1db3 uni1DB3 2.3 -U+1db4 uni1DB4 2.3 -U+1db5 uni1DB5 2.3 -U+1db6 uni1DB6 2.3 -U+1db7 uni1DB7 2.3 -U+1db8 uni1DB8 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1db9 uni1DB9 2.3 -U+1dba uni1DBA 2.3 -U+1dbb uni1DBB 2.3 -U+1dbc uni1DBC 2.3 -U+1dbd uni1DBD 2.3 -U+1dbe uni1DBE 2.3 -U+1dbf uni1DBF 2.3 -U+1dc4 uni1DC4 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1dc5 uni1DC5 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1dc6 uni1DC6 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1dc7 uni1DC7 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1dc8 uni1DC8 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1dc9 uni1DC9 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1e00 uni1E00 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e01 uni1E01 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e02 uni1E02 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e03 uni1E03 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e04 uni1E04 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e05 uni1E05 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e06 uni1E06 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e07 uni1E07 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e08 uni1E08 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e09 uni1E09 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e0a uni1E0A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e0b uni1E0B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e0c uni1E0C 2.1 -U+1e0d uni1E0D 2.1 -U+1e0e uni1E0E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e0f uni1E0F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e10 uni1E10 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e11 uni1E11 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e12 uni1E12 1.13 -U+1e13 uni1E13 1.13 -U+1e14 uni1E14 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e15 uni1E15 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e16 uni1E16 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e17 uni1E17 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e18 uni1E18 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e19 uni1E19 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e1a uni1E1A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e1b uni1E1B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e1c uni1E1C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.17 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1e1d uni1E1D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.17 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+1e1e uni1E1E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e1f uni1E1F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e20 uni1E20 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e21 uni1E21 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e22 uni1E22 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e23 uni1E23 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e24 uni1E24 2.1 -U+1e25 uni1E25 2.1 -U+1e26 uni1E26 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e27 uni1E27 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e28 uni1E28 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e29 uni1E29 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e2a uni1E2A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e2b uni1E2B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e2c uni1E2C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e2d uni1E2D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e2e uni1E2E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e2f uni1E2F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e30 uni1E30 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e31 uni1E31 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e32 uni1E32 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e33 uni1E33 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e34 uni1E34 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e35 uni1E35 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e36 uni1E36 2.1 -U+1e37 uni1E37 2.1 -U+1e38 uni1E38 2.1 -U+1e39 uni1E39 2.1 -U+1e3a uni1E3A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e3b uni1E3B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e3c uni1E3C 1.13 -U+1e3d uni1E3D 1.13 -U+1e3e uni1E3E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e3f uni1E3F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e40 uni1E40 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e41 uni1E41 2.1 -U+1e42 uni1E42 2.1 -U+1e43 uni1E43 2.1 -U+1e44 uni1E44 1.13 -U+1e45 uni1E45 1.13 -U+1e46 uni1E46 2.1 -U+1e47 uni1E47 2.1 -U+1e48 uni1E48 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e49 uni1E49 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e4a uni1E4A 1.13 -U+1e4b uni1E4B 1.13 -U+1e4c uni1E4C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1e4d uni1E4D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1e4e uni1E4E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e4f uni1E4F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e50 uni1E50 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e51 uni1E51 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e52 uni1E52 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e53 uni1E53 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e54 uni1E54 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e55 uni1E55 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e56 uni1E56 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e57 uni1E57 2.1 -U+1e58 uni1E58 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e59 uni1E59 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e5a uni1E5A 2.1 -U+1e5b uni1E5B 2.1 -U+1e5c uni1E5C 2.1 -U+1e5d uni1E5D 2.1 -U+1e5e uni1E5E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e5f uni1E5F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e60 uni1E60 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e61 uni1E61 2.1 -U+1e62 uni1E62 2.1 -U+1e63 uni1E63 2.1 -U+1e64 uni1E64 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e65 uni1E65 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e66 uni1E66 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e67 uni1E67 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e68 uni1E68 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e69 uni1E69 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e6a uni1E6A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e6b uni1E6B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e6c uni1E6C 2.1 -U+1e6d uni1E6D 2.1 -U+1e6e uni1E6E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e6f uni1E6F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e70 uni1E70 1.13 -U+1e71 uni1E71 1.13 -U+1e72 uni1E72 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e73 uni1E73 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e74 uni1E74 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e75 uni1E75 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e76 uni1E76 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e77 uni1E77 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e78 uni1E78 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e79 uni1E79 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e7a uni1E7A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e7b uni1E7B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e7c uni1E7C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e7d uni1E7D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e7e uni1E7E 2.1 -U+1e7f uni1E7F 2.1 -U+1e80 Wgrave 1.2 -U+1e81 wgrave 1.2 -U+1e82 Wacute 1.2 -U+1e83 wacute 1.2 -U+1e84 Wdieresis 1.2 -U+1e85 wdieresis 1.2 -U+1e86 uni1E86 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e87 uni1E87 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e88 uni1E88 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e89 uni1E89 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e8a uni1E8A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e8b uni1E8B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e8c uni1E8C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e8d uni1E8D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e8e uni1E8E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e8f uni1E8F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e90 uni1E90 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e91 uni1E91 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e92 uni1E92 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e93 uni1E93 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e94 uni1E94 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e95 uni1E95 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e96 uni1E96 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e97 uni1E97 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e98 uni1E98 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e99 uni1E99 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1e9a uni1E9A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1e9b uni1E9B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1e9c uni1E9C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e9d uni1E9D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1e9e uni1E9E 2.28 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) 2.32 (Sans ExtraLight) -U+1e9f uni1E9F 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1ea0 uni1EA0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ea1 uni1EA1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ea2 uni1EA2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ea3 uni1EA3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ea4 uni1EA4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ea5 uni1EA5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ea6 uni1EA6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ea7 uni1EA7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ea8 uni1EA8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ea9 uni1EA9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eaa uni1EAA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eab uni1EAB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eac uni1EAC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1ead uni1EAD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1eae uni1EAE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eaf uni1EAF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eb0 uni1EB0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1eb1 uni1EB1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.22 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1eb2 uni1EB2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eb3 uni1EB3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eb4 uni1EB4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eb5 uni1EB5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eb6 uni1EB6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1eb7 uni1EB7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1eb8 uni1EB8 2.2 -U+1eb9 uni1EB9 2.2 -U+1eba uni1EBA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ebb uni1EBB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ebc uni1EBC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ebd uni1EBD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ebe uni1ebe 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ebf uni1ebF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec0 uni1EC0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec1 uni1EC1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec2 uni1EC2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec3 uni1EC3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec4 uni1EC4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec5 uni1EC5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ec6 uni1EC6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1ec7 uni1EC7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1ec8 uni1EC8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ec9 uni1EC9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1eca uni1ECA 2.2 -U+1ecb uni1ECB 2.2 -U+1ecc uni1ECC 2.2 -U+1ecd uni1ECD 2.2 -U+1ece uni1ECE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ecf uni1ECF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ed0 uni1ED0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed1 uni1ED1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed2 uni1ED2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed3 uni1ED3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed4 uni1ED4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed5 uni1ED5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed6 uni1ED6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed7 uni1ED7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ed8 uni1ED8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1ed9 uni1ED9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+1eda uni1EDA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1edb uni1EDB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1edc uni1EDC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1edd uni1EDD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ede uni1EDE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1edf uni1EDF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ee0 uni1EE0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ee1 uni1EE1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ee2 uni1EE2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ee3 uni1EE3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ee4 uni1EE4 2.2 -U+1ee5 uni1EE5 2.2 -U+1ee6 uni1EE6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ee7 uni1EE7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ee8 uni1EE8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ee9 uni1EE9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eea uni1EEA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eeb uni1EEB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eec uni1EEC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eed uni1EED 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eee uni1EEE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1eef uni1EEF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ef0 uni1EF0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ef1 uni1EF1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1ef2 Ygrave 1.2 -U+1ef3 ygrave 1.2 -U+1ef4 uni1EF4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ef5 uni1EF5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ef6 uni1EF6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ef7 uni1EF7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ef8 uni1EF8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ef9 uni1EF9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1efa uni1EFA 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1efb uni1EFB 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+1f00 uni1F00 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f01 uni1F01 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f02 uni1F02 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f03 uni1F03 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f04 uni1F04 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f05 uni1F05 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f06 uni1F06 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f07 uni1F07 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f08 uni1F08 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f09 uni1F09 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f0a uni1F0A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f0b uni1F0B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f0c uni1F0C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f0d uni1F0D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f0e uni1F0E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f0f uni1F0F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f10 uni1F10 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f11 uni1F11 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f12 uni1F12 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f13 uni1F13 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f14 uni1F14 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f15 uni1F15 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f18 uni1F18 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f19 uni1F19 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f1a uni1F1A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f1b uni1F1B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f1c uni1F1C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f1d uni1F1D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f20 uni1F20 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f21 uni1F21 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f22 uni1F22 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f23 uni1F23 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f24 uni1F24 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f25 uni1F25 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f26 uni1F26 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f27 uni1F27 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f28 uni1F28 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f29 uni1F29 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f2a uni1F2A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f2b uni1F2B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f2c uni1F2C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f2d uni1F2D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f2e uni1F2E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f2f uni1F2F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f30 uni1F30 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f31 uni1F31 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f32 uni1F32 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f33 uni1F33 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f34 uni1F34 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f35 uni1F35 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f36 uni1F36 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f37 uni1F37 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f38 uni1F38 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f39 uni1F39 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f3a uni1F3A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f3b uni1F3B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f3c uni1F3C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f3d uni1F3D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f3e uni1F3E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f3f uni1F3F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f40 uni1F40 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f41 uni1F41 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f42 uni1F42 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f43 uni1F43 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f44 uni1F44 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f45 uni1F45 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f48 uni1F48 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f49 uni1F49 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f4a uni1F4A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f4b uni1F4B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f4c uni1F4C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f4d uni1F4D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f50 uni1F50 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f51 uni1F51 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f52 uni1F52 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f53 uni1F53 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f54 uni1F54 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f55 uni1F55 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f56 uni1F56 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f57 uni1F57 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f59 uni1F59 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f5b uni1F5B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f5d uni1F5D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f5f uni1F5F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f60 uni1F60 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f61 uni1F61 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f62 uni1F62 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f63 uni1F63 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f64 uni1F64 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f65 uni1F65 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f66 uni1F66 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f67 uni1F67 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f68 uni1F68 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f69 uni1F69 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f6a uni1F6A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f6b uni1F6B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f6c uni1F6C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f6d uni1F6D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f6e uni1F6E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f6f uni1F6F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f70 uni1F70 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f71 uni1F71 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f72 uni1F72 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f73 uni1F73 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f74 uni1F74 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f75 uni1F75 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f76 uni1F76 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f77 uni1F77 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f78 uni1F78 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f79 uni1F79 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1f7a uni1F7A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f7b uni1F7B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f7c uni1F7C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f7d uni1F7D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f80 uni1F80 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f81 uni1F81 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f82 uni1F82 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f83 uni1F83 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f84 uni1F84 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f85 uni1F85 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f86 uni1F86 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f87 uni1F87 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f88 uni1F88 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f89 uni1F89 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f8a uni1F8A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f8b uni1F8B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f8c uni1F8C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f8d uni1F8D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f8e uni1F8E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f8f uni1F8F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f90 uni1F90 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f91 uni1F91 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f92 uni1F92 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f93 uni1F93 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f94 uni1F94 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f95 uni1F95 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f96 uni1F96 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f97 uni1F97 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f98 uni1F98 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f99 uni1F99 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f9a uni1F9A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f9b uni1F9B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f9c uni1F9C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f9d uni1F9D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f9e uni1F9E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1f9f uni1F9F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa0 uni1FA0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa1 uni1FA1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa2 uni1FA2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa3 uni1FA3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa4 uni1FA4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa5 uni1FA5 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa6 uni1FA6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa7 uni1FA7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa8 uni1FA8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fa9 uni1FA9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1faa uni1FAA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fab uni1FAB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fac uni1FAC 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fad uni1FAD 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fae uni1FAE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1faf uni1FAF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb0 uni1FB0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb1 uni1FB1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb2 uni1FB2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb3 uni1FB3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb4 uni1FB4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb6 uni1FB6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb7 uni1FB7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fb8 uni1FB8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fb9 uni1FB9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fba uni1FBA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fbb uni1FBB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fbc uni1FBC 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fbd uni1FBD 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fbe uni1FBE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fbf uni1FBF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fc0 uni1FC0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fc1 uni1FC1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fc2 uni1FC2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fc3 uni1FC3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fc4 uni1FC4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fc6 uni1FC6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fc7 uni1FC7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fc8 uni1FC8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fc9 uni1FC9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fca uni1FCA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fcb uni1FCB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fcc uni1FCC 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fcd uni1FCD 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fce uni1FCE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fcf uni1FCF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd0 uni1FD0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd1 uni1FD1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd2 uni1FD2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd3 uni1FD3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd6 uni1FD6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd7 uni1FD7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fd8 uni1FD8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fd9 uni1FD9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fda uni1FDA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fdb uni1FDB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fdd uni1FDD 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fde uni1FDE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fdf uni1FDF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe0 uni1FE0 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe1 uni1FE1 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe2 uni1FE2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe3 uni1FE3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe4 uni1FE4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe5 uni1FE5 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe6 uni1FE6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe7 uni1FE7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fe8 uni1FE8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fe9 uni1FE9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fea uni1FEA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1feb uni1FEB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fec uni1FEC 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1fed uni1FED 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fee uni1FEE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1fef uni1FEF 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ff2 uni1FF2 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ff3 uni1FF3 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ff4 uni1FF4 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ff6 uni1FF6 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ff7 uni1FF7 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ff8 uni1FF8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ff9 uni1FF9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ffa uni1FFA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ffb uni1FFB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.10 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ffc uni1FFC 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+1ffd uni1FFD 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+1ffe uni1FFE 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.5 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+2000 uni2000 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2001 uni2001 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2002 uni2002 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2003 uni2003 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2004 uni2004 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2005 uni2005 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2006 uni2006 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2007 uni2007 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2008 uni2008 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2009 uni2009 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+200a uni200A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+200b uni200B 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.8 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+200c uni200C 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.8 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+200d uni200D 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.8 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+200e uni200E 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.8 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+200f uni200F 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.8 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2010 uni2010 1.5 -U+2011 uni2011 1.5 -U+2012 figuredash 1.5 -U+2013 endash original -U+2014 emdash original -U+2015 uni2015 1.5 -U+2016 uni2016 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2017 underscoredbl 2.3 -U+2018 quoteleft original -U+2019 quoteright original -U+201a quotesinglbase original -U+201b quotereversed 2.3 -U+201c quotedblleft original -U+201d quotedblright original -U+201e quotedblbase original -U+201f uni201F 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.3 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+2020 dagger original -U+2021 daggerdbl original -U+2022 bullet original -U+2023 uni2023 2.2 -U+2024 onedotenleader 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2025 twodotenleader 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2026 ellipsis original -U+2027 uni2027 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2028 uni2028 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2029 uni2029 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+202a uni202A 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+202b uni202B 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+202c uni202C 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+202d uni202D 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+202e uni202E 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+202f uni202F 2.11 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.23 (Serif Italic Condensed) -U+2030 perthousand original -U+2031 uni2031 2.1 -U+2032 minute 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2033 second 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2034 uni2034 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2035 uni2035 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2036 uni2036 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2037 uni2037 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2038 uni2038 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2039 guilsinglleft original -U+203a guilsinglright original -U+203b uni203B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+203c exclamdbl 2.0 -U+203d uni203D 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.11 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.14 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+203e uni203E 2.3 -U+203f uni203F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2040 uni2040 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2041 uni2041 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2042 uni2042 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2043 uni2043 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2044 fraction 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2045 uni2045 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2046 uni2046 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2047 uni2047 2.0 -U+2048 uni2048 2.0 -U+2049 uni2049 2.0 -U+204a uni204A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+204b uni204B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) 2.33 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+204c uni204C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+204d uni204D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+204e uni204E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+204f uni204F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2050 uni2050 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2051 uni2051 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2052 uni2052 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2053 uni2053 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2054 uni2054 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2055 uni2055 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2056 uni2056 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2057 uni2057 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2058 uni2058 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2059 uni2059 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+205a uni205A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+205b uni205B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+205c uni205C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+205d uni205D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+205e uni205E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+205f uni205F 2.14 -U+2060 uni2060 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2061 uni2061 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2062 uni2062 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2063 uni2063 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2064 uni2064 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+206a uni206A 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+206b uni206B 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+206c uni206C 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+206d uni206D 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+206e uni206E 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+206f uni206F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2070 uni2070 2.2 -U+2071 uni2071 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2074 uni2074 2.2 -U+2075 uni2075 2.2 -U+2076 uni2076 2.2 -U+2077 uni2077 2.2 -U+2078 uni2078 2.2 -U+2079 uni2079 2.2 -U+207a uni207A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+207b uni207B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+207c uni207C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+207d uni207D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+207e uni207E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+207f uni207F 1.14 -U+2080 uni2080 2.4 -U+2081 uni2081 2.4 -U+2082 uni2082 2.4 -U+2083 uni2083 2.4 -U+2084 uni2084 2.4 -U+2085 uni2085 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2086 uni2086 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2087 uni2087 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2088 uni2088 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2089 uni2089 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+208a uni208A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+208b uni208B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+208c uni208C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+208d uni208D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+208e uni208E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2090 uni2090 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2091 uni2091 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2092 uni2092 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2093 uni2093 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2094 uni2094 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.17 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.18 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2095 uni2095 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2096 uni2096 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2097 uni2097 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2098 uni2098 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2099 uni2099 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+209a uni209A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+209b uni209B 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+209c uni209C 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+20a0 uni20A0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a1 colonmonetary 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a2 uni20A2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a3 franc 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a4 lira 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a5 uni20A5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a6 uni20A6 2.3 -U+20a7 peseta 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a8 uni20A8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20a9 uni20A9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20aa uni20AA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20ab dong 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20ac Euro original -U+20ad uni20AD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20ae uni20AE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20af uni20AF 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20b0 uni20B0 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20b1 uni20B1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.14 (Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+20b2 uni20B2 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20b3 uni20B3 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+20b4 uni20B4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+20b5 uni20B5 2.2 -U+20b8 uni20B8 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+20b9 uni20B9 2.32 -U+20d0 uni20D0 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+20d1 uni20D1 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+20d6 uni20D6 2.8 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+20d7 uni20D7 2.8 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+20db uni20DB 2.23 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+20dc uni20DC 2.23 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+20e1 uni20E1 2.23 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2100 uni2100 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2101 uni2101 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2102 uni2102 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.16 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Condensed) -U+2103 uni2103 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2104 uni2104 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2105 uni2105 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2106 uni2106 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2107 uni2107 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2108 uni2108 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2109 uni2109 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+210b uni210B 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+210c uni210C 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+210d uni210D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+210e uni210E 2.5 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.23 (Serif Italic Condensed) 2.26 (Sans ExtraLight) -U+210f uni210F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.28 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2110 uni2110 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2111 Ifraktur 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2112 uni2112 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2113 uni2113 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2114 uni2114 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2115 uni2115 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2116 uni2116 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2117 uni2117 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2118 weierstrass 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2119 uni2119 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+211a uni211A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+211b uni211B 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+211c Rfraktur 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+211d uni211D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+211e prescription 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+211f uni211F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2120 uni2120 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2121 uni2121 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2122 trademark original -U+2123 uni2123 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2124 uni2124 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2125 uni2125 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2126 uni2126 2.2 -U+2127 uni2127 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2128 uni2128 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2129 uni2129 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+212a uni212A 2.2 -U+212b uni212B 2.2 -U+212c uni212C 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+212d uni212D 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+212e estimated 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+212f uni212F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2130 uni2130 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2131 uni2131 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2132 uni2132 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) -U+2133 uni2133 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2134 uni2134 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2135 aleph 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2136 uni2136 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2137 uni2137 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2138 uni2138 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2139 uni2139 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+213a uni213A 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+213b uni213B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+213c uni213C 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+213d uni213D 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+213e uni213E 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+213f uni213F 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+2140 uni2140 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+2141 uni2141 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2142 uni2142 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2143 uni2143 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2144 uni2144 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2145 uni2145 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+2146 uni2146 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+2147 uni2147 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+2148 uni2148 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+2149 uni2149 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.22 (Serif, Serif Condensed) -U+214b uni214B 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+214e uni214E 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2150 uni2150 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2151 uni2151 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2152 uni2152 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2153 onethird 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2154 twothirds 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2155 uni2155 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2156 uni2156 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2157 uni2157 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2158 uni2158 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2159 uni2159 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+215a uni215A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+215b oneeighth 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+215c threeeighths 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+215d fiveeighths 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+215e seveneighths 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+215f uni215F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.6 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2160 uni2160 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2161 uni2161 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2162 uni2162 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2163 uni2163 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2164 uni2164 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2165 uni2165 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2166 uni2166 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2167 uni2167 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2168 uni2168 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2169 uni2169 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+216a uni216A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+216b uni216B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+216c uni216C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+216d uni216D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+216e uni216E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+216f uni216F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2170 uni2170 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2171 uni2171 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2172 uni2172 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2173 uni2173 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2174 uni2174 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2175 uni2175 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2176 uni2176 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2177 uni2177 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2178 uni2178 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2179 uni2179 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+217a uni217A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+217b uni217B 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+217c uni217C 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+217d uni217D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+217e uni217E 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+217f uni217F 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2180 uni2180 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2181 uni2181 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+2182 uni2182 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans ExtraLight) -U+2183 uni2183 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2184 uni2184 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2185 uni2185 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2189 uni2189 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2190 arrowleft 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2191 arrowup 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2192 arrowright 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2193 arrowdown 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2194 arrowboth 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2195 arrowupdn 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2196 uni2196 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2197 uni2197 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2198 uni2198 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2199 uni2199 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+219a uni219A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+219b uni219B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+219c uni219C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+219d uni219D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+219e uni219E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+219f uni219F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a0 uni21A0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a1 uni21A1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a2 uni21A2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a3 uni21A3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a4 uni21A4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a5 uni21A5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a6 uni21A6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a7 uni21A7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a8 arrowupdnbse 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21a9 uni21A9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21aa uni21AA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ab uni21AB 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ac uni21AC 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ad uni21AD 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ae uni21AE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21af uni21AF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b0 uni21B0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b1 uni21B1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b2 uni21B2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b3 uni21B3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b4 uni21B4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b5 carriagereturn 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b6 uni21B6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b7 uni21B7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b8 uni21B8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21b9 uni21B9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ba uni21BA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21bb uni21BB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21bc uni21BC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21bd uni21BD 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21be uni21BE 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21bf uni21BF 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c0 uni21C0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c1 uni21C1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c2 uni21C2 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c3 uni21C3 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c4 uni21C4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c5 uni21C5 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c6 uni21C6 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c7 uni21C7 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c8 uni21C8 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21c9 uni21C9 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ca uni21CA 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21cb uni21CB 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21cc uni21CC 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21cd uni21CD 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ce uni21CE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21cf uni21CF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d0 arrowdblleft 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d1 arrowdblup 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d2 arrowdblright 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d3 arrowdbldown 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d4 arrowdblboth 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d5 uni21D5 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d6 uni21D6 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d7 uni21D7 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d8 uni21D8 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21d9 uni21D9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21da uni21DA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21db uni21DB 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21dc uni21DC 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21dd uni21DD 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21de uni21DE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21df uni21DF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e0 uni21E0 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e1 uni21E1 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e2 uni21E2 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e3 uni21E3 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e4 uni21E4 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e5 uni21E5 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e6 uni21E6 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e7 uni21E7 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e8 uni21E8 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21e9 uni21E9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ea uni21EA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21eb uni21EB 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ec uni21EC 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ed uni21ED 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ee uni21EE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ef uni21EF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f0 uni21F0 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f1 uni21F1 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f2 uni21F2 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f3 uni21F3 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f4 uni21F4 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f5 uni21F5 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f6 uni21F6 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f7 uni21F7 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f8 uni21F8 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21f9 uni21F9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21fa uni21FA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21fb uni21FB 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21fc uni21FC 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21fd uni21FD 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21fe uni21FE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+21ff uni21FF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2200 universal 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2201 uni2201 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2202 partialdiff original -U+2203 existential 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans ExtraLight) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2204 uni2204 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2205 emptyset 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2206 Delta original -U+2207 gradient 2.1 -U+2208 element 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2209 notelement 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+220a uni220A 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+220b suchthat 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+220c uni220C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+220d uni220D 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+220e uni220E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+220f product original -U+2210 uni2210 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2211 summation original -U+2212 minus original -U+2213 uni2213 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2214 uni2214 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2215 uni2215 original -U+2216 uni2216 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2217 asteriskmath 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2218 uni2218 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2219 uni2219 original -U+221a radical original -U+221b uni221B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+221c uni221C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.26 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+221d proportional 2.1 -U+221e infinity original -U+221f orthogonal 2.1 -U+2220 angle 2.3 -U+2221 uni2221 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2222 uni2222 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2223 uni2223 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2224 uni2224 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2225 uni2225 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2226 uni2226 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2227 logicaland 2.1 -U+2228 logicalor 2.1 -U+2229 intersection 2.1 -U+222a union 2.1 -U+222b integral original -U+222c uni222C 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+222d uni222D 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+222e uni222E 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+222f uni222F 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2230 uni2230 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2231 uni2231 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2232 uni2232 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2233 uni2233 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2234 therefore 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2235 uni2235 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2236 uni2236 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2237 uni2237 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2238 uni2238 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2239 uni2239 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+223a uni223A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+223b uni223B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+223c similar 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+223d uni223D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+223e uni223E 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+223f uni223F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2240 uni2240 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2241 uni2241 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2242 uni2242 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2243 uni2243 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2244 uni2244 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2245 congruent 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2246 uni2246 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2247 uni2247 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2248 approxequal original -U+2249 uni2249 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+224a uni224A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+224b uni224B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+224c uni224C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+224d uni224D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+224e uni224E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+224f uni224F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2250 uni2250 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2251 uni2251 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2252 uni2252 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2253 uni2253 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2254 uni2254 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2255 uni2255 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2256 uni2256 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2257 uni2257 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2258 uni2258 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2259 uni2259 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+225a uni225A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+225b uni225B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+225c uni225C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+225d uni225D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+225e uni225E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+225f uni225F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2260 notequal original -U+2261 equivalence 2.1 -U+2262 uni2262 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2263 uni2263 2.1 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2264 lessequal original -U+2265 greaterequal original -U+2266 uni2266 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2267 uni2267 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2268 uni2268 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2269 uni2269 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+226a uni226A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+226b uni226B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+226c uni226C 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+226d uni226D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+226e uni226E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+226f uni226F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2270 uni2270 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2271 uni2271 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2272 uni2272 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2273 uni2273 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2274 uni2274 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2275 uni2275 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2276 uni2276 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2277 uni2277 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2278 uni2278 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2279 uni2279 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+227a uni227A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+227b uni227B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+227c uni227C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+227d uni227D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+227e uni227E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+227f uni227F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2280 uni2280 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2281 uni2281 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2282 propersubset 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2283 propersuperset 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2284 notsubset 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2285 uni2285 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2286 reflexsubset 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2287 reflexsuperset 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2288 uni2288 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2289 uni2289 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+228a uni228A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+228b uni228B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.6 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+228c uni228C 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+228d uni228D 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+228e uni228E 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+228f uni228F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2290 uni2290 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2291 uni2291 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2292 uni2292 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2293 uni2293 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2294 uni2294 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2295 circleplus 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2296 uni2296 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2297 circlemultiply 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2298 uni2298 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2299 uni2299 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+229a uni229A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+229b uni229B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+229c uni229C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+229d uni229D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+229e uni229E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+229f uni229F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22a0 uni22A0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22a1 uni22A1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22a2 uni22A2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+22a3 uni22A3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+22a4 uni22A4 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+22a5 perpendicular 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) 2.32 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+22a6 uni22A6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22a7 uni22A7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22a8 uni22A8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22a9 uni22A9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22aa uni22AA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22ab uni22AB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22ac uni22AC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22ad uni22AD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22ae uni22AE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22af uni22AF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22b0 uni22B0 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b1 uni22B1 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b2 uni22B2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b3 uni22B3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b4 uni22B4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b5 uni22B5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b6 uni22B6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b7 uni22B7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b8 uni22B8 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22b9 uni22B9 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ba uni22BA 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22bb uni22BB 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22bc uni22BC 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22bd uni22BD 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22be uni22BE 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+22bf uni22BF 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+22c0 uni22C0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c1 uni22C1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c2 uni22C2 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c3 uni22C3 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c4 uni22C4 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c5 dotmath 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+22c6 uni22C6 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+22c7 uni22C7 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c8 uni22C8 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22c9 uni22C9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ca uni22CA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22cb uni22CB 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22cc uni22CC 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22cd uni22CD 2.6 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.7 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+22ce uni22CE 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22cf uni22CF 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d0 uni22D0 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d1 uni22D1 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d2 uni22D2 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d3 uni22D3 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d4 uni22D4 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d5 uni22D5 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d6 uni22D6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d7 uni22D7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22d8 uni22D8 2.6 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+22d9 uni22D9 2.6 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+22da uni22DA 2.6 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.7 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+22db uni22DB 2.6 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.7 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+22dc uni22DC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22dd uni22DD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22de uni22DE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22df uni22DF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e0 uni22E0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e1 uni22E1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e2 uni22E2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e3 uni22E3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e4 uni22E4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e5 uni22E5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e6 uni22E6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e7 uni22E7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e8 uni22E8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22e9 uni22E9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+22ea uni22EA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22eb uni22EB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ec uni22EC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ed uni22ED 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ee uni22EE 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ef uni22EF 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f0 uni22F0 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f1 uni22F1 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f2 uni22F2 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f3 uni22F3 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f4 uni22F4 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f5 uni22F5 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f6 uni22F6 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f7 uni22F7 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f8 uni22F8 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22f9 uni22F9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22fa uni22FA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22fb uni22FB 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22fc uni22FC 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22fd uni22FD 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22fe uni22FE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+22ff uni22FF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2300 uni2300 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2301 uni2301 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2302 house 1.14 -U+2303 uni2303 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2304 uni2304 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2305 uni2305 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2306 uni2306 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2307 uni2307 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2308 uni2308 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2309 uni2309 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+230a uni230A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+230b uni230B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+230c uni230C 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+230d uni230D 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+230e uni230E 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+230f uni230F 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2310 revlogicalnot 1.14 -U+2311 uni2311 1.15 -U+2312 uni2312 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2313 uni2313 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2314 uni2314 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2315 uni2315 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2318 uni2318 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2319 uni2319 1.14 -U+231c uni231C 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+231d uni231D 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+231e uni231E 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+231f uni231F 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2320 integraltp 2.3 -U+2321 integralbt 2.3 -U+2324 uni2324 2.16 (Sans, Sans Bold, Sans Bold Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique) 2.19 (Sans Condensed Oblique, Sans Oblique) -U+2325 uni2325 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2326 uni2326 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2327 uni2327 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2328 uni2328 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2329 angleleft 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) -U+232a angleright 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.10 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) -U+232b uni232B 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+232c uni232C 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2335 uni2335 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2337 uni2337 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2338 uni2338 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2339 uni2339 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+233a uni233A 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+233b uni233B 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+233c uni233C 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+233d uni233D 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+233e uni233E 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2341 uni2341 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2342 uni2342 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2343 uni2343 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2344 uni2344 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2347 uni2347 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2348 uni2348 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2349 uni2349 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+234b uni234B 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+234c uni234C 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+234d uni234D 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2350 uni2350 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2352 uni2352 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2353 uni2353 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2354 uni2354 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2357 uni2357 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2358 uni2358 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2359 uni2359 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+235a uni235A 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+235b uni235B 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+235c uni235C 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+235e uni235E 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+235f uni235F 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2360 uni2360 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2363 uni2363 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2364 uni2364 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2365 uni2365 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2368 uni2368 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2369 uni2369 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+236b uni236B 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+236c uni236C 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+236d uni236D 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+236e uni236E 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+236f uni236F 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2370 uni2370 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2373 uni2373 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2374 uni2374 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2375 uni2375 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2376 uni2376 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2377 uni2377 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2378 uni2378 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2379 uni2379 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+237a uni237A 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+237d uni237D 1.15 -U+2380 uni2380 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2381 uni2381 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2382 uni2382 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2383 uni2383 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2387 uni2387 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2388 uni2388 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2389 uni2389 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+238a uni238A 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+238b uni238B 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+2394 uni2394 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2395 uni2395 2.14 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) -U+239b uni239B 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+239c uni239C 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+239d uni239D 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+239e uni239E 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+239f uni239F 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a0 uni23A0 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a1 uni23A1 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a2 uni23A2 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a3 uni23A3 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a4 uni23A4 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a5 uni23A5 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a6 uni23A6 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a7 uni23A7 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a8 uni23A8 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23a9 uni23A9 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23aa uni23AA 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23ab uni23AB 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23ac uni23AC 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23ad uni23AD 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+23ae uni23AE 2.3 -U+23ce uni23CE 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+23cf uni23CF 2.3 -U+23e3 uni23E3 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+23e5 uni23E5 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+23e8 uni23E8 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2422 uni2422 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2423 uni2423 1.6 -U+2460 uni2460 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2461 uni2461 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2462 uni2462 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2463 uni2463 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2464 uni2464 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2465 uni2465 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2466 uni2466 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2467 uni2467 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2468 uni2468 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2469 uni2469 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2500 SF100000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2501 uni2501 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2502 SF110000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2503 uni2503 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2504 uni2504 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2505 uni2505 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2506 uni2506 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2507 uni2507 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2508 uni2508 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2509 uni2509 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+250a uni250A 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+250b uni250B 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+250c SF010000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+250d uni250D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+250e uni250E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+250f uni250F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2510 SF030000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2511 uni2511 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2512 uni2512 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2513 uni2513 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2514 SF020000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2515 uni2515 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2516 uni2516 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2517 uni2517 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2518 SF040000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2519 uni2519 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+251a uni251A 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+251b uni251B 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+251c SF080000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+251d uni251D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+251e uni251E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+251f uni251F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2520 uni2520 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2521 uni2521 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2522 uni2522 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2523 uni2523 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2524 SF090000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2525 uni2525 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2526 uni2526 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2527 uni2527 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2528 uni2528 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2529 uni2529 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+252a uni252A 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+252b uni252B 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+252c SF060000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+252d uni252D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+252e uni252E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+252f uni252F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2530 uni2530 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2531 uni2531 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2532 uni2532 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2533 uni2533 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2534 SF070000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2535 uni2535 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2536 uni2536 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2537 uni2537 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2538 uni2538 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2539 uni2539 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+253a uni253A 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+253b uni253B 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+253c SF050000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+253d uni253D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+253e uni253E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+253f uni253F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2540 uni2540 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2541 uni2541 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2542 uni2542 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2543 uni2543 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2544 uni2544 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2545 uni2545 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2546 uni2546 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2547 uni2547 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2548 uni2548 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2549 uni2549 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+254a uni254A 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+254b uni254B 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+254c uni254C 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+254d uni254D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+254e uni254E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+254f uni254F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2550 SF430000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2551 SF240000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2552 SF510000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2553 SF520000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2554 SF390000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2555 SF220000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2556 SF210000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2557 SF250000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2558 SF500000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2559 SF490000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+255a SF380000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+255b SF280000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+255c SF270000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+255d SF260000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+255e SF360000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+255f SF370000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2560 SF420000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2561 SF190000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2562 SF200000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2563 SF230000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2564 SF470000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2565 SF480000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2566 SF410000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2567 SF450000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2568 SF460000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2569 SF400000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+256a SF540000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+256b SF530000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+256c SF440000 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+256d uni256D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+256e uni256E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+256f uni256F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2570 uni2570 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2571 uni2571 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2572 uni2572 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2573 uni2573 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2574 uni2574 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2575 uni2575 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2576 uni2576 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2577 uni2577 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2578 uni2578 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2579 uni2579 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+257a uni257A 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+257b uni257B 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+257c uni257C 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+257d uni257D 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+257e uni257E 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+257f uni257F 1.12 (Sans Mono, Sans Mono Oblique) 2.21 (Sans, Sans Condensed, Sans Condensed Oblique, Sans Oblique, Serif, Serif Condensed, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2580 upblock 1.14 -U+2581 uni2581 1.14 -U+2582 uni2582 1.14 -U+2583 uni2583 1.14 -U+2584 dnblock 1.14 -U+2585 uni2585 1.14 -U+2586 uni2586 1.14 -U+2587 uni2587 1.14 -U+2588 block 1.14 -U+2589 uni2589 1.14 -U+258a uni258A 1.14 -U+258b uni258B 1.14 -U+258c lfblock 1.14 -U+258d uni258D 1.14 -U+258e uni258E 1.14 -U+258f uni258F 1.14 -U+2590 rtblock 1.14 -U+2591 ltshade 1.15 -U+2592 shade 1.15 -U+2593 dkshade 1.15 -U+2594 uni2594 1.14 -U+2595 uni2595 1.14 -U+2596 uni2596 1.14 -U+2597 uni2597 1.14 -U+2598 uni2598 1.14 -U+2599 uni2599 1.14 -U+259a uni259A 1.14 -U+259b uni259B 1.14 -U+259c uni259C 1.14 -U+259d uni259D 1.14 -U+259e uni259E 1.14 -U+259f uni259F 1.14 -U+25a0 filledbox 2.3 -U+25a1 H22073 2.3 -U+25a2 uni25A2 2.3 -U+25a3 uni25A3 2.3 -U+25a4 uni25A4 2.3 -U+25a5 uni25A5 2.3 -U+25a6 uni25A6 2.3 -U+25a7 uni25A7 2.3 -U+25a8 uni25A8 2.3 -U+25a9 uni25A9 2.3 -U+25aa H18543 2.3 -U+25ab H18551 2.3 -U+25ac filledrect 2.3 -U+25ad uni25AD 2.3 -U+25ae uni25AE 2.3 -U+25af uni25AF 2.3 -U+25b0 uni25B0 2.3 -U+25b1 uni25B1 2.3 -U+25b2 triagup 2.3 -U+25b3 uni25B3 2.3 -U+25b4 uni25B4 2.3 -U+25b5 uni25B5 2.3 -U+25b6 uni25B6 2.3 -U+25b7 uni25B7 2.3 -U+25b8 uni25B8 2.3 -U+25b9 uni25B9 2.3 -U+25ba triagrt 2.3 -U+25bb uni25BB 2.3 -U+25bc triagdn 2.3 -U+25bd uni25BD 2.3 -U+25be uni25BE 2.3 -U+25bf uni25BF 2.3 -U+25c0 uni25C0 2.3 -U+25c1 uni25C1 2.3 -U+25c2 uni25C2 2.3 -U+25c3 uni25C3 2.3 -U+25c4 triaglf 2.3 -U+25c5 uni25C5 2.3 -U+25c6 uni25C6 2.3 -U+25c7 uni25C7 2.3 -U+25c8 uni25C8 2.3 -U+25c9 uni25C9 2.3 -U+25ca lozenge original -U+25cb circle 2.3 -U+25cc uni25CC 2.3 -U+25cd uni25CD 2.3 -U+25ce uni25CE 2.3 -U+25cf H18533 2.3 -U+25d0 uni25D0 2.3 -U+25d1 uni25D1 2.3 -U+25d2 uni25D2 2.3 -U+25d3 uni25D3 2.3 -U+25d4 uni25D4 2.3 -U+25d5 uni25D5 2.3 -U+25d6 uni25D6 2.3 -U+25d7 uni25D7 2.3 -U+25d8 invbullet 2.2 -U+25d9 invcircle 2.3 -U+25da uni25DA 2.3 -U+25db uni25DB 2.3 -U+25dc uni25DC 2.3 -U+25dd uni25DD 2.3 -U+25de uni25DE 2.3 -U+25df uni25DF 2.3 -U+25e0 uni25E0 2.3 -U+25e1 uni25E1 2.3 -U+25e2 uni25E2 2.3 -U+25e3 uni25E3 2.3 -U+25e4 uni25E4 2.3 -U+25e5 uni25E5 2.3 -U+25e6 openbullet 2.2 -U+25e7 uni25E7 2.3 -U+25e8 uni25E8 2.3 -U+25e9 uni25E9 2.3 -U+25ea uni25EA 2.3 -U+25eb uni25EB 2.3 -U+25ec uni25EC 2.3 -U+25ed uni25ED 2.3 -U+25ee uni25EE 2.3 -U+25ef uni25EF 2.3 -U+25f0 uni25F0 2.3 -U+25f1 uni25F1 2.3 -U+25f2 uni25F2 2.3 -U+25f3 uni25F3 2.3 -U+25f4 uni25F4 2.3 -U+25f5 uni25F5 2.3 -U+25f6 uni25F6 2.3 -U+25f7 uni25F7 2.3 -U+25f8 uni25F8 2.3 -U+25f9 uni25F9 2.3 -U+25fa uni25FA 2.3 -U+25fb uni25FB 2.3 -U+25fc uni25FC 2.3 -U+25fd uni25FD 2.3 -U+25fe uni25FE 2.3 -U+25ff uni25FF 2.3 -U+2600 uni2600 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2601 uni2601 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2602 uni2602 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2603 uni2603 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2604 uni2604 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2605 uni2605 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2606 uni2606 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2607 uni2607 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2608 uni2608 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2609 uni2609 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+260a uni260A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+260b uni260B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+260c uni260C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+260d uni260D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+260e uni260E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+260f uni260F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2610 uni2610 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2611 uni2611 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2612 uni2612 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2613 uni2613 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2614 uni2614 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2615 uni2615 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2616 uni2616 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2617 uni2617 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2618 uni2618 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2619 uni2619 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+261a uni261A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+261b uni261B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+261c uni261C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+261d uni261D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+261e uni261E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+261f uni261F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2620 uni2620 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2621 uni2621 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2622 uni2622 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2623 uni2623 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2624 uni2624 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2625 uni2625 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2626 uni2626 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2627 uni2627 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2628 uni2628 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2629 uni2629 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+262a uni262A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+262b uni262B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+262c uni262C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+262d uni262D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+262e uni262E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+262f uni262F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2630 uni2630 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2631 uni2631 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2632 uni2632 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2633 uni2633 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2634 uni2634 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2635 uni2635 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2636 uni2636 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2637 uni2637 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2638 uni2638 1.15 -U+2639 uni2639 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+263a smileface 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+263b invsmileface 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+263c sun 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+263d uni263D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+263e uni263E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+263f uni263F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2640 female 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2641 uni2641 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2642 male 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2643 uni2643 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2644 uni2644 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2645 uni2645 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2646 uni2646 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2647 uni2647 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2648 uni2648 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2649 uni2649 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+264a uni264A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+264b uni264B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+264c uni264C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+264d uni264D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+264e uni264E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+264f uni264F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2650 uni2650 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2651 uni2651 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2652 uni2652 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2653 uni2653 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2654 uni2654 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2655 uni2655 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2656 uni2656 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2657 uni2657 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2658 uni2658 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2659 uni2659 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+265a uni265A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+265b uni265B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+265c uni265C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+265d uni265D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+265e uni265E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+265f uni265F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2660 spade 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2661 uni2661 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2662 uni2662 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2663 club 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2664 uni2664 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2665 heart 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2666 diamond 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2667 uni2667 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2668 uni2668 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2669 uni2669 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+266a musicalnote 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+266b musicalnotedbl 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+266c uni266C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+266d uni266D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+266e uni266E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+266f uni266F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2670 uni2670 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2671 uni2671 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2672 uni2672 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2673 uni2673 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2674 uni2674 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2675 uni2675 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2676 uni2676 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2677 uni2677 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2678 uni2678 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2679 uni2679 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+267a uni267A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+267b uni267B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+267c uni267C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+267d uni267D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+267e uni267E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+267f uni267F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2680 uni2680 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold) -U+2681 uni2681 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold) -U+2682 uni2682 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold) -U+2683 uni2683 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold) -U+2684 uni2684 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold) -U+2685 uni2685 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.4 (Sans ExtraLight) 2.7 (Sans Mono, Sans Mono Bold) -U+2686 uni2686 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2687 uni2687 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2688 uni2688 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2689 uni2689 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+268a uni268A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+268b uni268B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+268c uni268C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+268d uni268D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+268e uni268E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+268f uni268F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+2690 uni2690 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2691 uni2691 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2692 uni2692 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2693 uni2693 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2694 uni2694 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2695 uni2695 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2696 uni2696 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2697 uni2697 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2698 uni2698 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2699 uni2699 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+269a uni269A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+269b uni269B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+269c uni269C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+26a0 uni26A0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+26a1 uni26A1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+26a2 uni26A2 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a3 uni26A3 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a4 uni26A4 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a5 uni26A5 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a6 uni26A6 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a7 uni26A7 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a8 uni26A8 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26a9 uni26A9 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26aa uni26AA 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26ab uni26AB 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26ac uni26AC 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26ad uni26AD 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26ae uni26AE 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26af uni26AF 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+26b0 uni26B0 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+26b1 uni26B1 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+26b2 uni26B2 2.12 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26b3 uni26B3 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26b4 uni26B4 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26b5 uni26B5 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26b6 uni26B6 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26b7 uni26B7 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26b8 uni26B8 2.29 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26c0 uni26C0 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26c1 uni26C1 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26c2 uni26C2 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26c3 uni26C3 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+26e2 uni26E2 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2701 uni2701 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2702 uni2702 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2703 uni2703 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2704 uni2704 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2706 uni2706 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2707 uni2707 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2708 uni2708 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2709 uni2709 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+270c uni270C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+270d uni270D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+270e uni270E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+270f uni270F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2710 uni2710 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2711 uni2711 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2712 uni2712 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2713 uni2713 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2714 uni2714 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2715 uni2715 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2716 uni2716 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2717 uni2717 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2718 uni2718 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2719 uni2719 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+271a uni271A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+271b uni271B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+271c uni271C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+271d uni271D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+271e uni271E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+271f uni271F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2720 uni2720 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2721 uni2721 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2722 uni2722 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2723 uni2723 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2724 uni2724 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2725 uni2725 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2726 uni2726 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2727 uni2727 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2729 uni2729 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+272a uni272A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+272b uni272B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+272c uni272C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+272d uni272D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+272e uni272E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+272f uni272F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2730 uni2730 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2731 uni2731 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2732 uni2732 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2733 uni2733 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2734 uni2734 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2735 uni2735 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2736 uni2736 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2737 uni2737 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2738 uni2738 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2739 uni2739 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+273a uni273A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+273b uni273B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+273c uni273C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+273d uni273D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+273e uni273E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+273f uni273F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2740 uni2740 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2741 uni2741 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2742 uni2742 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2743 uni2743 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2744 uni2744 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2745 uni2745 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2746 uni2746 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2747 uni2747 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2748 uni2748 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2749 uni2749 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+274a uni274A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+274b uni274B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+274d uni274D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+274f uni274F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2750 uni2750 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2751 uni2751 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2752 uni2752 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2756 uni2756 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2758 uni2758 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2759 uni2759 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+275a uni275A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+275b uni275B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+275c uni275C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+275d uni275D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+275e uni275E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2761 uni2761 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2762 uni2762 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2763 uni2763 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2764 uni2764 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2765 uni2765 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2766 uni2766 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2767 uni2767 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2768 uni2768 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2769 uni2769 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+276a uni276A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+276b uni276B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+276c uni276C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+276d uni276D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+276e uni276E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+276f uni276F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2770 uni2770 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2771 uni2771 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2772 uni2772 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2773 uni2773 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2774 uni2774 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2775 uni2775 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Sans Mono, Sans Mono Bold) -U+2776 uni2776 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2777 uni2777 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2778 uni2778 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2779 uni2779 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+277a uni277A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+277b uni277B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+277c uni277C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+277d uni277D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+277e uni277E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+277f uni277F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2780 uni2780 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2781 uni2781 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2782 uni2782 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2783 uni2783 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2784 uni2784 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2785 uni2785 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2786 uni2786 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2787 uni2787 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2788 uni2788 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2789 uni2789 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+278a uni278A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+278b uni278B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+278c uni278C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+278d uni278D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+278e uni278E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+278f uni278F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2790 uni2790 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2791 uni2791 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2792 uni2792 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2793 uni2793 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2794 uni2794 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2798 uni2798 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+2799 uni2799 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+279a uni279A 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+279b uni279B 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+279c uni279C 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+279d uni279D 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+279e uni279E 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+279f uni279F 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a0 uni27A0 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a1 uni27A1 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27a2 uni27A2 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a3 uni27A3 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a4 uni27A4 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a5 uni27A5 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a6 uni27A6 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a7 uni27A7 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a8 uni27A8 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27a9 uni27A9 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27aa uni27AA 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27ab uni27AB 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27ac uni27AC 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27ad uni27AD 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27ae uni27AE 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27af uni27AF 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b1 uni27B1 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b2 uni27B2 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b3 uni27B3 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b4 uni27B4 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b5 uni27B5 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b6 uni27B6 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b7 uni27B7 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b8 uni27B8 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27b9 uni27B9 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27ba uni27BA 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27bb uni27BB 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27bc uni27BC 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27bd uni27BD 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27be uni27BE 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.7 (Sans Mono, Sans Mono Bold) -U+27bf uni27BF 2.7 (Sans Mono Bold) -U+27c5 uni27C5 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+27c6 uni27C6 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+27e0 uni27E0 2.3 -U+27e6 uni27E6 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+27e7 uni27E7 2.15 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+27e8 uni27E8 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27e9 uni27E9 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.13 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27ea uni27EA 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+27eb uni27EB 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+27f0 uni27F0 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f1 uni27F1 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f2 uni27F2 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f3 uni27F3 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f4 uni27F4 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f5 uni27F5 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f6 uni27F6 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f7 uni27F7 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f8 uni27F8 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27f9 uni27F9 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27fa uni27FA 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27fb uni27FB 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27fc uni27FC 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27fd uni27FD 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27fe uni27FE 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+27ff uni27FF 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2800 uni2800 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2801 uni2801 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2802 uni2802 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2803 uni2803 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2804 uni2804 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2805 uni2805 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2806 uni2806 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2807 uni2807 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2808 uni2808 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2809 uni2809 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+280a uni280A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+280b uni280B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+280c uni280C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+280d uni280D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+280e uni280E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+280f uni280F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2810 uni2810 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2811 uni2811 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2812 uni2812 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2813 uni2813 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2814 uni2814 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2815 uni2815 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2816 uni2816 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2817 uni2817 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2818 uni2818 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2819 uni2819 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+281a uni281A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+281b uni281B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+281c uni281C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+281d uni281D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+281e uni281E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+281f uni281F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2820 uni2820 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2821 uni2821 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2822 uni2822 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2823 uni2823 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2824 uni2824 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2825 uni2825 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2826 uni2826 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2827 uni2827 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2828 uni2828 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2829 uni2829 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+282a uni282A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+282b uni282B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+282c uni282C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+282d uni282D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+282e uni282E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+282f uni282F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2830 uni2830 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2831 uni2831 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2832 uni2832 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2833 uni2833 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2834 uni2834 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2835 uni2835 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2836 uni2836 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2837 uni2837 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2838 uni2838 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2839 uni2839 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+283a uni283A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+283b uni283B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+283c uni283C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+283d uni283D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+283e uni283E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+283f uni283F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2840 uni2840 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2841 uni2841 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2842 uni2842 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2843 uni2843 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2844 uni2844 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2845 uni2845 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2846 uni2846 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2847 uni2847 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2848 uni2848 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2849 uni2849 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+284a uni284A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+284b uni284B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+284c uni284C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+284d uni284D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+284e uni284E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+284f uni284F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2850 uni2850 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2851 uni2851 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2852 uni2852 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2853 uni2853 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2854 uni2854 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2855 uni2855 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2856 uni2856 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2857 uni2857 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2858 uni2858 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2859 uni2859 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+285a uni285A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+285b uni285B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+285c uni285C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+285d uni285D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+285e uni285E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+285f uni285F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2860 uni2860 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2861 uni2861 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2862 uni2862 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2863 uni2863 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2864 uni2864 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2865 uni2865 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2866 uni2866 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2867 uni2867 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2868 uni2868 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2869 uni2869 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+286a uni286A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+286b uni286B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+286c uni286C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+286d uni286D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+286e uni286E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+286f uni286F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2870 uni2870 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2871 uni2871 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2872 uni2872 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2873 uni2873 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2874 uni2874 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2875 uni2875 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2876 uni2876 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2877 uni2877 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2878 uni2878 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2879 uni2879 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+287a uni287A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+287b uni287B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+287c uni287C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+287d uni287D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+287e uni287E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+287f uni287F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2880 uni2880 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2881 uni2881 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2882 uni2882 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2883 uni2883 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2884 uni2884 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2885 uni2885 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2886 uni2886 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2887 uni2887 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2888 uni2888 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2889 uni2889 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+288a uni288A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+288b uni288B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+288c uni288C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+288d uni288D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+288e uni288E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+288f uni288F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2890 uni2890 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2891 uni2891 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2892 uni2892 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2893 uni2893 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2894 uni2894 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2895 uni2895 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2896 uni2896 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2897 uni2897 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2898 uni2898 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2899 uni2899 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+289a uni289A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+289b uni289B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+289c uni289C 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+289d uni289D 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+289e uni289E 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+289f uni289F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a0 uni28A0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a1 uni28A1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a2 uni28A2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a3 uni28A3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a4 uni28A4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a5 uni28A5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a6 uni28A6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a7 uni28A7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a8 uni28A8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28a9 uni28A9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28aa uni28AA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ab uni28AB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ac uni28AC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ad uni28AD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ae uni28AE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28af uni28AF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b0 uni28B0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b1 uni28B1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b2 uni28B2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b3 uni28B3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b4 uni28B4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b5 uni28B5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b6 uni28B6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b7 uni28B7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b8 uni28B8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28b9 uni28B9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ba uni28BA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28bb uni28BB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28bc uni28BC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28bd uni28BD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28be uni28BE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28bf uni28BF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c0 uni28C0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c1 uni28C1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c2 uni28C2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c3 uni28C3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c4 uni28C4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c5 uni28C5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c6 uni28C6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c7 uni28C7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c8 uni28C8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28c9 uni28C9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ca uni28CA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28cb uni28CB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28cc uni28CC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28cd uni28CD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ce uni28CE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28cf uni28CF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d0 uni28D0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d1 uni28D1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d2 uni28D2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d3 uni28D3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d4 uni28D4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d5 uni28D5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d6 uni28D6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d7 uni28D7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d8 uni28D8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28d9 uni28D9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28da uni28DA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28db uni28DB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28dc uni28DC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28dd uni28DD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28de uni28DE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28df uni28DF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e0 uni28E0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e1 uni28E1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e2 uni28E2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e3 uni28E3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e4 uni28E4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e5 uni28E5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e6 uni28E6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e7 uni28E7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e8 uni28E8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28e9 uni28E9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ea uni28EA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28eb uni28EB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ec uni28EC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ed uni28ED 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ee uni28EE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ef uni28EF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f0 uni28F0 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f1 uni28F1 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f2 uni28F2 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f3 uni28F3 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f4 uni28F4 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f5 uni28F5 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f6 uni28F6 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f7 uni28F7 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f8 uni28F8 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28f9 uni28F9 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28fa uni28FA 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28fb uni28FB 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28fc uni28FC 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28fd uni28FD 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28fe uni28FE 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+28ff uni28FF 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2900 uni2900 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2901 uni2901 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2902 uni2902 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2903 uni2903 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2904 uni2904 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2905 uni2905 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2906 uni2906 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2907 uni2907 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2908 uni2908 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2909 uni2909 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+290a uni290A 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+290b uni290B 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+290c uni290C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+290d uni290D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+290e uni290E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+290f uni290F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2910 uni2910 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2911 uni2911 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2912 uni2912 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2913 uni2913 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2914 uni2914 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2915 uni2915 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2916 uni2916 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2917 uni2917 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2918 uni2918 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2919 uni2919 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+291a uni291A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+291b uni291B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+291c uni291C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+291d uni291D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+291e uni291E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+291f uni291F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2920 uni2920 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2921 uni2921 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2922 uni2922 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2923 uni2923 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2924 uni2924 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2925 uni2925 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2926 uni2926 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2927 uni2927 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2928 uni2928 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2929 uni2929 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+292a uni292A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+292b uni292B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+292c uni292C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+292d uni292D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+292e uni292E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+292f uni292F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2930 uni2930 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2931 uni2931 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2932 uni2932 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2933 uni2933 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2934 uni2934 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2935 uni2935 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2936 uni2936 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2937 uni2937 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2938 uni2938 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2939 uni2939 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+293a uni293A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+293b uni293B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+293c uni293C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+293d uni293D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+293e uni293E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+293f uni293F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2940 uni2940 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2941 uni2941 2.13 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2942 uni2942 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2943 uni2943 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2944 uni2944 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2945 uni2945 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2946 uni2946 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2947 uni2947 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2948 uni2948 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2949 uni2949 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+294a uni294A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+294b uni294B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+294c uni294C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+294d uni294D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+294e uni294E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+294f uni294F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2950 uni2950 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2951 uni2951 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2952 uni2952 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2953 uni2953 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2954 uni2954 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2955 uni2955 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2956 uni2956 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2957 uni2957 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2958 uni2958 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2959 uni2959 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+295a uni295A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+295b uni295B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+295c uni295C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+295d uni295D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+295e uni295E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+295f uni295F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2960 uni2960 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2961 uni2961 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2962 uni2962 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2963 uni2963 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2964 uni2964 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2965 uni2965 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2966 uni2966 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2967 uni2967 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2968 uni2968 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2969 uni2969 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+296a uni296A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+296b uni296B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+296c uni296C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+296d uni296D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+296e uni296E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+296f uni296F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2970 uni2970 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2971 uni2971 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2972 uni2972 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2973 uni2973 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2974 uni2974 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2975 uni2975 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2976 uni2976 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2977 uni2977 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2978 uni2978 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2979 uni2979 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+297a uni297A 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+297b uni297B 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+297c uni297C 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+297d uni297D 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+297e uni297E 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+297f uni297F 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2983 uni2983 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2984 uni2984 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+29ce uni29CE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29cf uni29CF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29d0 uni29D0 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29d1 uni29D1 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29d2 uni29D2 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29d3 uni29D3 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29d4 uni29D4 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29d5 uni29D5 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+29eb uni29EB 2.2 -U+29fa uni29FA 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+29fb uni29FB 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+2a00 uni2A00 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a01 uni2A01 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a02 uni2A02 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a0c uni2A0C 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2a0d uni2A0D 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2a0e uni2A0E 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.9 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2a0f uni2A0F 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a10 uni2A10 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a11 uni2A11 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a12 uni2A12 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a13 uni2A13 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a14 uni2A14 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a15 uni2A15 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a16 uni2A16 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a17 uni2A17 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a18 uni2A18 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a19 uni2A19 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a1a uni2A1A 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a1b uni2A1B 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a1c uni2A1C 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a2f uni2A2F 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2a7d uni2A7D 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a7e uni2A7E 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a7f uni2A7F 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a80 uni2A80 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a81 uni2A81 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a82 uni2A82 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a83 uni2A83 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a84 uni2A84 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a85 uni2A85 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a86 uni2A86 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a87 uni2A87 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a88 uni2A88 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a89 uni2A89 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a8a uni2A8A 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a8b uni2A8B 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a8c uni2A8C 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a8d uni2A8D 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a8e uni2A8E 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a8f uni2A8F 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a90 uni2A90 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a91 uni2A91 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a92 uni2A92 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a93 uni2A93 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a94 uni2A94 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a95 uni2A95 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a96 uni2A96 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a97 uni2A97 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a98 uni2A98 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a99 uni2A99 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a9a uni2A9A 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a9b uni2A9B 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a9c uni2A9C 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a9d uni2A9D 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a9e uni2A9E 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2a9f uni2A9F 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2aa0 uni2AA0 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2aae uni2AAE 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2aaf uni2AAF 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab0 uni2AB0 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab1 uni2AB1 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab2 uni2AB2 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab3 uni2AB3 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab4 uni2AB4 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab5 uni2AB5 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab6 uni2AB6 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab7 uni2AB7 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab8 uni2AB8 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2ab9 uni2AB9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2aba uni2ABA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2af9 uni2AF9 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2afa uni2AFA 2.7 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2b00 uni2B00 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b01 uni2B01 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b02 uni2B02 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b03 uni2B03 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b04 uni2B04 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b05 uni2B05 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b06 uni2B06 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b07 uni2B07 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b08 uni2B08 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b09 uni2B09 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b0a uni2B0A 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b0b uni2B0B 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b0c uni2B0C 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b0d uni2B0D 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b0e uni2B0E 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b0f uni2B0F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b10 uni2B10 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b11 uni2B11 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2b12 uni2B12 2.3 -U+2b13 uni2B13 2.3 -U+2b14 uni2B14 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b15 uni2B15 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b16 uni2B16 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b17 uni2B17 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b18 uni2B18 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b19 uni2B19 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b1a uni2B1A 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Italic) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+2b1f uni2B1F 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+2b20 uni2B20 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2b21 uni2B21 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2b22 uni2B22 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2b23 uni2B23 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2b24 uni2B24 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+2b53 uni2B53 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+2b54 uni2B54 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+2c60 uni2C60 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c61 uni2C61 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c62 uni2C62 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2c63 uni2C63 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c64 uni2C64 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2c65 uni2C65 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2c66 uni2C66 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2c67 uni2C67 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2c68 uni2C68 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2c69 uni2C69 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2c6a uni2C6A 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2c6b uni2C6B 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2c6c uni2C6C 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) 2.18 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+2c6d uni2C6D 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.31 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c6e uni2C6E 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2c6f uni2C6F 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2c70 uni2C70 2.31 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c71 uni2C71 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.31 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c72 uni2C72 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.31 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c73 uni2C73 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.31 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c74 uni2C74 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+2c75 uni2C75 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.20 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2c76 uni2C76 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.20 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2c77 uni2C77 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.18 (Sans ExtraLight, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.20 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.23 (Serif Italic Condensed) -U+2c79 uni2C79 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2c7a uni2C7A 2.27 -U+2c7b uni2C7B 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+2c7c uni2C7C 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2c7d uni2C7D 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.28 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.31 (Serif Condensed Italic) -U+2c7e uni2C7E 2.31 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2c7f uni2C7F 2.31 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+2d00 uni2D00 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d01 uni2D01 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d02 uni2D02 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d03 uni2D03 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d04 uni2D04 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d05 uni2D05 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d06 uni2D06 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d07 uni2D07 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d08 uni2D08 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d09 uni2D09 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d0a uni2D0A 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d0b uni2D0B 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d0c uni2D0C 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d0d uni2D0D 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d0e uni2D0E 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d0f uni2D0F 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d10 uni2D10 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d11 uni2D11 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d12 uni2D12 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d13 uni2D13 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d14 uni2D14 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d15 uni2D15 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d16 uni2D16 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d17 uni2D17 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d18 uni2D18 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d19 uni2D19 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d1a uni2D1A 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d1b uni2D1B 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d1c uni2D1C 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d1d uni2D1D 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d1e uni2D1E 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d1f uni2D1F 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d20 uni2D20 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d21 uni2D21 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d22 uni2D22 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d23 uni2D23 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d24 uni2D24 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d25 uni2D25 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+2d30 uni2D30 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d31 uni2D31 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d32 uni2D32 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d33 uni2D33 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d34 uni2D34 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d35 uni2D35 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d36 uni2D36 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d37 uni2D37 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d38 uni2D38 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d39 uni2D39 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d3a uni2D3A 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d3b uni2D3B 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d3c uni2D3C 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d3d uni2D3D 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d3e uni2D3E 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d3f uni2D3F 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d40 uni2D40 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d41 uni2D41 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d42 uni2D42 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d43 uni2D43 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d44 uni2D44 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d45 uni2D45 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d46 uni2D46 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d47 uni2D47 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d48 uni2D48 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d49 uni2D49 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d4a uni2D4A 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d4b uni2D4B 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d4c uni2D4C 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d4d uni2D4D 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d4e uni2D4E 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d4f uni2D4F 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d50 uni2D50 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d51 uni2D51 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d52 uni2D52 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d53 uni2D53 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d54 uni2D54 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d55 uni2D55 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d56 uni2D56 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d57 uni2D57 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d58 uni2D58 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d59 uni2D59 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d5a uni2D5A 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d5b uni2D5B 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d5c uni2D5C 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d5d uni2D5D 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d5e uni2D5E 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d5f uni2D5F 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d60 uni2D60 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d61 uni2D61 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d62 uni2D62 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d63 uni2D63 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d64 uni2D64 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d65 uni2D65 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2d6f uni2D6F 2.18 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+2e18 uni2E18 2.26 -U+2e22 uni2E22 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.30 (Sans Bold) 2.31 (Serif Condensed Italic) -U+2e23 uni2E23 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.30 (Sans Bold) 2.31 (Serif Condensed Italic) -U+2e24 uni2E24 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.30 (Sans Bold) 2.31 (Serif Condensed Italic) -U+2e25 uni2E25 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.30 (Sans Bold) 2.31 (Serif Condensed Italic) -U+2e2e uni2E2E 2.26 -U+4dc0 uni4DC0 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc1 uni4DC1 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc2 uni4DC2 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc3 uni4DC3 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc4 uni4DC4 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc5 uni4DC5 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc6 uni4DC6 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc7 uni4DC7 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc8 uni4DC8 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dc9 uni4DC9 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dca uni4DCA 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dcb uni4DCB 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dcc uni4DCC 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dcd uni4DCD 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dce uni4DCE 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dcf uni4DCF 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd0 uni4DD0 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd1 uni4DD1 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd2 uni4DD2 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd3 uni4DD3 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd4 uni4DD4 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd5 uni4DD5 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd6 uni4DD6 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd7 uni4DD7 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd8 uni4DD8 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dd9 uni4DD9 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dda uni4DDA 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4ddb uni4DDB 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4ddc uni4DDC 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4ddd uni4DDD 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dde uni4DDE 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4ddf uni4DDF 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de0 uni4DE0 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de1 uni4DE1 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de2 uni4DE2 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de3 uni4DE3 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de4 uni4DE4 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de5 uni4DE5 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de6 uni4DE6 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de7 uni4DE7 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de8 uni4DE8 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4de9 uni4DE9 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dea uni4DEA 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4deb uni4DEB 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dec uni4DEC 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4ded uni4DED 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dee uni4DEE 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4def uni4DEF 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df0 uni4DF0 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df1 uni4DF1 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df2 uni4DF2 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df3 uni4DF3 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df4 uni4DF4 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df5 uni4DF5 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df6 uni4DF6 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df7 uni4DF7 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df8 uni4DF8 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4df9 uni4DF9 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dfa uni4DFA 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dfb uni4DFB 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dfc uni4DFC 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dfd uni4DFD 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dfe uni4DFE 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+4dff uni4DFF 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a644 uniA644 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a645 uniA645 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a646 uniA646 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a647 uniA647 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a64c uniA64C 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a64d uniA64D 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a650 uniA650 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a651 uniA651 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a654 uniA654 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a655 uniA655 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a656 uniA656 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a657 uniA657 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a662 uniA662 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a663 uniA663 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a664 uniA664 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a665 uniA665 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a666 uniA666 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a667 uniA667 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a668 uniA668 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a669 uniA669 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a66a uniA66A 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a66b uniA66B 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a66c uniA66C 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a66d uniA66D 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a66e uniA66E 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a68a uniA68A 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a68b uniA68B 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a68c uniA68C 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a68d uniA68D 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a694 uniA694 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a695 uniA695 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+a708 uniA708 2.27 -U+a709 uniA709 2.27 -U+a70a uniA70A 2.27 -U+a70b uniA70B 2.27 -U+a70c uniA70C 2.27 -U+a70d uniA70D 2.27 -U+a70e uniA70E 2.27 -U+a70f uniA70F 2.27 -U+a710 uniA710 2.27 -U+a711 uniA711 2.27 -U+a712 uniA712 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a713 uniA713 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a714 uniA714 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a715 uniA715 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a716 uniA716 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.27 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+a71b uniA71B 2.27 -U+a71c uniA71C 2.27 -U+a71d uniA71D 2.27 -U+a71e uniA71E 2.27 -U+a71f uniA71F 2.27 -U+a722 uniA722 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a723 uniA723 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a724 uniA724 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a725 uniA725 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a726 uniA726 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a727 uniA727 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.30 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a728 uniA728 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a729 uniA729 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a72a uniA72A 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a72b uniA72B 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a72c uniA72C 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a72d uniA72D 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a72e uniA72E 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a72f uniA72F 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a730 uniA730 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a731 uniA731 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a732 uniA732 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a733 uniA733 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a734 uniA734 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a735 uniA735 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a736 uniA736 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a737 uniA737 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a738 uniA738 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a739 uniA739 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a73a uniA73A 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a73b uniA73B 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a73c uniA73C 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a73d uniA73D 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.32 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a73e uniA73E 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a73f uniA73F 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a746 uniA746 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a747 uniA747 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a748 uniA748 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a749 uniA749 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a74a uniA74A 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a74b uniA74B 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a74e uniA74E 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a74f uniA74F 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a750 uniA750 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a751 uniA751 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a752 uniA752 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a753 uniA753 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a756 uniA756 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a757 uniA757 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a764 uniA764 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a765 uniA765 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a766 uniA766 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a767 uniA767 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+a768 uniA768 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a769 uniA769 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a77b uniA77B 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a77c uniA77C 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a780 uniA780 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a781 uniA781 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a782 uniA782 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a783 uniA783 2.27 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a784 uniA784 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a785 uniA785 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a786 uniA786 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a787 uniA787 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a789 uniA789 2.28 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a78a uniA78A 2.28 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a78b uniA78B 2.26 -U+a78c uniA78C 2.26 -U+a78d uniA78D 2.31 -U+a78e uniA78E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique) -U+a790 uniA790 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a791 uniA791 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a7fa uniA7FA 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a7fb uniA7FB 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a7fc uniA7FC 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a7fd uniA7FD 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a7fe uniA7FE 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+a7ff uniA7FF 2.26 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) 2.33 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) -U+e000 uniE000 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e001 uniE001 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e002 uniE002 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e003 uniE003 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e004 uniE004 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e005 uniE005 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e006 uniE006 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e007 uniE007 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e008 uniE008 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique) -U+e009 uniE009 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e00a uniE00A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e00b uniE00B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e00c uniE00C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e00d uniE00D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e00e uniE00E 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e00f uniE00F 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e010 uniE010 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e011 uniE011 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e012 uniE012 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e013 uniE013 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e014 uniE014 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) -U+e015 uniE015 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e016 uniE016 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e017 uniE017 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e018 uniE018 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e019 uniE019 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e01a uniE01A 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e01b uniE01B 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e01c uniE01C 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+e01d uniE01D 2.4 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef00 uni02E5.5 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef01 uni02E6.5 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef02 uni02E7.5 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef03 uni02E8.5 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef04 uni02E9.5 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef05 uni02E5.4 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef06 uni02E6.4 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef07 uni02E7.4 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef08 uni02E8.4 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef09 uni02E9.4 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef0a uni02E5.3 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef0b uni02E6.3 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef0c uni02E7.3 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef0d uni02E8.3 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef0e uni02E9.3 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef0f uni02E5.2 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef10 uni02E6.2 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef11 uni02E7.2 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef12 uni02E8.2 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef13 uni02E9.2 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef14 uni02E5.1 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef15 uni02E6.1 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef16 uni02E7.1 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef17 uni02E8.1 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef18 uni02E9.1 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+ef19 stem 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f000 uniF000 2.10 (Sans) 2.11 (Sans Condensed) -U+f001 uniF001 2.10 (Sans) 2.11 (Sans Condensed) -U+f002 uniF002 2.33 (Sans, Sans Condensed) -U+f003 uniF003 2.33 (Sans, Sans Condensed) -U+f208 uniF208 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f20a uniF20A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f215 uniF215 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f216 uniF216 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f217 uniF217 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f21a uniF21A 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f21b uniF21B 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f25f uniF25F 2.6 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+f400 uniF400 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f401 uniF401 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f402 uniF402 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f403 uniF403 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f404 uniF404 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f405 uniF405 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f406 uniF406 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f407 uniF407 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f408 uniF408 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f409 uniF409 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f40a uniF40A 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f40b uniF40B 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f40c uniF40C 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f40d uniF40D 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f40e uniF40E 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f40f uniF40F 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f410 uniF410 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f411 uniF411 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f412 uniF412 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f413 uniF413 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f414 uniF414 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f415 uniF415 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f416 uniF416 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f417 uniF417 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f418 uniF418 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f419 uniF419 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f41a uniF41A 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f41b uniF41B 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f41c uniF41C 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f41d uniF41D 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f41e uniF41E 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f41f uniF41F 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f420 uniF420 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f421 uniF421 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f422 uniF422 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f423 uniF423 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f424 uniF424 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f425 uniF425 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f426 uniF426 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f428 uniF428 2.28 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+f5c5 uniF5C5 2.9 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+f6c4 uniF6C4 2.10 (Serif Bold Italic, Serif Italic) 2.11 (Serif Condensed Bold Italic, Serif Condensed Italic) 2.23 (Serif Italic Condensed) -U+f6c5 uniF6C5 2.5 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.7 (Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique) 2.9 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold, Sans Condensed Oblique, Sans Oblique) 2.18 (Sans ExtraLight) 2.23 (Serif Italic Condensed) -U+f6c6 uniF6C6 2.5 (Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+f6c7 uniF6C7 2.11 (Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+f6c8 uniF6C8 2.11 (Serif Bold Italic, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+f6d1 cyrBreve 2.5 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+f6d4 cyrbreve 2.5 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fb00 uniFB00 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.8 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fb01 fi original -U+fb02 fl original -U+fb03 uniFB03 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.8 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fb04 uniFB04 2.2 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.5 (Sans ExtraLight) 2.8 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fb05 uniFB05 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fb06 uniFB06 2.5 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.8 (Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fb13 uniFB13 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb14 uniFB14 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb15 uniFB15 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb16 uniFB16 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb17 uniFB17 2.3 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb1d uniFB1D 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb1e uniFB1E 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb1f uniFB1F 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb20 uniFB20 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb21 uniFB21 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb22 uniFB22 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb23 uniFB23 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb24 uniFB24 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb25 uniFB25 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb26 uniFB26 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb27 uniFB27 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb28 uniFB28 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb29 uniFB29 2.10 (Sans, Sans Bold, Sans Bold Oblique, Sans ExtraLight, Sans Oblique) 2.11 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb2a uniFB2A 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb2b uniFB2B 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb2c uniFB2C 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb2d uniFB2D 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb2e uniFB2E 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb2f uniFB2F 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb30 uniFB30 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb31 uniFB31 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb32 uniFB32 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb33 uniFB33 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb34 uniFB34 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb35 uniFB35 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb36 uniFB36 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb37 uniFB37 2.11 (Sans Condensed Oblique, Sans Oblique) 2.33 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+fb38 uniFB38 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb39 uniFB39 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb3a uniFB3A 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb3b uniFB3B 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb3c uniFB3C 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb3d uniFB3D 2.33 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb3e uniFB3E 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb3f uniFB3F 2.33 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb40 uniFB40 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb41 uniFB41 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb42 uniFB42 2.33 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb43 uniFB43 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb44 uniFB44 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb45 uniFB45 2.33 (Sans Bold Oblique, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb46 uniFB46 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb47 uniFB47 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb48 uniFB48 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb49 uniFB49 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb4a uniFB4A 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb4b uniFB4B 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb4c uniFB4C 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb4d uniFB4D 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb4e uniFB4E 2.9 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fb4f uniFB4F 2.16 (Sans, Sans Bold, Sans Bold Oblique, Sans Oblique) 2.17 (Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique) -U+fb52 uniFB52 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb53 uniFB53 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb54 uniFB54 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb55 uniFB55 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb56 uniFB56 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb57 uniFB57 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb58 uniFB58 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb59 uniFB59 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb5a uniFB5A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb5b uniFB5B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb5c uniFB5C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb5d uniFB5D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb5e uniFB5E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb5f uniFB5F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb60 uniFB60 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb61 uniFB61 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb62 uniFB62 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb63 uniFB63 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb64 uniFB64 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb65 uniFB65 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb66 uniFB66 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb67 uniFB67 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb68 uniFB68 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb69 uniFB69 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb6a uniFB6A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb6b uniFB6B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb6c uniFB6C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb6d uniFB6D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb6e uniFB6E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb6f uniFB6F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb70 uniFB70 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb71 uniFB71 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb72 uniFB72 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb73 uniFB73 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb74 uniFB74 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb75 uniFB75 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb76 uniFB76 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb77 uniFB77 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb78 uniFB78 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb79 uniFB79 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb7a uniFB7A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb7b uniFB7B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb7c uniFB7C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb7d uniFB7D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb7e uniFB7E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb7f uniFB7F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb80 uniFB80 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb81 uniFB81 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb82 uniFB82 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb83 uniFB83 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb84 uniFB84 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb85 uniFB85 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb86 uniFB86 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb87 uniFB87 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb88 uniFB88 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb89 uniFB89 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb8a uniFB8A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb8b uniFB8B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb8c uniFB8C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb8d uniFB8D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb8e uniFB8E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb8f uniFB8F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb90 uniFB90 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb91 uniFB91 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb92 uniFB92 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb93 uniFB93 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb94 uniFB94 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb95 uniFB95 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb96 uniFB96 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb97 uniFB97 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb98 uniFB98 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb99 uniFB99 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb9a uniFB9A 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb9b uniFB9B 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb9c uniFB9C 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb9d uniFB9D 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fb9e uniFB9E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fb9f uniFB9F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fba0 uniFBA0 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fba1 uniFBA1 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fba2 uniFBA2 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fba3 uniFBA3 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbaa uniFBAA 2.16 (Sans Mono, Sans Mono Bold) 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbab uniFBAB 2.16 (Sans Mono, Sans Mono Bold) 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbac uniFBAC 2.16 (Sans Mono, Sans Mono Bold) 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbad uniFBAD 2.16 (Sans Mono, Sans Mono Bold) 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbd3 uniFBD3 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbd4 uniFBD4 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbd5 uniFBD5 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbd6 uniFBD6 2.31 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbd9 uniFBD9 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbda uniFBDA 2.7 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) -U+fbe8 uniFBE8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fbe9 uniFBE9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fbfc uniFBFC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fbfd uniFBFD 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fbfe uniFBFE 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fbff uniFBFF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe00 uniFE00 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe01 uniFE01 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe02 uniFE02 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe03 uniFE03 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe04 uniFE04 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe05 uniFE05 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe06 uniFE06 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe07 uniFE07 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe08 uniFE08 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe09 uniFE09 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe0a uniFE0A 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe0b uniFE0B 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe0c uniFE0C 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe0d uniFE0D 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe0e uniFE0E 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe0f uniFE0F 2.14 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans ExtraLight, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fe20 uniFE20 2.21 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fe21 uniFE21 2.21 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fe22 uniFE22 2.21 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fe23 uniFE23 2.21 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+fe70 uniFE70 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe71 uniFE71 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe72 uniFE72 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe73 uniFE73 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe74 uniFE74 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe76 uniFE76 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe77 uniFE77 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe78 uniFE78 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe79 uniFE79 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe7a uniFE7A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe7b uniFE7B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe7c uniFE7C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe7d uniFE7D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe7e uniFE7E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe7f uniFE7F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe80 uniFE80 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe81 uniFE81 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe82 uniFE82 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe83 uniFE83 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe84 uniFE84 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe85 uniFE85 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe86 uniFE86 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe87 uniFE87 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe88 uniFE88 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe89 uniFE89 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe8a uniFE8A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe8b uniFE8B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe8c uniFE8C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe8d uniFE8D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe8e uniFE8E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe8f uniFE8F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe90 uniFE90 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe91 uniFE91 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe92 uniFE92 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe93 uniFE93 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe94 uniFE94 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe95 uniFE95 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe96 uniFE96 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe97 uniFE97 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe98 uniFE98 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe99 uniFE99 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe9a uniFE9A 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe9b uniFE9B 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe9c uniFE9C 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe9d uniFE9D 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe9e uniFE9E 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fe9f uniFE9F 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea0 uniFEA0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea1 uniFEA1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea2 uniFEA2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea3 uniFEA3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea4 uniFEA4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea5 uniFEA5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea6 uniFEA6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea7 uniFEA7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea8 uniFEA8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fea9 uniFEA9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feaa uniFEAA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feab uniFEAB 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feac uniFEAC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fead uniFEAD 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feae uniFEAE 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feaf uniFEAF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb0 uniFEB0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb1 uniFEB1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb2 uniFEB2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb3 uniFEB3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb4 uniFEB4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb5 uniFEB5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb6 uniFEB6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb7 uniFEB7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb8 uniFEB8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feb9 uniFEB9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feba uniFEBA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+febb uniFEBB 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+febc uniFEBC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+febd uniFEBD 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+febe uniFEBE 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+febf uniFEBF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec0 uniFEC0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec1 uniFEC1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec2 uniFEC2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec3 uniFEC3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec4 uniFEC4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec5 uniFEC5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec6 uniFEC6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec7 uniFEC7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec8 uniFEC8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fec9 uniFEC9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feca uniFECA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fecb uniFECB 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fecc uniFECC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fecd uniFECD 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fece uniFECE 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fecf uniFECF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed0 uniFED0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed1 uniFED1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed2 uniFED2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed3 uniFED3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed4 uniFED4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed5 uniFED5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed6 uniFED6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed7 uniFED7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed8 uniFED8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fed9 uniFED9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feda uniFEDA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fedb uniFEDB 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fedc uniFEDC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fedd uniFEDD 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fede uniFEDE 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fedf uniFEDF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee0 uniFEE0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee1 uniFEE1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee2 uniFEE2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee3 uniFEE3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee4 uniFEE4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee5 uniFEE5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee6 uniFEE6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee7 uniFEE7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee8 uniFEE8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fee9 uniFEE9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feea uniFEEA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feeb uniFEEB 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feec uniFEEC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feed uniFEED 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feee uniFEEE 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feef uniFEEF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef0 uniFEF0 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef1 uniFEF1 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef2 uniFEF2 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef3 uniFEF3 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef4 uniFEF4 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef5 uniFEF5 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef6 uniFEF6 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef7 uniFEF7 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef8 uniFEF8 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fef9 uniFEF9 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fefa uniFEFA 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fefb uniFEFB 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fefc uniFEFC 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+feff uniFEFF 2.4 (Sans, Sans Bold, Sans Condensed, Sans Condensed Bold) 2.16 (Sans Mono, Sans Mono Bold) -U+fff9 uniFFF9 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fffa uniFFFA 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fffb uniFFFB 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fffc uniFFFC 2.22 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Mono, Sans Mono Bold, Sans Mono Bold Oblique, Sans Mono Oblique, Sans Oblique, Serif, Serif Bold, Serif Bold Italic, Serif Condensed, Serif Condensed Bold, Serif Condensed Bold Italic, Serif Condensed Italic, Serif Italic) 2.23 (Serif Italic Condensed) -U+fffd uniFFFD 1.12 -U+10300 u10300 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10301 u10301 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10302 u10302 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10303 u10303 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10304 u10304 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10305 u10305 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10306 u10306 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10307 u10307 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10308 u10308 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10309 u10309 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1030a u1030A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1030b u1030B 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1030c u1030C 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1030d u1030D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1030e u1030E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1030f u1030F 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10310 u10310 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10311 u10311 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10312 u10312 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10313 u10313 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10314 u10314 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10315 u10315 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10316 u10316 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10317 u10317 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10318 u10318 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10319 u10319 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1031a u1031A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1031b u1031B 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1031c u1031C 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1031d u1031D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1031e u1031E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10320 u10320 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10321 u10321 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10322 u10322 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+10323 u10323 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d300 u1D300 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d301 u1D301 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d302 u1D302 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d303 u1D303 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d304 u1D304 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d305 u1D305 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d306 u1D306 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d307 u1D307 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d308 u1D308 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d309 u1D309 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d30a u1D30A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d30b u1D30B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d30c u1D30C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d30d u1D30D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d30e u1D30E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d30f u1D30F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d310 u1D310 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d311 u1D311 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d312 u1D312 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d313 u1D313 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d314 u1D314 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d315 u1D315 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d316 u1D316 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d317 u1D317 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d318 u1D318 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d319 u1D319 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d31a u1D31A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d31b u1D31B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d31c u1D31C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d31d u1D31D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d31e u1D31E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d31f u1D31F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d320 u1D320 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d321 u1D321 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d322 u1D322 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d323 u1D323 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d324 u1D324 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d325 u1D325 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d326 u1D326 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d327 u1D327 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d328 u1D328 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d329 u1D329 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d32a u1D32A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d32b u1D32B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d32c u1D32C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d32d u1D32D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d32e u1D32E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d32f u1D32F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d330 u1D330 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d331 u1D331 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d332 u1D332 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d333 u1D333 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d334 u1D334 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d335 u1D335 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d336 u1D336 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d337 u1D337 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d338 u1D338 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d339 u1D339 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d33a u1D33A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d33b u1D33B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d33c u1D33C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d33d u1D33D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d33e u1D33E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d33f u1D33F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d340 u1D340 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d341 u1D341 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d342 u1D342 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d343 u1D343 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d344 u1D344 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d345 u1D345 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d346 u1D346 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d347 u1D347 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d348 u1D348 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d349 u1D349 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d34a u1D34A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d34b u1D34B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d34c u1D34C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d34d u1D34D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d34e u1D34E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d34f u1D34F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d350 u1D350 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d351 u1D351 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d352 u1D352 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d353 u1D353 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d354 u1D354 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d355 u1D355 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d356 u1D356 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1d400 u1D400 2.23 (Serif Bold, Serif Condensed Bold) -U+1d401 u1D401 2.23 (Serif Bold, Serif Condensed Bold) -U+1d402 u1D402 2.23 (Serif Bold, Serif Condensed Bold) -U+1d403 u1D403 2.23 (Serif Bold, Serif Condensed Bold) -U+1d404 u1D404 2.23 (Serif Bold, Serif Condensed Bold) -U+1d405 u1D405 2.23 (Serif Bold, Serif Condensed Bold) -U+1d406 u1D406 2.23 (Serif Bold, Serif Condensed Bold) -U+1d407 u1D407 2.23 (Serif Bold, Serif Condensed Bold) -U+1d408 u1D408 2.23 (Serif Bold, Serif Condensed Bold) -U+1d409 u1D409 2.23 (Serif Bold, Serif Condensed Bold) -U+1d40a u1D40A 2.23 (Serif Bold, Serif Condensed Bold) -U+1d40b u1D40B 2.23 (Serif Bold, Serif Condensed Bold) -U+1d40c u1D40C 2.23 (Serif Bold, Serif Condensed Bold) -U+1d40d u1D40D 2.23 (Serif Bold, Serif Condensed Bold) -U+1d40e u1D40E 2.23 (Serif Bold, Serif Condensed Bold) -U+1d40f u1D40F 2.23 (Serif Bold, Serif Condensed Bold) -U+1d410 u1D410 2.23 (Serif Bold, Serif Condensed Bold) -U+1d411 u1D411 2.23 (Serif Bold, Serif Condensed Bold) -U+1d412 u1D412 2.23 (Serif Bold, Serif Condensed Bold) -U+1d413 u1D413 2.23 (Serif Bold, Serif Condensed Bold) -U+1d414 u1D414 2.23 (Serif Bold, Serif Condensed Bold) -U+1d415 u1D415 2.23 (Serif Bold, Serif Condensed Bold) -U+1d416 u1D416 2.23 (Serif Bold, Serif Condensed Bold) -U+1d417 u1D417 2.23 (Serif Bold, Serif Condensed Bold) -U+1d418 u1D418 2.23 (Serif Bold, Serif Condensed Bold) -U+1d419 u1D419 2.23 (Serif Bold, Serif Condensed Bold) -U+1d41a u1D41A 2.23 (Serif Bold, Serif Condensed Bold) -U+1d41b u1D41B 2.23 (Serif Bold, Serif Condensed Bold) -U+1d41c u1D41C 2.23 (Serif Bold, Serif Condensed Bold) -U+1d41d u1D41D 2.23 (Serif Bold, Serif Condensed Bold) -U+1d41e u1D41E 2.23 (Serif Bold, Serif Condensed Bold) -U+1d41f u1D41F 2.23 (Serif Bold, Serif Condensed Bold) -U+1d420 u1D420 2.23 (Serif Bold, Serif Condensed Bold) -U+1d421 u1D421 2.23 (Serif Bold, Serif Condensed Bold) -U+1d422 u1D422 2.23 (Serif Bold, Serif Condensed Bold) -U+1d423 u1D423 2.23 (Serif Bold, Serif Condensed Bold) -U+1d424 u1D424 2.23 (Serif Bold, Serif Condensed Bold) -U+1d425 u1D425 2.23 (Serif Bold, Serif Condensed Bold) -U+1d426 u1D426 2.23 (Serif Bold, Serif Condensed Bold) -U+1d427 u1D427 2.23 (Serif Bold, Serif Condensed Bold) -U+1d428 u1D428 2.23 (Serif Bold, Serif Condensed Bold) -U+1d429 u1D429 2.23 (Serif Bold, Serif Condensed Bold) -U+1d42a u1D42A 2.23 (Serif Bold, Serif Condensed Bold) -U+1d42b u1D42B 2.23 (Serif Bold, Serif Condensed Bold) -U+1d42c u1D42C 2.23 (Serif Bold, Serif Condensed Bold) -U+1d42d u1D42D 2.23 (Serif Bold, Serif Condensed Bold) -U+1d42e u1D42E 2.23 (Serif Bold, Serif Condensed Bold) -U+1d42f u1D42F 2.23 (Serif Bold, Serif Condensed Bold) -U+1d430 u1D430 2.23 (Serif Bold, Serif Condensed Bold) -U+1d431 u1D431 2.23 (Serif Bold, Serif Condensed Bold) -U+1d432 u1D432 2.23 (Serif Bold, Serif Condensed Bold) -U+1d433 u1D433 2.23 (Serif Bold, Serif Condensed Bold) -U+1d434 u1D434 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d435 u1D435 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d436 u1D436 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d437 u1D437 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d438 u1D438 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d439 u1D439 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d43a u1D43A 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d43b u1D43B 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d43c u1D43C 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d43d u1D43D 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d43e u1D43E 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d43f u1D43F 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d440 u1D440 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d441 u1D441 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d442 u1D442 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d443 u1D443 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d444 u1D444 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d445 u1D445 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d446 u1D446 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d447 u1D447 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d448 u1D448 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d449 u1D449 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d44a u1D44A 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d44b u1D44B 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d44c u1D44C 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d44d u1D44D 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d44e u1D44E 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d44f u1D44F 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d450 u1D450 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d451 u1D451 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d452 u1D452 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d453 u1D453 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d454 u1D454 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d456 u1D456 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d457 u1D457 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d458 u1D458 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d459 u1D459 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d45a u1D45A 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d45b u1D45B 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d45c u1D45C 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d45d u1D45D 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d45e u1D45E 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d45f u1D45F 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d460 u1D460 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d461 u1D461 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d462 u1D462 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d463 u1D463 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d464 u1D464 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d465 u1D465 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d466 u1D466 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d467 u1D467 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d468 u1D468 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d469 u1D469 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d46a u1D46A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d46b u1D46B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d46c u1D46C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d46d u1D46D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d46e u1D46E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d46f u1D46F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d470 u1D470 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d471 u1D471 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d472 u1D472 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d473 u1D473 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d474 u1D474 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d475 u1D475 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d476 u1D476 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d477 u1D477 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d478 u1D478 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d479 u1D479 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d47a u1D47A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d47b u1D47B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d47c u1D47C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d47d u1D47D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d47e u1D47E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d47f u1D47F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d480 u1D480 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d481 u1D481 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d482 u1D482 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d483 u1D483 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d484 u1D484 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d485 u1D485 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d486 u1D486 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d487 u1D487 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d488 u1D488 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d489 u1D489 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d48a u1D48A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d48b u1D48B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d48c u1D48C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d48d u1D48D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d48e u1D48E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d48f u1D48F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d490 u1D490 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d491 u1D491 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d492 u1D492 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d493 u1D493 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d494 u1D494 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d495 u1D495 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d496 u1D496 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d497 u1D497 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d498 u1D498 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d499 u1D499 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d49a u1D49A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d49b u1D49B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d538 u1D538 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d539 u1D539 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d53b u1D53B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d53c u1D53C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d53d u1D53D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d53e u1D53E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d540 u1D540 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d541 u1D541 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d542 u1D542 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d543 u1D543 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d544 u1D544 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d546 u1D546 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d54a u1D54A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d54b u1D54B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d54c u1D54C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d54d u1D54D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d54e u1D54E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d54f u1D54F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d550 u1D550 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d552 u1D552 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d553 u1D553 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d554 u1D554 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d555 u1D555 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d556 u1D556 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d557 u1D557 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d558 u1D558 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d559 u1D559 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d55a u1D55A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d55b u1D55B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d55c u1D55C 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d55d u1D55D 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d55e u1D55E 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d55f u1D55F 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d560 u1D560 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d561 u1D561 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d562 u1D562 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d563 u1D563 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d564 u1D564 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d565 u1D565 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d566 u1D566 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d567 u1D567 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d568 u1D568 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d569 u1D569 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d56a u1D56A 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d56b u1D56B 2.18 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.22 (Serif, Serif Condensed) -U+1d5a0 u1D5A0 2.23 (Sans, Sans Condensed) -U+1d5a1 u1D5A1 2.23 (Sans, Sans Condensed) -U+1d5a2 u1D5A2 2.23 (Sans, Sans Condensed) -U+1d5a3 u1D5A3 2.23 (Sans, Sans Condensed) -U+1d5a4 u1D5A4 2.23 (Sans, Sans Condensed) -U+1d5a5 u1D5A5 2.23 (Sans, Sans Condensed) -U+1d5a6 u1D5A6 2.23 (Sans, Sans Condensed) -U+1d5a7 u1D5A7 2.23 (Sans, Sans Condensed) -U+1d5a8 u1D5A8 2.23 (Sans, Sans Condensed) -U+1d5a9 u1D5A9 2.23 (Sans, Sans Condensed) -U+1d5aa u1D5AA 2.23 (Sans, Sans Condensed) -U+1d5ab u1D5AB 2.23 (Sans, Sans Condensed) -U+1d5ac u1D5AC 2.23 (Sans, Sans Condensed) -U+1d5ad u1D5AD 2.23 (Sans, Sans Condensed) -U+1d5ae u1D5AE 2.23 (Sans, Sans Condensed) -U+1d5af u1D5AF 2.23 (Sans, Sans Condensed) -U+1d5b0 u1D5B0 2.23 (Sans, Sans Condensed) -U+1d5b1 u1D5B1 2.23 (Sans, Sans Condensed) -U+1d5b2 u1D5B2 2.23 (Sans, Sans Condensed) -U+1d5b3 u1D5B3 2.23 (Sans, Sans Condensed) -U+1d5b4 u1D5B4 2.23 (Sans, Sans Condensed) -U+1d5b5 u1D5B5 2.23 (Sans, Sans Condensed) -U+1d5b6 u1D5B6 2.23 (Sans, Sans Condensed) -U+1d5b7 u1D5B7 2.23 (Sans, Sans Condensed) -U+1d5b8 u1D5B8 2.23 (Sans, Sans Condensed) -U+1d5b9 u1D5B9 2.23 (Sans, Sans Condensed) -U+1d5ba u1D5BA 2.23 (Sans, Sans Condensed) -U+1d5bb u1D5BB 2.23 (Sans, Sans Condensed) -U+1d5bc u1D5BC 2.23 (Sans, Sans Condensed) -U+1d5bd u1D5BD 2.23 (Sans, Sans Condensed) -U+1d5be u1D5BE 2.23 (Sans, Sans Condensed) -U+1d5bf u1D5BF 2.23 (Sans, Sans Condensed) -U+1d5c0 u1D5C0 2.23 (Sans, Sans Condensed) -U+1d5c1 u1D5C1 2.23 (Sans, Sans Condensed) -U+1d5c2 u1D5C2 2.23 (Sans, Sans Condensed) -U+1d5c3 u1D5C3 2.23 (Sans, Sans Condensed) -U+1d5c4 u1D5C4 2.23 (Sans, Sans Condensed) -U+1d5c5 u1D5C5 2.23 (Sans, Sans Condensed) -U+1d5c6 u1D5C6 2.23 (Sans, Sans Condensed) -U+1d5c7 u1D5C7 2.23 (Sans, Sans Condensed) -U+1d5c8 u1D5C8 2.23 (Sans, Sans Condensed) -U+1d5c9 u1D5C9 2.23 (Sans, Sans Condensed) -U+1d5ca u1D5CA 2.23 (Sans, Sans Condensed) -U+1d5cb u1D5CB 2.23 (Sans, Sans Condensed) -U+1d5cc u1D5CC 2.23 (Sans, Sans Condensed) -U+1d5cd u1D5CD 2.23 (Sans, Sans Condensed) -U+1d5ce u1D5CE 2.23 (Sans, Sans Condensed) -U+1d5cf u1D5CF 2.23 (Sans, Sans Condensed) -U+1d5d0 u1D5D0 2.23 (Sans, Sans Condensed) -U+1d5d1 u1D5D1 2.23 (Sans, Sans Condensed) -U+1d5d2 u1D5D2 2.23 (Sans, Sans Condensed) -U+1d5d3 u1D5D3 2.23 (Sans, Sans Condensed) -U+1d5d4 u1D5D4 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5d5 u1D5D5 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5d6 u1D5D6 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5d7 u1D5D7 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5d8 u1D5D8 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5d9 u1D5D9 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5da u1D5DA 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5db u1D5DB 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5dc u1D5DC 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5dd u1D5DD 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5de u1D5DE 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5df u1D5DF 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e0 u1D5E0 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e1 u1D5E1 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e2 u1D5E2 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e3 u1D5E3 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e4 u1D5E4 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e5 u1D5E5 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e6 u1D5E6 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e7 u1D5E7 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e8 u1D5E8 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5e9 u1D5E9 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5ea u1D5EA 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5eb u1D5EB 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5ec u1D5EC 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5ed u1D5ED 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5ee u1D5EE 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5ef u1D5EF 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f0 u1D5F0 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f1 u1D5F1 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f2 u1D5F2 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f3 u1D5F3 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f4 u1D5F4 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f5 u1D5F5 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f6 u1D5F6 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f7 u1D5F7 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f8 u1D5F8 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5f9 u1D5F9 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5fa u1D5FA 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5fb u1D5FB 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5fc u1D5FC 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5fd u1D5FD 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5fe u1D5FE 2.23 (Sans Bold, Sans Condensed Bold) -U+1d5ff u1D5FF 2.23 (Sans Bold, Sans Condensed Bold) -U+1d600 u1D600 2.23 (Sans Bold, Sans Condensed Bold) -U+1d601 u1D601 2.23 (Sans Bold, Sans Condensed Bold) -U+1d602 u1D602 2.23 (Sans Bold, Sans Condensed Bold) -U+1d603 u1D603 2.23 (Sans Bold, Sans Condensed Bold) -U+1d604 u1D604 2.23 (Sans Bold, Sans Condensed Bold) -U+1d605 u1D605 2.23 (Sans Bold, Sans Condensed Bold) -U+1d606 u1D606 2.23 (Sans Bold, Sans Condensed Bold) -U+1d607 u1D607 2.23 (Sans Bold, Sans Condensed Bold) -U+1d608 u1D608 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d609 u1D609 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d60a u1D60A 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d60b u1D60B 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d60c u1D60C 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d60d u1D60D 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d60e u1D60E 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d60f u1D60F 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d610 u1D610 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d611 u1D611 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d612 u1D612 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d613 u1D613 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d614 u1D614 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d615 u1D615 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d616 u1D616 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d617 u1D617 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d618 u1D618 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d619 u1D619 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d61a u1D61A 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d61b u1D61B 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d61c u1D61C 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d61d u1D61D 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d61e u1D61E 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d61f u1D61F 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d620 u1D620 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d621 u1D621 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d622 u1D622 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d623 u1D623 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d624 u1D624 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d625 u1D625 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d626 u1D626 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d627 u1D627 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d628 u1D628 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d629 u1D629 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d62a u1D62A 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d62b u1D62B 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d62c u1D62C 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d62d u1D62D 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d62e u1D62E 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d62f u1D62F 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d630 u1D630 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d631 u1D631 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d632 u1D632 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d633 u1D633 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d634 u1D634 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d635 u1D635 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d636 u1D636 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d637 u1D637 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d638 u1D638 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d639 u1D639 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d63a u1D63A 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d63b u1D63B 2.23 (Sans Condensed Oblique, Sans Oblique) -U+1d63c u1D63C 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d63d u1D63D 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d63e u1D63E 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d63f u1D63F 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d640 u1D640 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d641 u1D641 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d642 u1D642 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d643 u1D643 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d644 u1D644 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d645 u1D645 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d646 u1D646 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d647 u1D647 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d648 u1D648 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d649 u1D649 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d64a u1D64A 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d64b u1D64B 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d64c u1D64C 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d64d u1D64D 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d64e u1D64E 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d64f u1D64F 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d650 u1D650 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d651 u1D651 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d652 u1D652 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d653 u1D653 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d654 u1D654 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d655 u1D655 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d656 u1D656 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d657 u1D657 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d658 u1D658 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d659 u1D659 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d65a u1D65A 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d65b u1D65B 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d65c u1D65C 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d65d u1D65D 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d65e u1D65E 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d65f u1D65F 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d660 u1D660 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d661 u1D661 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d662 u1D662 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d663 u1D663 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d664 u1D664 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d665 u1D665 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d666 u1D666 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d667 u1D667 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d668 u1D668 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d669 u1D669 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d66a u1D66A 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d66b u1D66B 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d66c u1D66C 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d66d u1D66D 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d66e u1D66E 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d66f u1D66F 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d670 u1D670 2.23 (Sans Mono) -U+1d671 u1D671 2.23 (Sans Mono) -U+1d672 u1D672 2.23 (Sans Mono) -U+1d673 u1D673 2.23 (Sans Mono) -U+1d674 u1D674 2.23 (Sans Mono) -U+1d675 u1D675 2.23 (Sans Mono) -U+1d676 u1D676 2.23 (Sans Mono) -U+1d677 u1D677 2.23 (Sans Mono) -U+1d678 u1D678 2.23 (Sans Mono) -U+1d679 u1D679 2.23 (Sans Mono) -U+1d67a u1D67A 2.23 (Sans Mono) -U+1d67b u1D67B 2.23 (Sans Mono) -U+1d67c u1D67C 2.23 (Sans Mono) -U+1d67d u1D67D 2.23 (Sans Mono) -U+1d67e u1D67E 2.23 (Sans Mono) -U+1d67f u1D67F 2.23 (Sans Mono) -U+1d680 u1D680 2.23 (Sans Mono) -U+1d681 u1D681 2.23 (Sans Mono) -U+1d682 u1D682 2.23 (Sans Mono) -U+1d683 u1D683 2.23 (Sans Mono) -U+1d684 u1D684 2.23 (Sans Mono) -U+1d685 u1D685 2.23 (Sans Mono) -U+1d686 u1D686 2.23 (Sans Mono) -U+1d687 u1D687 2.23 (Sans Mono) -U+1d688 u1D688 2.23 (Sans Mono) -U+1d689 u1D689 2.23 (Sans Mono) -U+1d68a u1D68A 2.23 (Sans Mono) -U+1d68b u1D68B 2.23 (Sans Mono) -U+1d68c u1D68C 2.23 (Sans Mono) -U+1d68d u1D68D 2.23 (Sans Mono) -U+1d68e u1D68E 2.23 (Sans Mono) -U+1d68f u1D68F 2.23 (Sans Mono) -U+1d690 u1D690 2.23 (Sans Mono) -U+1d691 u1D691 2.23 (Sans Mono) -U+1d692 u1D692 2.23 (Sans Mono) -U+1d693 u1D693 2.23 (Sans Mono) -U+1d694 u1D694 2.23 (Sans Mono) -U+1d695 u1D695 2.23 (Sans Mono) -U+1d696 u1D696 2.23 (Sans Mono) -U+1d697 u1D697 2.23 (Sans Mono) -U+1d698 u1D698 2.23 (Sans Mono) -U+1d699 u1D699 2.23 (Sans Mono) -U+1d69a u1D69A 2.23 (Sans Mono) -U+1d69b u1D69B 2.23 (Sans Mono) -U+1d69c u1D69C 2.23 (Sans Mono) -U+1d69d u1D69D 2.23 (Sans Mono) -U+1d69e u1D69E 2.23 (Sans Mono) -U+1d69f u1D69F 2.23 (Sans Mono) -U+1d6a0 u1D6A0 2.23 (Sans Mono) -U+1d6a1 u1D6A1 2.23 (Sans Mono) -U+1d6a2 u1D6A2 2.23 (Sans Mono) -U+1d6a3 u1D6A3 2.23 (Sans Mono) -U+1d6a4 u1D6A4 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6a5 u1D6A5 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6a8 u1D6A8 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6a9 u1D6A9 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6aa u1D6AA 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ab u1D6AB 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ac u1D6AC 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ad u1D6AD 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ae u1D6AE 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6af u1D6AF 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b0 u1D6B0 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b1 u1D6B1 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b2 u1D6B2 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b3 u1D6B3 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b4 u1D6B4 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b5 u1D6B5 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b6 u1D6B6 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b7 u1D6B7 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b8 u1D6B8 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6b9 u1D6B9 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ba u1D6BA 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6bb u1D6BB 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6bc u1D6BC 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6bd u1D6BD 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6be u1D6BE 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6bf u1D6BF 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c0 u1D6C0 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c1 u1D6C1 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c2 u1D6C2 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c3 u1D6C3 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c4 u1D6C4 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c5 u1D6C5 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c6 u1D6C6 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c7 u1D6C7 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c8 u1D6C8 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6c9 u1D6C9 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ca u1D6CA 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6cb u1D6CB 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6cc u1D6CC 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6cd u1D6CD 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6ce u1D6CE 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6cf u1D6CF 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d0 u1D6D0 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d1 u1D6D1 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d2 u1D6D2 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d3 u1D6D3 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d4 u1D6D4 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d5 u1D6D5 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d6 u1D6D6 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d7 u1D6D7 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d8 u1D6D8 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6d9 u1D6D9 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6da u1D6DA 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6db u1D6DB 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6dc u1D6DC 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6dd u1D6DD 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6de u1D6DE 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6df u1D6DF 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6e0 u1D6E0 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6e1 u1D6E1 2.23 (Serif Bold, Serif Condensed Bold) -U+1d6e2 u1D6E2 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e3 u1D6E3 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e4 u1D6E4 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e5 u1D6E5 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e6 u1D6E6 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e7 u1D6E7 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e8 u1D6E8 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6e9 u1D6E9 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6ea u1D6EA 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6eb u1D6EB 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6ec u1D6EC 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6ed u1D6ED 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6ee u1D6EE 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6ef u1D6EF 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f0 u1D6F0 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f1 u1D6F1 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f2 u1D6F2 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f3 u1D6F3 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f4 u1D6F4 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f5 u1D6F5 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f6 u1D6F6 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f7 u1D6F7 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f8 u1D6F8 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6f9 u1D6F9 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6fa u1D6FA 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6fb u1D6FB 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6fc u1D6FC 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6fd u1D6FD 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6fe u1D6FE 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d6ff u1D6FF 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d700 u1D700 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d701 u1D701 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d702 u1D702 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d703 u1D703 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d704 u1D704 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d705 u1D705 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d706 u1D706 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d707 u1D707 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d708 u1D708 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d709 u1D709 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d70a u1D70A 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d70b u1D70B 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d70c u1D70C 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d70d u1D70D 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d70e u1D70E 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d70f u1D70F 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d710 u1D710 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d711 u1D711 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d712 u1D712 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d713 u1D713 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d714 u1D714 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d715 u1D715 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d716 u1D716 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d717 u1D717 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d718 u1D718 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d719 u1D719 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d71a u1D71A 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d71b u1D71B 2.23 (Serif Italic, Serif Italic Condensed) 2.31 (Serif Condensed Italic) -U+1d71c u1D71C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d71d u1D71D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d71e u1D71E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d71f u1D71F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d720 u1D720 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d721 u1D721 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d722 u1D722 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d723 u1D723 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d724 u1D724 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d725 u1D725 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d726 u1D726 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d727 u1D727 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d728 u1D728 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d729 u1D729 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d72a u1D72A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d72b u1D72B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d72c u1D72C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d72d u1D72D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d72e u1D72E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d72f u1D72F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d730 u1D730 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d731 u1D731 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d732 u1D732 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d733 u1D733 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d734 u1D734 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d735 u1D735 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d736 u1D736 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d737 u1D737 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d738 u1D738 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d739 u1D739 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d73a u1D73A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d73b u1D73B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d73c u1D73C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d73d u1D73D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d73e u1D73E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d73f u1D73F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d740 u1D740 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d741 u1D741 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d742 u1D742 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d743 u1D743 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d744 u1D744 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d745 u1D745 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d746 u1D746 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d747 u1D747 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d748 u1D748 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d749 u1D749 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d74a u1D74A 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d74b u1D74B 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d74c u1D74C 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d74d u1D74D 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d74e u1D74E 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d74f u1D74F 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d750 u1D750 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d751 u1D751 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d752 u1D752 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d753 u1D753 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d754 u1D754 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d755 u1D755 2.23 (Serif Bold Italic, Serif Condensed Bold Italic) -U+1d756 u1D756 2.23 (Sans Bold, Sans Condensed Bold) -U+1d757 u1D757 2.23 (Sans Bold, Sans Condensed Bold) -U+1d758 u1D758 2.23 (Sans Bold, Sans Condensed Bold) -U+1d759 u1D759 2.23 (Sans Bold, Sans Condensed Bold) -U+1d75a u1D75A 2.23 (Sans Bold, Sans Condensed Bold) -U+1d75b u1D75B 2.23 (Sans Bold, Sans Condensed Bold) -U+1d75c u1D75C 2.23 (Sans Bold, Sans Condensed Bold) -U+1d75d u1D75D 2.23 (Sans Bold, Sans Condensed Bold) -U+1d75e u1D75E 2.23 (Sans Bold, Sans Condensed Bold) -U+1d75f u1D75F 2.23 (Sans Bold, Sans Condensed Bold) -U+1d760 u1D760 2.23 (Sans Bold, Sans Condensed Bold) -U+1d761 u1D761 2.23 (Sans Bold, Sans Condensed Bold) -U+1d762 u1D762 2.23 (Sans Bold, Sans Condensed Bold) -U+1d763 u1D763 2.23 (Sans Bold, Sans Condensed Bold) -U+1d764 u1D764 2.23 (Sans Bold, Sans Condensed Bold) -U+1d765 u1D765 2.23 (Sans Bold, Sans Condensed Bold) -U+1d766 u1D766 2.23 (Sans Bold, Sans Condensed Bold) -U+1d767 u1D767 2.23 (Sans Bold, Sans Condensed Bold) -U+1d768 u1D768 2.23 (Sans Bold, Sans Condensed Bold) -U+1d769 u1D769 2.23 (Sans Bold, Sans Condensed Bold) -U+1d76a u1D76A 2.23 (Sans Bold, Sans Condensed Bold) -U+1d76b u1D76B 2.23 (Sans Bold, Sans Condensed Bold) -U+1d76c u1D76C 2.23 (Sans Bold, Sans Condensed Bold) -U+1d76d u1D76D 2.23 (Sans Bold, Sans Condensed Bold) -U+1d76e u1D76E 2.23 (Sans Bold, Sans Condensed Bold) -U+1d76f u1D76F 2.23 (Sans Bold, Sans Condensed Bold) -U+1d770 u1D770 2.23 (Sans Bold, Sans Condensed Bold) -U+1d771 u1D771 2.23 (Sans Bold, Sans Condensed Bold) -U+1d772 u1D772 2.23 (Sans Bold, Sans Condensed Bold) -U+1d773 u1D773 2.23 (Sans Bold, Sans Condensed Bold) -U+1d774 u1D774 2.23 (Sans Bold, Sans Condensed Bold) -U+1d775 u1D775 2.23 (Sans Bold, Sans Condensed Bold) -U+1d776 u1D776 2.23 (Sans Bold, Sans Condensed Bold) -U+1d777 u1D777 2.23 (Sans Bold, Sans Condensed Bold) -U+1d778 u1D778 2.23 (Sans Bold, Sans Condensed Bold) -U+1d779 u1D779 2.23 (Sans Bold, Sans Condensed Bold) -U+1d77a u1D77A 2.23 (Sans Bold, Sans Condensed Bold) -U+1d77b u1D77B 2.23 (Sans Bold, Sans Condensed Bold) -U+1d77c u1D77C 2.23 (Sans Bold, Sans Condensed Bold) -U+1d77d u1D77D 2.23 (Sans Bold, Sans Condensed Bold) -U+1d77e u1D77E 2.23 (Sans Bold, Sans Condensed Bold) -U+1d77f u1D77F 2.23 (Sans Bold, Sans Condensed Bold) -U+1d780 u1D780 2.23 (Sans Bold, Sans Condensed Bold) -U+1d781 u1D781 2.23 (Sans Bold, Sans Condensed Bold) -U+1d782 u1D782 2.23 (Sans Bold, Sans Condensed Bold) -U+1d783 u1D783 2.23 (Sans Bold, Sans Condensed Bold) -U+1d784 u1D784 2.23 (Sans Bold, Sans Condensed Bold) -U+1d785 u1D785 2.23 (Sans Bold, Sans Condensed Bold) -U+1d786 u1D786 2.23 (Sans Bold, Sans Condensed Bold) -U+1d787 u1D787 2.23 (Sans Bold, Sans Condensed Bold) -U+1d788 u1D788 2.23 (Sans Bold, Sans Condensed Bold) -U+1d789 u1D789 2.23 (Sans Bold, Sans Condensed Bold) -U+1d78a u1D78A 2.23 (Sans Bold, Sans Condensed Bold) -U+1d78b u1D78B 2.23 (Sans Bold, Sans Condensed Bold) -U+1d78c u1D78C 2.23 (Sans Bold, Sans Condensed Bold) -U+1d78d u1D78D 2.23 (Sans Bold, Sans Condensed Bold) -U+1d78e u1D78E 2.23 (Sans Bold, Sans Condensed Bold) -U+1d78f u1D78F 2.23 (Sans Bold, Sans Condensed Bold) -U+1d790 u1D790 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d791 u1D791 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d792 u1D792 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d793 u1D793 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d794 u1D794 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d795 u1D795 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d796 u1D796 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d797 u1D797 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d798 u1D798 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d799 u1D799 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d79a u1D79A 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d79b u1D79B 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d79c u1D79C 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d79d u1D79D 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d79e u1D79E 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d79f u1D79F 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a0 u1D7A0 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a1 u1D7A1 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a2 u1D7A2 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a3 u1D7A3 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a4 u1D7A4 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a5 u1D7A5 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a6 u1D7A6 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a7 u1D7A7 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a8 u1D7A8 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7a9 u1D7A9 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7aa u1D7AA 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7ab u1D7AB 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7ac u1D7AC 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7ad u1D7AD 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7ae u1D7AE 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7af u1D7AF 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b0 u1D7B0 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b1 u1D7B1 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b2 u1D7B2 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b3 u1D7B3 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b4 u1D7B4 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b5 u1D7B5 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b6 u1D7B6 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b7 u1D7B7 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b8 u1D7B8 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7b9 u1D7B9 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7ba u1D7BA 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7bb u1D7BB 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7bc u1D7BC 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7bd u1D7BD 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7be u1D7BE 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7bf u1D7BF 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c0 u1D7C0 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c1 u1D7C1 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c2 u1D7C2 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c3 u1D7C3 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c4 u1D7C4 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c5 u1D7C5 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c6 u1D7C6 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c7 u1D7C7 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c8 u1D7C8 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7c9 u1D7C9 2.23 (Sans Bold Oblique, Sans Condensed Bold Oblique) -U+1d7ca u1D7CA 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7cb u1D7CB 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7ce u1D7CE 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7cf u1D7CF 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d0 u1D7D0 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d1 u1D7D1 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d2 u1D7D2 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d3 u1D7D3 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d4 u1D7D4 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d5 u1D7D5 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d6 u1D7D6 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d7 u1D7D7 2.23 (Serif Bold, Serif Condensed Bold) -U+1d7d8 u1D7D8 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7d9 u1D7D9 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7da u1D7DA 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7db u1D7DB 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7dc u1D7DC 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7dd u1D7DD 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7de u1D7DE 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7df u1D7DF 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7e0 u1D7E0 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7e1 u1D7E1 2.22 (Serif, Serif Condensed) 2.29 (Sans, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) 2.30 (Sans Bold) -U+1d7e2 u1D7E2 2.23 (Sans, Sans Condensed) -U+1d7e3 u1D7E3 2.23 (Sans, Sans Condensed) -U+1d7e4 u1D7E4 2.23 (Sans, Sans Condensed) -U+1d7e5 u1D7E5 2.23 (Sans, Sans Condensed) -U+1d7e6 u1D7E6 2.23 (Sans, Sans Condensed) -U+1d7e7 u1D7E7 2.23 (Sans, Sans Condensed) -U+1d7e8 u1D7E8 2.23 (Sans, Sans Condensed) -U+1d7e9 u1D7E9 2.23 (Sans, Sans Condensed) -U+1d7ea u1D7EA 2.23 (Sans, Sans Condensed) -U+1d7eb u1D7EB 2.23 (Sans, Sans Condensed) -U+1d7ec u1D7EC 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7ed u1D7ED 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7ee u1D7EE 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7ef u1D7EF 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f0 u1D7F0 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f1 u1D7F1 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f2 u1D7F2 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f3 u1D7F3 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f4 u1D7F4 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f5 u1D7F5 2.23 (Sans Bold, Sans Condensed Bold) -U+1d7f6 u1D7F6 2.23 (Sans Mono) -U+1d7f7 u1D7F7 2.23 (Sans Mono) -U+1d7f8 u1D7F8 2.23 (Sans Mono) -U+1d7f9 u1D7F9 2.23 (Sans Mono) -U+1d7fa u1D7FA 2.23 (Sans Mono) -U+1d7fb u1D7FB 2.23 (Sans Mono) -U+1d7fc u1D7FC 2.23 (Sans Mono) -U+1d7fd u1D7FD 2.23 (Sans Mono) -U+1d7fe u1D7FE 2.23 (Sans Mono) -U+1d7ff u1D7FF 2.23 (Sans Mono) -U+1f030 u1F030 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f031 u1F031 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f032 u1F032 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f033 u1F033 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f034 u1F034 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f035 u1F035 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f036 u1F036 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f037 u1F037 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f038 u1F038 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f039 u1F039 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f03a u1F03A 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f03b u1F03B 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f03c u1F03C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f03d u1F03D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f03e u1F03E 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f03f u1F03F 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f040 u1F040 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f041 u1F041 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f042 u1F042 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f043 u1F043 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f044 u1F044 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f045 u1F045 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f046 u1F046 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f047 u1F047 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f048 u1F048 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f049 u1F049 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f04a u1F04A 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f04b u1F04B 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f04c u1F04C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f04d u1F04D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f04e u1F04E 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f04f u1F04F 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f050 u1F050 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f051 u1F051 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f052 u1F052 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f053 u1F053 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f054 u1F054 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f055 u1F055 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f056 u1F056 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f057 u1F057 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f058 u1F058 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f059 u1F059 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f05a u1F05A 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f05b u1F05B 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f05c u1F05C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f05d u1F05D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f05e u1F05E 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f05f u1F05F 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f060 u1F060 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f061 u1F061 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f062 u1F062 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f063 u1F063 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f064 u1F064 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f065 u1F065 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f066 u1F066 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f067 u1F067 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f068 u1F068 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f069 u1F069 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f06a u1F06A 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f06b u1F06B 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f06c u1F06C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f06d u1F06D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f06e u1F06E 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f06f u1F06F 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f070 u1F070 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f071 u1F071 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f072 u1F072 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f073 u1F073 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f074 u1F074 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f075 u1F075 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f076 u1F076 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f077 u1F077 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f078 u1F078 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f079 u1F079 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f07a u1F07A 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f07b u1F07B 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f07c u1F07C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f07d u1F07D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f07e u1F07E 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f07f u1F07F 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f080 u1F080 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f081 u1F081 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f082 u1F082 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f083 u1F083 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f084 u1F084 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f085 u1F085 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f086 u1F086 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f087 u1F087 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f088 u1F088 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f089 u1F089 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f08a u1F08A 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f08b u1F08B 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f08c u1F08C 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f08d u1F08D 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f08e u1F08E 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f08f u1F08F 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f090 u1F090 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f091 u1F091 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f092 u1F092 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f093 u1F093 2.32 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a0 u1F0A0 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a1 u1F0A1 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a2 u1F0A2 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a3 u1F0A3 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a4 u1F0A4 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a5 u1F0A5 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a6 u1F0A6 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a7 u1F0A7 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a8 u1F0A8 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0a9 u1F0A9 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0aa u1F0AA 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ab u1F0AB 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ac u1F0AC 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ad u1F0AD 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ae u1F0AE 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b1 u1F0B1 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b2 u1F0B2 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b3 u1F0B3 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b4 u1F0B4 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b5 u1F0B5 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b6 u1F0B6 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b7 u1F0B7 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b8 u1F0B8 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0b9 u1F0B9 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ba u1F0BA 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0bb u1F0BB 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0bc u1F0BC 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0bd u1F0BD 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0be u1F0BE 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c1 u1F0C1 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c2 u1F0C2 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c3 u1F0C3 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c4 u1F0C4 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c5 u1F0C5 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c6 u1F0C6 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c7 u1F0C7 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c8 u1F0C8 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0c9 u1F0C9 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ca u1F0CA 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0cb u1F0CB 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0cc u1F0CC 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0cd u1F0CD 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0ce u1F0CE 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0cf u1F0CF 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d1 u1F0D1 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d2 u1F0D2 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d3 u1F0D3 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d4 u1F0D4 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d5 u1F0D5 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d6 u1F0D6 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d7 u1F0D7 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d8 u1F0D8 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0d9 u1F0D9 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0da u1F0DA 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0db u1F0DB 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0dc u1F0DC 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0dd u1F0DD 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0de u1F0DE 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f0df u1F0DF 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f42d u1F42D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f42e u1F42E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f431 u1F431 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f435 u1F435 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f601 u1F601 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f602 u1F602 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f603 u1F603 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f604 u1F604 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f605 u1F605 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f606 u1F606 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f607 u1F607 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f608 u1F608 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f609 u1F609 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f60a u1F60A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f60b u1F60B 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f60c u1F60C 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f60d u1F60D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f60e u1F60E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f60f u1F60F 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f610 u1F610 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f612 u1F612 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f613 u1F613 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f614 u1F614 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f616 u1F616 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f618 u1F618 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f61a u1F61A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f61c u1F61C 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f61d u1F61D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f61e u1F61E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f620 u1F620 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f621 u1F621 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f622 u1F622 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f623 u1F623 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f625 u1F625 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f628 u1F628 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f629 u1F629 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f62a u1F62A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f62b u1F62B 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f62d u1F62D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f630 u1F630 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f631 u1F631 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f632 u1F632 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f633 u1F633 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f635 u1F635 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f636 u1F636 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f637 u1F637 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f638 u1F638 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f639 u1F639 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f63a u1F63A 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f63b u1F63B 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f63c u1F63C 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f63d u1F63D 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f63e u1F63E 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f63f u1F63F 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) -U+1f640 u1F640 2.33 (Sans, Sans Bold, Sans Bold Oblique, Sans Condensed, Sans Condensed Bold, Sans Condensed Bold Oblique, Sans Condensed Oblique, Sans Oblique) +Unexpected error. File contents could not be restored from local history during undo/redo. \ No newline at end of file diff --git a/htdocs/includes/tecnickcom/tcpdf/include/barcodes/pdf417.php b/htdocs/includes/tecnickcom/tcpdf/include/barcodes/pdf417.php index 4fbe6522c92..3b1774eaae4 100644 --- a/htdocs/includes/tecnickcom/tcpdf/include/barcodes/pdf417.php +++ b/htdocs/includes/tecnickcom/tcpdf/include/barcodes/pdf417.php @@ -740,16 +740,6 @@ class PDF417 { * @protected */ protected function getErrorCorrectionLevel($ecl, $numcw) { - // get maximum correction level - $maxecl = 8; // starting error level - $maxerrsize = (928 - $numcw); // available codewords for error - while ($maxecl > 0) { - $errsize = (2 << $ecl); - if ($maxerrsize >= $errsize) { - break; - } - --$maxecl; - } // check for automatic levels if (($ecl < 0) OR ($ecl > 8)) { if ($numcw < 41) { @@ -764,6 +754,16 @@ class PDF417 { $ecl = $maxecl; } } + // get maximum correction level + $maxecl = 8; // starting error level + $maxerrsize = (928 - $numcw); // available codewords for error + while ($maxecl > 0) { + $errsize = (2 << $ecl); + if ($maxerrsize >= $errsize) { + break; + } + --$maxecl; + } if ($ecl > $maxecl) { $ecl = $maxecl; } diff --git a/htdocs/includes/tecnickcom/tcpdf/include/barcodes/qrcode.php b/htdocs/includes/tecnickcom/tcpdf/include/barcodes/qrcode.php index 9165222e727..5217600291a 100644 --- a/htdocs/includes/tecnickcom/tcpdf/include/barcodes/qrcode.php +++ b/htdocs/includes/tecnickcom/tcpdf/include/barcodes/qrcode.php @@ -236,6 +236,7 @@ if (!defined('QRCODEDEFS')) { /** * if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly */ + // @CHANGE LDR define('QR_FIND_FROM_RANDOM', false); /** @@ -730,7 +731,7 @@ class QRcode { $this->eccLength = $this->rsEccLength($spec); $this->ecccode = array_fill(0, $this->eccLength, 0); $this->blocks = $this->rsBlockNum($spec); - $ret = $this->init($spec); + $ret = $this->init($spec); if ($ret < 0) { return NULL; } diff --git a/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_fonts.php b/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_fonts.php index 70ca89393d9..ba89c7cfbed 100644 --- a/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_fonts.php +++ b/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_fonts.php @@ -665,7 +665,7 @@ class TCPDF_FONTS { $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset); $offset += 2; } - for ($k = 0; $k < $segCount; ++$k) { + for ($k = 0; $k < $segCount - 1; ++$k) { for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { if ($idRangeOffset[$k] == 0) { $g = ($idDelta[$k] + $c) % 65536; diff --git a/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_static.php b/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_static.php index f6d4d2b9eb4..aa42c850a27 100644 --- a/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_static.php +++ b/htdocs/includes/tecnickcom/tcpdf/include/tcpdf_static.php @@ -55,7 +55,7 @@ class TCPDF_STATIC { * Current TCPDF version. * @private static */ - private static $tcpdf_version = '6.2.12'; + private static $tcpdf_version = '6.2.17'; /** * String alias for total number of pages. @@ -1910,7 +1910,7 @@ class TCPDF_STATIC { && !preg_match('%^//%', $file) ) { $urldata = @parse_url($_SERVER['SCRIPT_URI']); - return $urldata['scheme'].'://'.$urldata['host'].(($file[0] == '/') ? '' : '/').$file; + $alt[] = $urldata['scheme'].'://'.$urldata['host'].(($file[0] == '/') ? '' : '/').$file; } // $alt = array_unique($alt); diff --git a/htdocs/includes/tecnickcom/tcpdf/tcpdf.php b/htdocs/includes/tecnickcom/tcpdf/tcpdf.php index 599a3f8b968..ef411a17dee 100644 --- a/htdocs/includes/tecnickcom/tcpdf/tcpdf.php +++ b/htdocs/includes/tecnickcom/tcpdf/tcpdf.php @@ -1,7 +1,7 @@
  • P or Portrait (default)
  • L or Landscape
  • '' (empty string) for automatic orientation
  • * @param $unit (string) User measure unit. Possible values are:
    • pt: point
    • mm: millimeter (default)
    • cm: centimeter
    • in: inch

    A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). @@ -4691,7 +4691,7 @@ class TCPDF { * Defines the page and position a link points to. * @param $link (int) The link identifier returned by AddLink() * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) - * @param $page (int) Number of target page; -1 indicates the current page (default value). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages. + * @param $page (int) Number of target page; -1 indicates the current page (default value). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages. * @public * @since 1.5 * @see AddLink() @@ -5944,7 +5944,9 @@ class TCPDF { if ($startpage == $endpage) { // single page for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column - $this->selectColumn($column); + if ($column != $this->current_column) { + $this->selectColumn($column); + } if ($this->rtl) { $this->x -= $mc_margin['R']; } else { @@ -5973,7 +5975,9 @@ class TCPDF { } // end for each column } elseif ($page == $startpage) { // first page for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column - $this->selectColumn($column); + if ($column != $this->current_column) { + $this->selectColumn($column); + } if ($this->rtl) { $this->x -= $mc_margin['R']; } else { @@ -5992,7 +5996,9 @@ class TCPDF { } // end for each column } elseif ($page == $endpage) { // last page for ($column = 0; $column <= $endcolumn; ++$column) { // for each column - $this->selectColumn($column); + if ($column != $this->current_column) { + $this->selectColumn($column); + } if ($this->rtl) { $this->x -= $mc_margin['R']; } else { @@ -8151,7 +8157,9 @@ class TCPDF { $annots .= ' /FT /'.$pl['opt']['ft']; $formfield = true; } - $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); + if ($pl['opt']['subtype'] !== 'Link') { + $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); + } $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id); $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp); @@ -8358,7 +8366,7 @@ class TCPDF { break; } case 'link': { - if (is_string($pl['txt'])) { + if (is_string($pl['txt']) && !empty($pl['txt'])) { if ($pl['txt'][0] == '#') { // internal destination $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1)); @@ -9798,7 +9806,7 @@ class TCPDF { //$out .= ' /XFA '; $out .= ' >>'; // signatures - if ($this->sign AND isset($this->signature_data['cert_type']) + if ($this->sign AND isset($this->signature_data['cert_type']) AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) { if ($this->signature_data['cert_type'] > 0) { $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>'; @@ -12576,7 +12584,7 @@ class TCPDF { $k = $this->k; $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n"; $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; - while (list($key, $val) = each($prop)) { + foreach($prop as $key => $val) { if (strcmp(substr($key, -5), 'Color') == 0) { $val = TCPDF_COLORS::_JScolor($val); } else { @@ -15184,7 +15192,7 @@ class TCPDF { * @since 3.1.000 (2008-06-09) * @public */ - public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') { + public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style=array(), $align='') { if (TCPDF_STATIC::empty_string(trim($code))) { return; } @@ -15503,7 +15511,7 @@ class TCPDF { * @since 4.5.037 (2009-04-07) * @public */ - public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) { + public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style=array(), $align='', $distort=false) { if (TCPDF_STATIC::empty_string(trim($code))) { return; } @@ -16539,9 +16547,9 @@ class TCPDF { // get attributes preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER); $dom[$key]['attribute'] = array(); // reset attribute array - while (list($id, $name) = each($attr_array[1])) { - $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; - } + foreach($attr_array[1] as $id => $name) { + $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; + } if (!empty($css)) { // merge CSS style to current style list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css); @@ -16552,10 +16560,10 @@ class TCPDF { // get style attributes preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); $dom[$key]['style'] = array(); // reset style attribute array - while (list($id, $name) = each($style_array[1])) { - // in case of duplicate attribute the last replace the previous - $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); - } + foreach($style_array[1] as $id => $name) { + // in case of duplicate attribute the last replace the previous + $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); + } // --- get some style attributes --- // text direction if (isset($dom[$key]['style']['direction'])) { @@ -17170,10 +17178,10 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: if ($cell) { if ($this->rtl) { $this->x -= $this->cell_padding['R']; - $this->lMargin += $this->cell_padding['R']; + $this->lMargin += $this->cell_padding['L']; } else { $this->x += $this->cell_padding['L']; - $this->rMargin += $this->cell_padding['L']; + $this->rMargin += $this->cell_padding['R']; } } if ($this->customlistindent >= 0) { @@ -17723,7 +17731,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: $spacew = ($spacewidth * $ns); } $offset = $strpiece[2][1] + strlen($strpiece[2][0]); - $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset); + $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset); if ($epsposend !== null) { $epsposend += strlen($this->epsmarker.'Q'); $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); @@ -19749,7 +19757,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: break; } case 'a': { - $this->HREF = ''; + $this->HREF = array(); break; } case 'sup': { @@ -21505,7 +21513,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } else { // placemark to be replaced with the correct number $pagenum = '{#'.($outline['p']).'}'; - if ($templates['F'.$outline['l']]) { + if (isset($templates['F'.$outline['l']]) && $templates['F'.$outline['l']]) { $pagenum = '{'.$pagenum.'}'; } $maxpage = max($maxpage, $outline['p']); @@ -23681,7 +23689,7 @@ Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: } return $name; } - + /** * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. diff --git a/htdocs/index.php b/htdocs/index.php index 3eb1aa2285a..99b5f1ade7d 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -262,7 +262,7 @@ if (empty($user->societe_id)) ); // Dashboard Link lines $links=array( - DOL_URL_ROOT.'/user/index.php', + DOL_URL_ROOT.'/user/list.php', DOL_URL_ROOT.'/societe/list.php?type=c&mainmenu=companies', DOL_URL_ROOT.'/societe/list.php?type=p&mainmenu=companies', DOL_URL_ROOT.'/societe/list.php?type=f&mainmenu=companies', @@ -339,14 +339,14 @@ if (empty($user->societe_id)) } } - $boxstat.=''; - $boxstat.=''; - $boxstat.=''; - $boxstat.=''; - $boxstat.=''; - $boxstat.=''; - $boxstat.=''; - $boxstat.=''; + $boxstat.='
    '; + $boxstat.='
    '; + $boxstat.='
    '; + $boxstat.='
    '; + $boxstat.='
    '; + $boxstat.='
    '; + $boxstat.='
    '; + $boxstat.='
    '; $boxstat.=''; $boxstat.=''; @@ -583,12 +583,12 @@ if (! empty($valid_dashboardlines)) $boxwork .="\n"; } - $boxwork .='
    '; - $boxwork .='
    '; - $boxwork .='
    '; - $boxwork .='
    '; - $boxwork .='
    '; - $boxwork .='
    '; + $boxwork .='
    '; + $boxwork .='
    '; + $boxwork .='
    '; + $boxwork .='
    '; + $boxwork .='
    '; + $boxwork .='
    '; $boxwork .=''; } else diff --git a/htdocs/install/check.php b/htdocs/install/check.php index 7e64b6ae951..bba5ea85afa 100644 --- a/htdocs/install/check.php +++ b/htdocs/install/check.php @@ -76,8 +76,8 @@ if (! empty($useragent)) // Check PHP version -$arrayphpminversionerror = array(5,3,0); -$arrayphpminversionwarning = array(5,3,0); +$arrayphpminversionerror = array(5,4,0); +$arrayphpminversionwarning = array(5,4,0); if (versioncompare(versionphparray(),$arrayphpminversionerror) < 0) // Minimum to use (error if lower) { print 'Error '.$langs->trans("ErrorPHPVersionTooLow", versiontostring($arrayphpminversionerror)); diff --git a/htdocs/install/mysql/data/llx_00_c_country.sql b/htdocs/install/mysql/data/llx_00_c_country.sql index 45cf8622355..5db58817dd2 100644 --- a/htdocs/install/mysql/data/llx_00_c_country.sql +++ b/htdocs/install/mysql/data/llx_00_c_country.sql @@ -232,7 +232,7 @@ INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (20 INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (202,'SI','SVN','Slovénie',1,0); INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (203,'SB','SLB','Iles Salomon',1,0); INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (204,'SO','SOM','Somalie',1,0); -INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (205,'ZA','ZAF','Afrique du Sud',1,0); +INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (205,'ZA','ZAF','South Africa',1,0); INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (206,'GS','SGS','Iles Géorgie du Sud et Sandwich du Sud',1,0); INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (207,'LK','LKA','Sri Lanka',1,0); INSERT INTO llx_c_country (rowid,code,code_iso,label,active,favorite) VALUES (208,'SD','SDN','Soudan',1,0); diff --git a/htdocs/install/mysql/data/llx_accounting_abc.sql b/htdocs/install/mysql/data/llx_accounting_abc.sql index f6fd8ff5539..86ce4580d9e 100644 --- a/htdocs/install/mysql/data/llx_accounting_abc.sql +++ b/htdocs/install/mysql/data/llx_accounting_abc.sql @@ -5,7 +5,7 @@ -- Copyright (C) 2004 Guillaume Delecourt -- Copyright (C) 2005-2009 Regis Houssin -- Copyright (C) 2007 Patrick Raguin --- Copyright (C) 2011-2017 Alexandre Spangaro +-- Copyright (C) 2011-2018 Alexandre Spangaro -- Copyright (C) 2015-2017 Juanjo Menent -- -- This program is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ INSERT INTO llx_accounting_journal (code, label, nature, active, entity) VALUES INSERT INTO llx_accounting_journal (code, label, nature, active, entity) VALUES ('OD', 'Other Journal', 1, 1, 1); INSERT INTO llx_accounting_journal (code, label, nature, active, entity) VALUES ('AN', 'Has new Journal', 9, 1, 1); INSERT INTO llx_accounting_journal (code, label, nature, active, entity) VALUES ('ER', 'Expense Report Journal', 5, 1, 1); +INSERT INTO llx_accounting_journal (code, label, nature, active, entity) VALUES ('INV', 'Inventory Journal' , 8, 1, 1); -- Description of chart of account FR PCG99-ABREGE diff --git a/htdocs/install/mysql/data/llx_accounting_category.sql b/htdocs/install/mysql/data/llx_accounting_category.sql index 4954ed3f0fc..3d96ea205eb 100644 --- a/htdocs/install/mysql/data/llx_accounting_category.sql +++ b/htdocs/install/mysql/data/llx_accounting_category.sql @@ -17,8 +17,8 @@ -- --- Group of accounting account for French result. This is a minimal default setup. -INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 1, 'VENTES', 'Income of products/services', 'Example: 7xxxxx', 0, 0, '', '10', 1, 1); -INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 2, 'DEPENSES', 'Expenses of products/services', 'Example: 6xxxxx', 0, 0, '', '20', 1, 1); -INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 3, 'PROFIT', 'Balance', '', 0, 1, 'VENTES+DEPENSES', '30', 1, 1); +-- Group of accounting accounts for report. This is a minimal default setup. +INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 1, 'INCOMES', 'Income of products/services', 'Example: 7xxxxx', 0, 0, '', '10', 0, 1); +INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 2, 'EXPENSES', 'Expenses of products/services', 'Example: 6xxxxx', 0, 0, '', '20', 0, 1); +INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 3, 'PROFIT', 'Balance', '', 0, 1, 'INCOMES+EXPENSES', '30', 0, 1); diff --git a/htdocs/install/mysql/data/llx_c_action_trigger.sql b/htdocs/install/mysql/data/llx_c_action_trigger.sql index ccc1f9b77bd..35077eb5bc5 100644 --- a/htdocs/install/mysql/data/llx_c_action_trigger.sql +++ b/htdocs/install/mysql/data/llx_c_action_trigger.sql @@ -74,7 +74,9 @@ insert into llx_c_action_trigger (code,label,description,elementtype,rang) value insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('SHIPPING_SENTBYMAIL','Shipping sent by mail','Executed when a shipping is sent by mail','shipping',21); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_VALIDATE','Member validated','Executed when a member is validated','member',22); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SENTBYMAIL','Mails sent from member card','Executed when you send email from member card','member',23); -insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION','Member subscribed','Executed when a member is subscribed','member',24); +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION_CREATE','Member subscribtion recorded','Executed when a member subscribtion is deleted','member',24); +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION_MODIFY','Member subscribtion modified','Executed when a member subscribtion is modified','member',24); +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION_DELETE','Member subscribtion deleted','Executed when a member subscribtion is deleted','member',24); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_RESILIATE','Member resiliated','Executed when a member is resiliated','member',25); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_DELETE','Member deleted','Executed when a member is deleted','member',26); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('FICHINTER_VALIDATE','Intervention validated','Executed when a intervention is validated','ficheinter',30); diff --git a/htdocs/install/mysql/data/llx_c_email_templates.sql b/htdocs/install/mysql/data/llx_c_email_templates.sql index 3a99e44cd8b..b913fca13f3 100644 --- a/htdocs/install/mysql/data/llx_c_email_templates.sql +++ b/htdocs/install/mysql/data/llx_c_email_templates.sql @@ -20,7 +20,13 @@ -- de l'install et tous les sigles '--' sont supprimés. -- -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines) VALUES (0,'adherent','member','',0,null,null,'(SendAnEMailToMember)',1,1,1,'__(CardContent)__','__(Hello)__,

    \n\n__(ThisIsContentOfYourCard)__
    \n__(ID)__ : __ID__
    \n__(Civiliyty)__ : __MEMBER_CIVILITY__
    \n__(Firstname)__ : __MEMBER_FIRSTNAME__
    \n__(Lastname)__ : __MEMBER_LASTNAME__
    \n__(Fullname)__ : __MEMBER_FULLNAME__
    \n__(Company)__ : __MEMBER_COMPANY__
    \n__(Address)__ : __MEMBER_ADDRESS__
    \n__(Zip)__ : __MEMBER_ZIP__
    \n__(Town)__ : __MEMBER_TOWN__
    \n__(Country)__ : __MEMBER_COUNTRY__
    \n__(Email)__ : __MEMBER_EMAIL__
    \n__(Birthday)__ : __MEMBER_BIRTH__
    \n__(Photo)__ : __MEMBER_PHOTO__
    \n__(Login)__ : __MEMBER_LOGIN__
    \n__(Password)__ : __MEMBER_PASSWORD__
    \n__(Phone)__ : __MEMBER_PHONE__
    \n__(PhonePerso)__ : __MEMBER_PHONEPRO__
    \n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

    \n__(Sincerely)__
    __USER_SIGNATURE__',null); INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines) VALUES (0,'banque','thirdparty','',0,null,null,'(YourSEPAMandate)',1,1,0,'__(YourSEPAMandate)__','__(Hello)__,

    \n\n__(FindYourSEPAMandate)__ :
    \n__MYCOMPANY_NAME__
    \n__MYCOMPANY_FULLADDRESS__

    \n__(Sincerely)__
    \n__USER_SIGNATURE__',null); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnAutoSubscription)' ,10,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipRequestWasReceived)__','__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfYourMembershipRequestWasReceived)__
    \n
    __ONLINE_PAYMENT_TEXT_AND_URL__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnMemberValidation)' ,20,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasValidated)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfYourMembershipWasValidated)__
    __INFOS__
    \n
    __ONLINE_PAYMENT_TEXT_AND_URL__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnNewSubscription)' ,30,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourSubscriptionWasRecorded)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfYourSubscriptionWasRecorded)__
    \n\n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingReminderForExpiredSubscription)',40,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(SubscriptionReminderEmail)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfSubscriptionReminderEmail)__
    \n
    __ONLINE_PAYMENT_TEXT_AND_URL__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnCancelation)' ,50,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasCanceled)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(YourMembershipWasCanceled)__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)' ,60,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(CardContent)__', '__(Hello)__,

    \n\n__(ThisIsContentOfYourCard)__
    \n__(ID)__ : __ID__
    \n__(Civiliyty)__ : __MEMBER_CIVILITY__
    \n__(Firstname)__ : __MEMBER_FIRSTNAME__
    \n__(Lastname)__ : __MEMBER_LASTNAME__
    \n__(Fullname)__ : __MEMBER_FULLNAME__
    \n__(Company)__ : __MEMBER_COMPANY__
    \n__(Address)__ : __MEMBER_ADDRESS__
    \n__(Zip)__ : __MEMBER_ZIP__
    \n__(Town)__ : __MEMBER_TOWN__
    \n__(Country)__ : __MEMBER_COUNTRY__
    \n__(Email)__ : __MEMBER_EMAIL__
    \n__(Birthday)__ : __MEMBER_BIRTH__
    \n__(Photo)__ : __MEMBER_PHOTO__
    \n__(Login)__ : __MEMBER_LOGIN__
    \n__(Password)__ : __MEMBER_PASSWORD__
    \n__(Phone)__ : __MEMBER_PHONE__
    \n__(PhonePerso)__ : __MEMBER_PHONEPRO__
    \n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); + diff --git a/htdocs/install/mysql/data/llx_c_paiement.sql b/htdocs/install/mysql/data/llx_c_paiement.sql index b192dd3c8b0..2e459cd12a7 100644 --- a/htdocs/install/mysql/data/llx_c_paiement.sql +++ b/htdocs/install/mysql/data/llx_c_paiement.sql @@ -30,16 +30,13 @@ -- Types paiement -- -delete from llx_c_paiement; -insert into llx_c_paiement (id,code,libelle,type,active) values ( 0, '', '-', 3,1); insert into llx_c_paiement (id,code,libelle,type,active) values ( 1, 'TIP', 'TIP', 2,0); -insert into llx_c_paiement (id,code,libelle,type,active) values ( 2, 'VIR', 'Virement', 2,1); -insert into llx_c_paiement (id,code,libelle,type,active) values ( 3, 'PRE', 'Prélèvement', 2,1); -insert into llx_c_paiement (id,code,libelle,type,active) values ( 4, 'LIQ', 'Espèces', 2,1); -insert into llx_c_paiement (id,code,libelle,type,active) values ( 6, 'CB', 'Carte Bancaire', 2,1); -insert into llx_c_paiement (id,code,libelle,type,active) values ( 7, 'CHQ', 'Chèque', 2,1); -insert into llx_c_paiement (id,code,libelle,type,active) values (50, 'VAD', 'Paiement en ligne', 2,0); +insert into llx_c_paiement (id,code,libelle,type,active) values ( 2, 'VIR', 'Transfer', 2,1); +insert into llx_c_paiement (id,code,libelle,type,active) values ( 3, 'PRE', 'Debit order', 2,1); +insert into llx_c_paiement (id,code,libelle,type,active) values ( 4, 'LIQ', 'Cash', 2,1); +insert into llx_c_paiement (id,code,libelle,type,active) values ( 6, 'CB', 'Credit card', 2,1); +insert into llx_c_paiement (id,code,libelle,type,active) values ( 7, 'CHQ', 'Cheque', 2,1); +insert into llx_c_paiement (id,code,libelle,type,active) values (50, 'VAD', 'Online payment', 2,0); insert into llx_c_paiement (id,code,libelle,type,active) values (51, 'TRA', 'Traite', 2,0); insert into llx_c_paiement (id,code,libelle,type,active) values (52, 'LCR', 'LCR', 2,0); insert into llx_c_paiement (id,code,libelle,type,active) values (53, 'FAC', 'Factor', 2,0); ---insert into llx_c_paiement (id,code,libelle,type,active) values (54, 'PRO', 'Proforma', 2,0); diff --git a/htdocs/install/mysql/tables/llx_website_account_extrafields.key.sql b/htdocs/install/mysql/data/llx_c_ticketsup_category.sql similarity index 67% rename from htdocs/install/mysql/tables/llx_website_account_extrafields.key.sql rename to htdocs/install/mysql/data/llx_c_ticketsup_category.sql index 9970f8bc680..0f00ec77502 100644 --- a/htdocs/install/mysql/tables/llx_website_account_extrafields.key.sql +++ b/htdocs/install/mysql/data/llx_c_ticketsup_category.sql @@ -1,5 +1,4 @@ --- =================================================================== --- Copyright (C) 2017 Laurent Destailleur +-- Copyright (C) 2018 Laurent Destailleur -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -14,7 +13,8 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- --- =================================================================== +-- +-- Contenu de la table llx_c_ticketsup_category +-- - -ALTER TABLE llx_website_account_extrafields ADD INDEX idx_website_account_extrafields (fk_object); +INSERT INTO llx_c_ticketsup_category (code, pos, label, active, use_default, description) VALUES('OTHER', '10', 'Other', 1, 1, NULL); diff --git a/htdocs/install/mysql/data/llx_c_ticketsup_severity.sql b/htdocs/install/mysql/data/llx_c_ticketsup_severity.sql new file mode 100644 index 00000000000..1b9c3e42ca0 --- /dev/null +++ b/htdocs/install/mysql/data/llx_c_ticketsup_severity.sql @@ -0,0 +1,23 @@ +-- Copyright (C) 2018 Laurent Destailleur +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- +-- Contenu de la table llx_c_ticketsup_severity +-- + +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('LOW', '10', 'Low', '', 1, 0, NULL); +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('NORMAL', '20', 'Normal', '', 1, 1, NULL); +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('HIGH', '30', 'High', '', 1, 0, NULL); +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('BLOCKING', '40', 'Critical / blocking', '', 1, 0, NULL); diff --git a/htdocs/install/mysql/data/llx_c_ticketsup_type.sql b/htdocs/install/mysql/data/llx_c_ticketsup_type.sql new file mode 100644 index 00000000000..310b075a604 --- /dev/null +++ b/htdocs/install/mysql/data/llx_c_ticketsup_type.sql @@ -0,0 +1,24 @@ +-- Copyright (C) 2018 Laurent Destailleur +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- +-- Contenu de la table llx_c_ticketsup_type +-- + +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('COM', '10', 'Commercial question', 1, 1, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('ISSUE', '20', 'Issue or problem' , 1, 0, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('REQUEST', '25', 'Change or enhancement request', 1, 0, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('PROJECT', '30', 'Project', 0, 0, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('OTHER', '40', 'Other', 1, 0, NULL); diff --git a/htdocs/install/mysql/data/llx_c_tva.sql b/htdocs/install/mysql/data/llx_c_tva.sql index 9d343f46a8f..47fccc476a8 100644 --- a/htdocs/install/mysql/data/llx_c_tva.sql +++ b/htdocs/install/mysql/data/llx_c_tva.sql @@ -259,6 +259,10 @@ INSERT INTO llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) VALUES (20 INSERT INTO llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) VALUES (2022, 202,'9.5', '0', 'VAT reduced rate', 1); INSERT INTO llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) VALUES (2023, 202, '0', '0', 'VAT Rate 0', 1); +-- SOUTH AFRICA (id country=205) +INSERT INTO llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) VALUES (2051, 205, '15', '0', 'VAT standard rate', 1); +INSERT INTO llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) VALUES (2053, 205, '0', '0', 'VAT Rate 0', 1); + -- SPAIN (id country=4) insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,localtax1,localtax1_type,localtax2,localtax2_type,note,active) values ( 41, 4,'21','0','5.2','3','-19:-15:-9','5','VAT standard rate',1); insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,localtax1,localtax1_type,localtax2,localtax2_type,note,active) values ( 42, 4,'10','0','1.4','3','-19:-15:-9','5','VAT reduced rate',1); diff --git a/htdocs/install/mysql/data/llx_c_type_contact_ticketsup.sql b/htdocs/install/mysql/data/llx_c_type_contact_ticketsup.sql new file mode 100644 index 00000000000..2e7fe218827 --- /dev/null +++ b/htdocs/install/mysql/data/llx_c_type_contact_ticketsup.sql @@ -0,0 +1,5 @@ + +INSERT INTO llx_c_type_contact (rowid, element, source, code, libelle, active, module) VALUES(110120, 'ticketsup', 'internal', 'SUPPORTTEC', 'Utilisateur contact support', 1, NULL); +INSERT INTO llx_c_type_contact (rowid, element, source, code, libelle, active, module) VALUES(110121, 'ticketsup', 'internal', 'CONTRIBUTOR', 'Intervenant', 1, NULL); +INSERT INTO llx_c_type_contact (rowid, element, source, code, libelle, active, module) VALUES(110122, 'ticketsup', 'external', 'SUPPORTCLI', 'Contact client suivi incident', 1, NULL); +INSERT INTO llx_c_type_contact (rowid, element, source, code, libelle, active, module) VALUES(110123, 'ticketsup', 'external', 'CONTRIBUTOR', 'Intervenant', 1, NULL); diff --git a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql index 1e0388cff94..56659ea714c 100644 --- a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql +++ b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql @@ -242,7 +242,7 @@ UPDATE llx_bank_account SET accountancy_journal = 'OD' WHERE accountancy_journal ALTER TABLE llx_bank_account ADD COLUMN fk_accountancy_journal integer; ALTER TABLE llx_bank_account ADD INDEX idx_fk_accountancy_journal (fk_accountancy_journal); -UPDATE llx_bank_account AS ba SET fk_accountancy_journal = (SELECT rowid FROM llx_accounting_journal AS aj WHERE ba.accountancy_journal = aj.code) WHERE accountancy_journal NOT IN ('1', '2', '3', '4', '5', '6', '5', '8', '9', '10', '11', '12', '13', '14', '15'); +UPDATE llx_bank_account AS ba SET fk_accountancy_journal = (SELECT rowid FROM llx_accounting_journal AS aj WHERE ba.accountancy_journal = aj.code AND aj.entity = ba.entity) WHERE accountancy_journal NOT IN ('1', '2', '3', '4', '5', '6', '5', '8', '9', '10', '11', '12', '13', '14', '15'); ALTER TABLE llx_bank_account ADD CONSTRAINT fk_bank_account_accountancy_journal FOREIGN KEY (fk_accountancy_journal) REFERENCES llx_accounting_journal (rowid); --Update general ledger for FEC format & harmonization diff --git a/htdocs/install/mysql/migration/6.0.0-7.0.0.sql b/htdocs/install/mysql/migration/6.0.0-7.0.0.sql index 6e9e480cdb0..c19530acd01 100644 --- a/htdocs/install/mysql/migration/6.0.0-7.0.0.sql +++ b/htdocs/install/mysql/migration/6.0.0-7.0.0.sql @@ -94,6 +94,11 @@ ALTER TABLE llx_website_page ADD COLUMN type_container varchar(16) NOT NULL DEFA -- For 7.0 +delete from llx_c_action_trigger where code = 'MEMBER_SUBSCRIPTION'; +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION_CREATE','Member subscribtion recorded','Executed when a member subscribtion is deleted','member',24); +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION_MODIFY','Member subscribtion modified','Executed when a member subscribtion is modified','member',24); +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MEMBER_SUBSCRIPTION_DELETE','Member subscribtion deleted','Executed when a member subscribtion is deleted','member',24); + ALTER TABLE llx_product_attribute_value DROP INDEX unique_ref; ALTER TABLE llx_product_attribute_value ADD UNIQUE INDEX uk_product_attribute_value (fk_product_attribute, ref); @@ -192,7 +197,7 @@ ALTER TABLE llx_c_email_templates ADD COLUMN enabled varchar(255) DEFAULT '1'; ALTER TABLE llx_c_email_templates ADD COLUMN joinfiles varchar(255) DEFAULT '1'; ALTER TABLE llx_c_email_templates MODIFY COLUMN content mediumtext; -INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines) VALUES (0,'adherent','member','',0,null,null,'(SendAnEMailToMember)',1,1,1,'__(CardContent)__','__(Hello)__,

    \n\n__(ThisIsContentOfYourCard)__
    \n__(ID)__ : __ID__
    \n__(Civiliyty)__ : __MEMBER_CIVILITY__
    \n__(Firstname)__ : __MEMBER_FIRSTNAME__
    \n__(Lastname)__ : __MEMBER_LASTNAME__
    \n__(Fullname)__ : __MEMBER_FULLNAME__
    \n__(Company)__ : __MEMBER_COMPANY__
    \n__(Address)__ : __MEMBER_ADDRESS__
    \n__(Zip)__ : __MEMBER_ZIP__
    \n__(Town)__ : __MEMBER_TOWN__
    \n__(Country)__ : __MEMBER_COUNTRY__
    \n__(Email)__ : __MEMBER_EMAIL__
    \n__(Birthday)__ : __MEMBER_BIRTH__
    \n__(Photo)__ : __MEMBER_PHOTO__
    \n__(Login)__ : __MEMBER_LOGIN__
    \n__(Password)__ : __MEMBER_PASSWORD__
    \n__(Phone)__ : __MEMBER_PHONE__
    \n__(PhonePerso)__ : __MEMBER_PHONEPRO__
    \n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

    \n__(Sincerely)__
    __USER_SIGNATURE__',null); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)',1,1,1,'__(CardContent)__','__(Hello)__,

    \n\n__(ThisIsContentOfYourCard)__
    \n__(ID)__ : __ID__
    \n__(Civiliyty)__ : __MEMBER_CIVILITY__
    \n__(Firstname)__ : __MEMBER_FIRSTNAME__
    \n__(Lastname)__ : __MEMBER_LASTNAME__
    \n__(Fullname)__ : __MEMBER_FULLNAME__
    \n__(Company)__ : __MEMBER_COMPANY__
    \n__(Address)__ : __MEMBER_ADDRESS__
    \n__(Zip)__ : __MEMBER_ZIP__
    \n__(Town)__ : __MEMBER_TOWN__
    \n__(Country)__ : __MEMBER_COUNTRY__
    \n__(Email)__ : __MEMBER_EMAIL__
    \n__(Birthday)__ : __MEMBER_BIRTH__
    \n__(Photo)__ : __MEMBER_PHOTO__
    \n__(Login)__ : __MEMBER_LOGIN__
    \n__(Password)__ : __MEMBER_PASSWORD__
    \n__(Phone)__ : __MEMBER_PHONE__
    \n__(PhonePerso)__ : __MEMBER_PHONEPRO__
    \n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

    \n__(Sincerely)__
    __USER_SIGNATURE__',null); INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines) VALUES (0,'banque','thirdparty','',0,null,null,'(YourSEPAMandate)',1,1,0,'__(YourSEPAMandate)__','__(Hello)__,

    \n\n__(FindYourSEPAMandate)__ :
    \n__MYCOMPANY_NAME__
    \n__MYCOMPANY_FULLADDRESS__

    \n__(Sincerely)__
    \n__USER_SIGNATURE__',null); INSERT INTO llx_c_accounting_category (rowid, code, label, range_account, sens, category_type, formula, position, fk_country, active) VALUES ( 1, 'VENTES', 'Income of products/services', 'Exemple: 7xxxxx', 0, 0, '', '10', 1, 1); @@ -321,6 +326,8 @@ CREATE TABLE IF NOT EXISTS llx_expensereport_ik ( active integer DEFAULT 1 )ENGINE=innodb; +ALTER TABLE llx_expensereport_ik ADD COLUMN ikoffset double DEFAULT 0 NOT NULL; + CREATE TABLE IF NOT EXISTS llx_c_exp_tax_cat ( rowid integer AUTO_INCREMENT PRIMARY KEY, label varchar(48) NOT NULL, @@ -435,7 +442,7 @@ CREATE TABLE llx_expensereport_rules ( fk_usergroup integer DEFAULT NULL, fk_c_type_fees integer NOT NULL, code_expense_rules_type varchar(50) NOT NULL, - is_for_all tinyint DEFAULT '0', + is_for_all tinyint DEFAULT 0, entity integer DEFAULT 1 )ENGINE=innodb; @@ -695,3 +702,9 @@ create table llx_facture_rec_extrafields ALTER TABLE llx_facture_rec_extrafields ADD INDEX idx_facture_rec_extrafields (fk_object); + +-- VMYSQL4.1 ALTER TABLE llx_product_association ADD COLUMN rowid integer AUTO_INCREMENT PRIMARY KEY; + + +DROP TABLE llx_c_accountancy_category; + diff --git a/htdocs/install/mysql/migration/7.0.0-8.0.0.sql b/htdocs/install/mysql/migration/7.0.0-8.0.0.sql index a0ec8f58e37..880bd4e7ca2 100644 --- a/htdocs/install/mysql/migration/7.0.0-8.0.0.sql +++ b/htdocs/install/mysql/migration/7.0.0-8.0.0.sql @@ -27,8 +27,43 @@ -- -- VMYSQL4.1 DELETE FROM llx_usergroup_user WHERE fk_usergroup NOT IN (SELECT rowid from llx_usergroup); + +-- Forgot in 7.0 + +-- VMYSQL4.1 ALTER TABLE llx_product_association ADD COLUMN rowid integer AUTO_INCREMENT PRIMARY KEY; + +ALTER TABLE llx_website_page ADD COLUMN fk_user_create integer; +ALTER TABLE llx_website_page ADD COLUMN fk_user_modif integer; +ALTER TABLE llx_website_page ADD COLUMN type_container varchar(16) NOT NULL DEFAULT 'page'; + +DROP TABLE llx_c_accountancy_category; +DROP TABLE llx_c_accountingaccount; + +update llx_propal set fk_statut = 1 where fk_statut = -1; + +ALTER TABLE llx_inventory ADD COLUMN fk_user_creat integer; +ALTER TABLE llx_inventory ADD COLUMN fk_user_modif integer; +ALTER TABLE llx_inventory ADD COLUMN fk_user_valid integer; +ALTER TABLE llx_inventory ADD COLUMN import_key varchar(14); + + -- For 8.0 +-- delete old permission no more used +DELETE FROM llx_rights_def WHERE perms = 'main' and module = 'commercial'; + +delete from llx_rights_def where perms IS NULL; +delete from llx_user_rights where fk_user not IN (select rowid from llx_user); +delete from llx_usergroup_rights where fk_usergroup not in (select rowid from llx_usergroup); +delete from llx_usergroup_rights where fk_id not in (select id from llx_rights_def); + +ALTER TABLE llx_inventory ADD COLUMN fk_product integer DEFAULT NULL; +ALTER TABLE llx_inventory MODIFY COLUMN fk_warehouse integer DEFAULT NULL; + +ALTER TABLE llx_c_type_fees ADD COLUMN llx_c_type_fees integer DEFAULT 0; + +ALTER TABLE llx_product_fournisseur_price DROP COLUMN unitcharges; + ALTER TABLE llx_societe ADD COLUMN fk_entrepot integer DEFAULT 0; ALTER TABLE llx_projet ADD COLUMN bill_time integer DEFAULT 0; @@ -48,11 +83,331 @@ create table llx_c_type_container ALTER TABLE llx_c_type_container ADD UNIQUE INDEX uk_c_type_container_id (code, entity); + +ALTER TABLE llx_societe_remise_except ADD COLUMN discount_type integer DEFAULT 0 NOT NULL AFTER fk_soc; +ALTER TABLE llx_societe_remise_except ADD INDEX idx_societe_remise_except_discount_type (discount_type); +ALTER TABLE llx_societe ADD COLUMN remise_supplier real DEFAULT 0 AFTER remise_client; +CREATE TABLE llx_societe_remise_supplier +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1 NOT NULL, -- multi company id + fk_soc integer NOT NULL, + tms timestamp, + datec datetime, -- creation date + fk_user_author integer, -- creation user + remise_supplier double(6,3) DEFAULT 0 NOT NULL, -- discount + note text +)ENGINE=innodb; insert into llx_c_type_container (code,label,module,active) values ('page', 'Page', 'system', 1); insert into llx_c_type_container (code,label,module,active) values ('banner', 'Banner', 'system', 1); insert into llx_c_type_container (code,label,module,active) values ('blogpost', 'BlogPost', 'system', 1); insert into llx_c_type_container (code,label,module,active) values ('other', 'Other', 'system', 1); +-- For supplier product buy price in multicurency +ALTER TABLE llx_product_fournisseur_price CHANGE COLUMN multicurrency_price_ttc multicurrency_unitprice DOUBLE(24,8) NULL DEFAULT NULL; +ALTER TABLE llx_product_fournisseur_price_log CHANGE COLUMN multicurrency_price_ttc multicurrency_unitprice DOUBLE(24,8) NULL DEFAULT NULL; + +ALTER TABLE llx_expensereport_det ADD COLUMN docnumber varchar(128) after fk_expensereport; + +ALTER TABLE llx_website_page ADD COLUMN aliasalt varchar(255) after pageurl; + +-- Add missing keys and primary key +DELETE FROM llx_c_paiement WHERE code = '' or code = '-' or id = 0; +ALTER TABLE llx_c_paiement DROP INDEX uk_c_paiement; +ALTER TABLE llx_c_paiement ADD UNIQUE INDEX uk_c_paiement_code(entity, code); +ALTER TABLE llx_c_paiement CHANGE COLUMN id id INTEGER AUTO_INCREMENT PRIMARY KEY; + +-- Add missing keys and primary key +ALTER TABLE llx_c_payment_term DROP INDEX uk_c_payment_term; +ALTER TABLE llx_c_payment_term CHANGE COLUMN rowid rowid INTEGER AUTO_INCREMENT PRIMARY KEY; +ALTER TABLE llx_c_payment_term ADD UNIQUE INDEX uk_c_payment_term_code(entity, code); + +ALTER TABLE llx_oauth_token ADD COLUMN tokenstring text; + +-- Add field for payment modes +ALTER TABLE llx_societe_rib ADD COLUMN type varchar(32) DEFAULT 'ban' after rowid; +ALTER TABLE llx_societe_rib ADD COLUMN last_four varchar(4); +ALTER TABLE llx_societe_rib ADD COLUMN card_type varchar(255); +ALTER TABLE llx_societe_rib ADD COLUMN cvn varchar(255); +ALTER TABLE llx_societe_rib ADD COLUMN exp_date_month INTEGER; +ALTER TABLE llx_societe_rib ADD COLUMN exp_date_year INTEGER; +ALTER TABLE llx_societe_rib ADD COLUMN country_code varchar(10); +ALTER TABLE llx_societe_rib ADD COLUMN approved integer DEFAULT 0; +ALTER TABLE llx_societe_rib ADD COLUMN email varchar(255); +ALTER TABLE llx_societe_rib ADD COLUMN ending_date date; +ALTER TABLE llx_societe_rib ADD COLUMN max_total_amount_of_all_payments double(24,8); +ALTER TABLE llx_societe_rib ADD COLUMN preapproval_key varchar(255); +ALTER TABLE llx_societe_rib ADD COLUMN starting_date date; +ALTER TABLE llx_societe_rib ADD COLUMN total_amount_of_all_payments double(24,8); +ALTER TABLE llx_societe_rib ADD COLUMN stripe_card_ref varchar(128); +ALTER TABLE llx_societe_rib ADD COLUMN status integer NOT NULL DEFAULT 1; + +UPDATE llx_societe_rib set type = 'ban' where type = '' OR type IS NULL; +-- VMYSQL4.3 ALTER TABLE llx_societe_rib MODIFY COLUMN type varchar(32) NOT NULL; +-- VPGSQL8.2 ALTER TABLE llx_societe_rib ALTER COLUMN type SET NOT NULL; + +CREATE TABLE llx_ticketsup +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + ref varchar(128) NOT NULL, + track_id varchar(128) NOT NULL, + fk_soc integer DEFAULT 0, + fk_project integer DEFAULT 0, + origin_email varchar(128), + fk_user_create integer, + fk_user_assign integer, + subject varchar(255), + message text, + fk_statut integer, + resolution integer, + progress varchar(100), + timing varchar(20), + type_code varchar(32), + category_code varchar(32), + severity_code varchar(32), + datec datetime, + date_read datetime, + date_close datetime, + notify_tiers_at_create tinyint, + tms timestamp +)ENGINE=innodb; + +ALTER TABLE llx_ticketsup ADD COLUMN notify_tiers_at_create integer; +ALTER TABLE llx_ticketsup ADD UNIQUE uk_ticketsup_rowid_track_id (rowid, track_id); +ALTER TABLE llx_ticketsup ADD INDEX id_ticketsup_track_id (track_id); + +CREATE TABLE llx_ticketsup_msg +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + fk_track_id varchar(128), + fk_user_action integer, + datec datetime, + message text, + private integer DEFAULT 0 +)ENGINE=innodb; + + +ALTER TABLE llx_ticketsup_msg ADD CONSTRAINT fk_ticketsup_msg_fk_track_id FOREIGN KEY (fk_track_id) REFERENCES llx_ticketsup (track_id); + +CREATE TABLE llx_ticketsup_logs +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + fk_track_id varchar(128), + fk_user_create integer, + datec datetime, + message text +)ENGINE=innodb; + +ALTER TABLE llx_ticketsup_logs ADD CONSTRAINT fk_ticketsup_logs_fk_track_id FOREIGN KEY (fk_track_id) REFERENCES llx_ticketsup (track_id); + +CREATE TABLE llx_ticketsup_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, + import_key varchar(14) +)ENGINE=innodb; +-- Create dictionaries tables for ticket +create table llx_c_ticketsup_severity +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + color varchar(10) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; + +create table llx_c_ticketsup_type +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; + +create table llx_c_ticketsup_category +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; + +ALTER TABLE llx_c_ticketsup_category ADD UNIQUE INDEX uk_code (code, entity); +ALTER TABLE llx_c_ticketsup_severity ADD UNIQUE INDEX uk_code (code, entity); +ALTER TABLE llx_c_ticketsup_type ADD UNIQUE INDEX uk_code (code, entity); + + + +-- Load data +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('LOW', '10', 'Low', '', 1, 0, NULL); +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('NORMAL', '20', 'Normal', '', 1, 1, NULL); +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('HIGH', '30', 'High', '', 1, 0, NULL); +INSERT INTO llx_c_ticketsup_severity (code, pos, label, color, active, use_default, description) VALUES('BLOCKING', '40', 'Critical / blocking', '', 1, 0, NULL); + +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('COM', '10', 'Commercial question', 1, 1, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('ISSUE', '20', 'Issue or problem' , 1, 0, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('REQUEST', '25', 'Change or enhancement request', 1, 0, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('PROJECT', '30', 'Project', 0, 0, NULL); +INSERT INTO llx_c_ticketsup_type (code, pos, label, active, use_default, description) VALUES('OTHER', '40', 'Other', 1, 0, NULL); + +INSERT INTO llx_c_ticketsup_category (code, pos, label, active, use_default, description) VALUES('OTHER', '10', 'Other', 1, 1, NULL); + + + + + +ALTER TABLE llx_facturedet_rec ADD COLUMN date_start_fill integer DEFAULT 0; +ALTER TABLE llx_facturedet_rec ADD COLUMN date_end_fill integer DEFAULT 0; + + + +CREATE TABLE llx_societe_account( + -- BEGIN MODULEBUILDER FIELDS + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + entity integer DEFAULT 1, + key_account varchar(128), + login varchar(128) NOT NULL, + pass_encoding varchar(24), + pass_crypted varchar(128), + pass_temp varchar(128), -- temporary password when asked for forget password + fk_soc integer, + site varchar(128), + fk_website integer, + note_private text, + date_last_login datetime, + date_previous_login datetime, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer + -- END MODULEBUILDER FIELDS +) ENGINE=innodb; + +-- VMYSQL4.3 ALTER TABLE llx_societe_account MODIFY COLUMN pass_encoding varchar(24) NULL; + +ALTER TABLE llx_const MODIFY type varchar(64) DEFAULT 'string'; + +UPDATE llx_const set type = 'text' where type = 'texte'; +UPDATE llx_const set type = 'html' where name in (__ENCRYPT('ADHERENT_AUTOREGISTER_NOTIF_MAIL')__,__ENCRYPT('ADHERENT_AUTOREGISTER_MAIL')__,__ENCRYPT('ADHERENT_MAIL_VALID')__,__ENCRYPT('ADHERENT_MAIL_COTIS')__,__ENCRYPT('ADHERENT_MAIL_RESIL')__); + +--UPDATE llx_const SET value = '', type = 'emailtemplate:member' WHERE name = __ENCRYPT('ADHERENT_AUTOREGISTER_MAIL')__ AND type != 'emailtemplate:member'; +--UPDATE llx_const SET value = '', type = 'emailtemplate:member' WHERE name = __ENCRYPT('ADHERENT_MAIL_VALID')__ AND type != 'emailtemplate:member'; +--UPDATE llx_const SET value = '', type = 'emailtemplate:member' WHERE name = __ENCRYPT('ADHERENT_MAIL_COTIS')__ AND type != 'emailtemplate:member'; +--UPDATE llx_const SET value = '', type = 'emailtemplate:member' WHERE name = __ENCRYPT('ADHERENT_MAIL_RESIL')__ AND type != 'emailtemplate:member'; + +ALTER TABLE llx_societe_account ADD COLUMN key_account varchar(128); + +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_rowid (rowid); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_login (login); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_status (status); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_fk_website (fk_website); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_fk_soc (fk_soc); + +ALTER TABLE llx_societe_account ADD UNIQUE INDEX uk_societe_account_login_website_soc(entity, fk_soc, login, site, fk_website); +ALTER TABLE llx_societe_account ADD UNIQUE INDEX uk_societe_account_key_account_soc(entity, fk_soc, key_account, site, fk_website); + +ALTER TABLE llx_societe_account ADD CONSTRAINT llx_societe_account_fk_website FOREIGN KEY (fk_website) REFERENCES llx_website(rowid); +ALTER TABLE llx_societe_account ADD CONSTRAINT llx_societe_account_fk_societe FOREIGN KEY (fk_soc) REFERENCES llx_societe(rowid); + + +ALTER TABLE llx_societe_rib MODIFY COLUMN max_total_amount_of_all_payments double(24,8); +ALTER TABLE llx_societe_rib MODIFY COLUMN total_amount_of_all_payments double(24,8); + + +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnAutoSubscription)' ,10,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipRequestWasReceived)__','__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfYourMembershipRequestWasReceived)__
    \n
    __ONLINE_PAYMENT_TEXT_AND_URL__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnMemberValidation)' ,20,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasValidated)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfYourMembershipWasValidated)__
    \n
    __ONLINE_PAYMENT_TEXT_AND_URL__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnNewSubscription)' ,30,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourSubscriptionWasRecorded)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfYourSubscriptionWasRecorded)__
    \n\n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingReminderForExpiredSubscription)',40,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(SubscriptionReminderEmail)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(ThisIsContentOfSubscriptionReminderEmail)__
    \n
    __ONLINE_PAYMENT_TEXT_AND_URL__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingEmailOnCancelation)' ,50,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(YourMembershipWasCanceled)__', '__(Hello)__ __MEMBER_FULLNAME__,

    \n\n__(YourMembershipWasCanceled)__
    \n

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); +INSERT INTO llx_c_email_templates (entity,module,type_template,lang,private,fk_user,datec,label,position,enabled,active,topic,content,content_lines,joinfiles) VALUES (0,'adherent','member','',0,null,null,'(SendingAnEMailToMember)' ,60,1,1,'[__[MAIN_INFO_SOCIETE_NOM]__] __(CardContent)__', '__(Hello)__,

    \n\n__(ThisIsContentOfYourCard)__
    \n__(ID)__ : __ID__
    \n__(Civiliyty)__ : __MEMBER_CIVILITY__
    \n__(Firstname)__ : __MEMBER_FIRSTNAME__
    \n__(Lastname)__ : __MEMBER_LASTNAME__
    \n__(Fullname)__ : __MEMBER_FULLNAME__
    \n__(Company)__ : __MEMBER_COMPANY__
    \n__(Address)__ : __MEMBER_ADDRESS__
    \n__(Zip)__ : __MEMBER_ZIP__
    \n__(Town)__ : __MEMBER_TOWN__
    \n__(Country)__ : __MEMBER_COUNTRY__
    \n__(Email)__ : __MEMBER_EMAIL__
    \n__(Birthday)__ : __MEMBER_BIRTH__
    \n__(Photo)__ : __MEMBER_PHOTO__
    \n__(Login)__ : __MEMBER_LOGIN__
    \n__(Password)__ : __MEMBER_PASSWORD__
    \n__(Phone)__ : __MEMBER_PHONE__
    \n__(PhonePerso)__ : __MEMBER_PHONEPRO__
    \n__(PhoneMobile)__ : __MEMBER_PHONEMOBILE__

    \n__(Sincerely)__
    __USER_SIGNATURE__',null, 1); + +ALTER TABLE llx_product ADD COLUMN fk_default_warehouse integer DEFAULT NULL; +ALTER TABLE llx_product ADD CONSTRAINT fk_product_default_warehouse FOREIGN KEY (fk_default_warehouse) REFERENCES llx_entrepot (rowid); + +-- Assets +CREATE TABLE llx_asset( + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + ref varchar(128) NOT NULL, + entity integer DEFAULT 1 NOT NULL, + label varchar(255), + amount double(24,8) DEFAULT NULL, + fk_asset_type integer NOT NULL, + fk_soc integer, + description text, + note_public text, + note_private text, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL +) ENGINE=innodb; + +ALTER TABLE llx_asset ADD INDEX idx_asset_rowid (rowid); +ALTER TABLE llx_asset ADD INDEX idx_asset_ref (ref); +ALTER TABLE llx_asset ADD INDEX idx_asset_entity (entity); +ALTER TABLE llx_asset ADD INDEX idx_asset_fk_soc (fk_soc); + +ALTER TABLE llx_asset ADD INDEX idx_asset_fk_asset_type (fk_asset_type); +ALTER TABLE llx_asset ADD CONSTRAINT fk_asset_asset_type FOREIGN KEY (fk_asset_type) REFERENCES llx_asset_type (rowid); + +create table llx_asset_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, + import_key varchar(14) +) ENGINE=innodb; + +create table llx_asset_type +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1 NOT NULL, -- multi company id + tms timestamp, + label varchar(50) NOT NULL, + accountancy_code_asset varchar(32), + accountancy_code_depreciation_asset varchar(32), + accountancy_code_depreciation_expense varchar(32), + note text +)ENGINE=innodb; + +ALTER TABLE llx_asset_type ADD UNIQUE INDEX uk_asset_type_label (label, entity); + +create table llx_asset_type_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, + import_key varchar(14) -- import key +) ENGINE=innodb; + +ALTER TABLE llx_asset_type_extrafields ADD INDEX idx_asset_type_extrafields (fk_object); + +INSERT INTO llx_accounting_journal (rowid, code, label, nature, active) VALUES (7,'INV', 'Inventory journal', 8, 1); + +UPDATE llx_accounting_account set account_parent = 0 WHERE account_parent = '' OR account_parent IS NULL; +ALTER TABLE llx_accounting_account MODIFY COLUMN account_parent integer DEFAULT 0; +ALTER TABLE llx_accounting_account ADD INDEX idx_accounting_account_account_parent (account_parent); + diff --git a/htdocs/install/mysql/migration/repair.sql b/htdocs/install/mysql/migration/repair.sql index cdd5bb2b653..69cd2a3bc79 100755 --- a/htdocs/install/mysql/migration/repair.sql +++ b/htdocs/install/mysql/migration/repair.sql @@ -94,7 +94,11 @@ delete from llx_livraison where ref = ''; delete from llx_expeditiondet where fk_expedition in (select rowid from llx_expedition where ref = ''); delete from llx_expedition where ref = ''; delete from llx_holiday_logs where fk_user_update not IN (select rowid from llx_user); + +delete from llx_rights_def where perms IS NULL; delete from llx_user_rights where fk_user not IN (select rowid from llx_user); +delete from llx_usergroup_rights where fk_usergroup not in (select rowid from llx_usergroup); +delete from llx_usergroup_rights where fk_id not in (select id from llx_rights_def); update llx_deplacement set dated='2010-01-01' where dated < '2000-01-01'; @@ -344,6 +348,8 @@ update llx_facturedet set product_type = 1 where product_type = 2; --update llx_commandedet as d set d.product_type = 1 where d.fk_product = 22 and d.product_type = 0; --update llx_facturedet as d set d.product_type = 1 where d.fk_product = 22 and d.product_type = 0; +update llx_propal set fk_statut = 1 where fk_statut = -1; + delete from llx_commande_fournisseur_dispatch where fk_commandefourndet = 0 or fk_commandefourndet IS NULL; diff --git a/htdocs/install/mysql/tables/llx_accounting_account.key.sql b/htdocs/install/mysql/tables/llx_accounting_account.key.sql index 19a6c95447a..cf62da87daa 100644 --- a/htdocs/install/mysql/tables/llx_accounting_account.key.sql +++ b/htdocs/install/mysql/tables/llx_accounting_account.key.sql @@ -19,6 +19,7 @@ ALTER TABLE llx_accounting_account ADD INDEX idx_accounting_account_fk_pcg_version (fk_pcg_version); +ALTER TABLE llx_accounting_account ADD INDEX idx_accounting_account_account_parent (account_parent); ALTER TABLE llx_accounting_account ADD UNIQUE INDEX uk_accounting_account (account_number, entity, fk_pcg_version); diff --git a/htdocs/install/mysql/tables/llx_accounting_account.sql b/htdocs/install/mysql/tables/llx_accounting_account.sql index 385d6b43bc8..79215115cfb 100644 --- a/htdocs/install/mysql/tables/llx_accounting_account.sql +++ b/htdocs/install/mysql/tables/llx_accounting_account.sql @@ -25,13 +25,13 @@ create table llx_accounting_account entity integer DEFAULT 1 NOT NULL, datec datetime, tms timestamp, - fk_pcg_version varchar(32) NOT NULL, - pcg_type varchar(20) NOT NULL, - pcg_subtype varchar(20) NOT NULL, + fk_pcg_version varchar(32) NOT NULL, -- Chart system + pcg_type varchar(20) NOT NULL, -- First part of Key for predefined groups + pcg_subtype varchar(20) NOT NULL, -- Second part of Key for predefined groups account_number varchar(32) NOT NULL, - account_parent varchar(32) DEFAULT '0', -- Hierarchic parent TODO Move this as integer, it is a foreign key of llx_accounting_account.rowid + account_parent integer DEFAULT 0, -- Hierarchic parent. label varchar(255) NOT NULL, - fk_accounting_category integer DEFAULT 0, + fk_accounting_category integer DEFAULT 0, -- ID of personalized group for report fk_user_author integer DEFAULT NULL, fk_user_modif integer DEFAULT NULL, active tinyint DEFAULT 1 NOT NULL, diff --git a/htdocs/install/mysql/tables/llx_asset.key.sql b/htdocs/install/mysql/tables/llx_asset.key.sql new file mode 100644 index 00000000000..6befef7455f --- /dev/null +++ b/htdocs/install/mysql/tables/llx_asset.key.sql @@ -0,0 +1,24 @@ +-- Copyright (C) 2018 Alexandre Spangaro +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see http://www.gnu.org/licenses/. + + +ALTER TABLE llx_asset ADD INDEX idx_asset_rowid (rowid); +ALTER TABLE llx_asset ADD INDEX idx_asset_ref (ref); +ALTER TABLE llx_asset ADD INDEX idx_asset_entity (entity); +ALTER TABLE llx_asset ADD INDEX idx_asset_fk_soc (fk_soc); + +ALTER TABLE llx_asset ADD INDEX idx_asset_fk_asset_type (fk_asset_type); +ALTER TABLE llx_asset ADD CONSTRAINT fk_asset_asset_type FOREIGN KEY (fk_asset_type) REFERENCES llx_asset_type (rowid); + diff --git a/htdocs/install/mysql/tables/llx_asset.sql b/htdocs/install/mysql/tables/llx_asset.sql new file mode 100644 index 00000000000..a583bd9275e --- /dev/null +++ b/htdocs/install/mysql/tables/llx_asset.sql @@ -0,0 +1,34 @@ +-- Copyright (C) 2018 Alexandre Spangaro +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see http://www.gnu.org/licenses/. + + +CREATE TABLE llx_asset( + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + ref varchar(128) NOT NULL, + entity integer DEFAULT 1 NOT NULL, + label varchar(255), + amount double(24,8) DEFAULT NULL, + fk_asset_type integer NOT NULL, + fk_soc integer, + description text, + note_public text, + note_private text, + date_creation datetime NOT NULL, + tms timestamp NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + import_key varchar(14), + status integer NOT NULL +) ENGINE=innodb; \ No newline at end of file diff --git a/htdocs/install/mysql/tables/llx_website_account_extrafields.sql b/htdocs/install/mysql/tables/llx_asset_extrafields.sql similarity index 88% rename from htdocs/install/mysql/tables/llx_website_account_extrafields.sql rename to htdocs/install/mysql/tables/llx_asset_extrafields.sql index 7bec7e958d9..bc5eedae6d6 100644 --- a/htdocs/install/mysql/tables/llx_website_account_extrafields.sql +++ b/htdocs/install/mysql/tables/llx_asset_extrafields.sql @@ -1,4 +1,4 @@ --- Copyright (C) 2017 Laurent Destailleur +-- Copyright (C) 2018 Alexandre Spangaro -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -13,7 +13,7 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see http://www.gnu.org/licenses/. -CREATE TABLE llx_website_account_extrafields +create table llx_asset_extrafields ( rowid integer AUTO_INCREMENT PRIMARY KEY, tms timestamp, diff --git a/htdocs/install/mysql/tables/llx_asset_type.key.sql b/htdocs/install/mysql/tables/llx_asset_type.key.sql new file mode 100644 index 00000000000..2186a4aafd2 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_asset_type.key.sql @@ -0,0 +1,16 @@ +-- Copyright (C) 2018 Alexandre Spangaro +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +ALTER TABLE llx_asset_type ADD UNIQUE INDEX uk_asset_type_label (label, entity); diff --git a/htdocs/install/mysql/tables/llx_asset_type.sql b/htdocs/install/mysql/tables/llx_asset_type.sql new file mode 100644 index 00000000000..6b6a8b23db1 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_asset_type.sql @@ -0,0 +1,26 @@ +-- Copyright (C) 2018 Alexandre Spangaro +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +create table llx_asset_type +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1 NOT NULL, -- multi company id + tms timestamp, + label varchar(50) NOT NULL, + accountancy_code_asset varchar(32), + accountancy_code_depreciation_asset varchar(32), + accountancy_code_depreciation_expense varchar(32), + note text +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_asset_type_extrafields.key.sql b/htdocs/install/mysql/tables/llx_asset_type_extrafields.key.sql new file mode 100644 index 00000000000..dbcc2500df8 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_asset_type_extrafields.key.sql @@ -0,0 +1,17 @@ +-- Copyright (C) 2018 Alexandre Spangaro +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + + +ALTER TABLE llx_asset_type_extrafields ADD INDEX idx_asset_type_extrafields (fk_object); diff --git a/htdocs/install/mysql/tables/llx_asset_type_extrafields.sql b/htdocs/install/mysql/tables/llx_asset_type_extrafields.sql new file mode 100644 index 00000000000..1b98bd3f524 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_asset_type_extrafields.sql @@ -0,0 +1,23 @@ +-- Copyright (C) 2018 Alexandre Spangaro +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +create table llx_asset_type_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, + import_key varchar(14) -- import key +) ENGINE=innodb; + diff --git a/htdocs/install/mysql/tables/llx_c_accounting_category.sql b/htdocs/install/mysql/tables/llx_c_accounting_category.sql index 683478aceef..253ff6aef46 100644 --- a/htdocs/install/mysql/tables/llx_c_accounting_category.sql +++ b/htdocs/install/mysql/tables/llx_c_accounting_category.sql @@ -15,18 +15,20 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- --- Table with category for accounting account +-- Table with category for accounting account. +-- Note: Each accounting account is inside one chart system, so we can have +-- a different dispatching of account in a category for each chart system. -- =================================================================== CREATE TABLE llx_c_accounting_category ( - rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY, - code varchar(16) NOT NULL, - label varchar(255) NOT NULL, - range_account varchar(255) NOT NULL, - sens tinyint NOT NULL DEFAULT '0', -- For international accounting 0 : credit - debit / 1 : debit - credit - category_type tinyint NOT NULL DEFAULT '0', -- Field calculated or not - formula varchar(255) NOT NULL, -- Example : 1 + 2 (rowid of the category) - position integer DEFAULT 0, - fk_country integer DEFAULT NULL, -- This category is dedicated to a country - active integer DEFAULT 1 + rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY, + code varchar(16) NOT NULL, + label varchar(255) NOT NULL, + range_account varchar(255) NOT NULL, -- Comment + sens tinyint NOT NULL DEFAULT '0', -- For international accounting 0 : credit - debit / 1 : debit - credit + category_type tinyint NOT NULL DEFAULT '0', -- Field calculated or not + formula varchar(255) NOT NULL, -- Example : 1 + 2 (rowid of the category) + position integer DEFAULT 0, + fk_country integer DEFAULT NULL, -- This category is dedicated to a country + active integer DEFAULT 1 ) ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_c_paiement.key.sql b/htdocs/install/mysql/tables/llx_c_paiement.key.sql index ad1930fba2e..fc8147805ef 100644 --- a/htdocs/install/mysql/tables/llx_c_paiement.key.sql +++ b/htdocs/install/mysql/tables/llx_c_paiement.key.sql @@ -17,4 +17,4 @@ -- -- ======================================================================== -ALTER TABLE llx_c_paiement ADD UNIQUE INDEX uk_c_paiement(id, entity, code); +ALTER TABLE llx_c_paiement ADD UNIQUE INDEX uk_c_paiement_code(entity, code); diff --git a/htdocs/install/mysql/tables/llx_c_paiement.sql b/htdocs/install/mysql/tables/llx_c_paiement.sql index 55cb44fb257..1c26cb7aaeb 100644 --- a/htdocs/install/mysql/tables/llx_c_paiement.sql +++ b/htdocs/install/mysql/tables/llx_c_paiement.sql @@ -21,7 +21,7 @@ create table llx_c_paiement ( - id integer, + id integer AUTO_INCREMENT PRIMARY KEY, entity integer DEFAULT 1 NOT NULL, -- multi company id code varchar(6) NOT NULL, libelle varchar(62), diff --git a/htdocs/install/mysql/tables/llx_c_payment_term.key.sql b/htdocs/install/mysql/tables/llx_c_payment_term.key.sql index 1bd86401945..fc2f49de529 100644 --- a/htdocs/install/mysql/tables/llx_c_payment_term.key.sql +++ b/htdocs/install/mysql/tables/llx_c_payment_term.key.sql @@ -17,4 +17,4 @@ -- -- ======================================================================== -ALTER TABLE llx_c_payment_term ADD UNIQUE INDEX uk_c_payment_term(rowid, entity, code); +ALTER TABLE llx_c_payment_term ADD UNIQUE INDEX uk_c_payment_term_code(entity, code); diff --git a/htdocs/install/mysql/tables/llx_c_payment_term.sql b/htdocs/install/mysql/tables/llx_c_payment_term.sql index cc7c7a22dea..39ccaa70916 100644 --- a/htdocs/install/mysql/tables/llx_c_payment_term.sql +++ b/htdocs/install/mysql/tables/llx_c_payment_term.sql @@ -20,7 +20,7 @@ create table llx_c_payment_term ( - rowid integer, + rowid integer AUTO_INCREMENT PRIMARY KEY, entity integer DEFAULT 1 NOT NULL, -- multi company id code varchar(16), sortorder smallint, diff --git a/htdocs/install/mysql/tables/llx_c_ticketsup_category.key.sql b/htdocs/install/mysql/tables/llx_c_ticketsup_category.key.sql new file mode 100644 index 00000000000..a43346b87cf --- /dev/null +++ b/htdocs/install/mysql/tables/llx_c_ticketsup_category.key.sql @@ -0,0 +1,18 @@ +-- Copyright (C) 2018 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +ALTER TABLE llx_c_ticketsup_category ADD UNIQUE INDEX uk_code (code, entity); diff --git a/htdocs/install/mysql/tables/llx_c_ticketsup_category.sql b/htdocs/install/mysql/tables/llx_c_ticketsup_category.sql new file mode 100755 index 00000000000..cce444d7f8a --- /dev/null +++ b/htdocs/install/mysql/tables/llx_c_ticketsup_category.sql @@ -0,0 +1,28 @@ +-- Copyright (C) 2013-2018 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +create table llx_c_ticketsup_category +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_c_ticketsup_severity.key.sql b/htdocs/install/mysql/tables/llx_c_ticketsup_severity.key.sql new file mode 100644 index 00000000000..2772b0b545e --- /dev/null +++ b/htdocs/install/mysql/tables/llx_c_ticketsup_severity.key.sql @@ -0,0 +1,18 @@ +-- Copyright (C) 2018 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +ALTER TABLE llx_c_ticketsup_severity ADD UNIQUE INDEX uk_code (code, entity); diff --git a/htdocs/install/mysql/tables/llx_c_ticketsup_severity.sql b/htdocs/install/mysql/tables/llx_c_ticketsup_severity.sql new file mode 100755 index 00000000000..9c26ec5cc32 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_c_ticketsup_severity.sql @@ -0,0 +1,29 @@ +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +create table llx_c_ticketsup_severity +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + color varchar(10) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_c_ticketsup_type.key.sql b/htdocs/install/mysql/tables/llx_c_ticketsup_type.key.sql new file mode 100644 index 00000000000..6dd4ec8fc9a --- /dev/null +++ b/htdocs/install/mysql/tables/llx_c_ticketsup_type.key.sql @@ -0,0 +1,18 @@ +-- Copyright (C) 2018 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +ALTER TABLE llx_c_ticketsup_type ADD UNIQUE INDEX uk_code (code, entity); diff --git a/htdocs/install/mysql/tables/llx_c_ticketsup_type.sql b/htdocs/install/mysql/tables/llx_c_ticketsup_type.sql new file mode 100755 index 00000000000..33f7b8a2973 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_c_ticketsup_type.sql @@ -0,0 +1,28 @@ +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +create table llx_c_ticketsup_type +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_c_type_fees.sql b/htdocs/install/mysql/tables/llx_c_type_fees.sql index 80bb3d9109d..15c6fe52182 100644 --- a/htdocs/install/mysql/tables/llx_c_type_fees.sql +++ b/htdocs/install/mysql/tables/llx_c_type_fees.sql @@ -16,6 +16,7 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- +-- Type of expense report -- ======================================================================== create table llx_c_type_fees @@ -23,6 +24,7 @@ create table llx_c_type_fees id integer AUTO_INCREMENT PRIMARY KEY, code varchar(12) NOT NULL, label varchar(30), + type integer DEFAULT 0, -- 0=type product, 1=type service accountancy_code varchar(32) NULL, active tinyint DEFAULT 1 NOT NULL, module varchar(32) NULL, diff --git a/htdocs/install/mysql/tables/llx_const.sql b/htdocs/install/mysql/tables/llx_const.sql index 22e552213aa..806a3337291 100644 --- a/htdocs/install/mysql/tables/llx_const.sql +++ b/htdocs/install/mysql/tables/llx_const.sql @@ -28,8 +28,8 @@ create table llx_const rowid integer AUTO_INCREMENT PRIMARY KEY, name varchar(180) NOT NULL, entity integer DEFAULT 1 NOT NULL, -- multi company id - value text NOT NULL, -- max 65535 caracteres - type varchar(6), + value text NOT NULL, -- max 65535 caracteres + type varchar(64) DEFAULT 'string', visible tinyint DEFAULT 1 NOT NULL, note text, tms timestamp diff --git a/htdocs/install/mysql/tables/llx_don.sql b/htdocs/install/mysql/tables/llx_don.sql index 2120f6a5b2b..a2e39f7039c 100644 --- a/htdocs/install/mysql/tables/llx_don.sql +++ b/htdocs/install/mysql/tables/llx_don.sql @@ -29,7 +29,7 @@ create table llx_don fk_statut smallint NOT NULL DEFAULT 0, -- Status of donation promise or validate datedon datetime, -- Date of the donation/promise amount double(24,8) DEFAULT 0, - fk_payment integer, + fk_payment integer, -- Id of payment mode paid smallint default 0 NOT NULL, firstname varchar(50), lastname varchar(50), diff --git a/htdocs/install/mysql/tables/llx_expensereport_det.sql b/htdocs/install/mysql/tables/llx_expensereport_det.sql index 5cc7b1b86f5..fc0bd6e697f 100644 --- a/htdocs/install/mysql/tables/llx_expensereport_det.sql +++ b/htdocs/install/mysql/tables/llx_expensereport_det.sql @@ -20,9 +20,10 @@ CREATE TABLE llx_expensereport_det ( rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY, fk_expensereport integer NOT NULL, - fk_c_type_fees integer NOT NULL, + docnumber varchar(128), -- To store a ref of a accounting doc (piece) + fk_c_type_fees integer NOT NULL, -- Type of expense fk_c_exp_tax_cat integer, - fk_projet integer, + fk_projet integer, -- Id of project comments text NOT NULL, product_type integer DEFAULT -1, qty real NOT NULL, diff --git a/htdocs/install/mysql/tables/llx_expensereport_rules.sql b/htdocs/install/mysql/tables/llx_expensereport_rules.sql index e6103ee8fb9..feacf572c1f 100644 --- a/htdocs/install/mysql/tables/llx_expensereport_rules.sql +++ b/htdocs/install/mysql/tables/llx_expensereport_rules.sql @@ -29,6 +29,6 @@ CREATE TABLE llx_expensereport_rules ( fk_usergroup integer DEFAULT NULL, fk_c_type_fees integer NOT NULL, code_expense_rules_type varchar(50) NOT NULL, - is_for_all tinyint DEFAULT '0', + is_for_all tinyint DEFAULT 0, entity integer DEFAULT 1 -) ENGINE=InnoDB \ No newline at end of file +) ENGINE=InnoDB; \ No newline at end of file diff --git a/htdocs/install/mysql/tables/llx_facturedet_rec.sql b/htdocs/install/mysql/tables/llx_facturedet_rec.sql index 8b2c70b09f7..341afd0d9ba 100644 --- a/htdocs/install/mysql/tables/llx_facturedet_rec.sql +++ b/htdocs/install/mysql/tables/llx_facturedet_rec.sql @@ -45,6 +45,8 @@ create table llx_facturedet_rec total_localtax1 double(24,8) DEFAULT 0, -- Total LocalTax1 for total quantity of line total_localtax2 double(24,8) DEFAULT 0, -- total LocalTax2 for total quantity of line total_ttc double(24,8), -- Total TTC de la ligne toute quantity et incluant remise ligne et globale + date_start_fill integer DEFAULT 0, -- 1=autofill the date_start of invoice with __INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__ + date_end_fill integer DEFAULT 0, -- 1=autofill the date_start of invoice with __INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__ info_bits integer DEFAULT 0, -- TVA NPR ou non special_code integer UNSIGNED DEFAULT 0, -- code pour les lignes speciales rang integer DEFAULT 0, -- ordre d'affichage diff --git a/htdocs/install/mysql/tables/llx_inventory.sql b/htdocs/install/mysql/tables/llx_inventory.sql index 1f3c3ecb111..9e4d02408c9 100644 --- a/htdocs/install/mysql/tables/llx_inventory.sql +++ b/htdocs/install/mysql/tables/llx_inventory.sql @@ -27,7 +27,8 @@ CREATE TABLE llx_inventory fk_user_creat integer, -- user making creation fk_user_modif integer, -- user making last change fk_user_valid integer, -- valideur de la fiche - fk_warehouse integer DEFAULT 0, + fk_warehouse integer DEFAULT NULL, + fk_product integer DEFAULT NULL, status integer DEFAULT 0, title varchar(255) NOT NULL, date_inventory datetime DEFAULT NULL, diff --git a/htdocs/install/mysql/tables/llx_oauth_token.sql b/htdocs/install/mysql/tables/llx_oauth_token.sql index 0ae4fed1ea8..81ae99c6ce6 100644 --- a/htdocs/install/mysql/tables/llx_oauth_token.sql +++ b/htdocs/install/mysql/tables/llx_oauth_token.sql @@ -18,8 +18,9 @@ CREATE TABLE llx_oauth_token ( rowid integer AUTO_INCREMENT PRIMARY KEY, service varchar(36), - token text, + token text, -- token in serialize() format, of an object StdOAuth2Token of library phpoauth2 + tokenstring text, -- token in json format '{"access_token": "sk_test_cccc", "refresh_token": "rt_aaa", "token_type": "bearer", ..., "scope": "read_write"} fk_user integer, fk_adherent integer, entity integer DEFAULT 1 -)ENGINE=InnoDB; \ No newline at end of file +)ENGINE=InnoDB; diff --git a/htdocs/install/mysql/tables/llx_payment_salary.sql b/htdocs/install/mysql/tables/llx_payment_salary.sql index 171c356415f..e3bcd0a9c4a 100644 --- a/htdocs/install/mysql/tables/llx_payment_salary.sql +++ b/htdocs/install/mysql/tables/llx_payment_salary.sql @@ -1,5 +1,5 @@ -- =================================================================== --- Copyright (C) 2011-2014 Alexandre Spangaro +-- Copyright (C) 2011-2018 Alexandre Spangaro -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -22,18 +22,18 @@ create table llx_payment_salary tms timestamp, datec datetime, -- Create date fk_user integer NOT NULL, - datep date, -- date de paiement - datev date, -- date de valeur (this field should not be here, only into bank tables) - salary double(24,8), -- salary of user when payment was done + datep date, -- payment date + datev date, -- value date (this field should not be here, only into bank tables) + salary double(24,8), -- salary of user when payment was done amount double(24,8) NOT NULL DEFAULT 0, fk_typepayment integer NOT NULL, - num_payment varchar(50), -- ref + num_payment varchar(50), -- ref label varchar(255), datesp date, -- date start period dateep date, -- date end period - entity integer DEFAULT 1 NOT NULL, -- multi company id + entity integer DEFAULT 1 NOT NULL, -- multi company id note text, - fk_bank integer, - fk_user_author integer, -- utilisateur qui a cree l'info - fk_user_modif integer -- utilisateur qui a modifié l'info + fk_bank integer, + fk_user_author integer, -- user creating + fk_user_modif integer -- user making last change )ENGINE=innodb; \ No newline at end of file diff --git a/htdocs/install/mysql/tables/llx_product.key.sql b/htdocs/install/mysql/tables/llx_product.key.sql index 5157861ba4b..40b4eb130a9 100644 --- a/htdocs/install/mysql/tables/llx_product.key.sql +++ b/htdocs/install/mysql/tables/llx_product.key.sql @@ -35,3 +35,4 @@ ALTER TABLE llx_product ADD CONSTRAINT fk_product_fk_unit FOREIGN KEY (fk_unit) ALTER TABLE llx_product ADD CONSTRAINT fk_product_fk_country FOREIGN KEY (fk_country) REFERENCES llx_c_country (rowid); ALTER TABLE llx_product ADD CONSTRAINT fk_product_barcode_type FOREIGN KEY (fk_barcode_type) REFERENCES llx_c_barcode_type (rowid); +ALTER TABLE llx_product ADD CONSTRAINT fk_product_default_warehouse FOREIGN KEY (fk_default_warehouse) REFERENCES llx_entrepot (rowid); diff --git a/htdocs/install/mysql/tables/llx_product.sql b/htdocs/install/mysql/tables/llx_product.sql index 21da795cd4e..1e201038122 100755 --- a/htdocs/install/mysql/tables/llx_product.sql +++ b/htdocs/install/mysql/tables/llx_product.sql @@ -85,6 +85,7 @@ create table llx_product pmp double(24,8) DEFAULT 0 NOT NULL, -- To store valuation of stock calculated using average price method, for this product fifo double(24,8), -- To store valuation of stock calculated using fifo method, for this product. TODO Not used, should be replaced by stock value stored into movement table. lifo double(24,8), -- To store valuation of stock calculated using lifo method, for this product. TODO Not used, should be replaced by stock value stored into movement table. + fk_default_warehouse integer DEFAULT NULL, canvas varchar(32) DEFAULT NULL, finished tinyint DEFAULT NULL, -- 1=manufactured product, 0=matiere premiere hidden tinyint DEFAULT 0, -- Not used. Deprecated. diff --git a/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql b/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql index 1c86bfe04f8..14664225a9d 100755 --- a/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql +++ b/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql @@ -29,13 +29,12 @@ create table llx_product_fournisseur_price fk_soc integer, ref_fourn varchar(30), fk_availability integer, - price double(24,8) DEFAULT 0, + price double(24,8) DEFAULT 0, -- price without tax for quantity quantity double, remise_percent double NOT NULL DEFAULT 0, remise double NOT NULL DEFAULT 0, - unitprice double(24,8) DEFAULT 0, + unitprice double(24,8) DEFAULT 0, -- unit price without tax charges double(24,8) DEFAULT 0, -- to store transport cost. Constant PRODUCT_CHARGES must be set to see it. - unitcharges double(24,8) DEFAULT 0, -- deprecated default_vat_code varchar(10), tva_tx double(6,3) NOT NULL, localtax1_tx double(6,3) DEFAULT 0, diff --git a/htdocs/install/mysql/tables/llx_societe.sql b/htdocs/install/mysql/tables/llx_societe.sql index 9ffb9734d7f..f1714021fb8 100644 --- a/htdocs/install/mysql/tables/llx_societe.sql +++ b/htdocs/install/mysql/tables/llx_societe.sql @@ -78,6 +78,7 @@ create table llx_societe customer_rate real DEFAULT 0, -- taux fiabilite client (0 a 1) supplier_rate real DEFAULT 0, -- taux fiabilite fournisseur (0 a 1) remise_client real DEFAULT 0, -- remise systematique pour le client + remise_supplier real DEFAULT 0, -- remise systematique auprès du fournisseur mode_reglement tinyint, -- mode de reglement cond_reglement tinyint, -- condition de reglement mode_reglement_supplier tinyint, -- mode de reglement fournisseur diff --git a/htdocs/install/mysql/tables/llx_website_account.key.sql b/htdocs/install/mysql/tables/llx_societe_account.key.sql similarity index 54% rename from htdocs/install/mysql/tables/llx_website_account.key.sql rename to htdocs/install/mysql/tables/llx_societe_account.key.sql index 1302ec50d2c..3e0dc130554 100644 --- a/htdocs/install/mysql/tables/llx_website_account.key.sql +++ b/htdocs/install/mysql/tables/llx_societe_account.key.sql @@ -15,14 +15,16 @@ -- BEGIN MODULEBUILDER INDEXES -ALTER TABLE llx_website_account ADD INDEX idx_website_account_rowid (rowid); -ALTER TABLE llx_website_account ADD INDEX idx_website_account_login (login); -ALTER TABLE llx_website_account ADD INDEX idx_website_account_status (status); -ALTER TABLE llx_website_account ADD INDEX idx_website_account_fk_website (fk_website); -ALTER TABLE llx_website_account ADD INDEX idx_website_account_fk_soc (fk_soc); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_rowid (rowid); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_login (login); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_status (status); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_fk_website (fk_website); +ALTER TABLE llx_societe_account ADD INDEX idx_societe_account_fk_soc (fk_soc); -- END MODULEBUILDER INDEXES -ALTER TABLE llx_website_account ADD UNIQUE INDEX uk_website_account_login_website_soc(login, fk_website, fk_soc); +ALTER TABLE llx_societe_account ADD UNIQUE INDEX uk_societe_account_login_website_soc(entity, fk_soc, login, site, fk_website); +ALTER TABLE llx_societe_account ADD UNIQUE INDEX uk_societe_account_key_account_soc(entity, fk_soc, key_account, site, fk_website); -ALTER TABLE llx_website_account ADD CONSTRAINT llx_website_account_fk_website FOREIGN KEY (fk_website) REFERENCES llx_website(rowid); +ALTER TABLE llx_societe_account ADD CONSTRAINT llx_societe_account_fk_website FOREIGN KEY (fk_website) REFERENCES llx_website(rowid); +ALTER TABLE llx_societe_account ADD CONSTRAINT llx_societe_account_fk_societe FOREIGN KEY (fk_soc) REFERENCES llx_societe(rowid); diff --git a/htdocs/install/mysql/tables/llx_website_account.sql b/htdocs/install/mysql/tables/llx_societe_account.sql similarity index 60% rename from htdocs/install/mysql/tables/llx_website_account.sql rename to htdocs/install/mysql/tables/llx_societe_account.sql index 373ba53f484..7a0f87cbe5d 100644 --- a/htdocs/install/mysql/tables/llx_website_account.sql +++ b/htdocs/install/mysql/tables/llx_societe_account.sql @@ -12,20 +12,24 @@ -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see http://www.gnu.org/licenses/. +-- +-- Table to store accounts of thirdparties on websites - -CREATE TABLE llx_website_account( +CREATE TABLE llx_societe_account( -- BEGIN MODULEBUILDER FIELDS - rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, - login varchar(64) NOT NULL, - pass_encoding varchar(24) NOT NULL, - pass_crypted varchar(128), - pass_temp varchar(128), -- temporary password when asked for forget password - fk_soc integer, - fk_website integer NOT NULL, + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + entity integer DEFAULT 1, + key_account varchar(128), + login varchar(128) NOT NULL, + pass_encoding varchar(24), + pass_crypted varchar(128), + pass_temp varchar(128), -- temporary password when asked for forget password + fk_soc integer, + site varchar(128), -- name of external web site + fk_website integer, -- id of local web site note_private text, - date_last_login datetime, - date_previous_login datetime, + date_last_login datetime, + date_previous_login datetime, date_creation datetime NOT NULL, tms timestamp NOT NULL, fk_user_creat integer NOT NULL, @@ -33,4 +37,4 @@ CREATE TABLE llx_website_account( import_key varchar(14), status integer -- END MODULEBUILDER FIELDS -) ENGINE=innodb; \ No newline at end of file +) ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_societe_remise_except.key.sql b/htdocs/install/mysql/tables/llx_societe_remise_except.key.sql index 148a8777915..525af592041 100644 --- a/htdocs/install/mysql/tables/llx_societe_remise_except.key.sql +++ b/htdocs/install/mysql/tables/llx_societe_remise_except.key.sql @@ -26,6 +26,7 @@ ALTER TABLE llx_societe_remise_except ADD INDEX idx_societe_remise_except_fk_soc ALTER TABLE llx_societe_remise_except ADD INDEX idx_societe_remise_except_fk_facture_line (fk_facture_line); ALTER TABLE llx_societe_remise_except ADD INDEX idx_societe_remise_except_fk_facture (fk_facture); ALTER TABLE llx_societe_remise_except ADD INDEX idx_societe_remise_except_fk_facture_source (fk_facture_source); +ALTER TABLE llx_societe_remise_except ADD INDEX idx_societe_remise_except_discount_type (discount_type); ALTER TABLE llx_societe_remise_except ADD CONSTRAINT fk_societe_remise_fk_user FOREIGN KEY (fk_user) REFERENCES llx_user (rowid); diff --git a/htdocs/install/mysql/tables/llx_societe_remise_except.sql b/htdocs/install/mysql/tables/llx_societe_remise_except.sql index 3a19a026ca9..151e33d7f1e 100644 --- a/htdocs/install/mysql/tables/llx_societe_remise_except.sql +++ b/htdocs/install/mysql/tables/llx_societe_remise_except.sql @@ -23,7 +23,8 @@ create table llx_societe_remise_except ( rowid integer AUTO_INCREMENT PRIMARY KEY, entity integer DEFAULT 1 NOT NULL, -- multi company id - fk_soc integer NOT NULL, -- client + fk_soc integer NOT NULL, -- customer or supplier + discount_type integer DEFAULT 0 NOT NULL, -- 0 => customer, 1 => supplier datec datetime, amount_ht double(24,8) NOT NULL, amount_tva double(24,8) DEFAULT 0 NOT NULL, diff --git a/htdocs/install/mysql/tables/llx_societe_remise_supplier.sql b/htdocs/install/mysql/tables/llx_societe_remise_supplier.sql new file mode 100644 index 00000000000..c1b56f225c7 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_societe_remise_supplier.sql @@ -0,0 +1,34 @@ +-- ======================================================================== +-- Copyright (C) 2000-2004 Rodolphe Quiedeville +-- Copyright (C) 2011-2016 Regis Houssin +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- +-- Historique evolution de la remise relative des tiers +-- ======================================================================== + +create table llx_societe_remise_supplier +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1 NOT NULL, -- multi company id + fk_soc integer NOT NULL, + tms timestamp, + datec datetime, -- creation date + fk_user_author integer, -- creation user + remise_supplier double(6,3) DEFAULT 0 NOT NULL, -- discount + note text + +)ENGINE=innodb; + diff --git a/htdocs/install/mysql/tables/llx_societe_rib.sql b/htdocs/install/mysql/tables/llx_societe_rib.sql index 168a2803c9d..2463378067a 100644 --- a/htdocs/install/mysql/tables/llx_societe_rib.sql +++ b/htdocs/install/mysql/tables/llx_societe_rib.sql @@ -17,15 +17,19 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- +-- Table with the payment modes of a thirdparty (BAN, Paypal, Card, ...) -- ============================================================================= create table llx_societe_rib ( rowid integer AUTO_INCREMENT PRIMARY KEY, + type varchar(32) DEFAULT 'ban' NOT NULL, -- 'ban' or 'paypal' or 'card' or 'stripe' + label varchar(30), fk_soc integer NOT NULL, datec datetime, tms timestamp, - label varchar(30), + + -- For BAN bank varchar(255), -- bank name code_banque varchar(128), -- bank code code_guichet varchar(6), -- desk code @@ -37,10 +41,28 @@ create table llx_societe_rib proprio varchar(60), owner_address varchar(255), default_rib smallint NOT NULL DEFAULT 0, - + -- For BAN direct debit feature rum varchar(32), -- RUM value to use for SEPA generation date_rum date, -- Date of mandate frstrecur varchar(16) default 'FRST', -- 'FRST' or 'RECUR' - + --For credit card + last_four varchar(4), -- last 4 + card_type varchar(255), -- card type 'VISA', 'MC' , ... + cvn varchar(255), + exp_date_month INTEGER, + exp_date_year INTEGER, + country_code varchar(10), + --For Paypal + approved INTEGER DEFAULT 0, + email varchar(255), + ending_date date, + max_total_amount_of_all_payments double(24,8), + preapproval_key varchar(255), + starting_date date, + total_amount_of_all_payments double(24,8), + --For Stripe + stripe_card_ref varchar(128), -- 'card_...' + + status integer NOT NULL DEFAULT 1, -- 1=ACTIVE, 0=IN_TRASH import_key varchar(14) -- import key )ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_ticketsup.key.sql b/htdocs/install/mysql/tables/llx_ticketsup.key.sql new file mode 100755 index 00000000000..abf853e718c --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup.key.sql @@ -0,0 +1,18 @@ +-- SQL definition for module ticketsup +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +ALTER TABLE llx_ticketsup ADD UNIQUE uk_ticketsup_rowid_track_id (rowid, track_id); +ALTER TABLE llx_ticketsup ADD INDEX id_ticketsup_track_id (track_id); diff --git a/htdocs/install/mysql/tables/llx_ticketsup.sql b/htdocs/install/mysql/tables/llx_ticketsup.sql new file mode 100644 index 00000000000..aeb5d41814d --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup.sql @@ -0,0 +1,42 @@ +-- SQL definition for module ticketsup +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +CREATE TABLE llx_ticketsup +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + ref varchar(128) NOT NULL, + track_id varchar(128) NOT NULL, + fk_soc integer DEFAULT 0, + fk_project integer DEFAULT 0, + origin_email varchar(128), + fk_user_create integer, + fk_user_assign integer, + subject varchar(255), + message text, + fk_statut integer, + resolution integer, + progress varchar(100), + timing varchar(20), + type_code varchar(32), + category_code varchar(32), + severity_code varchar(32), + datec datetime, + date_read datetime, + date_close datetime, + notify_tiers_at_create tinyint, + tms timestamp +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_ticketsup_extrafields.sql b/htdocs/install/mysql/tables/llx_ticketsup_extrafields.sql new file mode 100644 index 00000000000..0dd82b2566e --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup_extrafields.sql @@ -0,0 +1,24 @@ +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +-- +-- + +create table llx_ticketsup_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, -- ticket id + import_key varchar(14) -- import key +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_ticketsup_logs.key.sql b/htdocs/install/mysql/tables/llx_ticketsup_logs.key.sql new file mode 100755 index 00000000000..180febe01bf --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup_logs.key.sql @@ -0,0 +1,17 @@ +-- SQL definition for module ticketsup +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +ALTER TABLE llx_ticketsup_logs ADD CONSTRAINT fk_ticketsup_logs_fk_track_id FOREIGN KEY (fk_track_id) REFERENCES llx_ticketsup (track_id); diff --git a/htdocs/install/mysql/tables/llx_ticketsup_logs.sql b/htdocs/install/mysql/tables/llx_ticketsup_logs.sql new file mode 100755 index 00000000000..f573bd5751b --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup_logs.sql @@ -0,0 +1,25 @@ +-- SQL definition for module ticketsup +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +CREATE TABLE llx_ticketsup_logs +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + fk_track_id varchar(128), + fk_user_create integer, + datec datetime, + message text +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_ticketsup_msg.key.sql b/htdocs/install/mysql/tables/llx_ticketsup_msg.key.sql new file mode 100755 index 00000000000..098183bdfa0 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup_msg.key.sql @@ -0,0 +1,17 @@ +-- SQL definition for module ticketsup +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +ALTER TABLE llx_ticketsup_msg ADD CONSTRAINT fk_ticketsup_msg_fk_track_id FOREIGN KEY (fk_track_id) REFERENCES llx_ticketsup (track_id); diff --git a/htdocs/install/mysql/tables/llx_ticketsup_msg.sql b/htdocs/install/mysql/tables/llx_ticketsup_msg.sql new file mode 100755 index 00000000000..da96d5cfdb0 --- /dev/null +++ b/htdocs/install/mysql/tables/llx_ticketsup_msg.sql @@ -0,0 +1,26 @@ +-- SQL definition for module ticketsup +-- Copyright (C) 2013 Jean-François FERRY +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . + +CREATE TABLE llx_ticketsup_msg +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + fk_track_id varchar(128), + fk_user_action integer, + datec datetime, + message text, + private integer DEFAULT 0 +)ENGINE=innodb; diff --git a/htdocs/install/mysql/tables/llx_website_page.sql b/htdocs/install/mysql/tables/llx_website_page.sql index 5393177a3bb..7cb9705bcba 100644 --- a/htdocs/install/mysql/tables/llx_website_page.sql +++ b/htdocs/install/mysql/tables/llx_website_page.sql @@ -22,6 +22,7 @@ CREATE TABLE llx_website_page rowid integer AUTO_INCREMENT NOT NULL PRIMARY KEY, fk_website integer NOT NULL, pageurl varchar(255) NOT NULL, + aliasalt varchar(255), title varchar(255), description varchar(255), keywords varchar(255), diff --git a/htdocs/install/step1.php b/htdocs/install/step1.php index 6b662e4306d..838ef34afa3 100644 --- a/htdocs/install/step1.php +++ b/htdocs/install/step1.php @@ -491,7 +491,7 @@ if (! $error && $db->connected && $action == "set") 'thirdparties' => 'thirdparty', 'usergroups' => 'usergroups', 'users' => 'user', - 'usergroups' => 'usergroups', + 'usergroups' => 'usergroups', ); foreach($docs as $cursordir => $cursorfile) { diff --git a/htdocs/install/step2.php b/htdocs/install/step2.php index 44661c191d7..a891a2f590e 100644 --- a/htdocs/install/step2.php +++ b/htdocs/install/step2.php @@ -201,8 +201,10 @@ if ($action == "set") { $buffer=preg_replace('/type=innodb/i','ENGINE=innodb',$buffer); } - else if ($conf->db->type == 'mssql') + else { + // Keyword ENGINE is MySQL-specific, so scrub it for + // other database types (mssql, pgsql) $buffer=preg_replace('/type=innodb/i','',$buffer); $buffer=preg_replace('/ENGINE=innodb/i','',$buffer); } diff --git a/htdocs/install/step5.php b/htdocs/install/step5.php index d1f7e3eb31a..aa6e1f3e3c1 100644 --- a/htdocs/install/step5.php +++ b/htdocs/install/step5.php @@ -189,6 +189,8 @@ if ($action == "set" || empty($action) || preg_match('/upgrade/i',$action)) else dolibarr_set_const($db, "MAIN_SECURITY_HASH_ALGO", 'sha1md5', 'chaine', 0, '', 0); // All entities } + + dolibarr_install_syslog('step5: DATABASE_PWD_ENCRYPTED = '.$conf->global->DATABASE_PWD_ENCRYPTED.' MAIN_SECURITY_HASH_ALGO = '.$conf->global->MAIN_SECURITY_HASH_ALGO, LOG_INFO); } // Create user used to create the admin user diff --git a/htdocs/langs/ar_SA/incoterm.lang b/htdocs/langs/ar_SA/incoterm.lang deleted file mode 100644 index e37b2fd78f8..00000000000 --- a/htdocs/langs/ar_SA/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=شروط التجارة الدولية -Module62000Desc=إضافة ميزات لإدارة شروط التجارة الدولية -IncotermLabel=شروط التجارة الدولية diff --git a/htdocs/langs/bg_BG/incoterm.lang b/htdocs/langs/bg_BG/incoterm.lang deleted file mode 100644 index 48ef90b01a5..00000000000 --- a/htdocs/langs/bg_BG/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Инкотерм -Module62000Desc=Добяване на свойства за управление на Инкотерм -IncotermLabel=Инкотермс diff --git a/htdocs/langs/bn_BD/incoterm.lang b/htdocs/langs/bn_BD/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/bn_BD/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/bs_BA/incoterm.lang b/htdocs/langs/bs_BA/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/bs_BA/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/ca_ES/incoterm.lang b/htdocs/langs/ca_ES/incoterm.lang deleted file mode 100644 index 259ec38c7cb..00000000000 --- a/htdocs/langs/ca_ES/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Afegir funcions per gestionar Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/cs_CZ/incoterm.lang b/htdocs/langs/cs_CZ/incoterm.lang deleted file mode 100644 index e0f966a95cb..00000000000 --- a/htdocs/langs/cs_CZ/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Přidat funkce pro správu Incotermu -IncotermLabel=Incoterms diff --git a/htdocs/langs/da_DK/incoterm.lang b/htdocs/langs/da_DK/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/da_DK/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/de_AT/members.lang b/htdocs/langs/de_AT/members.lang index 063dfac0666..f90be1011c1 100644 --- a/htdocs/langs/de_AT/members.lang +++ b/htdocs/langs/de_AT/members.lang @@ -19,6 +19,4 @@ Physical=Physisch Moral=Rechtlich MorPhy=Physisch/Rechtlich Filehtpasswd=htpasswd-Datei -DescADHERENT_MAIL_RESIL_SUBJECT=E-Mail-Betreff für Zurückstellen eines Mitglieds -DescADHERENT_MAIL_RESIL=E-Mail-Text für Zurückstellen eines Mitglieds HTPasswordExport=htpassword-Dateierstellung diff --git a/htdocs/langs/de_CH/members.lang b/htdocs/langs/de_CH/members.lang index 73f9ee9b008..0709b29c82b 100644 --- a/htdocs/langs/de_CH/members.lang +++ b/htdocs/langs/de_CH/members.lang @@ -30,7 +30,6 @@ WelcomeEMail=Begrüssungsemail SubscriptionRequired=Abonnement notwendig VoteAllowed=Abstimmen erlaubt ShowSubscription=Abonnement anzeigen -SendAnEMailToMember=Informationsemail dem Mitglied senden HTPasswordExport=htpassword Datei generieren MembersAndSubscriptions=Mitglieder und Abonnemente SubscriptionPayment=Zahlung des Mitgliedsbeitrags diff --git a/htdocs/langs/de_DE/incoterm.lang b/htdocs/langs/de_DE/incoterm.lang deleted file mode 100644 index c84e313a70c..00000000000 --- a/htdocs/langs/de_DE/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Funktion hinzufügen um Incoterms zu verwalten -IncotermLabel=Incoterms diff --git a/htdocs/langs/el_GR/incoterm.lang b/htdocs/langs/el_GR/incoterm.lang deleted file mode 100644 index e1b8fddb2dc..00000000000 --- a/htdocs/langs/el_GR/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Διεθνείς Εμπορικός Όρος -Module62000Desc=Προσθέστε δυνατότητες για τη διαχείριση του Διεθνή Εμπορικού Όρου -IncotermLabel=Διεθνείς Εμπορικοί Όροι diff --git a/htdocs/langs/en_GB/accountancy.lang b/htdocs/langs/en_GB/accountancy.lang index 8639f6dde52..1fea94adb02 100644 --- a/htdocs/langs/en_GB/accountancy.lang +++ b/htdocs/langs/en_GB/accountancy.lang @@ -61,14 +61,12 @@ ACCOUNTING_PRODUCT_BUY_ACCOUNT=Default purchases account (used if not defined in ACCOUNTING_PRODUCT_SOLD_ACCOUNT=Default sales account (used if not defined in the product sheet) ACCOUNTING_SERVICE_BUY_ACCOUNT=Default services purchase account (used if not defined in the service sheet) ACCOUNTING_SERVICE_SOLD_ACCOUNT=Default services sales account (used if not defined in the service sheet) -Code_tiers=Third party LabelAccount=Account name Sens=Meaning NumPiece=Item number GroupByAccountAccounting=Group by finance account DelBookKeeping=Delete record in the Ledger FeeAccountNotDefined=Account for fees not defined -ThirdPartyAccount=Third party account NumMvts=Transaction Number ErrorDebitCredit=Debit and Credit fields cannot have values at the same time AddCompteFromBK=Add finance accounts to the group diff --git a/htdocs/langs/en_NZ/companies.lang b/htdocs/langs/en_NZ/companies.lang index fd92c40825e..afa8538da3b 100644 --- a/htdocs/langs/en_NZ/companies.lang +++ b/htdocs/langs/en_NZ/companies.lang @@ -3,3 +3,6 @@ VATIsUsed=GST is used VATIsNotUsed=GST is not used VATIntra=GST number VATIntraShort=GST number +VATIntraVeryShort=GST +VATIntraSyntaxIsValid=Syntax is valid +VATIntraValueIsValid=Value is valid diff --git a/htdocs/langs/en_NZ/compta.lang b/htdocs/langs/en_NZ/compta.lang index 90936c6ccf4..7b85434bb5c 100644 --- a/htdocs/langs/en_NZ/compta.lang +++ b/htdocs/langs/en_NZ/compta.lang @@ -9,3 +9,6 @@ VATPaid=GST paid VATCollected=GST collected VATPayment=GST Payment VATPayments=GST Payments +NewVATPayment=New GST payment +TotalToPay=Total to pay +TotalVATReceived=Total GST received \ No newline at end of file diff --git a/htdocs/langs/en_NZ/main.lang b/htdocs/langs/en_NZ/main.lang index 45235b7bf39..2af1b36ceb0 100644 --- a/htdocs/langs/en_NZ/main.lang +++ b/htdocs/langs/en_NZ/main.lang @@ -19,11 +19,16 @@ FormatDateHourShort=%d/%m/%Y %H:%M FormatDateHourSecShort=%m/%d/%Y %I:%M:%S %p FormatDateHourTextShort=%d %b %Y %H:%M FormatDateHourText=%d %B %Y %H:%M +UnitPrice=Unit price UnitPriceHT=Unit price (excl GST) +UnitPriceTTC=Unit price AmountHT=Amount (excl GST) AmountTTC=Amount (incl GST) AmountVAT=Amount GST +PriceQtyHT=Price for this quantity excl GST PriceQtyMinHT=Price quantity min. excl GST +PriceQtyTTC=Price for this quantity incl GST +PriceQtyMinTTC=Price quantity min. incl GST TotalHT=Total (excl GST) TotalTTC=Total (incl GST) TotalTTCToYourCredit=Total (incl GST) to your credit diff --git a/htdocs/langs/en_NZ/sendings.lang b/htdocs/langs/en_NZ/sendings.lang new file mode 100644 index 00000000000..ceb733e140f --- /dev/null +++ b/htdocs/langs/en_NZ/sendings.lang @@ -0,0 +1,3 @@ +# Dolibarr language file - en_NZ - main +# This file contains only line that must differs from en_US file +SendingSheet=Packing Slip \ No newline at end of file diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 28a77095084..c8973887d2d 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -149,7 +149,6 @@ ACCOUNTING_SERVICE_SOLD_ACCOUNT=Accounting account by default for the sold servi Doctype=Type of document Docdate=Date Docref=Reference -Code_tiers=Thirdparty LabelAccount=Label account LabelOperation=Label operation Sens=Sens @@ -169,7 +168,6 @@ DelYear=Year to delete DelJournal=Journal to delete ConfirmDeleteMvt=This will delete all lines of the Ledger for year and/or from a specific journal. At least one criteria is required. ConfirmDeleteMvtPartial=This will delete the transaction from the Ledger (all lines related to same transaction will be deleted) -DelBookKeeping=Delete record of the Ledger FinanceJournal=Finance journal ExpenseReportsJournal=Expense reports journal DescFinanceJournal=Finance journal including all the types of payments by bank account @@ -180,7 +178,7 @@ ProductAccountNotDefined=Account for product not defined FeeAccountNotDefined=Account for fee not defined BankAccountNotDefined=Account for bank not defined CustomerInvoicePayment=Payment of invoice customer -ThirdPartyAccount=Thirdparty account +ThirdPartyAccount=Third party account NewAccountingMvt=New transaction NumMvts=Numero of transaction ListeMvts=List of movements @@ -236,13 +234,15 @@ AccountingJournal=Accounting journal NewAccountingJournal=New accounting journal ShowAccoutingJournal=Show accounting journal Nature=Nature -AccountingJournalType1=Miscellaneous operation +AccountingJournalType1=Miscellaneous operations AccountingJournalType2=Sales AccountingJournalType3=Purchases AccountingJournalType4=Bank AccountingJournalType5=Expenses report +AccountingJournalType8=Inventory AccountingJournalType9=Has-new ErrorAccountingJournalIsAlreadyUse=This journal is already use +AccountingAccountForSalesTaxAreDefinedInto=Note: Accounting account for Sales tax are defined into menu %s - %s ## Export ExportDraftJournal=Export draft journal diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 005b77bd5c2..5c733fcfc7c 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -392,6 +392,7 @@ PriceBaseTypeToChange=Modify on prices with base reference value defined on MassConvert=Launch mass convert String=String TextLong=Long text +HtmlText=Html text Int=Integer Float=Float DateAndTime=Date and hour @@ -411,6 +412,7 @@ ExtrafieldCheckBoxFromList=Checkboxes from table ExtrafieldLink=Link to an object ComputedFormula=Computed field ComputedFormulaDesc=You can enter here a formula using other properties of object or any PHP coding to get a dynamic computed value. You can use any PHP compatible formulas including the "?" condition operator, and following global object: $db, $conf, $langs, $mysoc, $user, $object.
    WARNING: Only some properties of $object may be available. If you need a properties not loaded, just fetch yourself the object into your formula like in the second example.
    Using a computed field means you can't enter yourself any value from interface. Also, if there is a syntax error, the formula may return nothing.

    Example of formula:
    $object->id < 10 ? round($object->id / 2, 2) : ($object->id + 2 * $user->id) * (int) substr($mysoc->zip, 1, 2)

    Example to reload object
    (($reloadedobj = new Societe($db)) && ($reloadedobj->fetch($obj->id ? $obj->id : ($obj->rowid ? $obj->rowid : $object->id)) > 0)) ? $reloadedobj->array_options['options_extrafieldkey'] * $reloadedobj->capital / 5 : '-1'

    Other example of formula to force load of object and its parent object:
    (($reloadedobj = new Task($db)) && ($reloadedobj->fetch($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetch($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : 'Parent project not found' +ExtrafieldParamHelpPassword=Keep this field empty means value will be stored without encryption (field must be only hidden with star on screen).
    Set here value 'auto' to use the default encryption rule to save password into database (then value read will be the hash only, no way to retreive original value) ExtrafieldParamHelpselect=List of values must be lines with format key,value (where key can't be '0')

    for example :
    1,value1
    2,value2
    code3,value3
    ...

    In order to have the list depending on another complementary attribute list :
    1,value1|options_parent_list_code:parent_key
    2,value2|options_parent_list_code:parent_key

    In order to have the list depending on another list :
    1,value1|parent_list_code:parent_key
    2,value2|parent_list_code:parent_key ExtrafieldParamHelpcheckbox=List of values must be lines with format key,value (where key can't be '0')

    for example :
    1,value1
    2,value2
    3,value3
    ... ExtrafieldParamHelpradio=List of values must be lines with format key,value (where key can't be '0')

    for example :
    1,value1
    2,value2
    3,value3
    ... @@ -418,7 +420,6 @@ ExtrafieldParamHelpsellist=List of values comes from a table
    Syntax : table_n ExtrafieldParamHelpchkbxlst=List of values comes from a table
    Syntax : table_name:label_field:id_field::filter
    Example : c_typent:libelle:id::filter

    filter can be a simple test (eg active=1) to display only active value
    You can also use $ID$ in filter witch is the current id of current object
    To do a SELECT in filter use $SEL$
    if you want to filter on extrafields use syntax extra.fieldcode=... (where field code is the code of extrafield)

    In order to have the list depending on another complementary attribute list :
    c_typent:libelle:id:options_parent_list_code|parent_column:filter

    In order to have the list depending on another list:
    c_typent:libelle:id:parent_list_code|parent_column:filter ExtrafieldParamHelplink=Parameters must be ObjectName:Classpath
    Syntax : ObjectName:Classpath
    Examples :
    Societe:societe/class/societe.class.php
    Contact:contact/class/contact.class.php LibraryToBuildPDF=Library used for PDF generation -WarningUsingFPDF=Warning: Your conf.php contains directive dolibarr_pdf_force_fpdf=1. This means you use the FPDF library to generate PDF files. This library is old and does not support a lot of features (Unicode, image transparency, cyrillic, arab and asiatic languages, ...), so you may experience errors during PDF generation.
    To solve this and have a full support of PDF generation, please download TCPDF library, then comment or remove the line $dolibarr_pdf_force_fpdf=1, and add instead $dolibarr_lib_TCPDF_PATH='path_to_TCPDF_dir' LocalTaxDesc=Some countries apply 2 or 3 taxes on each invoice line. If this is the case, choose type for second and third tax and its rate. Possible type are:
    1 : local tax apply on products and services without vat (localtax is calculated on amount without tax)
    2 : local tax apply on products and services including vat (localtax is calculated on amount + main tax)
    3 : local tax apply on products without vat (localtax is calculated on amount without tax)
    4 : local tax apply on products including vat (localtax is calculated on amount + main vat)
    5 : local tax apply on services without vat (localtax is calculated on amount without tax)
    6 : local tax apply on services including vat (localtax is calculated on amount + tax) SMS=SMS LinkToTestClickToDial=Enter a phone number to call to show a link to test the ClickToDial url for user %s @@ -469,6 +470,7 @@ WatermarkOnDraftExpenseReports=Watermark on draft expense reports AttachMainDocByDefault=Set this to 1 if you want to attach main document to email by default (if applicable) FilesAttachedToEmail=Attach file SendEmailsReminders=Send agenda reminders by emails +davDescription=Add a component to be a DAV server # Modules Module0Name=Users & groups Module0Desc=Users / Employees and Groups management @@ -620,6 +622,8 @@ Module59000Name=Margins Module59000Desc=Module to manage margins Module60000Name=Commissions Module60000Desc=Module to manage commissions +Module62000Name=Incoterm +Module62000Desc=Add features to manage Incoterm Module63000Name=Resources Module63000Desc=Manage resources (printers, cars, room, ...) you can then share into events Permission11=Read customer invoices @@ -834,11 +838,11 @@ Permission1251=Run mass imports of external data into database (data load) Permission1321=Export customer invoices, attributes and payments Permission1322=Reopen a paid bill Permission1421=Export customer orders and attributes -Permission20001=Read leave requests (yours and your subordinates) -Permission20002=Create/modify your leave requests +Permission20001=Read leave requests (your leaves and the one of your subordinates) +Permission20002=Create/modify your leave requests (yours leaves and the one of your subordinates) Permission20003=Delete leave requests -Permission20004=Read all leave requests (even user not subordinates) -Permission20005=Create/modify leave requests for everybody +Permission20004=Read all leave requests (even of user not subordinates) +Permission20005=Create/modify leave requests for everybody (even of user not subordinates) Permission20006=Admin leave requests (setup and update balance) Permission23001=Read Scheduled job Permission23002=Create/update Scheduled job @@ -1051,6 +1055,7 @@ AreaForAdminOnly=Setup parameters can be set by administrator users only. SystemInfoDesc=System information is miscellaneous technical information you get in read only mode and visible for administrators only. SystemAreaForAdminOnly=This area is available for administrator users only. None of the Dolibarr permissions can reduce this limit. CompanyFundationDesc=Edit on this page all known information of the company or foundation you need to manage (For this, click on "Modify" or "Save" button at bottom of page) +AccountantDesc=Edit on this page all known information of your accountant/auditor to manage (For this, click on "Modify" or "Save" button at bottom of page) DisplayDesc=You can choose each parameter related to the Dolibarr look and feel here AvailableModules=Available app/modules ToActivateModule=To activate modules, go on setup Area (Home->Setup->Modules). @@ -1444,6 +1449,9 @@ SyslogFilename=File name and path YouCanUseDOL_DATA_ROOT=You can use DOL_DATA_ROOT/dolibarr.log for a log file in Dolibarr "documents" directory. You can set a different path to store this file. ErrorUnknownSyslogConstant=Constant %s is not a known Syslog constant OnlyWindowsLOG_USER=Windows only supports LOG_USER +CompressSyslogs=Syslog files compression and backup +SyslogFileNumberOfSaves=Log backups +ConfigureCleaningCronjobToSetFrequencyOfSaves=Configure cleaning scheduled job to set log backup frequency ##### Donations ##### DonationsSetup=Donation module setup DonationsReceiptModel=Template of donation receipt @@ -1540,10 +1548,12 @@ FailedToInitializeMenu=Failed to initialize menu ##### Tax ##### TaxSetup=Taxes, social or fiscal taxes and dividends module setup OptionVatMode=VAT due -OptionVATDefault=Cash basis +OptionVATDefault=Standard basis OptionVATDebitOption=Accrual basis OptionVatDefaultDesc=VAT is due:
    - on delivery for goods (we use invoice date)
    - on payments for services OptionVatDebitOptionDesc=VAT is due:
    - on delivery for goods (we use invoice date)
    - on invoice (debit) for services +OptionPaymentForProductAndServices=Cash basis for products and services +OptionPaymentForProductAndServicesDesc=VAT is due:
    - on payment for goods
    - on payments for services SummaryOfVatExigibilityUsedByDefault=Time of VAT exigibility by default according to chosen option: OnDelivery=On delivery OnPayment=On payment @@ -1721,6 +1731,7 @@ MailToSendContract=To send a contract MailToThirdparty=To send email from third party page MailToMember=To send email from member page MailToUser=To send email from user page +MailToProject= To send email from project page ByDefaultInList=Show by default on list view YouUseLastStableVersion=You use the latest stable version TitleExampleForMajorRelease=Example of message you can use to announce this major release (feel free to use it on your web sites) @@ -1767,6 +1778,10 @@ MAIN_PDF_MARGIN_LEFT=Left margin on PDF MAIN_PDF_MARGIN_RIGHT=Right margin on PDF MAIN_PDF_MARGIN_TOP=Top margin on PDF MAIN_PDF_MARGIN_BOTTOM=Bottom margin on PDF +SetToYesIfGroupIsComputationOfOtherGroups=Set this to yes if this group is a computation of other groups +EnterCalculationRuleIfPreviousFieldIsYes=Enter calculcation rule if previous field was set to Yes (For example 'CODEGRP1+CODEGRP2') +SeveralLangugeVariatFound=Several language variants found +WebDavServer=URL of %s server : %s ##### Resource #### ResourceSetup=Configuration du module Resource UseSearchToSelectResource=Use a search form to choose a resource (rather than a drop-down list). diff --git a/htdocs/langs/en_US/agenda.lang b/htdocs/langs/en_US/agenda.lang index 116fc189732..4ab64a98224 100644 --- a/htdocs/langs/en_US/agenda.lang +++ b/htdocs/langs/en_US/agenda.lang @@ -53,7 +53,9 @@ MemberValidatedInDolibarr=Member %s validated MemberModifiedInDolibarr=Member %s modified MemberResiliatedInDolibarr=Member %s terminated MemberDeletedInDolibarr=Member %s deleted -MemberSubscriptionAddedInDolibarr=Subscription for member %s added +MemberSubscriptionAddedInDolibarr=Subscription %s for member %s added +MemberSubscriptionModifiedInDolibarr=Subscription %s for member %s modified +MemberSubscriptionDeletedInDolibarr=Subscription %s for member %s deleted ShipmentValidatedInDolibarr=Shipment %s validated ShipmentClassifyClosedInDolibarr=Shipment %s classified billed ShipmentUnClassifyCloseddInDolibarr=Shipment %s classified reopened @@ -97,7 +99,8 @@ AgendaUrlOptions1=You can also add following parameters to filter output: AgendaUrlOptions3=logina=%s to restrict output to actions owned by a user %s. AgendaUrlOptionsNotAdmin=logina=!%s to restrict output to actions not owned by user %s. AgendaUrlOptions4=logint=%s to restrict output to actions assigned to user %s (owner and others). -AgendaUrlOptionsProject=project=PROJECT_ID to restrict output to actions associated to project PROJECT_ID. +AgendaUrlOptionsProject=project=__PROJECT_ID__ to restrict output to actions linked to project __PROJECT_ID__. +AgendaUrlOptionsNotAutoEvent=notactiontype=systemauto to exclude automatic event. AgendaShowBirthdayEvents=Show birthdays of contacts AgendaHideBirthdayEvents=Hide birthdays of contacts Busy=Busy diff --git a/htdocs/langs/en_US/assets.lang b/htdocs/langs/en_US/assets.lang new file mode 100644 index 00000000000..cd310e48333 --- /dev/null +++ b/htdocs/langs/en_US/assets.lang @@ -0,0 +1,48 @@ +# Copyright (C) 2018 Alexandre Spangaro +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# +# Generic +# +Assets = Assets +NewAsset = New asset +AccountancyAccountAsset = Accounting code (asset) +AccountancyAccountDepreciationAsset = Accounting code (depreciation asset account) +AccountancyAccountDepreciationExpense = Accounting code (depreciation expense account) + +# Module label 'ModuleAssetsName' +ModuleAssetsName = Assets +# Module description 'ModuleAssetsDesc' +ModuleAssetsDesc = Assets description + +# +# Admin page +# +AssetsSetup = Assets setup +Settings = Settings +AssetsSetupPage = Assets setup page +ExtraFieldsAssetsType = Complementary attributes (Assets type) +AssetsType=Asset type +AssetsTypeId=Asset type id +AssetsTypeLabel=Asset type label +AssetsTypes=Assets types + +# +# Menu +# +MenuAssets = Assets +MenuNewAsset = New asset +MenuTypeAssets = Type +MenuListAssets = List diff --git a/htdocs/langs/en_US/banks.lang b/htdocs/langs/en_US/banks.lang index 78ac859e706..93fea797c81 100644 --- a/htdocs/langs/en_US/banks.lang +++ b/htdocs/langs/en_US/banks.lang @@ -7,6 +7,7 @@ BankName=Bank name FinancialAccount=Account BankAccount=Bank account BankAccounts=Bank accounts +BankAccountsAndGateways=Bank accounts | Gateways ShowAccount=Show Account AccountRef=Financial account ref AccountLabel=Financial account label diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index 668090129ff..122d1a2a69a 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -67,6 +67,7 @@ PaidBack=Paid back DeletePayment=Delete payment ConfirmDeletePayment=Are you sure you want to delete this payment? ConfirmConvertToReduc=Do you want to convert this %s into an absolute discount ?
    The amount will so be saved among all discounts and could be used as a discount for a current or a future invoice for this customer. +ConfirmConvertToReducSupplier=Do you want to convert this %s into an absolute discount ?
    The amount will so be saved among all discounts and could be used as a discount for a current or a future invoice for this supplier. SupplierPayments=Suppliers payments ReceivedPayments=Received payments ReceivedCustomersPayments=Payments received from customers @@ -91,7 +92,7 @@ PaymentAmount=Payment amount ValidatePayment=Validate payment PaymentHigherThanReminderToPay=Payment higher than reminder to pay HelpPaymentHigherThanReminderToPay=Attention, the payment amount of one or more bills is higher than the rest to pay.
    Edit your entry, otherwise confirm and think about creating a credit note of the excess received for each overpaid invoices. -HelpPaymentHigherThanReminderToPaySupplier=Attention, the payment amount of one or more bills is higher than the rest to pay.
    Edit your entry, otherwise confirm. +HelpPaymentHigherThanReminderToPaySupplier=Attention, the payment amount of one or more bills is higher than the rest to pay.
    Edit your entry, otherwise confirm and think about creating a credit note of the excess paid for each overpaid invoice. ClassifyPaid=Classify 'Paid' ClassifyPaidPartially=Classify 'Paid partially' ClassifyCanceled=Classify 'Abandoned' @@ -110,6 +111,7 @@ DoPayment=Enter payment DoPaymentBack=Enter refund ConvertToReduc=Convert into future discount ConvertExcessReceivedToReduc=Convert excess received into future discount +ConvertExcessPaidToReduc=Convert excess paid into future discount EnterPaymentReceivedFromCustomer=Enter payment received from customer EnterPaymentDueToCustomer=Make payment due to customer DisabledBecauseRemainderToPayIsZero=Disabled because remaining unpaid is zero @@ -220,6 +222,7 @@ RemainderToPayBack=Remaining amount to refund Rest=Pending AmountExpected=Amount claimed ExcessReceived=Excess received +ExcessPaid=Excess paid EscompteOffered=Discount offered (payment before term) EscompteOfferedShort=Discount SendBillRef=Submission of invoice %s @@ -284,15 +287,19 @@ Deposits=Down payments DiscountFromCreditNote=Discount from credit note %s DiscountFromDeposit=Down payments from invoice %s DiscountFromExcessReceived=Payments from excess received of invoice %s +DiscountFromExcessPaid=Payments from excess paid of invoice %s AbsoluteDiscountUse=This kind of credit can be used on invoice before its validation CreditNoteDepositUse=Invoice must be validated to use this kind of credits NewGlobalDiscount=New absolute discount NewRelativeDiscount=New relative discount +DiscountType=Discount type NoteReason=Note/Reason ReasonDiscount=Reason DiscountOfferedBy=Granted by DiscountStillRemaining=Discounts available DiscountAlreadyCounted=Discounts already consumed +CustomerDiscounts=Customer discounts +SupplierDiscounts=Supplier discounts BillAddress=Bill address HelpEscompte=This discount is a discount granted to customer because its payment was made before term. HelpAbandonBadCustomer=This amount has been abandoned (customer said to be a bad customer) and is considered as an exceptional loose. @@ -521,3 +528,7 @@ BillCreated=%s bill(s) created StatusOfGeneratedDocuments=Status of document generation DoNotGenerateDoc=Do not generate document file AutogenerateDoc=Auto generate document file +AutoFillDateFrom=Set start date for service line with invoice date +AutoFillDateFromShort=Set start date +AutoFillDateTo=Set end date for service line with next invoice date +AutoFillDateToShort=Set end date diff --git a/htdocs/langs/en_US/categories.lang b/htdocs/langs/en_US/categories.lang index 41e5f4e4c13..8b38b2f1f42 100644 --- a/htdocs/langs/en_US/categories.lang +++ b/htdocs/langs/en_US/categories.lang @@ -16,7 +16,7 @@ MembersCategoriesArea=Members tags/categories area ContactsCategoriesArea=Contacts tags/categories area AccountsCategoriesArea=Accounts tags/categories area ProjectsCategoriesArea=Projects tags/categories area -SubCats=Subcategories +SubCats=Sub-categories CatList=List of tags/categories NewCategory=New tag/category ModifCat=Modify tag/category diff --git a/htdocs/langs/en_US/companies.lang b/htdocs/langs/en_US/companies.lang index 7d2b878c5cc..3473667fe55 100644 --- a/htdocs/langs/en_US/companies.lang +++ b/htdocs/langs/en_US/companies.lang @@ -43,7 +43,8 @@ Individual=Private individual ToCreateContactWithSameName=Will create automatically a contact/address with same information than third party under the third party. In most cases, even if your third party is a physical people, creating a third party alone is enough. ParentCompany=Parent company Subsidiaries=Subsidiaries -ReportByCustomers=Report by customers +ReportByMonth=Report by month +ReportByCustomers=Report by customer ReportByQuarter=Report by rate CivilityCode=Civility code RegisteredOffice=Registered office @@ -76,9 +77,11 @@ Web=Web Poste= Position DefaultLang=Language by default VATIsUsed=Sales tax is used +VATIsUsedWhenSelling=This define if this thirdparty includes a sale tax or not when it makes an invoice to its own customers VATIsNotUsed=Sales tax is not used CopyAddressFromSoc=Fill address with third party address ThirdpartyNotCustomerNotSupplierSoNoRef=Thirdparty neither customer nor supplier, no available refering objects +ThirdpartyIsNeitherCustomerNorClientSoCannotHaveDiscounts=Thirdparty neither customer nor supplier, discounts are not available PaymentBankAccount=Payment bank account OverAllProposals=Proposals OverAllOrders=Orders @@ -258,21 +261,31 @@ ProfId4DZ=NIS VATIntra=Sales tax ID VATIntraShort=Tax ID VATIntraSyntaxIsValid=Syntax is valid +VATReturn=VAT return ProspectCustomer=Prospect / Customer Prospect=Prospect CustomerCard=Customer Card Customer=Customer CustomerRelativeDiscount=Relative customer discount +SupplierRelativeDiscount=Relative supplier discount CustomerRelativeDiscountShort=Relative discount CustomerAbsoluteDiscountShort=Absolute discount CompanyHasRelativeDiscount=This customer has a default discount of %s%% CompanyHasNoRelativeDiscount=This customer has no relative discount by default +HasRelativeDiscountFromSupplier=You have a default discount of %s%% from this supplier +HasNoRelativeDiscountFromSupplier=You have no default relative discount from this supplier CompanyHasAbsoluteDiscount=This customer has discount available (credits notes or down payments) for %s %s CompanyHasDownPaymentOrCommercialDiscount=This customer has discount available (commercial, down payments) for %s %s CompanyHasCreditNote=This customer still has credit notes for %s %s +HasNoAbsoluteDiscountFromSupplier=You have no discount credit available from this supplier +HasAbsoluteDiscountFromSupplier=You have discounts available (credits notes or down payments) for %s %s from this supplier +HasDownPaymentOrCommercialDiscountFromSupplier=You have discounts available (commercial, down payments) for %s %s from this supplier +HasCreditNoteFromSupplier=You have credit notes for %s %s from this supplier CompanyHasNoAbsoluteDiscount=This customer has no discount credit available -CustomerAbsoluteDiscountAllUsers=Absolute discounts (granted by all users) -CustomerAbsoluteDiscountMy=Absolute discounts (granted by yourself) +CustomerAbsoluteDiscountAllUsers=Absolute customer discounts (granted by all users) +CustomerAbsoluteDiscountMy=Absolute customer discounts (granted by yourself) +SupplierAbsoluteDiscountAllUsers=Absolute supplier discounts (granted by all users) +SupplierAbsoluteDiscountMy=Absolute supplier discounts (granted by yourself) DiscountNone=None Supplier=Supplier AddContact=Create contact @@ -377,8 +390,8 @@ NoDolibarrAccess=No Dolibarr access ExportDataset_company_1=Third parties (Companies / foundations / physical people) and properties ExportDataset_company_2=Contacts and properties ImportDataset_company_1=Third parties (Companies / foundations / physical people) and properties -ImportDataset_company_2=Contacts/Addresses (of thirdparties or not) and attributes -ImportDataset_company_3=Bank accounts of thirdparties +ImportDataset_company_2=Contacts/Addresses (of third parties or not) and attributes +ImportDataset_company_3=Bank accounts of third parties ImportDataset_company_4=Third parties/Sales representatives (Assign sales representatives users to companies) PriceLevel=Price level DeliveryAddress=Delivery address @@ -413,9 +426,9 @@ ManagingDirectors=Manager(s) name (CEO, director, president...) MergeOriginThirdparty=Duplicate third party (third party you want to delete) MergeThirdparties=Merge third parties ConfirmMergeThirdparties=Are you sure you want to merge this third party into the current one? All linked objects (invoices, orders, ...) will be moved to current third party, then the thirdparty will be deleted. -ThirdpartiesMergeSuccess=Thirdparties have been merged +ThirdpartiesMergeSuccess=Third parties have been merged SaleRepresentativeLogin=Login of sales representative SaleRepresentativeFirstname=First name of sales representative SaleRepresentativeLastname=Last name of sales representative -ErrorThirdpartiesMerge=There was an error when deleting the thirdparties. Please check the log. Changes have been reverted. +ErrorThirdpartiesMerge=There was an error when deleting the third parties. Please check the log. Changes have been reverted. NewCustomerSupplierCodeProposed=New customer or supplier code suggested on duplicate code diff --git a/htdocs/langs/en_US/compta.lang b/htdocs/langs/en_US/compta.lang index 632ef67feb9..24724088e45 100644 --- a/htdocs/langs/en_US/compta.lang +++ b/htdocs/langs/en_US/compta.lang @@ -31,7 +31,7 @@ Credit=Credit Piece=Accounting Doc. AmountHTVATRealReceived=Net collected AmountHTVATRealPaid=Net paid -VATToPay=VAT sells +VATToPay=Tax sales VATReceived=Tax received VATToCollect=Tax purchases VATSummary=Tax Balance @@ -103,6 +103,7 @@ LT2PaymentsES=IRPF Payments VATPayment=Sales tax payment VATPayments=Sales tax payments VATRefund=Sales tax refund +NewVATPayment=New sales tax payment Refund=Refund SocialContributionsPayments=Social/fiscal taxes payments ShowVatPayment=Show VAT payment @@ -157,30 +158,34 @@ RulesResultDue=- It includes outstanding invoices, expenses, VAT, donations whet RulesResultInOut=- It includes the real payments made on invoices, expenses, VAT and salaries.
    - It is based on the payment dates of the invoices, expenses, VAT and salaries. The donation date for donation. RulesCADue=- It includes the client's due invoices whether they are paid or not.
    - It is based on the validation date of these invoices.
    RulesCAIn=- It includes all the effective payments of invoices received from clients.
    - It is based on the payment date of these invoices
    +RulesCATotalSaleJournal=It includes all credit lines from the Sale journal. RulesAmountOnInOutBookkeepingRecord=It includes record in your Ledger with accounting accounts that has the group "EXPENSE" or "INCOME" RulesResultBookkeepingPredefined=It includes record in your Ledger with accounting accounts that has the group "EXPENSE" or "INCOME" RulesResultBookkeepingPersonalized=It show record in your Ledger with accounting accounts grouped by personalized groups SeePageForSetup=See menu %s for setup DepositsAreNotIncluded=- Down payment invoices are nor included DepositsAreIncluded=- Down payment invoices are included -LT2ReportByCustomersInInputOutputModeES=Report by third party IRPF -LT1ReportByCustomersInInputOutputModeES=Report by third party RE -VATReport=VAT report +LT1ReportByCustomers=Report tax 2 by third party +LT2ReportByCustomers=Report tax 3 by third party +LT1ReportByCustomersES=Report by third party RE +LT2ReportByCustomersES=Report by third party IRPF +VATReport=Sale tax report +VATReportByPeriods=Sale tax report by period +VATReportByCustomers=Sale tax report by customer VATReportByCustomersInInputOutputMode=Report by the customer VAT collected and paid -VATReportByCustomersInDueDebtMode=Report by the customer VAT collected and paid -VATReportByQuartersInInputOutputMode=Report by rate of the VAT collected and paid -LT1ReportByQuartersInInputOutputMode=Report by RE rate -LT2ReportByQuartersInInputOutputMode=Report by IRPF rate -VATReportByQuartersInDueDebtMode=Report by rate of the VAT collected and paid -LT1ReportByQuartersInDueDebtMode=Report by RE rate -LT2ReportByQuartersInDueDebtMode=Report by IRPF rate +VATReportByQuartersInInputOutputMode=Report by Sale tax rate of the tax collected and paid +LT1ReportByQuarters=Report tax 2 by rate +LT2ReportByQuarters=Report tax 3 by rate +LT1ReportByQuartersES=Report by RE rate +LT2ReportByQuartersES=Report by IRPF rate SeeVATReportInInputOutputMode=See report %sVAT encasement%s for a standard calculation SeeVATReportInDueDebtMode=See report %sVAT on flow%s for a calculation with an option on the flow RulesVATInServices=- For services, the report includes the VAT regulations actually received or issued on the basis of the date of payment. -RulesVATInProducts=- For material assets, it includes the VAT invoices on the basis of the invoice date. +RulesVATInProducts=- For material assets, the report includes the VAT received or issued on the basis of the date of payment. RulesVATDueServices=- For services, the report includes VAT invoices due, paid or not, based on the invoice date. -RulesVATDueProducts=- For material assets, it includes the VAT invoices, based on the invoice date. +RulesVATDueProducts=- For material assets, the report includes the VAT invoices, based on the invoice date. OptionVatInfoModuleComptabilite=Note: For material assets, it should use the date of delivery to be more fair. +ThisIsAnEstimatedValue=This is a preview, based on business events and not from the final ledger table, so final results may differ from this preview values PercentOfInvoice=%%/invoice NotUsedForGoods=Not used on goods ProposalStats=Statistics on proposals @@ -213,8 +218,8 @@ CalculationRuleDescSupplier=According to supplier, choose appropriate method to TurnoverPerProductInCommitmentAccountingNotRelevant=Turnover report per product, when using a cash accountancy mode is not relevant. This report is only available when using engagement accountancy mode (see setup of accountancy module). CalculationMode=Calculation mode AccountancyJournal=Accounting code journal -ACCOUNTING_VAT_SOLD_ACCOUNT=Accounting account by default for collecting VAT - VAT on sales (used if not defined on VAT dictionary setup) -ACCOUNTING_VAT_BUY_ACCOUNT=Accounting account by default for recovered VAT - VAT on purchases (used if not defined on VAT dictionary setup) +ACCOUNTING_VAT_SOLD_ACCOUNT=Accounting account by default for VAT on sales (used if not defined on VAT dictionary setup) +ACCOUNTING_VAT_BUY_ACCOUNT=Accounting account by default for VAT on purchases (used if not defined on VAT dictionary setup) ACCOUNTING_VAT_PAY_ACCOUNT=Accounting account by default for paying VAT ACCOUNTING_ACCOUNT_CUSTOMER=Accounting account used for customer third parties ACCOUNTING_ACCOUNT_CUSTOMER_Desc=The dedicated accounting account defined on third party card will be used for Subledger accouting only. This one will be used for General Ledger and as default value of Subledger accounting if dedicated customer accouting account on third party is not defined. @@ -236,3 +241,4 @@ ErrorBankAccountNotFound=Error: Bank account not found FiscalPeriod=Accounting period ListSocialContributionAssociatedProject=List of social contributions associated with the project DeleteFromCat=Remove from accounting group +AccountingAffectation=Accounting assignement diff --git a/htdocs/langs/en_US/cron.lang b/htdocs/langs/en_US/cron.lang index 7f3385bef12..8947fcc47ac 100644 --- a/htdocs/langs/en_US/cron.lang +++ b/htdocs/langs/en_US/cron.lang @@ -74,7 +74,8 @@ CronFrom=From CronType=Job type CronType_method=Call method of a PHP Class CronType_command=Shell command -CronCannotLoadClass=Cannot load class %s or object %s +CronCannotLoadClass=Cannot load class file %s (to use class %s) +CronCannotLoadObject=Class file %s was loaded, but object %s was not found into it UseMenuModuleToolsToAddCronJobs=Go into menu "Home - Admin tools - Scheduled jobs" to see and edit scheduled jobs. JobDisabled=Job disabled MakeLocalDatabaseDumpShort=Local database backup diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index f87a30ecfb2..efee041cbf4 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -207,6 +207,7 @@ ErrorDiscountLargerThanRemainToPaySplitItBefore=The discount you try to apply is ErrorFileNotFoundWithSharedLink=File was not found. May be the share key was modified or file was removed recently. ErrorProductBarCodeAlreadyExists=The product barcode %s already exists on another product reference. ErrorNoteAlsoThatSubProductCantBeFollowedByLot=Note also that using virtual product to have auto increase/decrease of subproducts is not possible when at least one subproduct (or subproduct of subproducts) needs a serial/lot number. +ErrorDescRequiredForFreeProductLines=Description is mandatory for lines with free product # Warnings WarningPasswordSetWithNoAccount=A password was set for this member. However, no user account was created. So this password is stored but can't be used to login to Dolibarr. It may be used by an external module/interface but if you don't need to define any login nor password for a member, you can disable option "Manage a login for each member" from Member module setup. If you need to manage a login but don't need any password, you can keep this field empty to avoid this warning. Note: Email can also be used as a login if the member is linked to a user. @@ -228,4 +229,5 @@ WarningTooManyDataPleaseUseMoreFilters=Too many data (more than %s lines). Pleas WarningSomeLinesWithNullHourlyRate=Some times were recorded by some users while their hourly rate was not defined. A value of 0 %s per hour was used but this may result in wrong valuation of time spent. WarningYourLoginWasModifiedPleaseLogin=Your login was modified. For security purpose you will have to login with your new login before next action. WarningAnEntryAlreadyExistForTransKey=An entry already exists for the translation key for this language -WarningNumberOfRecipientIsRestrictedInMassAction=Warning, number of different recipient is limited to %s when using the bulk actions on lists \ No newline at end of file +WarningNumberOfRecipientIsRestrictedInMassAction=Warning, number of different recipient is limited to %s when using the bulk actions on lists +WarningDateOfLineMustBeInExpenseReportRange=Warning, the date of line is not in the range of the expense report \ No newline at end of file diff --git a/htdocs/langs/en_US/holiday.lang b/htdocs/langs/en_US/holiday.lang index f73bbdeee52..eca2bbdfe46 100644 --- a/htdocs/langs/en_US/holiday.lang +++ b/htdocs/langs/en_US/holiday.lang @@ -39,6 +39,10 @@ TypeOfLeaveId=Type of leave ID TypeOfLeaveCode=Type of leave code TypeOfLeaveLabel=Type of leave label NbUseDaysCP=Number of days of vacation consumed +NbUseDaysCPShort=Days consumed +NbUseDaysCPShortInMonth=Days consumed in month +DateStartInMonth=Start date in month +DateEndInMonth=End date in month EditCP=Edit DeleteCP=Delete ActionRefuseCP=Refuse @@ -67,6 +71,7 @@ DateRefusCP=Date of refusal DateCancelCP=Date of cancellation DefineEventUserCP=Assign an exceptional leave for a user addEventToUserCP=Assign leave +NotTheAssignedApprover=You are not the assigned approver MotifCP=Reason UserCP=User ErrorAddEventToUserCP=An error occurred while adding the exceptional leave. @@ -91,7 +96,10 @@ LastHolidays=Latest %s leave requests AllHolidays=All leave requests HalfDay=Half day NotTheAssignedApprover=You are not the assigned approver - +LEAVE_PAID=Paid vacation +LEAVE_SICK=Sick leave +LEAVE_OTHER=Other leave +LEAVE_PAID_FR=Paid vacation ## Configuration du Module ## LastUpdateCP=Latest automatic update of leaves allocation MonthOfLastMonthlyUpdate=Month of latest automatic update of leaves allocation diff --git a/htdocs/langs/en_US/incoterm.lang b/htdocs/langs/en_US/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/en_US/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/en_US/loan.lang b/htdocs/langs/en_US/loan.lang index d00b11738be..9aae3869cc3 100644 --- a/htdocs/langs/en_US/loan.lang +++ b/htdocs/langs/en_US/loan.lang @@ -50,4 +50,6 @@ ConfigLoan=Configuration of the module loan LOAN_ACCOUNTING_ACCOUNT_CAPITAL=Accounting account capital by default LOAN_ACCOUNTING_ACCOUNT_INTEREST=Accounting account interest by default LOAN_ACCOUNTING_ACCOUNT_INSURANCE=Accounting account insurance by default -CreateCalcSchedule=Créer / Modifier échéancier de pret +FinancialCommitment=Financial commitment +CreateCalcSchedule=Edit financial commitment +InterestAmount=Interest amount diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang index 8e2b093fdc2..e38b038cfee 100644 --- a/htdocs/langs/en_US/mails.lang +++ b/htdocs/langs/en_US/mails.lang @@ -78,6 +78,7 @@ ResultOfMailSending=Result of mass EMail sending NbSelected=Nb selected NbIgnored=Nb ignored NbSent=Nb sent +SentXXXmessages=%s message(s) sent. ConfirmUnvalidateEmailing=Are you sure you want to change email %s to draft status? MailingModuleDescContactsWithThirdpartyFilter=Contact with customer filters MailingModuleDescContactsByCompanyCategory=Contacts by third party category @@ -135,7 +136,7 @@ NbOfTargetedContacts=Current number of targeted contact emails UseFormatFileEmailToTarget=Imported file must have format email;name;firstname;other UseFormatInputEmailToTarget=Enter a string with format email;name;firstname;other MailAdvTargetRecipients=Recipients (advanced selection) -AdvTgtTitle=Fill input fields to preselect the thirdparties or contacts/addresses to target +AdvTgtTitle=Fill input fields to preselect the third parties or contacts/addresses to target AdvTgtSearchTextHelp=Use %% as magic caracters. For exemple to find all item like jean, joe, jim, you can input j%%, you can also use ; as separator for value, and use ! for except this value. For exemple jean;joe;jim%%;!jimo;!jima% will target all jean, joe, start with jim but not jimo and not everythnig taht start by jima AdvTgtSearchIntHelp=Use interval to select int or float value AdvTgtMinVal=Minimum value diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index cde8a485e59..38db3c58b99 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -70,6 +70,8 @@ SetDate=Set date SelectDate=Select a date SeeAlso=See also %s SeeHere=See here +ClickHere=Click here +Here=Here Apply=Apply BackgroundColorByDefault=Default background color FileRenamed=The file was successfully renamed @@ -185,6 +187,7 @@ ToLink=Link Select=Select Choose=Choose Resize=Resize +ResizeOrCrop=Resize or Crop Recenter=Recenter Author=Author User=User @@ -325,8 +328,10 @@ Default=Default DefaultValue=Default value DefaultValues=Default values Price=Price +PriceCurrency=Price (currency) UnitPrice=Unit price UnitPriceHT=Unit price (net) +UnitPriceHTCurrency=Unit price (net) (currency) UnitPriceTTC=Unit price PriceU=U.P. PriceUHT=U.P. (net) @@ -334,6 +339,7 @@ PriceUHTCurrency=U.P (currency) PriceUTTC=U.P. (inc. tax) Amount=Amount AmountInvoice=Invoice amount +AmountInvoiced=Amount invoiced AmountPayment=Payment amount AmountHTShort=Amount (net) AmountTTCShort=Amount (inc. tax) @@ -353,6 +359,7 @@ AmountLT2ES=Amount IRPF AmountTotal=Total amount AmountAverage=Average amount PriceQtyMinHT=Price quantity min. (net of tax) +PriceQtyMinHTCurrency=Price quantity min. (net of tax) (currency) Percentage=Percentage Total=Total SubTotal=Subtotal @@ -422,6 +429,7 @@ ActionDoneShort=Finished ActionUncomplete=Uncomplete LatestLinkedEvents=Latest %s linked events CompanyFoundation=Company/Organization +Accountant=Accountant ContactsForCompany=Contacts for this third party ContactsAddressesForCompany=Contacts/addresses for this third party AddressesForCompany=Addresses for this third party @@ -429,6 +437,9 @@ ActionsOnCompany=Events about this third party ActionsOnMember=Events about this member ActionsOnProduct=Events about this product NActionsLate=%s late +ToDo=To do +Completed=Completed +Running=In progress RequestAlreadyDone=Request already recorded Filter=Filter FilterOnInto=Search criteria '%s' into fields %s @@ -706,6 +717,8 @@ WarningYouAreInMaintenanceMode=Warning, you are in a maintenance mode, so only l CoreErrorTitle=System error CoreErrorMessage=Sorry, an error occurred. Contact your system administrator to check the logs or disable $dolibarr_main_prod=1 to get more information. CreditCard=Credit card +ValidatePayment=Validate payment +CreditOrDebitCard=Credit or debit card FieldsWithAreMandatory=Fields with %s are mandatory FieldsWithIsForPublic=Fields with %s are shown on public list of members. If you don't want this, check off the "public" box. AccordingToGeoIPDatabase=(according to GeoIP convertion) @@ -812,7 +825,6 @@ RelatedObjects=Related Objects ClassifyBilled=Classify billed ClassifyUnbilled=Classify unbilled Progress=Progress -ClickHere=Click here FrontOffice=Front office BackOffice=Back office View=View @@ -854,6 +866,8 @@ FileNotShared=File not shared to exernal public Project=Project Projects=Projects Rights=Permissions +LineNb=Line nb +IncotermLabel=Incoterms # Week day Monday=Monday Tuesday=Tuesday @@ -893,7 +907,7 @@ Select2MoreCharacters=or more characters Select2MoreCharactersMore=Search syntax:
    | OR (a|b)
    * Any character (a*b)
    ^ Start with (^ab)
    $ End with (ab$)
    Select2LoadingMoreResults=Loading more results... Select2SearchInProgress=Search in progress... -SearchIntoThirdparties=Thirdparties +SearchIntoThirdparties=Third parties SearchIntoContacts=Contacts SearchIntoMembers=Members SearchIntoUsers=Users @@ -919,3 +933,11 @@ CommentDeleted=Comment deleted Everybody=Everybody PayedBy=Payed by PayedTo=Payed to +Monthly=Monthly +Quarterly=Quarterly +Annual=Annual +Local=Local +Remote=Remote +LocalAndRemote=Local and Remote +KeyboardShortcut=Keyboard shortcut +AssignedTo=Assigned to \ No newline at end of file diff --git a/htdocs/langs/en_US/margins.lang b/htdocs/langs/en_US/margins.lang index 8633d910657..8a8a9f20788 100644 --- a/htdocs/langs/en_US/margins.lang +++ b/htdocs/langs/en_US/margins.lang @@ -41,4 +41,4 @@ rateMustBeNumeric=Rate must be a numeric value markRateShouldBeLesserThan100=Mark rate should be lower than 100 ShowMarginInfos=Show margin infos CheckMargins=Margins detail -MarginPerSaleRepresentativeWarning=The report of margin per user use the link between thirdparties and sale representatives to calculate the margin of each salerepresentaive. Because some thirdparties may not have any ddiated sale representative and some thirdparties may be linked to several, some amounts may not be included into this report (if there is no sale representative) and some may appear on different lines (for each sale representative). +MarginPerSaleRepresentativeWarning=The report of margin per user use the link between third parties and sale representatives to calculate the margin of each salerepresentaive. Because some thirdparties may not have any ddiated sale representative and some thirdparties may be linked to several, some amounts may not be included into this report (if there is no sale representative) and some may appear on different lines (for each sale representative). diff --git a/htdocs/langs/en_US/members.lang b/htdocs/langs/en_US/members.lang index 2a98f700027..8a8346aac4a 100644 --- a/htdocs/langs/en_US/members.lang +++ b/htdocs/langs/en_US/members.lang @@ -13,8 +13,6 @@ ListOfValidatedPublicMembers=List of validated public members ErrorThisMemberIsNotPublic=This member is not public ErrorMemberIsAlreadyLinkedToThisThirdParty=Another member (name: %s, login: %s) is already linked to a third party %s. Remove this link first because a third party can't be linked to only a member (and vice versa). ErrorUserPermissionAllowsToLinksToItselfOnly=For security reasons, you must be granted permissions to edit all users to be able to link a member to a user that is not yours. -ThisIsContentOfYourCard=Hi.

    This is a remind of the information we get about you. Feel free to contact us if something looks wrong.

    -CardContent=Content of your member card SetLinkToUser=Link to a Dolibarr user SetLinkToThirdParty=Link to a Dolibarr third party MembersCards=Members business cards @@ -108,17 +106,33 @@ PublicMemberCard=Member public card SubscriptionNotRecorded=Subscription not recorded AddSubscription=Create subscription ShowSubscription=Show subscription -SendAnEMailToMember=Send information email to member +# Label of email templates +SendingAnEMailToMember=Sending information email to member +SendingEmailOnAutoSubscription=Sending email on auto registration +SendingEmailOnMemberValidation=Sending email on new member validation +SendingEmailOnNewSubscription=Sending email on new subscription +SendingReminderForExpiredSubscription=Sending reminder for expired subscription +SendingEmailOnCancelation=Sending email on cancelation +# Topic of email templates +YourMembershipRequestWasReceived=Your membership was received. +YourMembershipWasValidated=Your membership was validated +YourSubscriptionWasRecorded=Your new subscription was recorded +SubscriptionReminderEmail=Subscription reminder +YourMembershipWasCanceled=You membership was canceled +CardContent=Content of your member card +# Text of email templates +ThisIsContentOfYourMembershipRequestWasReceived=We want to let you know that your membership request was received.

    +ThisIsContentOfYourMembershipWasValidated=We want to let you know that your membership was validated with the following information:

    +ThisIsContentOfYourSubscriptionWasRecorded=We want to let you know that your new subscription was recorded.

    +ThisIsContentOfSubscriptionReminderEmail=We want to let you know thet your subscription is about to expire. We hope you can make a renewal of it.

    +ThisIsContentOfYourCard=This is a remind of the information we get about you. Feel free to contact us if something looks wrong.

    DescADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT=Subject of the e-mail received in case of auto-inscription of a guest DescADHERENT_AUTOREGISTER_NOTIF_MAIL=E-mail received in case of auto-inscription of a guest -DescADHERENT_AUTOREGISTER_MAIL_SUBJECT=EMail subject for member autosubscription -DescADHERENT_AUTOREGISTER_MAIL=EMail for member autosubscription -DescADHERENT_MAIL_VALID_SUBJECT=EMail subject for member validation -DescADHERENT_MAIL_VALID=EMail for member validation -DescADHERENT_MAIL_COTIS_SUBJECT=EMail subject for subscription -DescADHERENT_MAIL_COTIS=EMail for subscription -DescADHERENT_MAIL_RESIL_SUBJECT=EMail subject for member resiliation -DescADHERENT_MAIL_RESIL=EMail for member resiliation +DescADHERENT_EMAIL_TEMPLATE_AUTOREGISTER=Template Email to use to send email to a member on member autosubscription +DescADHERENT_EMAIL_TEMPLATE_MEMBER_VALIDATION=Template EMail to use to send email to a member on member validation +DescADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION=Template Email to use to send email to a member on new subscription recording +DescADHERENT_EMAIL_TEMPLATE_REMIND_EXPIRATION=Template Email to use to send email remind when subscription is about to expire +DescADHERENT_EMAIL_TEMPLATE_CANCELATION=Template Email to use to send email to a member on member cancelation DescADHERENT_MAIL_FROM=Sender EMail for automatic emails DescADHERENT_ETIQUETTE_TYPE=Format of labels page DescADHERENT_ETIQUETTE_TEXT=Text printed on member address sheets @@ -176,4 +190,9 @@ VATToUseForSubscriptions=VAT rate to use for subscriptions NoVatOnSubscription=No TVA for subscriptions MEMBER_PAYONLINE_SENDEMAIL=Email to use for email warning when Dolibarr receive a confirmation of a validated payment for a subscription (Example: paymentdone@example.com) ADHERENT_PRODUCT_ID_FOR_SUBSCRIPTIONS=Product used for subscription line into invoice: %s -NameOrCompany=Name or company \ No newline at end of file +NameOrCompany=Name or company +SubscriptionRecorded=Subscription recorded +NoEmailSentToMember=No email sent to member +EmailSentToMember=Email sent to member at %s +SendReminderForExpiredSubscriptionTitle=Send reminder by email for expired subscription +SendReminderForExpiredSubscription=Send reminder by email to members when subscription is about to expire (parameter is number of days before end of subscription to send the remind) diff --git a/htdocs/langs/en_US/modulebuilder.lang b/htdocs/langs/en_US/modulebuilder.lang index 9e30ee6f1de..6638e1fa674 100644 --- a/htdocs/langs/en_US/modulebuilder.lang +++ b/htdocs/langs/en_US/modulebuilder.lang @@ -93,4 +93,5 @@ AddLanguageFile=Add language file YouCanUseTranslationKey=You can use here a key that is the translation key found into language file (see tab "Languages") DropTableIfEmpty=(Delete table if empty) TableDoesNotExists=The table %s does not exists -TableDropped=Table %s deleted \ No newline at end of file +TableDropped=Table %s deleted +InitStructureFromExistingTable=Build the structure array string of an existing table \ No newline at end of file diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index c4cb693716d..2afabe43b06 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -80,8 +80,8 @@ LinkedObject=Linked object NbOfActiveNotifications=Number of notifications (nb of recipient emails) PredefinedMailTest=__(Hello)__\nThis is a test mail sent to __EMAIL__.\nThe two lines are separated by a carriage return.\n\n__USER_SIGNATURE__ PredefinedMailTestHtml=__(Hello)__\nThis is a test mail (the word test must be in bold).
    The two lines are separated by a carriage return.

    __USER_SIGNATURE__ -PredefinedMailContentSendInvoice=__(Hello)__\n\nYou will find here the invoice __REF__\n\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ -PredefinedMailContentSendInvoiceReminder=__(Hello)__\n\nWe would like to warn you that the invoice __REF__ seems to not be payed. So this is the invoice in attachment again, as a reminder.\n\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ +PredefinedMailContentSendInvoice=__(Hello)__\n\nYou will find here the invoice __REF__\n\nThis is the link to make your online payment if this invoice is not already payed:\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ +PredefinedMailContentSendInvoiceReminder=__(Hello)__\n\nWe would like to warn you that the invoice __REF__ seems to not be payed. So this is the invoice in attachment again, as a reminder.\n\nThis is the link to make your online payment:\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ PredefinedMailContentSendProposal=__(Hello)__\n\nYou will find here the commercial proposal __PREF__\n\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ PredefinedMailContentSendSupplierProposal=__(Hello)__\n\nYou will find here the price request __REF__\n\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ PredefinedMailContentSendOrder=__(Hello)__\n\nYou will find here the order __REF__\n\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ @@ -216,6 +216,7 @@ StartUpload=Start upload CancelUpload=Cancel upload FileIsTooBig=Files is too big PleaseBePatient=Please be patient... +NewPassword=New password ResetPassword=Reset password RequestToResetPasswordReceived=A request to change your Dolibarr password has been received NewKeyIs=This is your new keys to login @@ -245,3 +246,4 @@ WEBSITE_PAGEURL=URL of page WEBSITE_TITLE=Title WEBSITE_DESCRIPTION=Description WEBSITE_KEYWORDS=Keywords +LinesToImport=Lines to import diff --git a/htdocs/langs/en_US/paypal.lang b/htdocs/langs/en_US/paypal.lang index ae41f4df314..2cbe4289654 100644 --- a/htdocs/langs/en_US/paypal.lang +++ b/htdocs/langs/en_US/paypal.lang @@ -14,7 +14,7 @@ PaypalModeOnlyPaypal=PayPal only ONLINE_PAYMENT_CSS_URL=Optionnal URL of CSS style sheet on online payment page ThisIsTransactionId=This is id of transaction: %s PAYPAL_ADD_PAYMENT_URL=Add the url of Paypal payment when you send a document by mail -PredefinedMailContentLink=You can click on the secure link below to make your payment (PayPal) if it is not already done.\n\n%s\n\n +PredefinedMailContentLink=You can click on the link below to make your payment if it is not already done.

    %s

    YouAreCurrentlyInSandboxMode=You are currently in the %s "sandbox" mode NewOnlinePaymentReceived=New online payment received NewOnlinePaymentFailed=New online payment tried but failed @@ -29,4 +29,7 @@ ShortErrorMessage=Short Error Message ErrorCode=Error Code ErrorSeverityCode=Error Severity Code OnlinePaymentSystem=Online payment system -PaypalLiveEnabled=Paypal live enabled (otherwise test/sandbox mode) \ No newline at end of file +PaypalLiveEnabled=Paypal live enabled (otherwise test/sandbox mode) +PaypalImportPayment=Import Paypal payments +PostActionAfterPayment=Post actions after payments +ARollbackWasPerformedOnPostActions=A rollback was performed on all Post actions. You must complete post actions manually if they are necessary. \ No newline at end of file diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index 1ff1fb0e427..e5393466c3d 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -123,6 +123,7 @@ ConfirmDeleteProductLine=Are you sure you want to delete this product line? ProductSpecial=Special QtyMin=Minimum Qty PriceQtyMin=Price for this min. qty (w/o discount) +PriceQtyMinCurrency=Price for this min. qty (w/o discount) (currency) VATRateForSupplierProduct=VAT Rate (for this supplier/product) DiscountQtyMin=Default discount for qty NoPriceDefinedForThisSupplier=No price/qty defined for this supplier/product diff --git a/htdocs/langs/en_US/projects.lang b/htdocs/langs/en_US/projects.lang index ab044618556..5cc94c39309 100644 --- a/htdocs/langs/en_US/projects.lang +++ b/htdocs/langs/en_US/projects.lang @@ -55,6 +55,7 @@ TasksOnOpenedProject=Tasks on open projects WorkloadNotDefined=Workload not defined NewTimeSpent=Time spent MyTimeSpent=My time spent +BillTime=Bill the time spent Tasks=Tasks Task=Task TaskDateStart=Task start date @@ -91,6 +92,7 @@ ListDonationsAssociatedProject=List of donations associated with the project ListVariousPaymentsAssociatedProject=List of miscellaneous payments associated with the project ListActionsAssociatedProject=List of events associated with the project ListTaskTimeUserProject=List of time consumed on tasks of project +ListTaskTimeForTask=List of time consumed on task ActivityOnProjectToday=Activity on project today ActivityOnProjectYesterday=Activity on project yesterday ActivityOnProjectThisWeek=Activity on project this week @@ -98,6 +100,7 @@ ActivityOnProjectThisMonth=Activity on project this month ActivityOnProjectThisYear=Activity on project this year ChildOfProjectTask=Child of project/task ChildOfTask=Child of task +TaskHasChild=Task has child NotOwnerOfProject=Not owner of this private project AffectedTo=Allocated to CantRemoveProject=This project can't be removed as it is referenced by some other objects (invoice, orders or other). See referers tab. @@ -137,6 +140,7 @@ ProjectReportDate=Change task dates according to new project start date ErrorShiftTaskDate=Impossible to shift task date according to new project start date ProjectsAndTasksLines=Projects and tasks ProjectCreatedInDolibarr=Project %s created +ProjectValidatedInDolibarr=Project %s validated ProjectModifiedInDolibarr=Project %s modified TaskCreatedInDolibarr=Task %s created TaskModifiedInDolibarr=Task %s modified @@ -219,4 +223,7 @@ NoAssignedTasks=No assigned tasks (assign project/tasks the current user from th # Comments trans AllowCommentOnTask=Allow user comments on tasks AllowCommentOnProject=Allow user comments on projects - +DontHavePermissionForCloseProject=You do not have permissions to close the project %s +DontHaveTheValidateStatus=The project %s must be open to be closed +RecordsClosed=%s project(s) closed +SendProjectRef=About project %s diff --git a/htdocs/langs/en_US/propal.lang b/htdocs/langs/en_US/propal.lang index 29c4fe16b64..7745dde08da 100644 --- a/htdocs/langs/en_US/propal.lang +++ b/htdocs/langs/en_US/propal.lang @@ -33,6 +33,7 @@ PropalStatusSigned=Signed (needs billing) PropalStatusNotSigned=Not signed (closed) PropalStatusBilled=Billed PropalStatusDraftShort=Draft +PropalStatusValidatedShort=Validated PropalStatusClosedShort=Closed PropalStatusSignedShort=Signed PropalStatusNotSignedShort=Not signed diff --git a/htdocs/langs/en_US/salaries.lang b/htdocs/langs/en_US/salaries.lang index f1db7ea1cf3..432ab894040 100644 --- a/htdocs/langs/en_US/salaries.lang +++ b/htdocs/langs/en_US/salaries.lang @@ -14,4 +14,5 @@ CurrentSalary=Current salary THMDescription=This value may be used to calculate cost of time consumed on a project entered by users if module project is used TJMDescription=This value is currently as information only and is not used for any calculation LastSalaries=Latest %s salary payments -AllSalaries=All salary payments \ No newline at end of file +AllSalaries=All salary payments +SalariesStatistics=Statistiques salaires \ No newline at end of file diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 62d2e23e66c..31873ad527e 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -8,7 +8,9 @@ WarehouseEdit=Modify warehouse MenuNewWarehouse=New warehouse WarehouseSource=Source warehouse WarehouseSourceNotDefined=No warehouse defined, +AddWarehouse=Create warehouse AddOne=Add one +DefaultWarehouse=Default warehouse WarehouseTarget=Target warehouse ValidateSending=Delete sending CancelSending=Cancel sending @@ -22,6 +24,7 @@ Movements=Movements ErrorWarehouseRefRequired=Warehouse reference name is required ListOfWarehouses=List of warehouses ListOfStockMovements=List of stock movements +ListOfInventories=List of inventories MovementId=Movement ID StockMovementForId=Movement ID %d ListMouvementStockProject=List of stock movements associated to project diff --git a/htdocs/langs/en_US/stripe.lang b/htdocs/langs/en_US/stripe.lang index cb49994ea14..607a2b5d5a8 100644 --- a/htdocs/langs/en_US/stripe.lang +++ b/htdocs/langs/en_US/stripe.lang @@ -35,6 +35,31 @@ NewStripePaymentReceived=New Stripe payment received NewStripePaymentFailed=New Stripe payment tried but failed STRIPE_TEST_SECRET_KEY=Secret test key STRIPE_TEST_PUBLISHABLE_KEY=Publishable test key +STRIPE_TEST_WEBHOOK_KEY=Webhook test key STRIPE_LIVE_SECRET_KEY=Secret live key STRIPE_LIVE_PUBLISHABLE_KEY=Publishable live key -StripeLiveEnabled=Stripe live enabled (otherwise test/sandbox mode) \ No newline at end of file +STRIPE_LIVE_WEBHOOK_KEY=Webhook live key +ONLINE_PAYMENT_WAREHOUSE=Stock to use for stock decrease when online payment is done
    (TODO When option to decrease stock is done on an action on invoice and the online payment generate itself the invoice ?) +StripeLiveEnabled=Stripe live enabled (otherwise test/sandbox mode) +StripeImportPayment=Import Stripe payments +ExampleOfTestCreditCard=Example of credit card for test: %s (valid), %s (error CVC), %s (expired), %s (charge fails) +StripeGateways=Stripe gateways +OAUTH_STRIPE_TEST_ID=Stripe Connect Client ID (ca_...) +OAUTH_STRIPE_LIVE_ID=Stripe Connect Client ID (ca_...) +BankAccountForBankTransfer=Bank account for fund payouts +StripeAccount=Stripe account +StripeChargeList=List of Stripe charges +StripeTransactionList=List of Stripe transactions +StripeCustomerId=Stripe customer id +StripePaymentModes=Stripe payment modes +LocalID=Local ID +StripeID=Stripe ID +NameOnCard=Name on card +CardNumber=Card Number +ExpiryDate=Expiry Date +CVN=CVN +DeleteACard=Delete Card record +ConfirmDeleteCard=Are you sure you want to delete this Card record? +CreateCustomerOnStripe=Create customer on Stripe +CreateCardOnStripe=Create card on Stripe +ShowInStripe=Show in Stripe \ No newline at end of file diff --git a/htdocs/langs/en_US/ticketsup.lang b/htdocs/langs/en_US/ticketsup.lang new file mode 100644 index 00000000000..d46a6be5dbd --- /dev/null +++ b/htdocs/langs/en_US/ticketsup.lang @@ -0,0 +1,301 @@ +# en_US lang file for module ticketsup +# Copyright (C) 2013 Jean-François FERRY +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# +# Generic +# + +Module56000Name=Tickets +Module56000Desc=Ticket system for issue or request management + +Permission56001=See tickets +Permission56002=Modify tickets +Permission56003=Delete tickets +Permission56004=Manage tickets +Permission56005=See tickets of all third parties (not effective for external users, always be limited to the thirdparty they depend on) + +TicketsupDictType=Tickets type +TicketsupDictCategory=Tickets categories +TicketsupDictSeverity=Tickets severity +TicketTypeShortBUGSOFT=Dysfonctionnement logiciel +TicketTypeShortBUGHARD=Dysfonctionnement matériel +TicketTypeShortCOM=Commercial question +TicketTypeShortINCIDENT=Request for assistance +TicketTypeShortPROJET=Project +TicketTypeShortOTHER=Other + +TicketSeverityShortLOW=Low +TicketSeverityShortNORMAL=Normal +TicketSeverityShortHIGH=High +TicketSeverityShortBLOCKING=Critical/Blocking + +ErrorBadEmailAddress=Field '%s' incorrect +MenuTicketsupMyAssign=My tickets +MenuTicketsupMyAssignNonClosed=My open tickets +MenuListNonClosed=Open tickets + +TypeContact_ticketsup_internal_CONTRIBUTOR=Contributor +TypeContact_ticketsup_internal_SUPPORTTEC=Assigned user +TypeContact_ticketsup_external_SUPPORTCLI=Customer contact / incident tracking +TypeContact_ticketsup_external_CONTRIBUTOR=External contributor + +OriginEmail=Email source +Notify_TICKETMESSAGE_SENTBYMAIL=Send ticket answer by email + +# Status +NotRead=Not read +Read=Read +Answered=Answered +Assigned=Assigned +InProgress=In progress +Waiting=Waiting +Closed=Closed +Deleted=Deleted + +# Dict +Type=Type +Category=Category +Severity=Severity + +# Email templates +MailToSendTicketsupMessage=To send email from ticket message + +# +# Admin page +# +TicketsupSetup=Ticket module setup +TicketSupSettings=Settings +TicketsupSetupPage= +TicketsupPublicAccess=A public interface requiring no identification is available at the following url +TicketsupSetupDictionaries=The type of application categories and severity level are configurable from dictionaries +TicketParamModule=Module variable setup +TicketParamMail=Email setup +TicketEmailNotificationFrom=Notification email from +TicketEmailNotificationFromHelp=Used into ticket message answer by example +TicketEmailNotificationTo=Notifications email to +TicketEmailNotificationToHelp=Send email notifications to this address. +TicketNewEmailBodyLabel=Text message sent after creating a ticket (public interface) +TicketNewEmailBodyHelp=The text specified here will be inserted into the email confirming the creation of a new ticket from the public interface. Information on the consultation of the ticket are automatically added. +TicketParamPublicInterface=Public interface setup +TicketsEmailMustExist=Require an existing email address to create a ticket +TicketsEmailMustExistHelp=In the public interface, the email address should already be filled in the database to create a new ticket. +PublicInterface=Public interface +TicketUrlPublicInterfaceLabelAdmin=Public interface URL +TicketUrlPublicInterfaceHelpAdmin=It is possible to define an alias to the web server and thus make available the public interface to another IP address. +TicketPublicInterfaceTextHomeLabelAdmin=Welcome text of the public interface +TicketPublicInterfaceTextHome=You can create a support ticket or view existing from its identifier tracking ticket. +TicketPublicInterfaceTextHomeHelpAdmin=The text defined here will appear on the home page of the public interface. +TicketPublicInterfaceTopicLabelAdmin=Interface title +TicketPublicInterfaceTopicHelp=This text will appear as the title of the public interface. +TicketPublicInterfaceTextHelpMessageLabelAdmin=Help text to the message entry +TicketPublicInterfaceTextHelpMessageHelpAdmin=This text will appear above the message input area of the user. +ExtraFieldsTicketSup=Extra attributes +TicketSupCkEditorEmailNotActivated=HTML editor is not activated. Please put FCKEDITOR_ENABLE_MAIL contant equal to 1 +TicketsDisableEmail=Do not send ticket creation or message send emails +TicketsDisableEmailHelp=By default, emails are sent when new tickets or messages created. Enable this option to disable *all* email notifications +TicketsLogEnableEmail=Enable log by email +TicketsLogEnableEmailHelp=At each change, an email will be sent **to each contact** associated with the ticket. +TicketParams=Params +TicketsShowModuleLogo=Display the logo of the module in the public interface +TicketsShowModuleLogoHelp=Enable this option to hide the logo module in the pages of the public interface +TicketsShowCompanyLogo=Display the logo of the company in the public interface +TicketsShowCompanyLogoHelp=Enable this option to hide the logo of the main company in the pages of the public interface +TicketsEmailAlsoSendToMainAddress=Also send notification to main email address +TicketsEmailAlsoSendToMainAddressHelp=Enable this option to send an email to "Notification email from" address (see setup below) +TicketsShowExtrafieldsIntoPublicArea=Show Extras fields in the public interface +TicketsShowExtrafieldsIntoPublicAreaHelp=When this option is enabled, additional attributes defined on the tickets will be shown in the public interface of ticket creation. +TicketsLimitViewAssignedOnly=Restrict the display to tickets assigned to the current user (not effective for external users, always be limited to the thirdparty they depend on) +TicketsLimitViewAssignedOnlyHelp=Only tickets assigned to the current user will be visible. Does not apply to a user with tickets management rights. +TicketsActivatePublicInterface=Activate public interface +TicketsActivatePublicInterfaceHelp=Public interface allow any visitors to create tickets. +TicketsAutoAssignTicket=Automatically assign the user who created the ticket +TicketsAutoAssignTicketHelp=When creating a ticket, the user can be automatically assigned to the ticket. +TicketSupNumberingModules=Tickets numbering module +TicketNotifyTiersAtCreation=Notify thirdparty at creation + +# +# About page +# +About=About +TicketSupAbout=About ticket module +TicketSupAboutModule=The development of this module has been initiated by the company Libr&thic. +TicketSupAboutModuleHelp=You can get help by using the contact form on the website librethic.io +TicketSupAboutModuleImprove=Feel free to suggest improvements! Please visit the project page on Doliforge website to report bugs and add tasks. +TicketSupAboutModuleThanks=Thanks to nwa who creates icons for this module./ + +# +# Index & list page +# +TicketsIndex=Ticket - home +TicketList=List of tickets +TicketAssignedToMeInfos=This page display ticket list which are assigned to current user +NoTicketsFound=No ticket found +TicketViewAllTickets=View all tickets +TicketViewNonClosedOnly=View only open tickets +TicketStatByStatus=Tickets by status + +# +# Ticket card +# +Ticketsup=Incident ticket +TicketCard=Ticket card +CreateTicket=Create new ticket +EditTicket=Edit ticket +TicketsManagement=Tickets Management +CreatedBy=Created by +NewTicket=New Ticket +SubjectAnswerToTicket=Ticket answer +TicketTypeRequest=Request type +TicketCategory=Category +SeeTicket=See ticket +TicketMarkedAsRead=Ticket has been marked as read +TicketReadOn=Read on +TicketCloseOn=Clotured on +MarkAsRead=Mark ticket as read +TicketMarkedAsReadButLogActionNotSaved=Ticket marked as closed but no action saved +TicketHistory=Ticket history +AssignUser=Assign to user +TicketAssigned=Ticket is now assigned +TicketChangeType=Change type +TicketChangeCategory=Change category +TicketChangeSeverity=Change severity +TicketAddMessage=Add a message +TicketEditProperties=Edit properties +AddMessage=Add a message +MessageSuccessfullyAdded=Ticket added +TicketMessageSuccessfullyAdded=Message successfully added +TicketMessagesList=Message list +NoMsgForThisTicket=No message for this ticket +Properties=Classification +LatestNewTickets=Latest %s newest tickets (not read) +TicketSeverity=Severity +ShowTicket=See ticket +RelatedTickets=Related tickets +TicketAddIntervention=Create intervention +CloseTicket=Close ticket +CloseATicket=Close a ticket +ConfirmCloseAticket=Confirm ticket closing +ConfirmDeleteTicket=Please confirm ticket deleting +TicketDeletedSuccess=Ticket deleted with success +TicketMarkedAsClosed=Ticket marked as closed +TicketMarkedAsClosedButLogActionNotSaved=Ticket marked as closed but no log saved ! +TicketDurationAuto=Calculated duration +TicketDurationAutoInfos=Duration calculated automatically from intervention related +TicketUpdated=Ticket updated +SendMessageByEmail=Send message by email +TicketNewMessage=New message +ErrorMailRecipientIsEmptyForSendTicketMessage=Recipient is empty. No email send +TicketGoIntoContactTab=Please go into "Contacts" tab to select them +TicketMessageMailIntro=Introduction +TicketMessageMailIntroHelp=This text is added only at the beginning of the email and will not be saved. +TicketMessageMailIntroLabelAdmin=Introduction to the message when sending email +TicketMessageMailIntroText=

    Hello A new response was sent on a ticket that you contact. Here is the message: +TicketMessageMailIntroHelpAdmin=This text will be inserted before the text of the response to a ticket. +TicketMessageMailSignature=Signature +TicketMessageMailSignatureHelp=This text is added only at the end of the email and will not be saved. +TicketMessageMailSignatureText=

    Cordialement,

    --

    +TicketMessageMailSignatureLabelAdmin=Signature of response email +TicketMessageMailSignatureHelpAdmin=This text will be inserted after the response message. +TicketMessageHelp=Only this text will be saved in the message list on ticket card. +TicketMessageSubstitutionReplacedByGenericValues=Substitutions variables are replaced by generic values. +TicketTimeToRead=Time elapsed before ticket read +TicketContacts=Contacts ticket +TicketDocumentsLinked=Documents linked to ticket +ConfirmReOpenTicket=Confirm reopen this ticket ? +TicketMessageMailIntroAutoNewPublicMessage=A new message was posted on the ticket with the subject %s : +TicketAssignedToYou=Ticket assigned +TicketAssignedEmailBody=You have been assigned the ticket #%s by %s +MarkMessageAsPrivate=Mark message as private +TicketMessagePrivateHelp=This message will not display to external users +TicketEmailOriginIssuer=Issuer at origin of the tickets +InitialMessage=Initial Message +LinkToAContract=Link to a contract +TicketSupPleaseSelectAContract=Select a contract +UnableToCreateInterIfNoSocid=Can not create an intervention when no third party are defined +TicketMailExchanges=Mail exchanges +TicketInitialMessageModified=Initial message modified +TicketMessageSuccesfullyUpdated=Message successfully updated +TicketChangeStatus=Change status +TicketConfirmChangeStatus=Confirm the status change : %s ? +TicketLogStatusChanged=Status changed : %s to %s +TicketNotNotifyTiersAtCreate=Not notify company at create + +# +# Logs +# +TicketLogMesgReadBy=Ticket read by %s +NoLogForThisTicket=No log for this ticket yet +TicketLogAssignedTo=Ticket assigned to %s +TicketAssignedButLogActionNotSaved=Ticket assigned but no log saved ! +TicketLogPropertyChanged=Change classification : from %s to %s +TicketLogClosedBy=Ticket closed by %s +TicketLogProgressSetTo=Progress change to %s percent +TicketLogReopen=Ticket re-opened + +# +# Public pages +# +TicketSystem=Ticket system +ShowListTicketWithTrackId=Display ticket list from track ID +ShowTicketWithTrackId=Display ticket from track ID +TicketPublicDesc=You can create a support ticket or check from an existing ID. +YourTicketSuccessfullySaved=Ticket has been successfully saved! +MesgInfosPublicTicketCreatedWithTrackId=A new ticket has been created with ID %s. +PleaseRememberThisId=Please keep the tracking number that we might ask you later. +TicketNewEmailSubject=Ticket creation confirmation +TicketNewEmailSubjectCustomer=New support ticket +TicketNewEmailBody=This is an automatic email to confirm you have registered a new ticket. +TicketNewEmailBodyCustomer=This is an automatic email to confirm a new ticket has just been created into your account. +TicketNewEmailBodyInfosTicket=Information for monitoring the ticket +TicketNewEmailBodyInfosTrackId=Ticket tracking number : %s +TicketNewEmailBodyInfosTrackUrl=You can view the progress of the ticket by clicking the link above. +TicketNewEmailBodyInfosTrackUrlCustomer=You can view the progress of the ticket in the specific interface by clicking the following link +TicketEmailPleaseDoNotReplyToThisEmail=Please do not reply directly to this email! Use the link to reply into the interface. +TicketPublicInfoCreateTicket=This form allows you to record a trouble ticket in our management system. +TicketPublicPleaseBeAccuratelyDescribe=Please accurately describe the problem. Provide the most information possible to allow us to correctly identify your request. +TicketPublicMsgViewLogIn=Please enter ticket tracking ID +TicketTrackId=Tracking ID +OneOfTicketTrackId=One of yours tracking ID +ErrorTicketNotFound=Ticket with tracking ID %s not found ! +Subject=Subject +ViewTicket=View ticket +ViewMyTicketList=View my ticket list +ErrorEmailMustExistToCreateTicket=Error : email address not found in our database +TicketNewEmailSubjectAdmin=New ticket created +TicketNewEmailBodyAdmin=

    Ticket has just been created with ID #%s, see informations :

    +SeeThisTicketIntomanagementInterface=See ticket in management interface +TicketPublicInterfaceForbidden=Access for this area : forbidden + +# notifications +TicketNotificationEmailSubject=Ticket %s updated +TicketNotificationEmailBody=This is an automatic message to notify you that ticket %s has just been updated +TicketNotificationRecipient=Notification recipient +TicketNotificationLogMessage=Log message +TicketNotificationEmailBodyInfosTrackUrlinternal=View ticket into interface +TicketNotificationNumberEmailSent=Notification email sent : %s + + +# +# Boxes +# +BoxLastTicketsup=Latest created tickets +BoxLastTicketsupDescription=Latest %s created tickets +BoxLastTicketsupContent= +BoxLastTicketsupNoRecordedTickets=No recent unread tickets +BoxLastModifiedTicketsup=Latest modified tickets +BoxLastModifiedTicketsupDescription=Latest %s modified tickets +BoxLastModifiedTicketsupContent= +BoxLastModifiedTicketsupNoRecordedTickets=No recent modified tickets diff --git a/htdocs/langs/en_US/trips.lang b/htdocs/langs/en_US/trips.lang index 2b254bf91e7..0f9fcac1d02 100644 --- a/htdocs/langs/en_US/trips.lang +++ b/htdocs/langs/en_US/trips.lang @@ -21,17 +21,17 @@ ListToApprove=Waiting for approval ExpensesArea=Expense reports area ClassifyRefunded=Classify 'Refunded' ExpenseReportWaitingForApproval=A new expense report has been submitted for approval -ExpenseReportWaitingForApprovalMessage=A new expense report has been submitted and is waiting for approval.\n - User: %s\n - Period: %s\nClick here to validate: %s +ExpenseReportWaitingForApprovalMessage=A new expense report has been submitted and is waiting for approval.
    - User: %s
    - Period: %s
    Click here to validate: %s ExpenseReportWaitingForReApproval=An expense report has been submitted for re-approval -ExpenseReportWaitingForReApprovalMessage=An expense report has been submitted and is waiting for re-approval.\nThe %s, you refused to approve the expense report for this reason: %s.\nA new version has been proposed and waiting for your approval.\n - User: %s\n - Period: %s\nClick here to validate: %s +ExpenseReportWaitingForReApprovalMessage=An expense report has been submitted and is waiting for re-approval.
    The %s, you refused to approve the expense report for this reason: %s.
    A new version has been proposed and waiting for your approval.
    - User: %s
    - Period: %s
    Click here to validate: %s ExpenseReportApproved=An expense report was approved -ExpenseReportApprovedMessage=The expense report %s was approved.\n - User: %s\n - Approved by: %s\nClick here to show the expense report: %s +ExpenseReportApprovedMessage=The expense report %s was approved.
    - User: %s
    - Approved by: %s
    Click here to show the expense report: %s ExpenseReportRefused=An expense report was refused -ExpenseReportRefusedMessage=The expense report %s was refused.\n - User: %s\n - Refused by: %s\n - Motive for refusal: %s\nClick here to show the expense report: %s +ExpenseReportRefusedMessage=The expense report %s was refused.
    - User: %s
    - Refused by: %s
    - Motive for refusal: %s
    Click here to show the expense report: %s ExpenseReportCanceled=An expense report was canceled -ExpenseReportCanceledMessage=The expense report %s was canceled.\n - User: %s\n - Canceled by: %s\n - Motive for cancellation: %s\nClick here to show the expense report: %s +ExpenseReportCanceledMessage=The expense report %s was canceled.
    - User: %s
    - Canceled by: %s
    - Motive for cancellation: %s
    Click here to show the expense report: %s ExpenseReportPaid=An expense report was paid -ExpenseReportPaidMessage=The expense report %s was paid.\n - User: %s\n - Paid by: %s\nClick here to show the expense report: %s +ExpenseReportPaidMessage=The expense report %s was paid.
    - User: %s
    - Paid by: %s
    Click here to show the expense report: %s TripId=Id expense report AnyOtherInThisListCanValidate=Person to inform for validation. TripSociete=Information company diff --git a/htdocs/langs/en_US/users.lang b/htdocs/langs/en_US/users.lang index d9c2f375106..57df8cc540d 100644 --- a/htdocs/langs/en_US/users.lang +++ b/htdocs/langs/en_US/users.lang @@ -93,6 +93,7 @@ NameToCreate=Name of third party to create YourRole=Your roles YourQuotaOfUsersIsReached=Your quota of active users is reached ! NbOfUsers=Nb of users +NbOfPermissions=Nb of permissions DontDowngradeSuperAdmin=Only a superadmin can downgrade a superadmin HierarchicalResponsible=Supervisor HierarchicView=Hierarchical view diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index a4c7d9e0211..2da76863d41 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -6,6 +6,7 @@ ConfirmDeleteWebsite=Are you sure you want to delete this web site. All its page WEBSITE_TYPE_CONTAINER=Type of page/container WEBSITE_PAGE_EXAMPLE=Web page to use as example WEBSITE_PAGENAME=Page name/alias +WEBSITE_ALIASALT=Alternative page names/aliases WEBSITE_CSS_URL=URL of external CSS file WEBSITE_CSS_INLINE=CSS file content (common to all pages) WEBSITE_JS_INLINE=Javascript file content (common to all pages) @@ -36,6 +37,7 @@ SetAsHomePage=Set as Home page RealURL=Real URL ViewWebsiteInProduction=View web site using home URLs SetHereVirtualHost=If you can create, on your web server (Apache, Nginx, ...), a dedicated Virtual Host with PHP enabled and a Root directory on
    %s
    then enter here the virtual hostname you have created, so the preview can be done also using this dedicated web server access instead of only using Dolibarr server. +YouCanAlsoTestWithPHPS=On develop environment, you may prefer to test the site with the PHP embedded web server (PHP 5.5 required) by running
    php -S 0.0.0.0:8080 -t %s CheckVirtualHostPerms=Check also that virtual host has %s on files into
    %s ReadPerm=Read permission WritePerm=Write permission diff --git a/htdocs/langs/en_US/withdrawals.lang b/htdocs/langs/en_US/withdrawals.lang index 7b33ad15947..5a25a9e96e2 100644 --- a/htdocs/langs/en_US/withdrawals.lang +++ b/htdocs/langs/en_US/withdrawals.lang @@ -1,8 +1,8 @@ # Dolibarr language file - Source file is en_US - withdrawals CustomersStandingOrdersArea=Direct debit payment orders area SuppliersStandingOrdersArea=Direct credit payment orders area -StandingOrders=Direct debit payment orders -StandingOrder=Direct debit payment order +StandingOrdersPayment=Direct debit payment orders +StandingOrderPayment=Direct debit payment order NewStandingOrder=New direct debit order StandingOrderToProcess=To process WithdrawalsReceipts=Direct debit orders @@ -78,7 +78,7 @@ ThisWillAlsoAddPaymentOnInvoice=This will also record payments to invoices and w StatisticsByLineStatus=Statistics by status of lines RUM=UMR RUMLong=Unique Mandate Reference -RUMWillBeGenerated=UMR number will be generated once bank account information are saved +RUMWillBeGenerated=If empty, UMR number will be generated once bank account information are saved WithdrawMode=Direct debit mode (FRST or RECUR) WithdrawRequestAmount=Amount of Direct debit request: WithdrawRequestErrorNilAmount=Unable to create direct debit request for empty amount. @@ -98,6 +98,10 @@ ModeFRST=One-off payment PleaseCheckOne=Please check one only DirectDebitOrderCreated=Direct debit order %s created AmountRequested=Amount requested +SEPARCUR=SEPA CUR +SEPAFRST=SEPA FRST +ExecutionDate=Execution date +CreateForSepa=Create direct debit file ### Notifications InfoCreditSubject=Payment of direct debit payment order %s by the bank diff --git a/htdocs/langs/es_CL/accountancy.lang b/htdocs/langs/es_CL/accountancy.lang index c98c669ebe4..c5508ab297a 100644 --- a/htdocs/langs/es_CL/accountancy.lang +++ b/htdocs/langs/es_CL/accountancy.lang @@ -110,7 +110,6 @@ ACCOUNTING_PRODUCT_BUY_ACCOUNT=Cuenta de contabilidad por defecto para productos ACCOUNTING_PRODUCT_SOLD_ACCOUNT=Cuenta de contabilidad por defecto para los productos vendidos (utilizada si no está definida en la hoja del producto) ACCOUNTING_SERVICE_BUY_ACCOUNT=Cuenta de contabilidad por defecto para los servicios comprados (se usa si no se define en la hoja de servicio) ACCOUNTING_SERVICE_SOLD_ACCOUNT=Cuenta de contabilidad por defecto para los servicios vendidos (utilizada si no está definida en la hoja de servicio) -Code_tiers=Socio de Negocio LabelAccount=Cuenta LabelOperation=Operación de etiqueta Sens=Significado diff --git a/htdocs/langs/es_CL/admin.lang b/htdocs/langs/es_CL/admin.lang index cbf83f9eb3b..b8b8c1ac533 100644 --- a/htdocs/langs/es_CL/admin.lang +++ b/htdocs/langs/es_CL/admin.lang @@ -318,7 +318,6 @@ ExtrafieldParamHelpsellist=Lista de valores proviene de una tabla
    Sintaxis: ExtrafieldParamHelpchkbxlst=La lista de valores proviene de una tabla
    Sintaxis: table_name: label_field: id_field :: filter
    Ejemplo: c_typent: libelle: id :: filter

    el filtro puede ser una prueba simple (por ejemplo, active = 1 ) para mostrar solo el valor activo
    También puede usar $ ID $ en el filtro bruja es la identificación actual del objeto actual
    Para hacer un SELECCIONAR en filtro use $ SEL $
    si desea filtrar el uso de campos extraños sintaxis extra.fieldcode = ... (donde el código de campo es el código de extrafield)

    Para que la lista dependa de otra lista de atributos complementarios:
    c_typent: libelle: id: options_ parent_list_code|parent_column: filter

    Para que la lista dependa de otra lista:
    c_typent: libelle: id: parent_list_codelparent_column: filter ExtrafieldParamHelplink=Los parámetros deben ser ObjectName: Classpath
    Sintaxis: ObjectName: Classpath
    Ejemplo: Societe: societe / class / societe.class.php LibraryToBuildPDF=Biblioteca utilizada para la generación de PDF -WarningUsingFPDF=Advertencia: su conf.php contiene la directiva dolibarr_pdf_force_fpdf = 1. Esto significa que usa la biblioteca FPDF para generar archivos PDF. Esta biblioteca es antigua y no admite muchas características (Unicode, transparencia de imagen, cirílico, árabe y asiático, ...), por lo que puede experimentar errores durante la generación de PDF.
    Para solucionar esto y tener un soporte completo de la generación de PDF, descargue la librería TCPDF, luego comente o elimine la línea $dolibarr_pdf_force_fpdf = 1, y en su lugar, agregue $dolibarr_lib_TCPDF_PATH = 'path_to_TCPDF_dir' LocalTaxDesc=Algunos países aplican 2 o 3 impuestos en cada línea de factura. Si este es el caso, elija tipo para el segundo y tercer impuesto y su tasa. El tipo posible es:
    1: impuesto local se aplica a productos y servicios sin IVA (el impuesto local se calcula sobre el monto sin impuestos)
    2: se aplica el impuesto local sobre productos y servicios, incluido el IVA (el impuesto local se calcula sobre el monto + impuesto principal )
    3: el impuesto local se aplica a los productos sin IVA (el impuesto local se calcula sobre el monto sin impuestos)
    4: se aplica el impuesto local sobre los productos, incluido IVA (el impuesto local se calcula sobre el monto + el IVA principal)
    5: local se aplica impuesto sobre los servicios sin IVA (el impuesto local se calcula sobre el monto sin impuestos)
    6: el impuesto local se aplica a los servicios, incluido el IVA (el impuesto local se calcula sobre el monto + impuestos) LinkToTestClickToDial=Ingrese un número de teléfono para llamar y mostrar un enlace para probar la URL de ClickToDial para el usuario %s RefreshPhoneLink=Actualizar enlace diff --git a/htdocs/langs/es_CL/members.lang b/htdocs/langs/es_CL/members.lang index 29d57cd65b8..a04a3869592 100644 --- a/htdocs/langs/es_CL/members.lang +++ b/htdocs/langs/es_CL/members.lang @@ -79,17 +79,8 @@ PublicMemberCard=Tarjeta pública de miembro SubscriptionNotRecorded=Suscripción no grabada AddSubscription=Crear suscripción ShowSubscription=Mostrar suscripción -SendAnEMailToMember=Enviar información por correo electrónico al miembro DescADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT=Asunto del correo electrónico recibido en caso de inscripción automática de un invitado DescADHERENT_AUTOREGISTER_NOTIF_MAIL=E-mail recibido en caso de auto inscripción de un invitado -DescADHERENT_AUTOREGISTER_MAIL_SUBJECT=Asunto de correo electrónico para la suscripción automática de miembros -DescADHERENT_AUTOREGISTER_MAIL=Correo electrónico para la suscripción automática de miembros -DescADHERENT_MAIL_VALID_SUBJECT=Asunto de correo electrónico para la validación de miembros -DescADHERENT_MAIL_VALID=Correo electrónico para la validación de miembro -DescADHERENT_MAIL_COTIS_SUBJECT=Correo electrónico sujeto a suscripción -DescADHERENT_MAIL_COTIS=Correo electrónico para suscripción -DescADHERENT_MAIL_RESIL_SUBJECT=Asunto de correo para la resiliencia de miembros -DescADHERENT_MAIL_RESIL=Correo electrónico para la resiliencia de miembros DescADHERENT_MAIL_FROM=Remitente Correo electrónico para correos electrónicos automáticos DescADHERENT_ETIQUETTE_TYPE=Formato de la página de etiquetas DescADHERENT_ETIQUETTE_TEXT=Texto impreso en las hojas de direcciones de los miembros diff --git a/htdocs/langs/es_EC/admin.lang b/htdocs/langs/es_EC/admin.lang index aa4ce9ecbfc..e6d9f06e3f0 100644 --- a/htdocs/langs/es_EC/admin.lang +++ b/htdocs/langs/es_EC/admin.lang @@ -325,7 +325,6 @@ ExtrafieldParamHelpsellist=La lista de valores viene de una tabla
    Sintaxis : ExtrafieldParamHelpchkbxlst=La lista de valores proviene de un tabla
    Syntax: nombre_tabla:label_field:id_field::filter
    Example : c_typent:libelle:id::filter

    filter puede ser una prueba simple (por ejemplo, activa=1) para mostrar sólo el valor activo
    También puede usar $ID$ en filtro con el id actual del objeto actual
    Para hacer un SELECT en el filtro use $SEL$
    si quiere filtrar en extrafields use sintaxis extra.fieldcode=...(donde código de campo es el código de extrafield)

    Para tener la lista dependiendo de otra lista de atributos complementarios:
    c_typent:libelle:id:options_parent_list_code|parent_column:filter

    Para que la lista dependa de otra lista:
    c_typent:libelle:id:parent_list_code|parent_column:filter ExtrafieldParamHelplink=Los parámetros deben ser ObjectName: Classpath
    Syntax: ObjectName: Classpath
    Examples:
    Societe: societe/class/societe.class.php
    Contacto: contact/class/contact.class.php LibraryToBuildPDF=Biblioteca utilizado para la generación de PDF -WarningUsingFPDF=Advertencia: Su conf.php contiene una directiva dolibarr_pdf_force_fpdf=1. Esto significa que utiliza la biblioteca FPDF para generar archivos PDF. Esta biblioteca es antigua y no soporta muchas características (Unicode, transparencia de imagen, cyrillic, árabe y idiomas asiáticos, ...), por lo que puede experimentar errores durante la generación de PDF.
    Para resolver esto y tener un soporte completo de generación de PDF, por favor descargue TCPDF library, luego comenta o elimina la línea $dolibarr_pdf_force_fpdf=1, and Agregar en su lugar $dolibarr_lib_TCPDF_PATH='path_to_TCPDF_dir' LocalTaxDesc=Algunos países aplican 2 o 3 impuestos en cada línea de factura. Si este es el caso, elija el tipo de segundo y tercer impuesto y su tasa. El tipo posible es:
    1 : el impuesto local se aplica sobre los productos y servicios sin IVA (impuesto local se calcula sobre la cantidad sin impuestos)
    2 : El impuesto local se aplica en los productos y los servicios incluyendo el IVA (el impuesto local se calcula sobre la cantidad + impuesto principal)
    3 : El impuesto local se aplica en los productos sin IVA (el impuesto local se calcula sobre la cantidad sin impuestos)
    4 : El impuesto local se aplica a los productos, incluido el IVA (el impuesto local se calcula sobre la cantidad + impuesto principal)
    5 : Se aplica el impuesto local sobre los servicios sin IVA (el impuesto local se calcula sobre la cantidad sin impuestos)
    6 : Se aplican impuestos locales sobre los servicios, incluido el IVA (el impuesto local se calcula sobre el importe + impuestos) LinkToTestClickToDial=Introduzca un número de teléfono para llamar, para mostrar un enlace y probar la URL de ClickToDial para el usuario %s RefreshPhoneLink=Actualizar enlace diff --git a/htdocs/langs/es_ES/incoterm.lang b/htdocs/langs/es_ES/incoterm.lang deleted file mode 100644 index 0b5aa9e1153..00000000000 --- a/htdocs/langs/es_ES/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Añade funciones para gestionar Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/et_EE/incoterm.lang b/htdocs/langs/et_EE/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/et_EE/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/eu_ES/incoterm.lang b/htdocs/langs/eu_ES/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/eu_ES/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/fa_IR/incoterm.lang b/htdocs/langs/fa_IR/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/fa_IR/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/fi_FI/incoterm.lang b/htdocs/langs/fi_FI/incoterm.lang deleted file mode 100644 index 847faa62288..00000000000 --- a/htdocs/langs/fi_FI/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Lisää Incoterm ominaisuuksia -IncotermLabel=Incoterm-ehdot diff --git a/htdocs/langs/fr_CA/incoterm.lang b/htdocs/langs/fr_CA/incoterm.lang deleted file mode 100644 index d982800ae3e..00000000000 --- a/htdocs/langs/fr_CA/incoterm.lang +++ /dev/null @@ -1,2 +0,0 @@ -# Dolibarr language file - Source file is en_US - incoterm -Module62000Desc=Ajouter des fonctionnalités pour gérer Incoterm diff --git a/htdocs/langs/fr_CA/members.lang b/htdocs/langs/fr_CA/members.lang index 26e7135f0df..a53f05ad15d 100644 --- a/htdocs/langs/fr_CA/members.lang +++ b/htdocs/langs/fr_CA/members.lang @@ -89,17 +89,8 @@ PublicMemberCard=Carte publique membre SubscriptionNotRecorded=L'abonnement n'est pas enregistré AddSubscription=Créer un abonnement ShowSubscription=Afficher l'abonnement -SendAnEMailToMember=Envoyer un email d'information au membre DescADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT=Objet de l'e-mail reçu en cas d'auto-inscription d'un invité DescADHERENT_AUTOREGISTER_NOTIF_MAIL=E-mail reçu en cas d'auto-inscription d'un invité -DescADHERENT_AUTOREGISTER_MAIL_SUBJECT=Émetteur pour les abonnements aux membres -DescADHERENT_AUTOREGISTER_MAIL=EMail pour les abonnements aux membres -DescADHERENT_MAIL_VALID_SUBJECT=Sujet d'envoi pour la validation des membres -DescADHERENT_MAIL_VALID=EMail pour la validation des membres -DescADHERENT_MAIL_COTIS_SUBJECT=Sujet d'envoi pour souscription -DescADHERENT_MAIL_COTIS=EMail pour souscription -DescADHERENT_MAIL_RESIL_SUBJECT=Sujet d'envoi pour la résiliation des membres -DescADHERENT_MAIL_RESIL=EMail pour la résiliation des membres DescADHERENT_MAIL_FROM=Expéditeur EMail pour les courriels automatiques DescADHERENT_ETIQUETTE_TYPE=Format de la page des étiquettes DescADHERENT_ETIQUETTE_TEXT=Texte imprimé sur les feuilles d'adresses des membres diff --git a/htdocs/langs/fr_FR/accountancy.lang b/htdocs/langs/fr_FR/accountancy.lang index 85d1f6a5b58..d8e307a1ee3 100644 --- a/htdocs/langs/fr_FR/accountancy.lang +++ b/htdocs/langs/fr_FR/accountancy.lang @@ -149,7 +149,6 @@ ACCOUNTING_SERVICE_SOLD_ACCOUNT=Compte comptable par défaut pour les services v Doctype=Type de documents Docdate=Date Docref=Référence -Code_tiers=Tiers LabelAccount=Libellé du compte LabelOperation=Libellé opération Sens=Sens @@ -180,7 +179,7 @@ ProductAccountNotDefined=Compte pour le produit non défini FeeAccountNotDefined=Compte de charge non défini BankAccountNotDefined=Compte pour la banque non défini CustomerInvoicePayment=Paiement de facture client -ThirdPartyAccount=Comptes de tiers +ThirdPartyAccount=Third party account NewAccountingMvt=Nouvelle transaction NumMvts=Numéro de transaction ListeMvts=Liste des mouvements @@ -220,12 +219,12 @@ ErrorAccountancyCodeIsAlreadyUse=Erreur, vous ne pouvez pas détruire de compte MvtNotCorrectlyBalanced=Mouvement non équilibré. Crédit = %s. Débit = %s FicheVentilation=Fiche lien GeneralLedgerIsWritten=Les transactions sont enregistrées dans le grand livre -GeneralLedgerSomeRecordWasNotRecorded=Certaines des opérations n'ont pu être enregistrées. S'il n'y a pas d'autres messages, c'est probablement car elles sont déjà enregistrées. +GeneralLedgerSomeRecordWasNotRecorded=Some of the transactions could not be journalized. If there is no other error message, this is probably because they were already journalized. NoNewRecordSaved=Plus d'enregistrements à journaliser ListOfProductsWithoutAccountingAccount=Liste des produits non liés à un compte comptable ChangeBinding=Changer les liens -Accounted=Comptabilisé dans le grand livre -NotYetAccounted=Pas encore comptabilisé dans le grand livre +Accounted=Comptabilisé +NotYetAccounted=Pas encore comptabilisé ## Admin ApplyMassCategories=Application en masse des catégories @@ -243,6 +242,7 @@ AccountingJournalType4=Banque AccountingJournalType5=Note de frais AccountingJournalType9=A-nouveaux ErrorAccountingJournalIsAlreadyUse=Le journal est déjà utilisé +AccountingAccountForSalesTaxAreDefinedInto=Note: Accounting account for Sales tax are defined into menu %s - %s ## Export ExportDraftJournal=Exporter le journal brouillon @@ -284,6 +284,8 @@ Formula=Formule ## Error SomeMandatoryStepsOfSetupWereNotDone=Certaines étapes obligatoires de la configuration n'ont pas été réalisées, merci de compléter cette dernière ErrorNoAccountingCategoryForThisCountry=Pas de catégories de regroupement comptable disponibles pour le pays %s (Voir Accueil - Configuration - Dictionnaires) +ErrorInvoiceContainsLinesNotYetBounded=You try to journalize some lines of the invoice %s, but some other lines are not yet bounded to accounting account. Journalization of all invoice lines for this invoice are refused. +ErrorInvoiceContainsLinesNotYetBoundedShort=Certaines lignes sur la facture ne sont pas liées au compte comptable. ExportNotSupported=Le format de l'export n'est pas supporté par cette page BookeppingLineAlreayExists=Lignes dejà existantes dans le grand livre NoJournalDefined=Pas de journal défini diff --git a/htdocs/langs/fr_FR/admin.lang b/htdocs/langs/fr_FR/admin.lang index 15cebb242f7..8a036be5678 100644 --- a/htdocs/langs/fr_FR/admin.lang +++ b/htdocs/langs/fr_FR/admin.lang @@ -68,8 +68,8 @@ ErrorCodeCantContainZero=Erreur, le code ne peut contenir la valeur 0 DisableJavascript=Désactive les fonctions Javascript et Ajax (Recommandé pour les personnes aveugles ou navigateurs text). UseSearchToSelectCompanyTooltip=Si vous avez un nombre important de tiers (>100 000), vous pourrez améliorer les performances en positionnant la constante COMPANY_DONOTSEARCH_ANYWHERE à 1 dans Configuration->Divers. La recherche sera alors limité au début des chaines. UseSearchToSelectContactTooltip=Si vous avez un nombre important de contacts (>100 000), vous pourrez améliorer les performances en positionnant la constante CONTACT_DONOTSEARCH_ANYWHERE à 1 dans Configuration->Divers. La recherche sera alors limité au début des chaines. -DelaiedFullListToSelectCompany=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des tiers (Cela peut augmenter les performances si vous avez un grand nombre de contacts) -DelaiedFullListToSelectContact=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des contacts/adresses (Cela peut augmenter les performances si vous avez un grand nombre de contacts) +DelaiedFullListToSelectCompany=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des tiers (Cela peut augmenter les performances si vous avez un grand nombre de tiers, mais cela est moins convivial) +DelaiedFullListToSelectContact=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des contacts/adresses (Cela peut augmenter les performances si vous avez un grand nombre de contacts, mais cela est moins convivial) NumberOfKeyToSearch=Nb carac. déclenchant recherche : %s NotAvailableWhenAjaxDisabled=Non disponible quand Ajax est désactivé AllowToSelectProjectFromOtherCompany=Sur les éléments d'un tiers, autorise la sélection d'un projet lié à un autre tiers @@ -151,7 +151,7 @@ PurgeDeleteAllFilesInDocumentsDir=Effacer tous les fichiers du répertoire %s PurgeRunNow=Lancer la purge maintenant PurgeNothingToDelete=Aucun dossier ou fichier à supprimer. PurgeNDirectoriesDeleted=%s fichiers ou répertoires supprimés. -PurgeNDirectoriesFailed=Echec de la suppression des fichiers ou du répertoire '%s'. +PurgeNDirectoriesFailed=Echec de la suppression de %s fichier(s) ou du répertoire(s). PurgeAuditEvents=Purger les événements d'audit de sécurité ConfirmPurgeAuditEvents=Êtes vous sûr de vouloir purger la liste des événements d'audit de sécurité. Toute la liste sera effacée, mais ceci est sans conséquence sur vos autres données. GenerateBackup=Générer sauvegarde @@ -342,7 +342,7 @@ ErrorCantUseRazIfNoYearInMask=Erreur, ne peut utiliser l'option @ pour remettre ErrorCantUseRazInStartedYearIfNoYearMonthInMask=Erreur, ne peut utiliser l'option @ si la séquence {yy}{mm} ou {yyyy}{mm} n'est pas dans le masque. UMask=Masque des nouveaux fichiers sous Unix/Linux/BSD/Mac. UMaskExplanation=Ce paramètre permet de définir les droits des fichiers créés sur le serveur par Dolibarr (lors d'envois par exemple).
    Ce doit être la valeur octale (par exemple 0666 signifie lecture/écriture pour tous).
    Ce paramètre n'a aucun effet sur un serveur Windows. -SeeWikiForAllTeam=Voir le wiki pour le détail de tous les acteurs et leur organisation +SeeWikiForAllTeam=Take a look at the wiki page for full list of all actors and their organization UseACacheDelay= Délai de mise en cache de l'export en secondes (0 ou vide pour aucun cache) DisableLinkToHelpCenter=Cacher le lien «Besoin d'aide ou d'assistance» sur la page de connexion DisableLinkToHelp=Cacher le lien vers l'aide en ligne %s @@ -392,6 +392,7 @@ PriceBaseTypeToChange=Modifier sur les prix dont la référence de base est le MassConvert=Convertir en masse String=Chaîne TextLong=Texte long +HtmlText=Html text Int=Numérique entier Float=Décimal DateAndTime=Date et heure @@ -411,6 +412,7 @@ ExtrafieldCheckBoxFromList=Cases à cocher issues d'une table ExtrafieldLink=Lien vers un objet ComputedFormula=Champ calculé ComputedFormulaDesc=Vous pouvez entrer ici une formule utilisant les propriétés objet ou tout code PHP pour obtenir des valeurs dynamiques. Vous pouvez utiliser toute formule compatible PHP, incluant l'opérateur conditionnel "?", et les objets globaux : $db, $conf, $langs, $mysoc, $user, $object.
    ATTENTION : Seulement quelques propriétés de l'objet $object pourraient être disponibles. Si vous avez besoin de propriétés non chargées, créez vous même une instance de l'objet dans votre formule, comme dans le deuxième exemple.
    Utiliser un champs calculé signifie que vous ne pouvez pas entrer vous même toute valeur à partir de l'interface. Aussi, s'il y a une erreur de syntaxe, la formule pourrait ne rien retourner.

    Exemple de formule:
    $object->id < 10 ? round($object->id / 2, 2) : ($object->id + 2 * $user->id) * (int) substr($mysoc->zip, 1, 2)

    Exemple pour recharger l'objet:
    (($reloadedobj = new Societe($db)) && ($reloadedobj->fetch($obj->id ? $obj->id : ($obj->rowid ? $obj->rowid : $object->id)) > 0)) ? $reloadedobj->array_options['options_extrafieldkey'] * $reloadedobj->capital / 5 : '-1'

    Un autre exemple de formule pour forcer le rechargement d'un objet et de son objet parent:
    (($reloadedobj = new Task($db)) && ($reloadedobj->fetch($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetch($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : 'Objet parent projet non trouvé' +ExtrafieldParamHelpPassword=Keep this field empty means value will be stored without encryption (field must be only hidden with star on screen).
    Set here value 'auto' to use the default encryption rule to save password into database (then value read will be the hash only, no way to retreive original value) ExtrafieldParamHelpselect=La liste doit être de la forme clef,valeur (où la clé ne peut être '0')

    par exemple :
    1,valeur1
    2,valeur2
    3,valeur3
    ...

    \nPour afficher une liste dépendant d'une autre liste attribut complémentaire:
    1, valeur1|options_code_liste_parente:clé_parente
    2,valeur2|option_Code_liste_parente:clé_parente

    \nPour que la liste soit dépendante d'une autre liste:
    1,valeur1|code_liste_parent:clef_parent
    2,valeur2|code_liste_parent:clef_parent ExtrafieldParamHelpcheckbox=La liste doit être de la forme clef,valeur (où la clé ne peut être '0')

    par exemple :
    1,valeur1
    2,valeur2
    3,valeur3
    ... ExtrafieldParamHelpradio=La liste doit être de la forme clef,valeur (où la clé ne peut être '0')

    par exemple :
    1,valeur1
    2,valeur2
    3,valeur3
    ... @@ -418,7 +420,6 @@ ExtrafieldParamHelpsellist=Les paramètres de la liste viennent d'une table
    S ExtrafieldParamHelpchkbxlst=Les paramètres de la liste proviennent d'une table
    :\nSyntaxe : nom_de_la_table:libelle_champ:id_champ::filtre
    \nExemple : c_typent:libelle:id::filter

    le filtre peut n'est qu'un test (ex : active=1) pour n'afficher que les valeurs actives.
    Vous pouvez aussi utiliser $ID$ dans les filtres pour indiquer l'ID de l'élément courant.
    \nPour utiliser un SELECT dans un filtre, utilisez $SEL$
    \nPour filtrer sur un attribut supplémentaire, utilisez la syntaxe\nextra.fieldcode=... (ou fieldcode est le code de l'attribut supplémentaire)

    Pour afficher une liste dépendant d'un autre attribut supplémentaire :
    c_typent:libelle:id:options_code_liste_parente|colonne_parente:filtre

    Pour afficher une liste dépendant d'une autre liste :
    c_typent:libelle:id:code_liste_parente|colonne_parente:filter ExtrafieldParamHelplink=Parameters must be ObjectName:Classpath
    Syntax : ObjectName:Classpath
    Examples :
    Societe:societe/class/societe.class.php
    Contact:contact/class/contact.class.php LibraryToBuildPDF=Bibliothèque utilisée pour la génération des PDF -WarningUsingFPDF=Attention : votre fichier conf.php contient la directive dolibarr_pdf_force_fpdf=1. Cela signifie que vous utilisez la librairie FPDF pour générer vos fichiers PDF. Cette librairie est ancienne et ne couvre pas de nombreuses fonctionnalités (Unicode, transparence des images, langues cyrilliques, arabes ou asiatiques...), aussi vous pouvez rencontrer des problèmes durant la génération des PDF.
    Pour résoudre cela et avoir une prise en charge complète de PDF, vous pouvez télécharger la bibliothèque TCPDF puis commenter ou supprimer la ligne $dolibarr_pdf_force_fpdf=1, et ajouter à la place $dolibarr_lib_TCPDF_PATH='chemin_vers_TCPDF' LocalTaxDesc=Certains pays appliquent 2 voire 3 taux sur chaque ligne de facture. Si c'est le cas, choisissez le type du deuxième et troisième taux et sa valeur. Les types possibles sont:
    1 : taxe locale sur les produits et services hors tva (la taxe locale est calculée sur le montant hors taxe)
    2 : taxe locale sur les produits et services avant tva (la taxe locale est calculée sur le montant + tva)
    3 : taxe locale uniquement sur les produits hors tva (la taxe locale est calculée sur le montant hors taxe)
    4 : taxe locale uniquement sur les produits avant tva (la taxe locale est calculée sur le montant + tva)
    5 : taxe locale uniquement sur les services hors tva (la taxe locale est calculée sur le montant hors taxe)
    6 : taxe locale uniquement sur les service avant tva (la taxe locale est calculée sur le montant + tva) SMS=SMS LinkToTestClickToDial=Entrez un numéro de téléphone à appeler pour tester le lien d'appel « ClickToDial » pour l'utilisateur %s @@ -449,7 +450,8 @@ ModuleCompanyCodePanicum=Retourne un code comptable vide ModuleCompanyCodeDigitaria=Renvoie un code comptable composé suivant le code tiers. Le code est composé du caractère 'C' en première position suivi des 5 premiers caractères du code tiers. Use3StepsApproval=Par défaut, les commandes fournisseurs nécessitent d'être créées et approuvées en deux étapes/utilisateurs (une étape/utilisateur pour créer et une étape/utilisateur pour approuver. Si un utilisateur à les deux permissions, ces deux actions sont effectuées en une seule fois). Cette option ajoute la nécessité d'une approbation par une troisième étape/utilisateur, si le montant de la commande est supérieur au montant d'une valeur définie (soit 3 étapes nécessaire: 1 =Validation, 2=Première approbation et 3=seconde approbation si le montant l'exige).
    Laissez le champ vide si une seule approbation (2 étapes) sont suffisantes, placez une valeur très faibe (0.1) si une deuxième approbation (3 étapes) est toujours exigée. UseDoubleApproval=Activer l'approbation en trois étapes si le montant HT est supérieur à... -WarningPHPMail=Attention : Certains serveurs de messagerie (Yahoo) ne permettent pas l'envoi d'e-mails depuis un autre serveur que le leur si l'adresse d'envoi utilisée est une adresse Yahoo (myemail@yahoo.fr, myemail@yahoo.com...) Votre configuration actuelle utilise le serveur de l'application pour l'envoi d'e-mails. Le serveur de certains destinataires (compatibles avec le protocole restrictif DMARC) demanderont aux serveurs Yahoo l'autorisation de recevoir les e-mails et Yahoo la refusera car le serveur n'est pas un serveur appartenant à Yahoo, aussi une partie de vos e-mails envoyés risquent de ne pas être reçus.\n
    Si votre fournisseur d'e-mail (comme Yahoo) impose cette restriction, vous devrez modifier votre configuration et opter pour l'autre méthode d'envoi "SMTP server" et saisissez les identifiants SMTP de votre compte fournis par votre fournisseur d'e-mail (à demander à votre fournisseur d'e-mail) +WarningPHPMail=WARNING: It is often better to setup outgoing emails to use the email server of your provider instead of the default setup. Some email providers (like Yahoo) does not allow you to send an email from another server than their own server. Your current setup use the server of the application to send email and not the server of your email provider, so some recipients (the one compatible with the restrictive DMARC protocol), will ask your email provider if they can accept your email and some email providers (like Yahoo) may respond "no" because the server is not a server of them, so few of your sent Emails may not be accepted (be car also to your email provider sending quota).
    If your Email provider (like Yahoo) has this restriction, you must change Email setup to choose the other method "SMTP server" and enter the SMTP server and credentials provided by your Email provider (ask your EMail provider to get SMTP credentials for your account). +WarningPHPMail2=If your email SMTP provider need to restrict email client to some IP addresses (very rare), this is the IP address of your ERP CRM application: %s. ClickToShowDescription=Cliquer pour afficher la description DependsOn=Ce module a besoin du(des) module(s) RequiredBy=Ce module est requis par le ou les module(s) @@ -619,6 +621,8 @@ Module59000Name=Marges Module59000Desc=Module pour gérer les marges Module60000Name=Commissions Module60000Desc=Module pour gérer les commissions +Module62000Name=Incoterm +Module62000Desc=Ajouts de fonctionnalités pour gérer les incoterms Module63000Name=Ressources Module63000Desc=Gère les ressources (imprimantes, voitures, salles...). les ressources peuvent être affectées à des événements. Permission11=Consulter les factures clients @@ -833,11 +837,11 @@ Permission1251=Lancer des importations en masse dans la base (chargement de donn Permission1321=Exporter les factures clients, attributs et règlements Permission1322=Rouvrir une facture payée Permission1421=Exporter les commandes clients et attributs -Permission20001=Lire les demandes de congé (les vôtres et celle de vos subordonnés) -Permission20002=Créer/modifier vos demandes de congé +Permission20001=Read leave requests (your leaves and the one of your subordinates) +Permission20002=Create/modify your leave requests (yours leaves and the one of your subordinates) Permission20003=Supprimer les demandes de congé -Permission20004=Lire toutes les demandes de congé (même celle des utilisateurs non subordonnés) -Permission20005=Créer/modifier les congés pour tout le monde +Permission20004=Read all leave requests (even of user not subordinates) +Permission20005=Create/modify leave requests for everybody (even of user not subordinates) Permission20006=Administration des demandes de congés (configuration et mise à jour du solde) Permission23001=Voir les travaux planifiés Permission23002=Créer/Modifier des travaux planifiées @@ -884,6 +888,7 @@ DictionaryRevenueStamp=Montants des timbres fiscaux DictionaryPaymentConditions=Conditions de règlement DictionaryPaymentModes=Modes de paiements DictionaryTypeContact=Types de contacts/adresses +DictionaryTypeOfContainer=Type of website pages/containers DictionaryEcotaxe=Barèmes Eco-participation (DEEE) DictionaryPaperFormat=Format papiers DictionaryFormatCards=Formats des cartes @@ -911,8 +916,8 @@ TypeOfRevenueStamp=Type de timbre fiscal VATManagement=Gestion TVA VATIsUsedDesc=Le taux de TVA proposé par défaut lors de la création de proposition commerciale, facture, commande, etc... répond à la règle standard suivante :
    Si vendeur non assujetti à TVA, TVA par défaut=0. Fin de règle.
    Si le (pays vendeur= pays acheteur) alors TVA par défaut=TVA du produit vendu. Fin de règle.
    Si vendeur et acheteur dans Communauté européenne et bien vendu= moyen de transport neuf (auto, bateau, avion), TVA par défaut=0 (La TVA doit être payée par acheteur au centre d'impôts de son pays et non au vendeur). Fin de règle.
    Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu (TVA pays vendeur si < seuil du pays et si avant 01/01/2015, TVA pays acheteur après le 01/01/2015). Fin de règle.
    Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle.
    Sinon TVA proposée par défaut=0. Fin de règle.
    VATIsNotUsedDesc=Le taux de TVA proposé par défaut est 0. C'est le cas d'associations, particuliers ou certaines petites sociétés. -VATIsUsedExampleFR=En France, il s'agit des sociétés ou organismes ayant choisi un régime fiscale réel (Réel simplifié ou Réel normal), régime dans lequel la TVA est déclarée. -VATIsNotUsedExampleFR=En France, il s'agit des associations ne déclarant pas de TVA ou sociétés, organismes ou professions libérales ayant choisi le régime fiscal micro entreprise (TVA en franchise) et payant une TVA en franchise sans faire de déclaration de TVA. Ce choix fait de plus apparaître la mention "TVA non applicable - art-293B du CGI" sur les factures. +VATIsUsedExampleFR=En France, cela signifie que les entreprises ou les organisations sont assuetis à la tva (réel ou normal). +VATIsNotUsedExampleFR=In France, it means associations that are non VAT declared or companies, organizations or liberal professions that have chosen the micro enterprise fiscal system (VAT in franchise) and paid a franchise VAT without any VAT declaration. This choice will display the reference "Non applicable VAT - art-293B of CGI" on invoices. ##### Local Taxes ##### LTRate=Taux LocalTax1IsNotUsed=Non assujeti @@ -977,7 +982,7 @@ Host=Serveur DriverType=Type du pilote SummarySystem=Résumé des informations systèmes SummaryConst=Liste de tous les paramètres de configuration Dolibarr -MenuCompanySetup=Société/Organisation +MenuCompanySetup=Company/Organization DefaultMenuManager= Gestionnaire du menu standard DefaultMenuSmartphoneManager=Gestionnaire du menu smartphone Skin=Thème visuel @@ -993,8 +998,8 @@ PermanentLeftSearchForm=Zone de recherche permanente du menu de gauche DefaultLanguage=Langue à utiliser par défaut (code langue) EnableMultilangInterface=Activer l'interface multi-langue EnableShowLogo=Afficher le logo dans le menu gauche -CompanyInfo=Informations sur la société/organisation -CompanyIds=Identifiants règlementaires +CompanyInfo=Company/organization information +CompanyIds=Company/organization identities CompanyName=Nom/Enseigne/Raison sociale CompanyAddress=Adresse CompanyZip=Code postal @@ -1049,6 +1054,7 @@ AreaForAdminOnly=Les paramètres d'installation ne peuvent être remplis que par SystemInfoDesc=Les informations systèmes sont des informations techniques diverses accessibles en lecture seule aux administrateurs uniquement. SystemAreaForAdminOnly=Cet espace n'est accessible qu'aux utilisateurs de type administrateur. Aucune permission Dolibarr ne permet d'étendre le cercle des utilisateurs autorisés à cet espace. CompanyFundationDesc=Éditez sur cette page toutes les informations connues de la société ou de l'association que vous souhaitez gérer (Pour cela, cliquez sur les boutons "Modifier" ou "Sauvegarder" en bas de page) +AccountantDesc=Edit on this page all known information of your accountant/auditor to manage (For this, click on "Modify" or "Save" button at bottom of page) DisplayDesc=Vous pouvez choisir ici tous les paramètres liés à l'apparence de Dolibarr AvailableModules=Modules/applications installés ToActivateModule=Pour activer des modules, aller dans l'espace Configuration (Accueil->Configuration->Modules). @@ -1425,7 +1431,7 @@ ViewProductDescInFormAbility=Visualisation des descriptions produits dans les fo MergePropalProductCard=Ajoute dans l'onglet Fichiers joints des produits/services, une option pour fusionner le document PDF du produit au PDF des propositions Azur si le produit/services est inclut dans la proposition. ViewProductDescInThirdpartyLanguageAbility=Visualisation des descriptions de produits dans la langue du tiers UseSearchToSelectProductTooltip=Si vous avez un nombre important de produits (>100 000), vous pourrez améliorer les performances en positionnant la constante PRODUCT_DONOTSEARCH_ANYWHERE à 1 dans Configuration->Divers. La recherche sera alors limité au début des chaines. -UseSearchToSelectProduct=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des produits (Cela peut augmenter les performances si vous avez un grand nombre de contacts) +UseSearchToSelectProduct=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des produits (Cela peut augmenter les performances si vous avez un grand nombre de produits, mais cela est moins convivial) SetDefaultBarcodeTypeProducts=Type de code-barre utilisé par défaut pour les produits SetDefaultBarcodeTypeThirdParties=Type de code-barre utilisé par défaut pour les tiers UseUnits=Définir une unité de mesure pour la quantité lors de l'édition de lignes de commande, proposition ou facture @@ -1441,6 +1447,9 @@ SyslogFilename=Nom et chemin du fichier YouCanUseDOL_DATA_ROOT=Vous pouvez utiliser DOL_DATA_ROOT/dolibarr.log pour un journal dans le répertoire "documents" de Dolibarr. Vous pouvez néanmoins définir un chemin différent pour stocker ce fichier. ErrorUnknownSyslogConstant=La constante %s n'est pas une constante syslog connue OnlyWindowsLOG_USER=Windows ne prend en charge que LOG_USER +CompressSyslogs=Syslog files compression and backup +SyslogFileNumberOfSaves=Log backups +ConfigureCleaningCronjobToSetFrequencyOfSaves=Configure cleaning scheduled job to set log backup frequency ##### Donations ##### DonationsSetup=Configuration du module Dons DonationsReceiptModel=Modèles de reçu de dons @@ -1537,10 +1546,12 @@ FailedToInitializeMenu=Échec à inisialiser le menu ##### Tax ##### TaxSetup=Configuration du module TVA, charges fiscales ou sociales et dividendes OptionVatMode=Option d'exigibilité de TVA par défaut -OptionVATDefault=Standard +OptionVATDefault=Standard basis OptionVATDebitOption=Option services sur Débit OptionVatDefaultDesc=TVA sur encaissement, l'exigibilité de la TVA est:
    - sur livraison pour les biens (en pratique on utilise la date de facturation)
    - sur paiement pour les services OptionVatDebitOptionDesc=TVA sur débit, l'exigibilité de la TVA est:
    - sur livraison pour les biens (en pratique on utilise la date de facturation)
    - sur facturation (débit) pour les services +OptionPaymentForProductAndServices=Cash basis for products and services +OptionPaymentForProductAndServicesDesc=VAT is due:
    - on payment for goods
    - on payments for services SummaryOfVatExigibilityUsedByDefault=Moment d'exigibilité par défaut de la TVA pour l'option choisie: OnDelivery=Sur livraison OnPayment=Sur paiement @@ -1550,7 +1561,7 @@ SupposedToBeInvoiceDate=Date de facture utilisée Buy=Achat Sell=Vente InvoiceDateUsed=Date de facture utilisée -YourCompanyDoesNotUseVAT=Votre société/institution est définie comme non assujettie à la TVA (voir Accueil > Configuration > Société/Institution), aussi il n'y a pas d'option paramétrage de la TVA. +YourCompanyDoesNotUseVAT=Your company has been defined to not use VAT (Home - Setup - Company/Organization), so there is no VAT options to setup. AccountancyCode=Code comptable AccountancyCodeSell=Code comptable vente AccountancyCodeBuy=Code comptable achat @@ -1632,7 +1643,7 @@ ProjectsSetup=Configuration du module Projets ProjectsModelModule=Modèles de document de rapport projets TasksNumberingModules=Modèles de numérotation des références tâches TaskModelModule=Modèles de document de rapport tâches -UseSearchToSelectProject=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des projets (Cela peut augmenter les performances si vous avez un grand nombre de projets) +UseSearchToSelectProject=Attendre que vous ayez appuyé sur une touche avant de charger le contenu de la liste déroulante des projets (Cela peut augmenter les performances si vous avez un grand nombre de projets, mais cela est moins convivial) ##### ECM (GED) ##### ##### Fiscal Year ##### AccountingPeriods=Période fiscales @@ -1714,10 +1725,10 @@ MailToSendIntervention=Pour l'envoi de fiche intervention MailToSendSupplierRequestForQuotation=Pour l'envoi de demande de prix fournisseur MailToSendSupplierOrder=Pour l'envoi de commande fournisseur MailToSendSupplierInvoice=Pour l'envoi de facture fournisseur -MailToSendContract=Pour envoyer un contrat +MailToSendContract=Pour l'envoie depuis un contrat MailToThirdparty=Pour l'envoi depuis la fiche Tiers -MailToMember=Pour envoyer un e-mail depuis la fiche d'un adhérent -MailToUser=Pour envoyer un e-mail depuis la page utilisateur +MailToMember=Pour l'envoi depuis la fiche d'un adhérent +MailToUser=Pour l'envoi depuis la page utilisateur ByDefaultInList=Afficher par défaut sur les vues listes YouUseLastStableVersion=Vous utilisez la dernière version stable TitleExampleForMajorRelease=Exemple de message que vous pouvez utiliser pour annonce une nouvelle version majeure (n'hésitez pas à l'utilisez pour vos propres news) @@ -1764,9 +1775,12 @@ MAIN_PDF_MARGIN_LEFT=Marge gauche sur les PDF MAIN_PDF_MARGIN_RIGHT=Marge droite sur les PDF MAIN_PDF_MARGIN_TOP=Marge haute sur les PDF MAIN_PDF_MARGIN_BOTTOM=Marge bas sur les PDF +SetToYesIfGroupIsComputationOfOtherGroups=Set this to yes if this group is a computation of other groups +EnterCalculationRuleIfPreviousFieldIsYes=Enter calculcation rule if previous field was set to Yes (For example 'CODEGRP1+CODEGRP2') +SeveralLangugeVariatFound=Several language variants found ##### Resource #### ResourceSetup=Configuration du module Ressource UseSearchToSelectResource=Utilisez un champ avec auto-complétion pour choisir les ressources (plutôt qu'une liste déroulante). -DisabledResourceLinkUser=Supprimer le lien entre la ressource et l'utilisateur -DisabledResourceLinkContact=Désactiver le lient entre la ressource et le contact/adresse +DisabledResourceLinkUser=Disable feature to link a resource to users +DisabledResourceLinkContact=Disable feature to link a resource to contacts ConfirmUnactivation=Confirmer réinitialisation du module diff --git a/htdocs/langs/fr_FR/companies.lang b/htdocs/langs/fr_FR/companies.lang index 05104ac860b..a3aea77e86c 100644 --- a/htdocs/langs/fr_FR/companies.lang +++ b/htdocs/langs/fr_FR/companies.lang @@ -43,6 +43,7 @@ Individual=Individu privé ToCreateContactWithSameName=Crée automatiquement un contact/adresse, sous le tiers, avec la même information que le tiers. Dans la plupart des cas, même si votre tiers est une personne physique, la création d'un tiers seul suffit. ParentCompany=Maison mère Subsidiaries=Filiales +ReportByMonth=Rapport par mois ReportByCustomers=Rapport par client ReportByQuarter=Rapport par taux CivilityCode=Code civilité diff --git a/htdocs/langs/fr_FR/compta.lang b/htdocs/langs/fr_FR/compta.lang index b3b2d14aa49..ea961bbc521 100644 --- a/htdocs/langs/fr_FR/compta.lang +++ b/htdocs/langs/fr_FR/compta.lang @@ -31,7 +31,7 @@ Credit=Crédit Piece=Pièce AmountHTVATRealReceived=HT collectée AmountHTVATRealPaid=HT payé -VATToPay=TVA ventes +VATToPay=Tax sales VATReceived=TVA collectée VATToCollect=TVA payée VATSummary=Balance de TVA @@ -74,7 +74,7 @@ MenuTaxAndDividends=Taxes et charges MenuSocialContributions=Charges fiscales/sociales MenuNewSocialContribution=Nouvelle charge NewSocialContribution=Nouvelle charge fiscale/sociale -AddSocialContribution=Ajouter taxe sociale/fiscale +AddSocialContribution=Créer taxe sociale/fiscale ContributionsToPay=Charges fiscales/sociales à payer AccountancyTreasuryArea=Espace comptabilité/trésorerie NewPayment=Nouveau règlement @@ -157,6 +157,7 @@ RulesResultDue=- Il comprend les factures impayées, les dépenses, la TVA, les RulesResultInOut=- Il comprend les paiements réels effectués sur les factures, les dépenses, la TVA et les salaires.
    - Il est basé sur les dates de paiement des factures, les dépenses, la TVA et les salaires. La date du don pour le don. RulesCADue=- Il comprend les factures dues par le client si elles sont payées ou non.
    - Il est basé sur la date de validation de ces factures.
    RulesCAIn=- Il inclut les règlements effectivement reçus des factures clients.
    - Il se base sur la date de règlement de ces factures
    +RulesCATotalSaleJournal=It includes all credit lines from the Sale journal. RulesAmountOnInOutBookkeepingRecord=Cela inclut les enregistrements dans votre Grand Livre ayant les comptes de comptabilité qui ont le groupe "EXPENSE" ou "INCOME" RulesResultBookkeepingPredefined=Cela inclut les enregistrements dans votre Grand Livre ayant les comptes de comptabilité qui ont le groupe "EXPENSE" ou "INCOME" RulesResultBookkeepingPersonalized=Cela inclut les enregistrements dans votre Grand Livre ayant les comptes de comptabilité regroupés par les groupes personnalisés @@ -165,22 +166,20 @@ DepositsAreNotIncluded=- Les factures d'acomptes ne sont pas incluses DepositsAreIncluded=- Les factures d'acomptes sont incluses LT2ReportByCustomersInInputOutputModeES=Rapport par client des IRPF LT1ReportByCustomersInInputOutputModeES=Rapport par tiers des RE -VATReport=Rapport TVA +VATReport=Sale tax report +VATReportByPeriods=Sale tax report by period VATReportByCustomersInInputOutputMode=Rapport par client des TVA collectées et payées -VATReportByCustomersInDueDebtMode=Rapport par client des TVA collectées et payées -VATReportByQuartersInInputOutputMode=Rapport par taux des TVA collectées et payées +VATReportByQuartersInInputOutputMode=Report by Sale tax rate of the tax collected and paid LT1ReportByQuartersInInputOutputMode=Rapport par taux de RE LT2ReportByQuartersInInputOutputMode=Rapport par taux de IRPF -VATReportByQuartersInDueDebtMode=Rapport par taux des TVA collectées et payées -LT1ReportByQuartersInDueDebtMode=Rapport par taux de RE -LT2ReportByQuartersInDueDebtMode=Rapport par taux de IRPF SeeVATReportInInputOutputMode=Cliquer sur %sTVA encaissement%s pour mode de calcul standard SeeVATReportInDueDebtMode=Cliquer sur %sTVA sur débit%s pour mode de calcul avec option sur les débits RulesVATInServices=- Pour les services, le rapport inclut les TVA des règlements effectivement reçus ou émis en se basant sur la date du règlement. -RulesVATInProducts=- Pour les biens matériels, il inclut les TVA des factures en se basant sur la date de facture. +RulesVATInProducts=- For material assets, the report includes the VAT received or issued on the basis of the date of payment. RulesVATDueServices=- Pour les services, le rapport inclut les TVA des factures dues, payées ou non en se basant sur la date de facture. -RulesVATDueProducts=- Pour les biens matériels, il inclut les TVA des factures en se basant sur la date de facture. +RulesVATDueProducts=- For material assets, the report includes the VAT invoices, based on the invoice date. OptionVatInfoModuleComptabilite=Remarque : Pour les biens matériels, il faudrait utiliser la date de livraison pour être plus juste. +ThisIsAnEstimatedValue=This is a preview, based on business events and not from the final ledger table, so final results may differ from this preview values PercentOfInvoice=%%/facture NotUsedForGoods=Non utilisé pour les biens ProposalStats=Statistiques sur les propales @@ -213,8 +212,8 @@ CalculationRuleDescSupplier=Selon le fournisseur, choisissez le mode approprié TurnoverPerProductInCommitmentAccountingNotRelevant=Le chiffre d'affaires par produit, dans une comptabilité en mode comptabilité de caisse n'est pas définissable. Ce rapport n'est disponible qu'en mode de comptabilité dit comptabilité d'engagement (voir la configuration du module de comptabilité). CalculationMode=Mode de calcul AccountancyJournal=Code journal comptable -ACCOUNTING_VAT_SOLD_ACCOUNT=Compte comptable par défaut pour l'encaissement de TVA - TVA sur les ventes (utilisé si non défini au niveau de la configuration du dictionnaire de TVA) -ACCOUNTING_VAT_BUY_ACCOUNT=Compte comptable par défaut pour le paiement de la TVA - TVA sur les achats (utilisé si non défini au niveau de la configuration du dictionnaire de TVA) +ACCOUNTING_VAT_SOLD_ACCOUNT=Accounting account by default for VAT on sales (used if not defined on VAT dictionary setup) +ACCOUNTING_VAT_BUY_ACCOUNT=Accounting account by default for VAT on purchases (used if not defined on VAT dictionary setup) ACCOUNTING_VAT_PAY_ACCOUNT=Compte comptable par défaut pour le paiement de la TVA ACCOUNTING_ACCOUNT_CUSTOMER=Compte comptable utilisé pour le tiers client ACCOUNTING_ACCOUNT_CUSTOMER_Desc=Le compte comptable dédié défini sur la fiche tiers sera utilisé pour l'affectation du compte auxiliaire uniquement. Celui-ci sera utilisé pour la comptabilité générale et comme valeur par défaut de la comptabilité auxiliaire si le compte comptable client dédié du ties n'est pas défini. @@ -236,3 +235,4 @@ ErrorBankAccountNotFound=Erreur: compte banque non trouvé FiscalPeriod=Période fiscale ListSocialContributionAssociatedProject=Liste des charges sociales liées au projet DeleteFromCat=Supprimer du groupe comptable +AccountingAffectation=Accounting assignement diff --git a/htdocs/langs/fr_FR/holiday.lang b/htdocs/langs/fr_FR/holiday.lang index a44463d0d1b..57d57c06bef 100644 --- a/htdocs/langs/fr_FR/holiday.lang +++ b/htdocs/langs/fr_FR/holiday.lang @@ -31,6 +31,10 @@ InfosWorkflowCP=Informations du workflow RequestByCP=Demandée par TitreRequestCP=Demande de congés NbUseDaysCP=Nombre de jours de congés consommés +NbUseDaysCPShort=Jours consommés +NbUseDaysCPShortInMonth=Jours consommés dans le mois +DateStartInMonth=Start date in month +DateEndInMonth=End date in month EditCP=Modifier DeleteCP=Supprimer ActionRefuseCP=Refuser @@ -81,6 +85,10 @@ EmployeeFirstname=Prénom du salarié TypeWasDisabledOrRemoved=Le type de congés (id %s) a été désactivé ou supprimé LastHolidays=Les %s dernières demandes de congés AllHolidays=Toutes les demandes de congés +LEAVE_PAID=Congé payés +LEAVE_SICK=Arrêt maladie +LEAVE_OTHER=Autre congé +LEAVE_PAID_FR=congé payés ## Configuration du Module ## LastUpdateCP=Dernière mise à jour automatique de l'allocation des congés diff --git a/htdocs/langs/fr_FR/incoterm.lang b/htdocs/langs/fr_FR/incoterm.lang deleted file mode 100644 index dc5c853590c..00000000000 --- a/htdocs/langs/fr_FR/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Ajouts de fonctionnalités pour gérer les incoterms -IncotermLabel=Incoterms diff --git a/htdocs/langs/fr_FR/loan.lang b/htdocs/langs/fr_FR/loan.lang index 11bcbd84f9b..cf4c2e16191 100644 --- a/htdocs/langs/fr_FR/loan.lang +++ b/htdocs/langs/fr_FR/loan.lang @@ -50,4 +50,6 @@ ConfigLoan=Configuration du module Emprunt LOAN_ACCOUNTING_ACCOUNT_CAPITAL=Compte comptable remboursement capital d'un emprunt par défaut LOAN_ACCOUNTING_ACCOUNT_INTEREST=Compte comptable paiement d'intérêt d'un emprunt par défaut LOAN_ACCOUNTING_ACCOUNT_INSURANCE=Compte comptable paiement de l'assurance d'un emprunt par défaut -CreateCalcSchedule=Créer / Modifier échéancier de prêt +FinancialCommitment=Financial commitment +CreateCalcSchedule=Edit financial commitment +InterestAmount=Interest amount diff --git a/htdocs/langs/fr_FR/mails.lang b/htdocs/langs/fr_FR/mails.lang index 487861f4c8b..8a223525f9f 100644 --- a/htdocs/langs/fr_FR/mails.lang +++ b/htdocs/langs/fr_FR/mails.lang @@ -135,7 +135,7 @@ NbOfTargetedContacts=Nombre courant d'emails de contacts cibles UseFormatFileEmailToTarget=Le fichier d'import doit être au format email;name;firstname;other UseFormatInputEmailToTarget=Saisissez une chaîne de caractères au format email;nom;prénom;autre MailAdvTargetRecipients=Destinataires (sélection avancée) -AdvTgtTitle=Remplissez les champs de saisie pour pré-sélectionner les tiers ou contacts/adresses cibles +AdvTgtTitle=Fill input fields to preselect the third parties or contacts/addresses to target AdvTgtSearchTextHelp=Astuces de recherche :
    - x%% pour rechercher tous les termes commençant par x,
    - ; comme séparateur de valeurs recherchées,
    - ! pour exclure une valeur de la recherche :
    Exemple : jean;joe;jim%%;!jimo;!jima% affichera tous les résultats jean et joe, ceux commençant par jim en excluant jimo et jima. AdvTgtSearchIntHelp=Utiliser un intervalle pour sélectionner un entier ou décimal AdvTgtMinVal=Valeur minimum diff --git a/htdocs/langs/fr_FR/main.lang b/htdocs/langs/fr_FR/main.lang index cd087a9a4b8..4d73b8e71bb 100644 --- a/htdocs/langs/fr_FR/main.lang +++ b/htdocs/langs/fr_FR/main.lang @@ -44,7 +44,7 @@ ErrorConstantNotDefined=Paramètre %s non défini ErrorUnknown=Erreur inconnue ErrorSQL=Erreur SQL ErrorLogoFileNotFound=Le fichier logo '%s' n'a pas été trouvé -ErrorGoToGlobalSetup=Allez dans la Configuration 'Société/Organisation' pour corriger +ErrorGoToGlobalSetup=Go to 'Company/Organization' setup to fix this ErrorGoToModuleSetup=Allez dans la Configuration du module pour corriger ErrorFailedToSendMail=Échec de l'envoi de l'email (émetteur=%s, destinataire=%s) ErrorFileNotUploaded=Le fichier n'a pas été transféré. Vérifiez que sa taille ne dépasse pas le maxium autorisé, que l'espace disque est disponible et qu'un fichier du même nom n'existe pas déjà. @@ -108,7 +108,7 @@ YouCanSetOptionDolibarrMainProdToZero=Vous pouvez lire le fichier log ou défini InformationToHelpDiagnose=Voici les informations qui pourront aider au diagnostic (Vous pouvez fixer l'option $dolibarr_main_prod sur '1' pour supprimer quelques notifications) MoreInformation=Plus d'information TechnicalInformation=Informations techniques -TechnicalID=Numéro technique +TechnicalID=ID technique NotePublic=Note (publique) NotePrivate=Note (privée) PrecisionUnitIsLimitedToXDecimals=Dolibarr a été configuré pour limiter la précision des prix unitaires à %s décimales. @@ -185,6 +185,7 @@ ToLink=Lier Select=Sélectionner Choose=Choisir Resize=Redimensionner +ResizeOrCrop=Resize or Crop Recenter=Recadrer Author=Auteur User=Utilisateur @@ -325,8 +326,10 @@ Default=Défaut DefaultValue=Valeur par défaut DefaultValues=Valeurs par défaut Price=Prix +PriceCurrency=Price (currency) UnitPrice=Prix unitaire UnitPriceHT=Prix unitaire HT +UnitPriceHTCurrency=Unit price (net) (currency) UnitPriceTTC=Prix unitaire TTC PriceU=P.U. PriceUHT=P.U. HT @@ -334,6 +337,7 @@ PriceUHTCurrency=P.U. (devise) PriceUTTC=P.U TTC Amount=Montant AmountInvoice=Montant facture +AmountInvoiced=Amount invoiced AmountPayment=Montant paiement AmountHTShort=Montant HT AmountTTCShort=Montant TTC @@ -353,6 +357,7 @@ AmountLT2ES=Montant IRPF AmountTotal=Montant total AmountAverage=Montant moyen PriceQtyMinHT=Prix quantité min. HT +PriceQtyMinHTCurrency=Price quantity min. (net of tax) (currency) Percentage=Pourcentage Total=Total SubTotal=Sous-total @@ -389,6 +394,8 @@ LT2ES=IRPF LT1IN=CGST LT2IN=SGST VATRate=Taux TVA +VATCode=Tax Rate code +VATNPR=Tax Rate NPR DefaultTaxRate=Taux de taxe par défaut Average=Moyenne Sum=Somme @@ -419,7 +426,8 @@ ActionRunningShort=En cours ActionDoneShort=Terminé ActionUncomplete=Incomplets LatestLinkedEvents=Les %s derniers événements liés -CompanyFoundation=Société/Organisation +CompanyFoundation=Company/Organization +Accountant=Accountant ContactsForCompany=Contacts de ce tiers ContactsAddressesForCompany=Contacts/adresses de ce tiers AddressesForCompany=Adresses de ce tiers @@ -427,6 +435,9 @@ ActionsOnCompany=Événements vis à vis de ce tiers ActionsOnMember=Événements vis à vis de cet adhérent ActionsOnProduct=Événements liés au produit NActionsLate=%s en retard +ToDo=À faire +Completed=Completed +Running=En cours RequestAlreadyDone=Requête déjà enregistrée Filter=Filtre FilterOnInto=Rechercher le critère '%s' dans les champs %s @@ -552,7 +563,7 @@ MonthShort11=Nov. MonthShort12=Déc. MonthVeryShort01=J MonthVeryShort02=F -MonthVeryShort03=L +MonthVeryShort03=M MonthVeryShort04=A MonthVeryShort05=M MonthVeryShort06=J @@ -704,6 +715,8 @@ WarningYouAreInMaintenanceMode=Attention, vous êtes en mode maintenance, aussi CoreErrorTitle=Erreur système CoreErrorMessage=Désolé, une erreur s'est produite. Contacter votre administrateur système pour vérifier les logs ou désactiver l'option $dolibarr_main_prod=1 pour obtenir plus d'information. CreditCard=Carte de crédit +ValidatePayment=Valider ce règlement +CreditOrDebitCard=Credit or debit card FieldsWithAreMandatory=Les champs marqués par un %s sont obligatoires FieldsWithIsForPublic=Les champs marqués par %s seront affichés sur la liste publique des membres. Si vous ne le souhaitez pas, décochez la case "public". AccordingToGeoIPDatabase=(obtenu par conversion GeoIP) @@ -808,6 +821,7 @@ ConfirmMassDeletion=Confirmation de suppression en masse ConfirmMassDeletionQuestion=Êtes-vous sur de vouloir supprimer la(les) %s valeur(s) sélectionnée(s) ? RelatedObjects=Objets liés ClassifyBilled=Classer facturé +ClassifyUnbilled=Classify unbilled Progress=Progression ClickHere=Cliquer ici FrontOffice=Front office @@ -851,6 +865,8 @@ FileNotShared=Fichier non partagé en public Project=Projet Projects=Projets Rights=Permissions +LineNb=Line nb +IncotermLabel=Incoterms # Week day Monday=Lundi Tuesday=Mardi @@ -916,3 +932,11 @@ CommentDeleted=Commentaire supprimé Everybody=Tout le monde PayedBy=Payé par PayedTo=Payé à +Monthly=Monthly +Quarterly=Quarterly +Annual=Annual +Local=Local +Remote=Remote +LocalAndRemote=Local and Remote +KeyboardShortcut=Keyboard shortcut +AssignedTo=Affecté à diff --git a/htdocs/langs/fr_FR/margins.lang b/htdocs/langs/fr_FR/margins.lang index 7c55055c781..5663553f4d6 100644 --- a/htdocs/langs/fr_FR/margins.lang +++ b/htdocs/langs/fr_FR/margins.lang @@ -41,4 +41,4 @@ rateMustBeNumeric=Le taux doit être une valeure numérique markRateShouldBeLesserThan100=Le taux de marque doit être inférieur à 100 ShowMarginInfos=Afficher les infos de marges CheckMargins=Détails des marges -MarginPerSaleRepresentativeWarning=Le rapport de la marge utilisateur utilise le lien entre les tiers et les représentants commerciaux pour calculer la marge de chaque représentant. Étant donné que certains tiers peuvent ne pas avoir de représentant de vente dédié et que certaines tiers peuvent être liés à plusieurs, certains montants peuvent ne pas être inclus dans ce rapport (s'il n'y a pas de représentant de vente), certains peuvent apparaître sur des lignes différentes (pour chaque représentant de vente). +MarginPerSaleRepresentativeWarning=The report of margin per user use the link between third parties and sale representatives to calculate the margin of each salerepresentaive. Because some thirdparties may not have any ddiated sale representative and some thirdparties may be linked to several, some amounts may not be included into this report (if there is no sale representative) and some may appear on different lines (for each sale representative). diff --git a/htdocs/langs/fr_FR/members.lang b/htdocs/langs/fr_FR/members.lang index 38b9cc35245..47e399f1d06 100644 --- a/htdocs/langs/fr_FR/members.lang +++ b/htdocs/langs/fr_FR/members.lang @@ -13,8 +13,6 @@ ListOfValidatedPublicMembers=Liste des adhérents publics validés ErrorThisMemberIsNotPublic=Cet adhérent n'est pas public ErrorMemberIsAlreadyLinkedToThisThirdParty=Un autre adhérent (nom: %s, identifiant : %s) est déjà lié au tiers %s. Supprimer le lien existant d'abord car un tiers ne peut être lié qu'à un seul adhérent (et vice versa). ErrorUserPermissionAllowsToLinksToItselfOnly=Pour des raisons de sécurité, il faut posséder les droits de modification de tous les utilisateurs pour pouvoir lier un adhérent à un utilisateur autre que vous même. -ThisIsContentOfYourCard=Bonjour,

    Ceci est un rappel des informations que nous avons vos concernant. N'hésitez pas à nous contacter en cas d'erreur.

    -CardContent=Contenu de votre fiche adhérent SetLinkToUser=Lier à un utilisateur Dolibarr SetLinkToThirdParty=Lier à un tiers Dolibarr MembersCards=Cartes d'adhérent @@ -108,17 +106,33 @@ PublicMemberCard=Fiche adhérent publique SubscriptionNotRecorded=Adhésion non enregistrée AddSubscription=Créer cotisation ShowSubscription=Afficher adhésion -SendAnEMailToMember=Envoyer email d'information à l'adhérent +# Label of email templates +SendingAnEMailToMember=Sending information email to member +SendingEmailOnAutoSubscription=Sending email on auto registration +SendingEmailOnMemberValidation=Sending email on new member validation +SendingEmailOnNewSubscription=Sending email on new subscription +SendingReminderForExpiredSubscription=Sending reminder for expired subscription +SendingEmailOnCancelation=Sending email on cancelation +# Topic of email templates +YourMembershipRequestWasReceived=Your membership was received. +YourMembershipWasValidated=Your membership was validated +YourSubscriptionWasRecorded=Your new subscription was recorded +SubscriptionReminderEmail=Subscription reminder +YourMembershipWasCanceled=You membership was canceled +CardContent=Contenu de votre fiche adhérent +# Text of email templates +ThisIsContentOfYourMembershipRequestWasReceived=We want to let you know that your membership request was received.

    +ThisIsContentOfYourMembershipWasValidated=We want to let you know that your membership was validated with the following information:

    +ThisIsContentOfYourSubscriptionWasRecorded=We want to let you know that your new subscription was recorded.

    +ThisIsContentOfSubscriptionReminderEmail=We want to let you know thet your subscription is about to expire. We hope you can make a renewal of it.

    +ThisIsContentOfYourCard=This is a remind of the information we get about you. Feel free to contact us if something looks wrong.

    DescADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT=Sujet de l'email reçu en cas d'auto-inscription d'un invité DescADHERENT_AUTOREGISTER_NOTIF_MAIL=Email reçu en cas d'auto-inscription d'un invité -DescADHERENT_AUTOREGISTER_MAIL_SUBJECT=Sujet de l'email envoyé en cas d'auto-inscription d'un invité -DescADHERENT_AUTOREGISTER_MAIL=Email envoyé en cas d'auto-inscription d'un invité -DescADHERENT_MAIL_VALID_SUBJECT=Sujet de l'email de validation adhérent -DescADHERENT_MAIL_VALID=Email de validation adhérent -DescADHERENT_MAIL_COTIS_SUBJECT=Sujet de l'email de validation adhésion -DescADHERENT_MAIL_COTIS=Email de validation d'une adhésion -DescADHERENT_MAIL_RESIL_SUBJECT=Sujet de l'email de résiliation -DescADHERENT_MAIL_RESIL=Email de résiliation +DescADHERENT_EMAIL_TEMPLATE_AUTOREGISTER=Template Email to use to send email to a member on member autosubscription +DescADHERENT_EMAIL_TEMPLATE_MEMBER_VALIDATION=Template EMail to use to send email to a member on member validation +DescADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION=Template Email to use to send email to a member on new subscription recording +DescADHERENT_EMAIL_TEMPLATE_REMIND_EXPIRATION=Template Email to use to send email remind when subscription is about to expire +DescADHERENT_EMAIL_TEMPLATE_CANCELATION=Template Email to use to send email to a member on member cancelation DescADHERENT_MAIL_FROM=Email émetteur pour les mails automatiques DescADHERENT_ETIQUETTE_TYPE=Format pages étiquettes DescADHERENT_ETIQUETTE_TEXT=Texte imprimé sur les planches d'adresses adhérent @@ -177,3 +191,8 @@ NoVatOnSubscription=Pas de TVA sur les adhésions MEMBER_PAYONLINE_SENDEMAIL=E-mail pour avertir des confirmations de validation d'un règlement d'une adhésion (exemple : paiementrecu@exemple.fr) ADHERENT_PRODUCT_ID_FOR_SUBSCRIPTIONS=Produit/Service utilisé pour la ligne de cotisation dans la facture: %s NameOrCompany=Nom ou société +SubscriptionRecorded=Subscription recorded +NoEmailSentToMember=No email sent to member +EmailSentToMember=Email sent to member at %s +SendReminderForExpiredSubscriptionTitle=Send reminder by email for expired subscription +SendReminderForExpiredSubscription=Send reminder by email to members when subscription is about to expire (parameter is number of days before end of subscription to send the remind) diff --git a/htdocs/langs/fr_FR/modulebuilder.lang b/htdocs/langs/fr_FR/modulebuilder.lang index ca1aa0ab1d4..43315ae45d6 100644 --- a/htdocs/langs/fr_FR/modulebuilder.lang +++ b/htdocs/langs/fr_FR/modulebuilder.lang @@ -94,3 +94,4 @@ YouCanUseTranslationKey=You can use here a key that is the translation key found DropTableIfEmpty=(Supprimer la table si vide) TableDoesNotExists=La table %s n'existe pas TableDropped=La table %s a été supprimée +InitStructureFromExistingTable=Build the structure array string of an existing table diff --git a/htdocs/langs/fr_FR/other.lang b/htdocs/langs/fr_FR/other.lang index 610a4f59ab3..d34ca3c3498 100644 --- a/htdocs/langs/fr_FR/other.lang +++ b/htdocs/langs/fr_FR/other.lang @@ -78,8 +78,8 @@ LinkedObject=Objet lié NbOfActiveNotifications=Nombre de notifications (nb de destinataires emails) PredefinedMailTest=__(Hello)__,\nCeci est un mail de test envoyé à __EMAIL__.\nLes deux lignes sont séparées par un saut de ligne.\n\n__USER_SIGNATURE__ PredefinedMailTestHtml=__(Hello)__\nCeci est un message de test (le mot test doit être en gras).
    Les 2 lignes sont séparées par un retour à la ligne.

    __SIGNATURE__ -PredefinedMailContentSendInvoice=__(Hello)__\n\nVeuillez trouver, ci-joint, la facture __REF__\n\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ -PredefinedMailContentSendInvoiceReminder=__(Hello)__\n\nNous voulons porter à votre connaissance le fait que la facture __REF__ semble non payée. Aussi, voici la facture à nouveau en pièce jointe pour rappel.\n\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ +PredefinedMailContentSendInvoice=__(Hello)__\n\nVeuillez trouver, ci-joint, la facture __REF__\n\nVoici le lien pour un paiement en ligne au cas ou celle-ci n'aurait pas encore été payé:\n\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ +PredefinedMailContentSendInvoiceReminder=__(Hello)__\n\nNous voulons porter à votre connaissance le fait que la facture __REF__ semble non payée. Aussi, voici la facture à nouveau en pièce jointe pour rappel.\n\nVoici le lien pour un paiement en ligne:\n__ONLINE_PAYMENT_URL__\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ PredefinedMailContentSendProposal=__(Hello)__\n\nVeuillez trouver, ci-joint, la proposition commerciale __PREF__\n\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ PredefinedMailContentSendSupplierProposal=__(Hello)__\n\nVeuillez trouver, ci-joint, une demande de prix avec la référence __REF__\n\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ PredefinedMailContentSendOrder=__(Hello)__\n\nVeuillez trouver, ci-joint, la commande __REF__\n\n\n__(Sincerely)__\n\n__USER_SIGNATURE__ diff --git a/htdocs/langs/fr_FR/paypal.lang b/htdocs/langs/fr_FR/paypal.lang index e4bd2341c06..dded657e3d6 100644 --- a/htdocs/langs/fr_FR/paypal.lang +++ b/htdocs/langs/fr_FR/paypal.lang @@ -14,7 +14,7 @@ PaypalModeOnlyPaypal=PayPal seul ONLINE_PAYMENT_CSS_URL=URL optionnelle de la feuille de style CSS sur la page de paiement en ligne ThisIsTransactionId=Voici l'identifiant de la transaction: %s PAYPAL_ADD_PAYMENT_URL=Ajouter l'URL de paiement Paypal lors de l'envoi d'un document par email -PredefinedMailContentLink=Vous pouvez cliquer sur le lien sécurisé ci-dessous pour effectuer votre paiement (Paypal) si ce dernier n'a pas encore été fait.\n\n%s\n\n +PredefinedMailContentLink=Vous pouvez cliquer sur le lien sécurisé ci-dessous pour effectuer votre paiement si ce dernier n'a pas encore été fait:

    %s

    YouAreCurrentlyInSandboxMode=Vous travaillez actuellement dans le mode "bac à sable" de %s NewOnlinePaymentReceived=Nouveau paiement en ligne reçu NewOnlinePaymentFailed=Nouvelle tentative de paiement en ligne échouée @@ -30,3 +30,6 @@ ErrorCode=Code erreur ErrorSeverityCode=Code d'erreur sévérité OnlinePaymentSystem=Système de paiement en ligne PaypalLiveEnabled=Paypal live activé (sinon mode test/bac à sable) +PaypalImportPayment=Import Paypal payments +PostActionAfterPayment=Post actions after payments +ARollbackWasPerformedOnPostActions=A rollback was performed on all Post actions. You must complete post actions manually if they are necessary. diff --git a/htdocs/langs/fr_FR/products.lang b/htdocs/langs/fr_FR/products.lang index 3a94e1cc0c6..ee33f184c92 100644 --- a/htdocs/langs/fr_FR/products.lang +++ b/htdocs/langs/fr_FR/products.lang @@ -27,6 +27,7 @@ ProductAccountancySellExportCode=Code comptable (vente à l'export) ProductOrService=Produit ou Service ProductsAndServices=Produits et Services ProductsOrServices=Produits ou Services +ProductsPipeServices=Products | Services ProductsOnSaleOnly=Uniquement produits en vente ProductsOnPurchaseOnly=Produits seulement en achat ProductsNotOnSell=Produits hors vente et hors achat @@ -122,6 +123,7 @@ ConfirmDeleteProductLine=Êtes-vous sûr de vouloir effacer cette ligne produit ProductSpecial=Special QtyMin=Quantité minimum PriceQtyMin=Prix quantité min. (sans remise) +PriceQtyMinCurrency=Price for this min. qty (w/o discount) (currency) VATRateForSupplierProduct=Taux TVA (pour ce produit/fournisseur) DiscountQtyMin=Remise par défaut quantité min. NoPriceDefinedForThisSupplier=Aucun prix/qté défini pour ce fournisseur/produit diff --git a/htdocs/langs/fr_FR/projects.lang b/htdocs/langs/fr_FR/projects.lang index acfe6547395..7884e4eb696 100644 --- a/htdocs/langs/fr_FR/projects.lang +++ b/htdocs/langs/fr_FR/projects.lang @@ -10,13 +10,13 @@ PrivateProject=Contacts projet ProjectsImContactFor=Projets dont je suis un contact explicite AllAllowedProjects=Tout projet que je peux lire (les miens + public) AllProjects=Tous les projets -MyProjectsDesc=Cette vue est limitée aux projets dont vous êtres contact +MyProjectsDesc=This view is limited to projects you are a contact for ProjectsPublicDesc=Cette vue présente tous les projets pour lesquels vous êtes habilité à avoir une visibilité. TasksOnProjectsPublicDesc=Cette vue affiche toutes les tâches de projets selon vos permissions utilisateur ProjectsPublicTaskDesc=Cette vue présente tous les projets et tâches pour lesquels vous êtes habilité à avoir une visibilité. ProjectsDesc=Cette vue présente tous les projets (vos habilitations vous offrant une vue exhaustive). TasksOnProjectsDesc=Cette vue présente toutes les tâches sur tous les projets (vos permissions d'utilisateur vous accordent la permission de voir tout). -MyTasksDesc=Cette vue est limitée au projets ou tâches dont vous êtes contact +MyTasksDesc=This view is limited to projects or tasks you are a contact for OnlyOpenedProject=Seuls les projets ouverts sont visibles (les projets à l'état brouillon ou fermé ne sont pas visibles). ClosedProjectsAreHidden=Les projets fermés ne sont pas visible. TasksPublicDesc=Cette vue présente tous les projets et tâches pour lesquels vous êtes habilité à avoir une visibilité. @@ -55,6 +55,7 @@ TasksOnOpenedProject=Tâches sur les projets ouverts WorkloadNotDefined=Charge de travail non définie NewTimeSpent=Temps consommés MyTimeSpent=Mon consommé +BillTime=Bill the time spent Tasks=Tâches Task=Tâche TaskDateStart=Date de début de tâche @@ -91,6 +92,7 @@ ListDonationsAssociatedProject=Liste des dons associés au projet ListVariousPaymentsAssociatedProject=Liste des frais divers liés au projet ListActionsAssociatedProject=Liste des événements associés au projet ListTaskTimeUserProject=Liste du temps consommé sur les tâches d'un projet +ListTaskTimeForTask=List of time consumed on task ActivityOnProjectToday=Activité projet aujourd'hui ActivityOnProjectYesterday=Activité projet hier ActivityOnProjectThisWeek=Activité sur les projets cette semaine @@ -98,6 +100,7 @@ ActivityOnProjectThisMonth=Activité sur les projets ce mois ActivityOnProjectThisYear=Activité sur les projets cette année ChildOfProjectTask=Fille du projet/tâche ChildOfTask=Enfant de la tâche +TaskHasChild=Task has child NotOwnerOfProject=Non responsable de ce projet privé AffectedTo=Affecté à CantRemoveProject=Ce projet ne peut être supprimé car il est référencé par de nombreux objets (factures, commandes ou autre). voir la liste sur l'onglet Reférents. @@ -137,6 +140,7 @@ ProjectReportDate=Reporter les dates des tâches en fonction de la date de dépa ErrorShiftTaskDate=Une erreur s'est produite dans le report des dates des tâches. ProjectsAndTasksLines=Projets et tâches ProjectCreatedInDolibarr=Projet %s créé +ProjectValidatedInDolibarr=Project %s validated ProjectModifiedInDolibarr=Projet %s modifié TaskCreatedInDolibarr=Tâche %s créée TaskModifiedInDolibarr=Tâche %s modifiée @@ -215,8 +219,10 @@ AllowToLinkFromOtherCompany=Allow to link project from other company

    S LatestProjects=Les %s derniers projets LatestModifiedProjects=Les %s derniers projets modifiés OtherFilteredTasks=Autres tâches filtrées -NoAssignedTasks=Aucune tâche assignée (assignez-vous le projet/tâche depuis la liste déroulante en haut pour pouvoir saisir du temps dessus) +NoAssignedTasks=No assigned tasks (assign project/tasks the current user from the top select box to enter time on it) # Comments trans AllowCommentOnTask=Autoriser les utilisateurs à ajouter des commentaires sur les tâches AllowCommentOnProject=Autoriser les commentaires utilisateur sur les projets - +DontHavePermissionForCloseProject=You do not have permissions to close the project %s +DontHaveTheValidateStatus=The project %s must be open to be closed +RecordsClosed=%s project(s) closed diff --git a/htdocs/langs/fr_FR/propal.lang b/htdocs/langs/fr_FR/propal.lang index f9b12c8c607..089a6a9ddbc 100644 --- a/htdocs/langs/fr_FR/propal.lang +++ b/htdocs/langs/fr_FR/propal.lang @@ -33,6 +33,7 @@ PropalStatusSigned=Signée (à facturer) PropalStatusNotSigned=Non signée (fermée) PropalStatusBilled=Facturée PropalStatusDraftShort=Brouillon +PropalStatusValidatedShort=Validé PropalStatusClosedShort=Fermée PropalStatusSignedShort=Signée PropalStatusNotSignedShort=Non signée diff --git a/htdocs/langs/fr_FR/salaries.lang b/htdocs/langs/fr_FR/salaries.lang index f6f76d690bb..6e36ddca890 100644 --- a/htdocs/langs/fr_FR/salaries.lang +++ b/htdocs/langs/fr_FR/salaries.lang @@ -15,3 +15,4 @@ THMDescription=Cette valeur peut être utilisé pour calculer le coût horaire c TJMDescription=Cette valeur est actuellement seulement une information et n'est utilisé pour aucun calcul LastSalaries=Les %s derniers règlements de salaires AllSalaries=Tous les règlements de salaires +SalariesStatistics=Statistiques salaires diff --git a/htdocs/langs/fr_FR/stripe.lang b/htdocs/langs/fr_FR/stripe.lang index 0ec18ab7f03..537ce42a4f3 100644 --- a/htdocs/langs/fr_FR/stripe.lang +++ b/htdocs/langs/fr_FR/stripe.lang @@ -35,6 +35,30 @@ NewStripePaymentReceived=Nouveau paiement Stripe reçu NewStripePaymentFailed=Nouveau paiement Stripe tenté mais en échec STRIPE_TEST_SECRET_KEY=Clé secrète de test STRIPE_TEST_PUBLISHABLE_KEY=Clé plublique de test +STRIPE_TEST_WEBHOOK_KEY=Webhook test key STRIPE_LIVE_SECRET_KEY=Clé secrète live STRIPE_LIVE_PUBLISHABLE_KEY=Clé plublique live +STRIPE_LIVE_WEBHOOK_KEY=Webhook live key +ONLINE_PAYMENT_WAREHOUSE=Stock to use for stock decrease when online payment is done
    (TODO When option to decrease stock is done on an action on invoice and the online payment generate itself the invoice ?) StripeLiveEnabled=Mode live activé (sinon mode test/bac a sable) +StripeImportPayment=Import Stripe payments +ExampleOfTestCreditCard=Example of credit card for test: %s (valid), %s (error CVC), %s (expired), %s (charge fails) +StripeGateways=Stripe gateways +OAUTH_STRIPE_TEST_ID=Stripe Connect Client ID (ca_...) +OAUTH_STRIPE_LIVE_ID=Stripe Connect Client ID (ca_...) +BankAccountForBankTransfer=Bank account for fund payouts +StripeAccount=Stripe account +StripeChargeList=List of Stripe charges +StripeCustomerId=Stripe customer id +StripePaymentModes=Stripe payment modes +LocalID=Local ID +StripeID=Stripe ID +NameOnCard=Name on card +CardNumber=Card Number +ExpiryDate=Expiry Date +CVN=CVN +DeleteACard=Delete Card record +ConfirmDeleteCard=Are you sure you want to delete this Card record? +CreateCustomerOnStripe=Create customer on Stripe +CreateCardOnStripe=Create card on Stripe +ShowInStripe=Show in Stripe diff --git a/htdocs/langs/fr_FR/ticketsup.lang b/htdocs/langs/fr_FR/ticketsup.lang new file mode 100644 index 00000000000..f8edb3e2dd4 --- /dev/null +++ b/htdocs/langs/fr_FR/ticketsup.lang @@ -0,0 +1,300 @@ +# en_US lang file for module ticketsup +# Copyright (C) 2013 Jean-François FERRY +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# +# Generic +# + +Module56000Name=Billets +Module56000Desc=Système de ticket pour de l'assistance ou une demande d'accompagnement + +Permission56001=Voir tickets +Permission56002=Modifier des billets +Permission56003=Supprimer tickets +Permission56004=Gérer les tickets +Permission56005=See tickets of all third parties (not effective for external users, always be limited to the thirdparty they depend on) + +TicketsupDictType=Type de ticket +TicketsupDictCategory=Catégories de tickets +TicketsupDictSeverity=Sévérité des tickets +TicketTypeShortBUGSOFT=Dysfonctionnement logiciel +TicketTypeShortBUGHARD=Dysfonctionnement matériel +TicketTypeShortCOM=Question commerciale +TicketTypeShortINCIDENT=Demande d'assistance +TicketTypeShortPROJET=Projet +TicketTypeShortOTHER=Autre + +TicketSeverityShortLOW=Faible +TicketSeverityShortNORMAL=Normal +TicketSeverityShortHIGH=Élevé +TicketSeverityShortBLOCKING=Critique/Bloquant + +ErrorBadEmailAddress=Field '%s' incorrect +MenuTicketsupMyAssign=Mes tickets +MenuTicketsupMyAssignNonClosed=My open tickets +MenuListNonClosed=Open tickets + +TypeContact_ticketsup_internal_CONTRIBUTOR=Contributeur +TypeContact_ticketsup_internal_SUPPORTTEC=Utilisateur assigné +TypeContact_ticketsup_external_SUPPORTCLI=Customer contact / incident tracking +TypeContact_ticketsup_external_CONTRIBUTOR=External contributor + +Notify_TICKETMESSAGE_SENTBYMAIL=Envoyée la réponse par email + +# Status +NotRead=Non lu +Read=Lu +Answered=Répondu +Assigned=assigné +InProgress=En cours +Waiting=En attente +Closed=Fermé +Deleted=Supprimé + +# Dict +Type=Type +Category=Catégorie +Severity=Sévérité + +# Email templates +MailToSendTicketsupMessage=To send email from ticket message + +# +# Admin page +# +TicketsupSetup=Ticket module setup +TicketSupSettings=Paramètres +TicketsupSetupPage= +TicketsupPublicAccess=A public interface requiring no identification is available at the following url +TicketsupSetupDictionaries=The type of application categories and severity level are configurable from dictionaries +TicketParamModule=Module variable setup +TicketParamMail=Email setup +TicketEmailNotificationFrom=Notification email from +TicketEmailNotificationFromHelp=Used into ticket message answer by example +TicketEmailNotificationTo=Notifications email to +TicketEmailNotificationToHelp=Send email notifications to this address. +TicketNewEmailBodyLabel=Text message sent after creating a ticket (public interface) +TicketNewEmailBodyHelp=The text specified here will be inserted into the email confirming the creation of a new ticket from the public interface. Information on the consultation of the ticket are automatically added. +TicketParamPublicInterface=Configuration de l'interface publique\n +TicketsEmailMustExist=Une adresse mail existante est requise pour créer un ticket +TicketsEmailMustExistHelp=Pour accéder à l'interface publique et créer un nouveau ticket, votre compte doit déjà être existant. +PublicInterface=Interface publique +TicketUrlPublicInterfaceLabelAdmin=URL interface publique +TicketUrlPublicInterfaceHelpAdmin=It is possible to define an alias to the web server and thus make available the public interface to another IP address. +TicketPublicInterfaceTextHomeLabelAdmin=Texte de bienvenu dans l'interface publique +TicketPublicInterfaceTextHome=You can create a support ticket or view existing from its identifier tracking ticket. +TicketPublicInterfaceTextHomeHelpAdmin=The text defined here will appear on the home page of the public interface. +TicketPublicInterfaceTopicLabelAdmin=Titre de l'interface +TicketPublicInterfaceTopicHelp=This text will appear as the title of the public interface. +TicketPublicInterfaceTextHelpMessageLabelAdmin=Help text to the message entry +TicketPublicInterfaceTextHelpMessageHelpAdmin=This text will appear above the message input area of the user. +ExtraFieldsTicketSup=Extra attributes +TicketSupCkEditorEmailNotActivated=HTML editor is not activated. Please put FCKEDITOR_ENABLE_MAIL contant equal to 1 +TicketsDisableEmail=Do not send ticket creation or message send emails +TicketsDisableEmailHelp=By default, emails are sent when new tickets or messages created. Enable this option to disable *all* email notifications +TicketsLogEnableEmail=Activer l'alerte par mail +TicketsLogEnableEmailHelp=A chaque changements, un email sera envoyé à chaque contact associé à ce ticket +TicketParams=Params +TicketsShowModuleLogo=Display the logo of the module in the public interface +TicketsShowModuleLogoHelp=Enable this option to hide the logo module in the pages of the public interface +TicketsShowCompanyLogo=Afficher le logo de la société dans l'interface publique +TicketsShowCompanyLogoHelp=Enable this option to hide the logo of the main company in the pages of the public interface +TicketsEmailAlsoSendToMainAddress=Also send notification to main email address +TicketsEmailAlsoSendToMainAddressHelp=Enable this option to send an email to "Notification email from" address (see setup below) +TicketsShowExtrafieldsIntoPublicArea=Show Extras fields in the public interface +TicketsShowExtrafieldsIntoPublicAreaHelp=When this option is enabled, additional attributes defined on the tickets will be shown in the public interface of ticket creation. +TicketsLimitViewAssignedOnly=Restrict the display to tickets assigned to the current user (not effective for external users, always be limited to the thirdparty they depend on) +TicketsLimitViewAssignedOnlyHelp=Seuls les tickets affectés à l'utilisateur actuel seront visibles. Ne s'applique pas à un utilisateur disposant de droits de gestion des tickets. +TicketsActivatePublicInterface=Activer l'interface publique +TicketsActivatePublicInterfaceHelp=Public interface allow any visitors to create tickets. +TicketsAutoAssignTicket=Automatically assign the user who created the ticket +TicketsAutoAssignTicketHelp=When creating a ticket, the user can be automatically assigned to the ticket. +TicketSupNumberingModules=Tickets numbering module +TicketNotifyTiersAtCreation=Notify thirdparty at creation + +# +# About page +# +About=À propos +TicketSupAbout=À propos du module Ticket +TicketSupAboutModule=The development of this module has been initiated by the company Libr&thic. +TicketSupAboutModuleHelp=You can get help by using the contact form on the website librethic.io +TicketSupAboutModuleImprove=Feel free to suggest improvements! Please visit the project page on Doliforge website to report bugs and add tasks. +TicketSupAboutModuleThanks=Thanks to nwa who creates icons for this module./ + +# +# Index & list page +# +TicketsIndex=Accueil +TicketList=Liste des tickets +TicketAssignedToMeInfos=Cette page présente la liste des tickets assignés à l'utilisateur actuel +NoTicketsFound=Aucun ticket trouvé +TicketViewAllTickets=Voir tous les tickets +TicketViewNonClosedOnly=View only open tickets +TicketStatByStatus=Tickets by status + +# +# Ticket card +# +Ticketsup=Incident ticket +TicketCard=Ticket card +CreateTicket=Créez un nouveau billet +EditTicket=Modifier un billet +TicketsManagement=Gestion de billets +CreatedBy=Créé par +NewTicket=Nouveau ticket +SubjectAnswerToTicket=Réponse ticket +TicketTypeRequest=Request type +TicketCategory=Catégorie +SeeTicket=Voir le ticket +TicketMarkedAsRead=Le ticket a été marqué comme lu +TicketReadOn=Read on +TicketCloseOn=Fermé le +MarkAsRead=Mark ticket as read +TicketMarkedAsReadButLogActionNotSaved=Ticket marked as closed but no action saved +TicketHistory=Historique de billets +AssignUser=Assign to user +TicketAssigned=Le ticket est à présent assigné +TicketChangeType=Change type +TicketChangeCategory=Changer la catégorie +TicketChangeSeverity=Changer la sévérité +TicketAddMessage=Ajouter un message +TicketEditProperties=Éditer les propriétés +AddMessage=Ajouter un message +MessageSuccessfullyAdded=Ticket créé +TicketMessageSuccessfullyAdded=Message ajouté avec succès +TicketMessagesList=Message list +NoMsgForThisTicket=Pas de message pour ce ticket +Properties=Classification +LatestNewTickets=Latest %s newest tickets (not read) +TicketSeverity=Sévérité +ShowTicket=Voir le ticket +RelatedTickets=Tickets liés +TicketAddIntervention=Créer intervention +CloseTicket=Fermer le ticket +CloseATicket=Fermer un billet +ConfirmCloseAticket=Confirmer la fermeture du ticket +ConfirmDeleteTicket=Confirmez la suppression du ticket +TicketDeletedSuccess=Ticket supprimé avec succès +TicketMarkedAsClosed=Ticket indiqué fermé +TicketMarkedAsClosedButLogActionNotSaved=Ticket marqué comme fermé mais pas commenté ! +TicketDurationAuto=Durée calculée +TicketDurationAutoInfos=Duration calculated automatically from intervention related +TicketUpdated=Ticket mis à jour +SendMessageByEmail=Envoyer ce message par email +TicketNewMessage=Nouveau message +ErrorMailRecipientIsEmptyForSendTicketMessage=Recipient is empty. No email send +TicketGoIntoContactTab=Please go into "Contacts" tab to select them +TicketMessageMailIntro=Introduction +TicketMessageMailIntroHelp=This text is added only at the beginning of the email and will not be saved. +TicketMessageMailIntroLabelAdmin=Introduction to the message when sending email +TicketMessageMailIntroText=

    Bonjour Une nouvelle réponse a été ajoutée à un ticket que vous suivez. Voici le message : +TicketMessageMailIntroHelpAdmin=This text will be inserted before the text of the response to a ticket. +TicketMessageMailSignature=Signature +TicketMessageMailSignatureHelp=This text is added only at the end of the email and will not be saved. +TicketMessageMailSignatureText=

    Cordialement,

    --

    +TicketMessageMailSignatureLabelAdmin=Signature of response email +TicketMessageMailSignatureHelpAdmin=This text will be inserted after the response message. +TicketMessageHelp=Only this text will be saved in the message list on ticket card. +TicketMessageSubstitutionReplacedByGenericValues=Substitutions variables are replaced by generic values. +TicketTimeToRead=Time elapsed before ticket read +TicketContacts=Contacts ticket +TicketDocumentsLinked=Documents liés +ConfirmReOpenTicket=Voulez-vous ré-ouvrir ce ticket ? +TicketMessageMailIntroAutoNewPublicMessage=A new message was posted on the ticket with the subject %s : +TicketAssignedToYou=Ticket assigned +TicketAssignedEmailBody=You have been assigned the ticket #%s by %s +MarkMessageAsPrivate=Mark message as private +TicketMessagePrivateHelp=This message will not display to external users +TicketEmailOriginIssuer=Issuer at origin of the tickets +InitialMessage=Message initial +LinkToAContract=Link to a contract +TicketSupPleaseSelectAContract=Sélectionner un contrat +UnableToCreateInterIfNoSocid=Can not create an intervention when no third party are defined +TicketMailExchanges=Mail exchanges +TicketInitialMessageModified=Message initial modifié +TicketMessageSuccesfullyUpdated=Message successfully updated +TicketChangeStatus=Changer l'état +TicketConfirmChangeStatus=Confirm the status change : %s ? +TicketLogStatusChanged=Status changed : %s to %s +TicketNotNotifyTiersAtCreate=Not notify company at create + +# +# Logs +# +TicketLogMesgReadBy=Ticket read by %s +NoLogForThisTicket=No log for this ticket yet +TicketLogAssignedTo=Ticket assigned to %s +TicketAssignedButLogActionNotSaved=Ticket assigned but no log saved ! +TicketLogPropertyChanged=Change classification : from %s to %s +TicketLogClosedBy=Ticket clôt par %s +TicketLogProgressSetTo=Progression de %s pour-cent appliquée +TicketLogReopen=Ticket ré-ouvert + +# +# Public pages +# +TicketSystem=Gestionnaire de tickets +ShowListTicketWithTrackId=Display ticket list from track ID +ShowTicketWithTrackId=Display ticket from track ID +TicketPublicDesc=You can create a support ticket or check from an existing ID. +YourTicketSuccessfullySaved=Le billet a été enregistré avec succès +MesgInfosPublicTicketCreatedWithTrackId=A new ticket has been created with ID %s. +PleaseRememberThisId=Merci de conserver le code de suivi du ticket, il vous sera peut-être nécessaire ultérieurement +TicketNewEmailSubject=Ticket creation confirmation +TicketNewEmailSubjectCustomer=New support ticket +TicketNewEmailBody=Ceci est un message automatique pour confirmer l'enregistrement de votre ticket. +TicketNewEmailBodyCustomer=This is an automatic email to confirm a new ticket has just been created into your account. +TicketNewEmailBodyInfosTicket=Information for monitoring the ticket +TicketNewEmailBodyInfosTrackId=Ticket tracking number : %s +TicketNewEmailBodyInfosTrackUrl=You can view the progress of the ticket by clicking the link above. +TicketNewEmailBodyInfosTrackUrlCustomer=You can view the progress of the ticket in the specific interface by clicking the following link +TicketEmailPleaseDoNotReplyToThisEmail=Merci de ne pas répondre directement à ce courriel ! Utilisez le lien pour répondre via l'interface. +TicketPublicInfoCreateTicket=This form allows you to record a trouble ticket in our management system. +TicketPublicPleaseBeAccuratelyDescribe=Veuillez décrire avec précision le problème. Fournissez le plus d'informations possibles pour nous permettre d'identifier correctement votre demande. +TicketPublicMsgViewLogIn=Merci d'entrer le code de suivi du ticket +TicketTrackId=Tracking ID +OneOfTicketTrackId=One of yours tracking ID +ErrorTicketNotFound=Ticket with tracking ID %s not found ! +Subject=Sujet +ViewTicket=Voir le ticket +ViewMyTicketList=Voir la liste de mes tickets +ErrorEmailMustExistToCreateTicket=Erreur : Email introuvable dans notre base de données +TicketNewEmailSubjectAdmin=Nouveau ticket créé +TicketNewEmailBodyAdmin=

    Ticket has just been created with ID #%s, see informations :

    +SeeThisTicketIntomanagementInterface=See ticket in management interface +TicketPublicInterfaceForbidden=Accès à cette partie : interdit + +# notifications +TicketNotificationEmailSubject=Ticket %s updated +TicketNotificationEmailBody=This is an automatic message to notify you that ticket %s has just been updated +TicketNotificationRecipient=Notification recipient +TicketNotificationLogMessage=Log message +TicketNotificationEmailBodyInfosTrackUrlinternal=View ticket into interface +TicketNotificationNumberEmailSent=Email de notification envoyé: %s + + +# +# Boxes +# +BoxLastTicketsup=Latest created tickets +BoxLastTicketsupDescription=Latest %s created tickets +BoxLastTicketsupContent= +BoxLastTicketsupNoRecordedTickets=Pas de ticket non lu +BoxLastModifiedTicketsup=Latest modified tickets +BoxLastModifiedTicketsupDescription=Latest %s modified tickets +BoxLastModifiedTicketsupContent= +BoxLastModifiedTicketsupNoRecordedTickets=Pas de tickets modifiés récemment diff --git a/htdocs/langs/fr_FR/trips.lang b/htdocs/langs/fr_FR/trips.lang index b8151ca57d8..909aee4ec55 100644 --- a/htdocs/langs/fr_FR/trips.lang +++ b/htdocs/langs/fr_FR/trips.lang @@ -12,7 +12,7 @@ ShowTrip=Afficher la note de frais NewTrip=Nouvelle note de frais LastExpenseReports=Les %s dernières notes de frais AllExpenseReports=Toutes les notes de frais -CompanyVisited=Société/organisation visitée +CompanyVisited=Company/organization visited FeesKilometersOrAmout=Montant ou kilomètres DeleteTrip=Supprimer les notes de frais / déplacements ConfirmDeleteTrip=Êtes-vous sûr de vouloir supprimer cette note de frais ? @@ -21,17 +21,17 @@ ListToApprove=En attente d'approbation ExpensesArea=Espace notes de frais ClassifyRefunded=Classer 'Remboursé' ExpenseReportWaitingForApproval=Une nouvelle note de frais a été soumise pour approbation -ExpenseReportWaitingForApprovalMessage=Une nouvelle note de frais a été enregistrée et est en attente d'approbation.\n- Utilisateur : %s\n- Période : %s\nCliquez ici pour afficher la note de frais: %s. +ExpenseReportWaitingForApprovalMessage=A new expense report has been submitted and is waiting for approval.
    - User: %s
    - Period: %s
    Click here to validate: %s ExpenseReportWaitingForReApproval=Une note de frais a été resoumise pour approbation -ExpenseReportWaitingForReApprovalMessage=Une note de frais a été enregistrée et est en attente à nouveau d'approbation.\nLe %s, vous avez refusé d'approuver la note de frais pour la raison suivante: %s\nUne nouvelle version a été soumise et attend d'être approuvé.\\n\n- Utilisateur : %s\n- Période : %s\nCliquez ici pour valider la note de frais %s +ExpenseReportWaitingForReApprovalMessage=An expense report has been submitted and is waiting for re-approval.
    The %s, you refused to approve the expense report for this reason: %s.
    A new version has been proposed and waiting for your approval.
    - User: %s
    - Period: %s
    Click here to validate: %s ExpenseReportApproved=Une note de frais a été approuvée -ExpenseReportApprovedMessage=La note de frais %s a été approuvée.\n- Utilisateur : %s\n- Approuvée par : %s\nCliquez ici pour afficher la note de frais %s +ExpenseReportApprovedMessage=The expense report %s was approved.
    - User: %s
    - Approved by: %s
    Click here to show the expense report: %s ExpenseReportRefused=Une note de frais a été refusée -ExpenseReportRefusedMessage=La note de frais %s a été refusée.\n- Utilisateur : %s\n- Refusée par : %s\n- Motif du refus : %s\nCliquez ici pour afficher la note de frais: %s +ExpenseReportRefusedMessage=The expense report %s was refused.
    - User: %s
    - Refused by: %s
    - Motive for refusal: %s
    Click here to show the expense report: %s ExpenseReportCanceled=Une note de frais a été annulée -ExpenseReportCanceledMessage=La note de frais %s a été annulée.\n- Utilisateur : %s\n- Annulée par : %s\n- Motif de l'annulation :%s\nCliquez ici pour afficher la note de frais %s. +ExpenseReportCanceledMessage=The expense report %s was canceled.
    - User: %s
    - Canceled by: %s
    - Motive for cancellation: %s
    Click here to show the expense report: %s ExpenseReportPaid=Une note de frais a été réglée -ExpenseReportPaidMessage=La note de frais %s a été réglée.\n- Utilisateur : %s\n- Réglée par : %s\nCliquez ici pour afficher la note de frais %s +ExpenseReportPaidMessage=The expense report %s was paid.
    - User: %s
    - Paid by: %s
    Click here to show the expense report: %s TripId=Id note de frais AnyOtherInThisListCanValidate=Personne à informer pour la validation. TripSociete=Information société @@ -74,6 +74,7 @@ EX_CAM_VP=Entretien et réparation DefaultCategoryCar=Mode de déplacement par défaut DefaultRangeNumber=Numéro de plage par défaut +Error_EXPENSEREPORT_ADDON_NotDefined=Error, the rule for expense report numbering ref was not defined into setup of module 'Expense Report' ErrorDoubleDeclaration=Vous avez déclaré une autre note de frais dans une période similaire. AucuneLigne=Aucune note de frais déclarée diff --git a/htdocs/langs/fr_FR/users.lang b/htdocs/langs/fr_FR/users.lang index 2b7da2d4507..18fe319ee95 100644 --- a/htdocs/langs/fr_FR/users.lang +++ b/htdocs/langs/fr_FR/users.lang @@ -69,8 +69,8 @@ InternalUser=Utilisateur interne ExportDataset_user_1=Utilisateurs Dolibarr et attributs DomainUser=Utilisateur du domaine %s Reactivate=Réactiver -CreateInternalUserDesc=Ce formulaire permet de créer un utilisateur interne à votre société/institution. Pour créer un utilisateur externe (client, fournisseur, ...), utilisez le bouton 'Créer compte utilisateur' qui se trouve sur la fiche du contact du tiers. -InternalExternalDesc=Un utilisateur interne est un utilisateur qui fait partie de votre société/institution.
    Un utilisateur externe est un compte utilisateur ouvert à un contact de tiers (client, fournisseur,...).

    Dans les deux cas, les permissions déterminent les accès aux fonctionnalités de Dolibarr. Aussi, les utilisateurs externes peuvent avoir un interface différent des utilisateurs internes (Voir Accueil > configuration > Affichage) +CreateInternalUserDesc=This form allows you to create an user internal to your company/organization. To create an external user (customer, supplier, ...), use the button 'Create Dolibarr user' from third party's contact card. +InternalExternalDesc=An internal user is a user that is part of your company/organization.
    An external user is a customer, supplier or other.

    In both cases, permissions defines rights on Dolibarr, also external user can have a different menu manager than internal user (See Home - Setup - Display) PermissionInheritedFromAGroup=La permission est accordée car héritée d'un groupe auquel appartient l'utilisateur. Inherited=Hérité UserWillBeInternalUser=L'utilisateur créé sera un utilisateur interne (car non lié à un tiers en particulier) @@ -93,6 +93,7 @@ NameToCreate=Nom du tiers à créer YourRole=Vos rôles YourQuotaOfUsersIsReached=Votre quota d'utilisateurs actifs est atteint ! NbOfUsers=Nombre d'utilisateurs +NbOfPermissions=Nb of permissions DontDowngradeSuperAdmin=Seul un superadministrateur peut rétrograder un superadministrateur HierarchicalResponsible=Responsable hiérarchique HierarchicView=Vue hiérarchique diff --git a/htdocs/langs/fr_FR/withdrawals.lang b/htdocs/langs/fr_FR/withdrawals.lang index 8d4b3ab1177..f1b42a988da 100644 --- a/htdocs/langs/fr_FR/withdrawals.lang +++ b/htdocs/langs/fr_FR/withdrawals.lang @@ -1,8 +1,8 @@ # Dolibarr language file - Source file is en_US - withdrawals CustomersStandingOrdersArea=Espace Ordres de prélèvement SuppliersStandingOrdersArea=Espace ordre de virements -StandingOrders=Ordres de prélèvement -StandingOrder=Ordre de prélèvement +StandingOrdersPayment=Ordres de prélèvement +StandingOrderPayment=Ordre de prélèvement NewStandingOrder=Nouveau prélèvement StandingOrderToProcess=À traiter WithdrawalsReceipts=Bons de prélèvements @@ -26,7 +26,7 @@ LastWithdrawalReceipt=Les %s derniers bons de prélèvements MakeWithdrawRequest=Faire une demande de prélèvement WithdrawRequestsDone=%s demandes de prélèvements enregistrées ThirdPartyBankCode=Code banque du tiers -NoInvoiceCouldBeWithdrawed=Aucune facture percevable, prélevée avec succès. Vérifiez que les factures sont sur des sociétés dont le compte bancaire par défaut est correctement renseigné. +NoInvoiceCouldBeWithdrawed=No invoice withdrawed with success. Check that invoices are on companies with a valid default BAN and that BAN has a RUM with mode %s. ClassCredited=Classer crédité ClassCreditedConfirm=Êtes-vous sûr de vouloir classer ce bon de prélèvement comme crédité sur votre compte bancaire ? TransData=Date de transmission @@ -78,7 +78,7 @@ ThisWillAlsoAddPaymentOnInvoice=Cette action enregistrera les règlements des fa StatisticsByLineStatus=Statistiques par statut des lignes RUM=RUM RUMLong=Référence Unique de Mandat -RUMWillBeGenerated=Le numéro de RUM sera généré une fois les informations de compte bancaire sont enregistrées +RUMWillBeGenerated=If empty, UMR number will be generated once bank account information are saved WithdrawMode=Mode de prélévement (FRST ou RECUR) WithdrawRequestAmount=Montant de la demande de prélèvement WithdrawRequestErrorNilAmount=Impossible de créer une demande de prélèvement pour un montant nul @@ -98,6 +98,10 @@ ModeFRST=Paiement unitaire PleaseCheckOne=Cocher un choix uniquement DirectDebitOrderCreated=Ordre de prélèvement %s créé AmountRequested=Montant réclamé +SEPARCUR=SEPA CUR +SEPAFRST=SEPA FRST +ExecutionDate=Execution date +CreateForSepa=Create direct debit file ### Notifications InfoCreditSubject=Crédit prélèvement %s à la banque diff --git a/htdocs/langs/he_IL/incoterm.lang b/htdocs/langs/he_IL/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/he_IL/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/hr_HR/incoterm.lang b/htdocs/langs/hr_HR/incoterm.lang deleted file mode 100644 index b6e33607c35..00000000000 --- a/htdocs/langs/hr_HR/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Dodaj mogučnosti za upravljanje Incoterm-om -IncotermLabel=Incoterms diff --git a/htdocs/langs/hu_HU/incoterm.lang b/htdocs/langs/hu_HU/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/hu_HU/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/id_ID/incoterm.lang b/htdocs/langs/id_ID/incoterm.lang deleted file mode 100644 index bd7ab196fca..00000000000 --- a/htdocs/langs/id_ID/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Istilah Ekonomi Internasional -Module62000Desc=Tambah fitur untuk mengatur Istilah Ekonomi Internasional -IncotermLabel=Istilah Ekonomi Internasional diff --git a/htdocs/langs/is_IS/incoterm.lang b/htdocs/langs/is_IS/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/is_IS/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/it_IT/incoterm.lang b/htdocs/langs/it_IT/incoterm.lang deleted file mode 100644 index f3181020491..00000000000 --- a/htdocs/langs/it_IT/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Aggiunge funzioni per la gestione Incoterm -IncotermLabel=Import-Export diff --git a/htdocs/langs/ja_JP/incoterm.lang b/htdocs/langs/ja_JP/incoterm.lang deleted file mode 100644 index dc4c473efa9..00000000000 --- a/htdocs/langs/ja_JP/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=インコターム -Module62000Desc=インコタームを管理する機能を追加 -IncotermLabel=インコタームズ diff --git a/htdocs/langs/ka_GE/incoterm.lang b/htdocs/langs/ka_GE/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/ka_GE/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/kn_IN/incoterm.lang b/htdocs/langs/kn_IN/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/kn_IN/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/ko_KR/incoterm.lang b/htdocs/langs/ko_KR/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/ko_KR/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/lo_LA/incoterm.lang b/htdocs/langs/lo_LA/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/lo_LA/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/lt_LT/incoterm.lang b/htdocs/langs/lt_LT/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/lt_LT/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/lv_LV/incoterm.lang b/htdocs/langs/lv_LV/incoterm.lang deleted file mode 100644 index 1b96e667fb5..00000000000 --- a/htdocs/langs/lv_LV/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Inkoterms -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Inkoterms diff --git a/htdocs/langs/mk_MK/incoterm.lang b/htdocs/langs/mk_MK/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/mk_MK/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/mn_MN/incoterm.lang b/htdocs/langs/mn_MN/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/mn_MN/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/nb_NO/incoterm.lang b/htdocs/langs/nb_NO/incoterm.lang deleted file mode 100644 index 7fa8aee4c19..00000000000 --- a/htdocs/langs/nb_NO/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Legg til egenskaper for å administrere Incoterm -IncotermLabel=Incotermer diff --git a/htdocs/langs/nl_NL/incoterm.lang b/htdocs/langs/nl_NL/incoterm.lang deleted file mode 100644 index b70a7de1612..00000000000 --- a/htdocs/langs/nl_NL/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Onderdelen toevoegen voor Incoterms -IncotermLabel=Incoterms diff --git a/htdocs/langs/pl_PL/incoterm.lang b/htdocs/langs/pl_PL/incoterm.lang deleted file mode 100644 index 8278ec220a4..00000000000 --- a/htdocs/langs/pl_PL/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Międzynarodowe Reguły Handlu -Module62000Desc=Dodaj funkcje do zarządzania Międzynarodowymi Regułami Handlu -IncotermLabel=Formuły handlowe diff --git a/htdocs/langs/pt_BR/accountancy.lang b/htdocs/langs/pt_BR/accountancy.lang index 82f62863844..ad4c29a5c0f 100644 --- a/htdocs/langs/pt_BR/accountancy.lang +++ b/htdocs/langs/pt_BR/accountancy.lang @@ -109,7 +109,6 @@ ACCOUNTING_PRODUCT_BUY_ACCOUNT=Conta contábil padrão para produtos comprados ( ACCOUNTING_PRODUCT_SOLD_ACCOUNT=Conta contábil padrão para os produtos vendidos (usado se não estiver definido na folha do produto) ACCOUNTING_SERVICE_BUY_ACCOUNT=Conta contábil padrão para os serviços comprados (se não for definido na listagem de serviços) ACCOUNTING_SERVICE_SOLD_ACCOUNT=Conta contábil padrão para os serviços vendidos (se não for definido na listagem de serviços) -Code_tiers=Terceiro LabelAccount=Conta rótulo LabelOperation=Operação de etiqueta Sens=Significado diff --git a/htdocs/langs/pt_BR/admin.lang b/htdocs/langs/pt_BR/admin.lang index d686d50cace..c88cca519b6 100644 --- a/htdocs/langs/pt_BR/admin.lang +++ b/htdocs/langs/pt_BR/admin.lang @@ -353,7 +353,6 @@ ExtrafieldParamHelpsellist=Lista de valores oriundos de uma tabela
    Sintaxe : ExtrafieldParamHelpchkbxlst=A lista de valores oriundos de uma tabela
    Sintaxe : table_name:label_field:id_field::filter
    Exemplo : c_typent:libelle:id::filter

    o filtro pode ser um teste simples (ex. ativo=1) para exibir somente valores ativos
    Você também pode usar $ID$ no filtro que é a id atual do objeto atual
    Para realizar uma SELEÇÃO no filtro, use $SEL$
    Se você deseja filtrar nos campos extras, use a sintaxe extra.fieldcode=... (onde o código do campo é o código do campo extra)

    A fim de ter a lista dependendo de uma outra lista de atributos complementares :
    c_typent:libelle:id:options_parent_list_code|parent_column:filter

    A fim de ter a lista dependendo de uma outra lista :
    c_typent:libelle:id:parent_list_code|parent_column:filter ExtrafieldParamHelplink=Os parâmetros devem ser ObjectName:Classpath
    Sintaxe: ObjectName:Classpath
    Exemplos:
    Societe:societe/class/societe.class.php
    Contact:contact/class/contact.class.php LibraryToBuildPDF=Biblioteca usada para a geração de PDF -WarningUsingFPDF=Aviso: Sua conf.php Contém diretrize dolibarr_pdf_force_fpdf=1. Isso significa que você usa a biblioteca FPDF para gerar arquivos em PDF. Essa biblioteca é velha e não suporta muitas novas funções (Unicode, imagem transparente, cyrillic, línguas arábicas e asiáticas,...), portanto pode ocorrer alguns erros durante a geração de PDF.
    Para corrigir esse problema e ter todo o suporte na geração de PDF, baixe Biblioteca TCPDF, então comente ou remova essa linha $dolibarr_pdf_force_fpdf=1, e adicione essa $dolibarr_lib_TCPDF_PATH='path_to_TCPDF_dir' LocalTaxDesc=Alguns paises aplicam de 2 a 3 taxas em cada linha de fatura. Se for esse caso, escolha o tipo de segunda e terceira taxa. Os possíveis tipos são:
    1 : Taxa local aplicam em produtos e serviços sem ICMS (ICMS não é aplicada em taxa local)
    2 : Taxa local aplicam em produtos e serviços antes do ICMS (ICMS é calculado no montante + taxa local)
    3 : Taxa local aplicam em produtos sem o ICMS (ICMS não é aplicada na taxa local)
    4 : Taxa local aplicam nos produtos antes do ICMS (ICMS é calculado no montante + taxa local)
    5 : Taxa local aplicam no serviço sem o ICMS (ICMS não é aplicado em taxa local)
    6 : Taxa local aplicam em serviços antes do ICMS (ICMS é calculado no montante + taxa local) LinkToTestClickToDial=Entre com um número telefônico para chamar e mostrar um link que testar a URL CliqueParaDiscar para usuário %s RefreshPhoneLink=Atualizar link diff --git a/htdocs/langs/pt_BR/incoterm.lang b/htdocs/langs/pt_BR/incoterm.lang deleted file mode 100644 index 8aa2070c3e8..00000000000 --- a/htdocs/langs/pt_BR/incoterm.lang +++ /dev/null @@ -1,2 +0,0 @@ -# Dolibarr language file - Source file is en_US - incoterm -Module62000Desc=Adicionar recursos para gerenciar Incoterm diff --git a/htdocs/langs/pt_BR/members.lang b/htdocs/langs/pt_BR/members.lang index 4197d9375a0..8d541fe0e14 100644 --- a/htdocs/langs/pt_BR/members.lang +++ b/htdocs/langs/pt_BR/members.lang @@ -60,8 +60,6 @@ LastSubscriptionsModified=Últimas %s subscrições modificadas PublicMemberCard=Ficha pública membro SubscriptionNotRecorded=Subscrição não registrada AddSubscription=Criar subscripção -DescADHERENT_AUTOREGISTER_MAIL_SUBJECT=Assunto do email em caso de inscrição automática -DescADHERENT_AUTOREGISTER_MAIL=Email a enviar em caso de convite para inscrição automática DescADHERENT_ETIQUETTE_TEXT=Texto impresso em folhas de endereço de membros DescADHERENT_CARD_TYPE=Formato da página fichas DescADHERENT_CARD_HEADER_TEXT=Texto a imprimir na parte superior do cartão de membro diff --git a/htdocs/langs/pt_PT/incoterm.lang b/htdocs/langs/pt_PT/incoterm.lang deleted file mode 100644 index 8cb400335b1..00000000000 --- a/htdocs/langs/pt_PT/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Adione funções para gerir Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/ro_RO/incoterm.lang b/htdocs/langs/ro_RO/incoterm.lang deleted file mode 100644 index 7d95bf2b65a..00000000000 --- a/htdocs/langs/ro_RO/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Adaugă functionalitati pentru gestionarea Incoterm -IncotermLabel=Incoterm diff --git a/htdocs/langs/ru_RU/incoterm.lang b/htdocs/langs/ru_RU/incoterm.lang deleted file mode 100644 index ef59e2cfa93..00000000000 --- a/htdocs/langs/ru_RU/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Обязанности по доставке товаров -Module62000Desc=Добавить функции для управления обязанностями по доставке товаров -IncotermLabel=Обязанности по доставке товаров diff --git a/htdocs/langs/sk_SK/incoterm.lang b/htdocs/langs/sk_SK/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/sk_SK/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/sl_SI/incoterm.lang b/htdocs/langs/sl_SI/incoterm.lang deleted file mode 100644 index 57641257ec0..00000000000 --- a/htdocs/langs/sl_SI/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Mednarodni poslovni izraz -Module62000Desc=Mednarodnemu poslovnemu izrazu dodaj lastnost -IncotermLabel=Mednarodni Poslovni Izrazi diff --git a/htdocs/langs/sq_AL/incoterm.lang b/htdocs/langs/sq_AL/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/sq_AL/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/sr_RS/incoterm.lang b/htdocs/langs/sr_RS/incoterm.lang deleted file mode 100644 index 196cf598381..00000000000 --- a/htdocs/langs/sr_RS/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Dodavanje funkcionalnosti za upravljanje Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/sv_SE/incoterm.lang b/htdocs/langs/sv_SE/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/sv_SE/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/sw_SW/incoterm.lang b/htdocs/langs/sw_SW/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/sw_SW/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/th_TH/incoterm.lang b/htdocs/langs/th_TH/incoterm.lang deleted file mode 100644 index 4f05bda372b..00000000000 --- a/htdocs/langs/th_TH/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=เพิ่มคุณสมบัติในการจัดการ Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/tr_TR/incoterm.lang b/htdocs/langs/tr_TR/incoterm.lang deleted file mode 100644 index d08b03c056c..00000000000 --- a/htdocs/langs/tr_TR/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Uluslararası ticari terimleri -Module62000Desc=Uluslararası ticari terimleri yönetmek için özellik ekle -IncotermLabel=Uluslararası Ticaret Terimleri diff --git a/htdocs/langs/uk_UA/incoterm.lang b/htdocs/langs/uk_UA/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/uk_UA/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/uz_UZ/incoterm.lang b/htdocs/langs/uz_UZ/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/uz_UZ/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/vi_VN/incoterm.lang b/htdocs/langs/vi_VN/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/vi_VN/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/langs/zh_CN/incoterm.lang b/htdocs/langs/zh_CN/incoterm.lang deleted file mode 100644 index f4ebcdeecbf..00000000000 --- a/htdocs/langs/zh_CN/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=国际贸易术语 -Module62000Desc=添加功能来管理国际贸易术语 -IncotermLabel=国际贸易术语解释通则 diff --git a/htdocs/langs/zh_TW/incoterm.lang b/htdocs/langs/zh_TW/incoterm.lang deleted file mode 100644 index 7ff371e3a95..00000000000 --- a/htdocs/langs/zh_TW/incoterm.lang +++ /dev/null @@ -1,3 +0,0 @@ -Module62000Name=Incoterm -Module62000Desc=Add features to manage Incoterm -IncotermLabel=Incoterms diff --git a/htdocs/livraison/card.php b/htdocs/livraison/card.php index b2218508ef6..0e61adcb279 100644 --- a/htdocs/livraison/card.php +++ b/htdocs/livraison/card.php @@ -165,7 +165,7 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->expeditio { $db->commit(); if (! empty($backtopage)) header("Location: ".$backtopage); - else header("Location: ".DOL_URL_ROOT.'/expedition/index.php'); + else header("Location: ".DOL_URL_ROOT.'/expedition/list.php?restore_lastsearch_values=1'); exit; } else @@ -193,9 +193,11 @@ elseif ($action == 'set_incoterms' && !empty($conf->incoterm->enabled)) // Update extrafields if ($action == 'update_extras') { + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute', 'none')); if ($ret < 0) $error++; if (! $error) diff --git a/htdocs/loan/card.php b/htdocs/loan/card.php index b920c8d1a6a..59b4da544db 100644 --- a/htdocs/loan/card.php +++ b/htdocs/loan/card.php @@ -182,8 +182,8 @@ if (empty($reshook)) $object->datestart = $datestart; $object->dateend = $dateend; $object->capital = $capital; - $object->nbterm = GETPOST("nbterm"); - $object->rate = GETPOST("rate"); + $object->nbterm = GETPOST("nbterm",'int'); + $object->rate = price2num(GETPOST("rate",'alpha')); $accountancy_account_capital = GETPOST('accountancy_account_capital'); $accountancy_account_insurance = GETPOST('accountancy_account_insurance'); @@ -423,11 +423,11 @@ if ($id > 0) print ''; @@ -676,7 +676,7 @@ if ($id > 0) $sql.= " p.amount_capital, p.amount_insurance, p.amount_interest,"; $sql.= " c.libelle as paiement_type"; $sql.= " FROM ".MAIN_DB_PREFIX."payment_loan as p"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_typepayment = c.id AND c.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as c ON p.fk_typepayment = c.id"; $sql.= ", ".MAIN_DB_PREFIX."loan as l"; $sql.= " WHERE p.fk_loan = ".$id; $sql.= " AND p.fk_loan = l.rowid"; @@ -692,7 +692,7 @@ if ($id > 0) $total_insurance = 0; $total_interest = 0; $total_capital = 0; - print ''; + print '
    '; print ''; print ''; print ''; @@ -729,7 +729,9 @@ if ($id > 0) $staytopay = $object->capital - $totalpaid; print ''; - print ''; + print ''; } print "
    '.$langs->trans("RefPayment").''.$langs->trans("Date").'
    '.$langs->trans("RemainderToPay").' :'.price($staytopay, 0, $langs, 0, 0, -1, $conf->currency).'
    '; + print price($staytopay, 0, $langs, 0, 0, -1, $conf->currency); + print '
    "; $db->free($resql); diff --git a/htdocs/loan/class/loan.class.php b/htdocs/loan/class/loan.class.php index 19e8f5f84bf..1de0e908c97 100644 --- a/htdocs/loan/class/loan.class.php +++ b/htdocs/loan/class/loan.class.php @@ -292,11 +292,18 @@ class Loan extends CommonObject { $this->db->begin(); + if (! is_numeric($this->nbterm)) + { + $this->error='BadValueForParameterForNbTerm'; + return -1; + } + $sql = "UPDATE ".MAIN_DB_PREFIX."loan"; $sql.= " SET label='".$this->db->escape($this->label)."',"; $sql.= " capital='".price2num($this->db->escape($this->capital))."',"; $sql.= " datestart='".$this->db->idate($this->datestart)."',"; $sql.= " dateend='".$this->db->idate($this->dateend)."',"; + $sql.= " nbterm=".$this->nbterm.","; $sql.= " accountancy_account_capital = '".$this->db->escape($this->account_capital)."',"; $sql.= " accountancy_account_insurance = '".$this->db->escape($this->account_insurance)."',"; $sql.= " accountancy_account_interest = '".$this->db->escape($this->account_interest)."',"; @@ -431,12 +438,12 @@ class Loan extends CommonObject $linkstart = ''; $linkend = ''; - + $result .= $linkstart; if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); if ($withpicto != 2) $result.= ($maxlen?dol_trunc($this->ref,$maxlen):$this->ref); $result .= $linkend; - + return $result; } diff --git a/htdocs/loan/class/loanschedule.class.php b/htdocs/loan/class/loanschedule.class.php index a72487e706a..a430f79366e 100644 --- a/htdocs/loan/class/loanschedule.class.php +++ b/htdocs/loan/class/loanschedule.class.php @@ -177,7 +177,7 @@ class LoanSchedule extends CommonObject $sql.= " pt.code as type_code, pt.libelle as type_libelle,"; $sql.= ' b.fk_account'; $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id AND pt.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id"; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid'; $sql.= " WHERE t.rowid = ".$id; @@ -386,7 +386,7 @@ class LoanSchedule extends CommonObject * @param int $loanid Id object * @return int <0 if KO, >0 if OK */ - function fetchall($loanid) + function fetchAll($loanid) { global $langs; @@ -409,7 +409,7 @@ class LoanSchedule extends CommonObject $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; $sql.= " WHERE t.fk_loan = ".$loanid; - dol_syslog(get_class($this)."::fetchall", LOG_DEBUG); + dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) diff --git a/htdocs/loan/class/paymentloan.class.php b/htdocs/loan/class/paymentloan.class.php index 557262de945..022cf050f11 100644 --- a/htdocs/loan/class/paymentloan.class.php +++ b/htdocs/loan/class/paymentloan.class.php @@ -176,7 +176,7 @@ class PaymentLoan extends CommonObject $sql.= " pt.code as type_code, pt.libelle as type_libelle,"; $sql.= ' b.fk_account'; $sql.= " FROM ".MAIN_DB_PREFIX."payment_loan as t"; - $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id AND pt.entity IN (".getEntity('c_paiement').")"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id"; $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid'; $sql.= " WHERE t.rowid = ".$id; diff --git a/htdocs/loan/createschedule.php b/htdocs/loan/createschedule.php index 70688461ae9..0ecc0038835 100644 --- a/htdocs/loan/createschedule.php +++ b/htdocs/loan/createschedule.php @@ -22,14 +22,11 @@ */ require '../main.inc.php'; - require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/loan/class/loanschedule.class.php'; -global $user; - $loanid = GETPOST('loanid', 'int'); $action = GETPOST('action','aZ09'); @@ -92,7 +89,7 @@ if ($action == 'updateecheancier') { } $echeance = new LoanSchedule($db); -$echeance->fetchall($object->id); +$echeance->fetchAll($object->id); top_htmlhead('', ''); $var = ! $var; @@ -107,6 +104,7 @@ $(document).ready(function() { var idcap=echeance-1; idcap = '#hi_capital'+idcap; var capital=$(idcap).val(); + console.log("Change montly amount echeance="+echeance+" idcap="+idcap+" capital="+capital); $.ajax({ dataType: 'json', url: 'calcmens.php', @@ -135,50 +133,59 @@ $(document).ready(function() { print '
    '; print ''; print ''; -if(count($echeance->lines)>0){ +if(count($echeance->lines)>0) +{ print ''; }else{ print ''; } print ''; print ''; -print '"; +print ''; print ''; print ''; -Print ''; -Print ''; -Print ''; -Print ''; -Print ''; -print ''; +Print ''; +Print ''; +Print ''; +Print ''; +Print ''; +print ''."\n"; if ($object->nbterm > 0 && count($echeance->lines)==0) { $i=1; $capital = $object->capital; - while($i <$object->nbterm+1){ - $mens = round($echeance->calc_mens($capital, $object->rate/100, $object->nbterm-$i+1),2,PHP_ROUND_HALF_UP); + while($i <$object->nbterm+1) + { + $mens = price2num($echeance->calc_mens($capital, $object->rate/100, $object->nbterm-$i+1), 'MT'); $int = ($capital*($object->rate/12))/100; - $int = round($int,2,PHP_ROUND_HALF_UP); - $cap_rest = round($capital - ($mens-$int),2,PHP_ROUND_HALF_UP); + $int = price2num($int, 'MT'); + $cap_rest = price2num($capital - ($mens-$int), 'MT'); print ''; print ''; print ''; print ''; print ''; print ''; - print ''; + print ''."\n"; $i++; $capital = $cap_rest; } -}elseif(count($echeance->lines)>0){ +} +elseif(count($echeance->lines)>0) +{ $i=1; $capital = $object->capital; foreach ($echeance->lines as $line){ $mens = $line->amount_capital+$line->amount_insurance+$line->amount_interest; $int = $line->amount_interest; - $cap_rest = round($capital - ($mens-$int),2,PHP_ROUND_HALF_UP); + $cap_rest = price2num($capital - ($mens-$int), 'MT'); print ''; print ''; print ''; @@ -189,7 +196,7 @@ if ($object->nbterm > 0 && count($echeance->lines)==0) } print ''; print ''; - print ''; + print ''."\n"; $i++; $capital = $cap_rest; } diff --git a/htdocs/loan/index.php b/htdocs/loan/index.php index d1436287db3..24ea1ab72f9 100644 --- a/htdocs/loan/index.php +++ b/htdocs/loan/index.php @@ -111,6 +111,12 @@ if ($resql) if ($search_amount) $param.="&search_amount=".urlencode($search_amount_ht); if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); + $newcardbutton=''; + if ($user->rights->loan->write) + { + $newcardbutton=''.$langs->trans('NewLoan').''; + } + print ''."\n"; if ($optioncss != '') print ''; print ''; @@ -120,7 +126,7 @@ if ($resql) print ''; print ''; - print_barre_liste($langs->trans("Loans"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_accountancy.png', 0, '', '', $limit); + print_barre_liste($langs->trans("Loans"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_accountancy.png', 0, $newcardbutton, '', $limit); print '
    '; print '
    ' . "Création d'échéancier'; +print $langs->trans("FinancialCommitment"); +print '
    Echéance Date Montant Intérêts Capital restant du
    '.$langs->trans("DueDate").''.$langs->trans("Date").''.$langs->trans("Amount").''.$langs->trans("InterestAmount").''.$langs->trans("Remain"); +print ' ('.price2num($object->capital).')'; +print ''; +print '
    ' . $i .'' . dol_print_date(dol_time_plus_duree($object->datestart, $i-1, 'm'),'day') . ''.price($int,0,'',1).' €'.price($cap_rest).' €
    ' . $i .'' . dol_print_date($line->datep,'day') . ''.price($int,0,'',1).' €'.price($cap_rest).' €
    '."\n"; diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index df49c541b41..359a7ea27a1 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -73,7 +73,7 @@ if (function_exists('get_magic_quotes_gpc')) // magic_quotes_* deprecated in PHP * * @param string $val Value * @param string $type 1=GET, 0=POST, 2=PHP_SELF - * @return int >0 if there is an injection + * @return int >0 if there is an injection, 0 if none */ function test_sql_and_script_inject($val, $type) { @@ -101,6 +101,7 @@ function test_sql_and_script_inject($val, $type) // More on https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet $inj += preg_match('/'; } - // Price qty min - print ''; - print ''; + if ($conf->multicurrency->enabled) { + // Currency + print ''; + print ''; + print ''; + + // Currency tx + print ''; + print ''; + print ''; + + // Currency price qty min + print ''; + $pricesupplierincurrencytouse=(GETPOST('multicurrency_price')?GETPOST('multicurrency_price'):(isset($object->fourn_multicurrency_price)?$object->fourn_multicurrency_price:'')); + print ''; + + // Price qty min + print ''; + print ''; + + $currencies = array(); + $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'multicurrency WHERE entity = '.$conf->entity; + $resql = $db->query($sql); + if ($resql) { + $currency = new MultiCurrency($db); + while ($obj = $db->fetch_object($resql)) { + $currency->fetch($obj->rowid); + $currencies[$currency->code] = $currency->rate->rate; + } + } + $currencies = json_encode($currencies); + + print << +SCRIPT; + } else { + // Price qty min + print ''; + print ''; + } + // Discount qty min print ''; @@ -634,15 +745,17 @@ if ($id > 0 || $ref) print_liste_field_titre("QtyMin",$_SERVER["PHP_SELF"],"pfp.quantity","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre("VATRate",$_SERVER["PHP_SELF"],'','',$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre("PriceQtyMinHT",$_SERVER["PHP_SELF"],'','',$param,'align="right"',$sortfield,$sortorder); - print_liste_field_titre("UnitPriceHT",$_SERVER["PHP_SELF"],"pfp.unitprice","",$param,'align="right"',$sortfield,$sortorder); + if ($conf->multicurrency->enabled) { + print_liste_field_titre("PriceQtyMinHTCurrency", $_SERVER["PHP_SELF"], '', '', $param, 'align="right"', $sortfield, $sortorder); + } + print_liste_field_titre("UnitPriceHT",$_SERVER["PHP_SELF"],"pfp.unitprice","",$param,'align="right"',$sortfield,$sortorder); + if ($conf->multicurrency->enabled) { + print_liste_field_titre("UnitPriceHTCurrency", $_SERVER["PHP_SELF"], "pfp.multicurrency_unitprice", "", $param, 'align="right"', $sortfield, $sortorder); + print_liste_field_titre("Currency", $_SERVER["PHP_SELF"], "", "", $param, 'align="right"', $sortfield, $sortorder); + } print_liste_field_titre("DiscountQtyMin",$_SERVER["PHP_SELF"],'','',$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre("NbDaysToDelivery",$_SERVER["PHP_SELF"],"pfp.delivery_time_days","",$param,'align="right"',$sortfield,$sortorder); print_liste_field_titre("ReputationForThisProduct",$_SERVER["PHP_SELF"],"pfp.supplier_reputation","",$param,'align="center"',$sortfield,$sortorder); - // Charges ???? - if ($conf->global->PRODUCT_CHARGES) - { - if (! empty($conf->margin->enabled)) print_liste_field_titre("UnitCharges"); - } print_liste_field_titre(''); print "\n"; @@ -685,12 +798,31 @@ if ($id > 0 || $ref) print $productfourn->fourn_price?price($productfourn->fourn_price):""; print ''; + if ($conf->multicurrency->enabled) { + // Price for the quantity in currency + print ''; + } + // Unit price print ''; + if ($conf->multicurrency->enabled) { + // Unit price in currency + print ''; + + // Currency + print ''; + } + // Discount print ''; - // Charges ???? - /* - if ($conf->global->PRODUCT_CHARGES) - { - if (! empty($conf->margin->enabled)) - { - print ''; - } - }*/ - if (is_object($hookmanager)) { $parameters=array('id_pfp'=>$productfourn->product_fourn_price_id,'id_fourn'=>$id_fourn,'prod_id'=>$object->id); @@ -727,7 +847,7 @@ if ($id > 0 || $ref) } // Modify-Remove - print '\n"; print ''; diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php index 84db46c879f..d41e3ea6d64 100644 --- a/htdocs/product/inventory/card.php +++ b/htdocs/product/inventory/card.php @@ -27,7 +27,7 @@ include_once DOL_DOCUMENT_ROOT.'/product/inventory/class/inventory.class.php'; include_once DOL_DOCUMENT_ROOT.'/product/inventory/lib/inventory.lib.php'; // Load traductions files requiredby by page -$langs->loadLangs(array("inventory","other")); +$langs->loadLangs(array("stocks","other")); // Get parameters $id = GETPOST('id', 'int'); @@ -36,6 +36,15 @@ $action = GETPOST('action', 'alpha'); $cancel = GETPOST('cancel', 'aZ09'); $backtopage = GETPOST('backtopage', 'alpha'); +if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) +{ + $result = restrictedArea($user, 'stock', $id); +} +else +{ + $result = restrictedArea($user, 'stock', $id, '', 'advance_inventory'); +} + // Initialize technical objects $object=new Inventory($db); $extrafields = new ExtraFields($db); @@ -66,6 +75,16 @@ $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); // Load object include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals +if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) +{ + $permissiontoadd = $user->rights->stock->write; + $permissiontodelete = $user->rights->stock->write; +} +else +{ + $permissiontoadd = $user->rights->stock->advance_inventory->create; + $permissiontodelete = $user->rights->stock->advance_inventory->write; +} /* @@ -80,8 +99,6 @@ if (empty($reshook)) { $error=0; - $permissiontoadd = $user->rights->stock->creer; - $permissiontodelete = $user->rights->stock->supprimer; $backurlforlist = DOL_URL_ROOT.'/product/inventory/list.php'; // Actions cancel, add, update or delete @@ -327,7 +344,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea // Send print '' . $langs->trans('SendMail') . ''."\n"; - if ($user->rights->inventory->write) + if ($permissiontoadd) { print ''.$langs->trans("Modify").''."\n"; } @@ -336,7 +353,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea print ''.$langs->trans('Modify').''."\n"; } - if ($user->rights->inventory->delete) + if ($permissiontodelete) { print ''.$langs->trans('Delete').''."\n"; } diff --git a/htdocs/product/inventory/class/inventory.class.php b/htdocs/product/inventory/class/inventory.class.php index 8e0fde69687..dc4f600b529 100644 --- a/htdocs/product/inventory/class/inventory.class.php +++ b/htdocs/product/inventory/class/inventory.class.php @@ -76,11 +76,12 @@ class Inventory extends CommonObject */ public $fields=array( 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-1, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>'Id',), - 'ref' => array('type'=>'varchar(64)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>'Reference of object', 'css'=>'maxwidth200'), - 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>20, 'notnull'=>1, 'index'=>1,), - 'title' => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>25, 'css'=>'minwidth300'), - 'fk_warehouse' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Warehouse', 'visible'=>1, 'enabled'=>1, 'index'=>1, 'help'=>'LinkToThirparty'), - 'date_inventory' => array('type'=>'date', 'label'=>'DateValue', 'visible'=>1, 'enabled'=>1,), + 'ref' => array('type'=>'varchar(64)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>'Reference of object', 'css'=>'maxwidth200'), + 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>20, 'notnull'=>1, 'index'=>1,), + 'title' => array('type'=>'varchar(255)', 'label'=>'Label', 'visible'=>1, 'enabled'=>1, 'position'=>25, 'css'=>'minwidth300'), + 'fk_warehouse' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Warehouse', 'visible'=>1, 'enabled'=>1, 'position'=>30, 'index'=>1, 'help'=>'LinkToThirparty'), + 'fk_product' => array('type'=>'integer:Product:product/class/product.class.php', 'label'=>'Product', 'visible'=>1, 'enabled'=>1, 'position'=>32, 'index'=>1, 'help'=>'LinkToProduct'), + 'date_inventory' => array('type'=>'date', 'label'=>'DateValue', 'visible'=>1, 'enabled'=>1, 'position'=>35), 'date_validation' => array('type'=>'datetime', 'label'=>'DateValidation', 'visible'=>-2, 'enabled'=>1, 'position'=>502,), 'fk_user_valid' => array('type'=>'integer', 'label'=>'UserValidation', 'visible'=>-2, 'enabled'=>1, 'position'=>512,), @@ -358,31 +359,31 @@ class Inventory extends CommonObject } if ($mode == 2) { - if ($status == 0) return img_picto($langs->trans('Draft'),'statut5').' '.$langs->trans('Draft'); + if ($status == 0) return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled'); if ($status == -1) return img_picto($langs->trans('Canceled'),'statut6').' '.$langs->trans('Canceled'); } if ($mode == 3) { - if ($status == 0) return img_picto($langs->trans('Draft'),'statut5'); + if ($status == 0) return img_picto($langs->trans('Draft'),'statut0'); if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4'); if ($status == -1) return img_picto($langs->trans('Canceled'),'statut6'); } if ($mode == 4) { - if ($status == 0) return img_picto($langs->trans('Draft'),'statut5').' '.$langs->trans('Draft'); + if ($status == 0) return img_picto($langs->trans('Draft'),'statut0').' '.$langs->trans('Draft'); if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled'); if ($status == -1) return img_picto($langs->trans('Canceled'),'statut6').' '.$langs->trans('Canceled'); } if ($mode == 5) { - if ($status == 0) return $langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut5'); + if ($status == 0) return $langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut0'); if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4'); if ($status == -1) return $langs->trans('Canceled').' '.img_picto($langs->trans('Canceled'),'statut6'); } if ($mode == 6) { - if ($status == 0) return $langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut5'); + if ($status == 0) return $langs->trans('Draft').' '.img_picto($langs->trans('Draft'),'statut0'); if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4'); if ($status == -1) return $langs->trans('Canceled').' '.img_picto($langs->trans('Canceled'),'statut6'); } diff --git a/htdocs/product/inventory/list.php b/htdocs/product/inventory/list.php index f9fef88edce..95670698380 100644 --- a/htdocs/product/inventory/list.php +++ b/htdocs/product/inventory/list.php @@ -28,19 +28,19 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/product/inventory/class/inventory.class.php'; // Load traductions files requiredby by page -$langs->loadLangs(array("inventory","other")); +$langs->loadLangs(array("stocks","other")); -$action = GETPOST('action','alpha'); -$massaction = GETPOST('massaction','alpha'); -$show_files = GETPOST('show_files','int'); -$confirm = GETPOST('confirm','alpha'); -$cancel = GETPOST('cancel', 'alpha'); -$toselect = GETPOST('toselect', 'array'); +$action = GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... +$massaction = GETPOST('massaction','alpha'); // The bulk action (combo box choice into lists) +$show_files = GETPOST('show_files','int'); // Show files area generated by bulk actions ? +$confirm = GETPOST('confirm','alpha'); // Result of a confirmation +$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button +$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list $contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'inventorylist'; // To manage different context of search +$backtopage = GETPOST('backtopage','alpha'); // Go back to a dedicated page +$optioncss = GETPOST('optioncss','aZ'); // Option for the css output (always '' except when 'print') $id = GETPOST('id','int'); -$backtopage = GETPOST('backtopage','alpha'); -$optioncss = GETPOST('optioncss','alpha'); // Load variable for pagination $limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit; @@ -65,13 +65,21 @@ $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. if (! $sortorder) $sortorder="ASC"; -// Protection if external user +// Security check $socid=0; if ($user->societe_id > 0) { //$socid = $user->societe_id; accessforbidden(); } +if (empty($conf->global->MAIN_USE_ADVANCED_PERMS)) +{ + $result = restrictedArea($user, 'stock', $objectid); +} +else +{ + $result = restrictedArea($user, 'stock', $objectid, '', 'advance_inventory'); +} // Initialize array of search criterias $search_all=trim(GETPOST("search_all",'alpha')); @@ -93,22 +101,24 @@ $arrayfields=array(); foreach($object->fields as $key => $val) { // If $val['visible']==0, then we never show the field - if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled']); + if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']); } // Extra fields -if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) +if (is_array($extrafields->attributes[$object->element]['label']) && count($extrafields->attributes[$object->element]['label'])) { - foreach($extrafields->attribute_label as $key => $val) + foreach($extrafields->attributes[$object->element]['label'] as $key => $val) { - if (! empty($extrafields->attribute_list[$key])) $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>(($extrafields->attribute_list[$key]<0)?0:1), 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>(abs($extrafields->attribute_list[$key])!=3 && $extrafields->attribute_perms[$key])); + if (! empty($extrafields->attributes[$object->element]['list'][$key])) + $arrayfields["ef.".$key]=array('label'=>$extrafields->attributes[$object->element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->element]['list'][$key]<0)?0:1), 'position'=>$extrafields->attributes[$object->element]['pos'][$key], 'enabled'=>(abs($extrafields->attributes[$object->element]['list'][$key])!=3 && $extrafields->attributes[$object->element]['perms'][$key])); } } - +$object->fields = dol_sort_array($object->fields, 'position'); +$arrayfields = dol_sort_array($arrayfields, 'position'); /* - * ACTIONS + * Actions * * Put here all code to do according to value of "$action" parameter */ @@ -153,9 +163,9 @@ if (empty($reshook)) /* - * VIEW + * View * - * Put here all code to build page + * Put here all code to render page */ $form=new Form($db); @@ -164,7 +174,7 @@ $now=dol_now(); //$help_url="EN:Module_Inventory|FR:Module_Inventory_FR|ES:Módulo_Inventory"; $help_url=''; -$title = $langs->trans('ListOf', $langs->transnoentitiesnoconv("Inventorys")); +$title = $langs->trans('ListOfInventories'); // Build and execute select @@ -175,18 +185,21 @@ foreach($object->fields as $key => $val) $sql.='t.'.$key.', '; } // Add fields from extrafields -foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ", ef.".$key.' as options_'.$key : ''); +if (! empty($extrafields->attributes[$object->element]['label'])) { + foreach ($extrafields->attributes[$object->element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->element]['type'][$key] != 'separate' ? ", ef.".$key.' as options_'.$key : ''); +} // Add fields from hooks $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook $sql.=$hookmanager->resPrint; $sql=preg_replace('/, $/','', $sql); $sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; -if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."myobject_extrafields as ef on (t.rowid = ef.fk_object)"; -if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity('inventory').")"; +if (is_array($extrafields->attributes[$object->element]['label']) && count($extrafields->attributes[$object->element]['label'])) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."myobject_extrafields as ef on (t.rowid = ef.fk_object)"; +if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity('myobject').")"; else $sql.=" WHERE 1 = 1"; foreach($search as $key => $val) { + if ($key == 'status' && $search[$key] == -1) continue; $mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0); if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'status')?2:$mode_search)); } @@ -199,18 +212,18 @@ $reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object) $sql.=$hookmanager->resPrint; /* If a group by is required - $sql.= " GROUP BY " - foreach($object->fields as $key => $val) - { - $sql.='t.'.$key.', '; - } - // Add fields from extrafields - foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key : ''); - // Add where from hooks - $parameters=array(); - $reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters); // Note that $action and $object may have been modified by hook - $sql.=$hookmanager->resPrint; - */ +$sql.= " GROUP BY " +foreach($object->fields as $key => $val) +{ + $sql.='t.'.$key.', '; +} +// Add fields from extrafields +foreach ($extrafields->attributes[$object->element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->element]['type'][$key] != 'separate' ? ",ef.".$key : ''); +// Add where from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +*/ $sql.=$db->order($sortfield,$sortorder); @@ -221,17 +234,31 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) $result = $db->query($sql); $nbtotalofrecords = $db->num_rows($result); } - -$sql.= $db->plimit($limit+1, $offset); - -$resql=$db->query($sql); -if (! $resql) +// if total resultset is smaller then paging size (filtering), goto and load page 0 +if (($page * $limit) > $nbtotalofrecords) { - dol_print_error($db); - exit; + $page = 0; + $offset = 0; } +// if total resultset is smaller the limit, no need to do paging. +if (is_numeric($nbtotalofrecords) && $limit > $nbtotalofrecords) +{ + $resql = $result; + $num = $nbtotalofrecords; +} +else +{ + $sql.= $db->plimit($limit+1, $offset); -$num = $db->num_rows($resql); + $resql=$db->query($sql); + if (! $resql) + { + dol_print_error($db); + exit; + } + + $num = $db->num_rows($resql); +} // Direct jump if only one record found if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all) @@ -276,9 +303,10 @@ if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; +// List of mass actions available $arrayofmassactions = array( - 'presend'=>$langs->trans("SendByMail"), - 'builddoc'=>$langs->trans("PDFMerge"), + //'presend'=>$langs->trans("SendByMail"), + //'builddoc'=>$langs->trans("PDFMerge"), ); if ($user->rights->stock->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); @@ -340,7 +368,7 @@ print ''; foreach($object->fields as $key => $val) { $align=''; - if (in_array($val['type'], array('date','datetime','timestamp'))) $align=($align?' ':'').'center'; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; if ($key == 'status') $align.=($align?' ':'').'center'; if (! empty($arrayfields['t.'.$key]['checked'])) print ''; @@ -366,7 +394,7 @@ print ''; foreach($object->fields as $key => $val) { $align=''; - if (in_array($val['type'], array('date','datetime','timestamp'))) $align=($align?' ':'').'center'; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; if ($key == 'status') $align.=($align?' ':'').'center'; if (! empty($arrayfields['t.'.$key]['checked'])) print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($align?'class="'.$align.'"':''), $sortfield, $sortorder, $align.' ')."\n"; @@ -374,21 +402,22 @@ foreach($object->fields as $key => $val) // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields -$parameters=array('arrayfields'=>$arrayfields); +$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; -print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; +print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],'','','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; print ''."\n"; // Detect if we need a fetch on each output line $needToFetchEachLine=0; -foreach ($extrafields->attribute_computed as $key => $val) -{ - if (preg_match('/\$object/',$val)) $needToFetchEachLine++; // There is at least one compute field that use $object +if (! empty($extrafields->attributes[$object->element]['computed'])) { + foreach ($extrafields->attributes[$object->element]['computed'] as $key => $val) + { + if (preg_match('/\$object/',$val)) $needToFetchEachLine++; // There is at least one compute field that use $object + } } - // Loop on record // -------------------------------------------------------------------- $i=0; diff --git a/htdocs/product/list.php b/htdocs/product/list.php index ebb2c0a421a..503fc6069cc 100644 --- a/htdocs/product/list.php +++ b/htdocs/product/list.php @@ -94,7 +94,8 @@ if ((string) $type == '1') { $contextpage='servicelist'; if ($search_type=='') $ if ((string) $type == '0') { $contextpage='productlist'; if ($search_type=='') $search_type='0'; } // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array of hooks -$hookmanager->initHooks(array($contextpage)); +$object=new Product($db); +$hookmanager->initHooks(array('productservicelist')); $extrafields = new ExtraFields($db); $form=new Form($db); @@ -179,13 +180,16 @@ $arrayfields=array( 'p.tobuy'=>array('label'=>$langs->trans("Status").' ('.$langs->trans("Buy").')', 'checked'=>1, 'position'=>1000) ); // Extra fields -if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) +if (is_array($extrafields->attributes[$object->element]['label']) && count($extrafields->attributes[$object->element]['label'])) { - foreach($extrafields->attribute_label as $key => $val) - { - if (! empty($extrafields->attribute_list[$key])) $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>(($extrafields->attribute_list[$key]<0)?0:1), 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>(abs($extrafields->attribute_list[$key])!=3 && $extrafields->attribute_perms[$key])); - } + foreach($extrafields->attributes[$object->element]['label'] as $key => $val) + { + if (! empty($extrafields->attributes[$object->element]['list'][$key])) + $arrayfields["ef.".$key]=array('label'=>$extrafields->attributes[$object->element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->element]['list'][$key]<0)?0:1), 'position'=>$extrafields->attributes[$object->element]['pos'][$key], 'enabled'=>(abs($extrafields->attributes[$object->element]['list'][$key])!=3 && $extrafields->attributes[$object->element]['perms'][$key])); + } } +$object->fields = dol_sort_array($object->fields, 'position'); +$arrayfields = dol_sort_array($arrayfields, 'position'); @@ -274,13 +278,15 @@ else $sql .= ', pac.rowid prod_comb_id'; } // Add fields from extrafields - foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key.' as options_'.$key : ''); + if (! empty($extrafields->attributes[$object->element]['label'])) { + foreach ($extrafields->attributes[$object->element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->element]['type'][$key] != 'separate' ? ",ef.".$key.' as options_'.$key : ''); + } // Add fields from hooks $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters); // Note that $action and $object may have been modified by hook $sql.=$hookmanager->resPrint; $sql.= ' FROM '.MAIN_DB_PREFIX.'product as p'; - if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_extrafields as ef on (p.rowid = ef.fk_object)"; + if (is_array($extrafields->attributes[$object->element]['label']) && count($extrafields->attributes[$object->element]['label'])) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_extrafields as ef on (p.rowid = ef.fk_object)"; if (! empty($search_categ) || ! empty($catid)) $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX."categorie_product as cp ON p.rowid = cp.fk_product"; // We'll need this table joined to the select in order to filter by categ $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; // multilang @@ -331,7 +337,9 @@ else $sql .= ', pac.rowid'; } // Add fields from extrafields - foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key : ''); + if (! empty($extrafields->attributes[$object->element]['label'])) { + foreach ($extrafields->attributes[$object->element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->element]['type'][$key] != 'separate' ? ",ef.".$key : ''); + } // Add fields from hooks $parameters=array(); $reshook=$hookmanager->executeHooks('printFieldSelect',$parameters); // Note that $action and $object may have been modified by hook @@ -412,6 +420,16 @@ else if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); + $newcardbutton=''; + $rightskey='produit'; + if($type == Product::TYPE_SERVICE) $rightskey='service'; + if($user->rights->{$rightskey}->creer) + { + $label='NewProduct'; + if($type == Product::TYPE_SERVICE) $label='NewService'; + $newcardbutton=''.$langs->trans($label).''; + } + print ''; if ($optioncss != '') print ''; print ''; @@ -423,7 +441,7 @@ else print ''; if (empty($arrayfields['p.fk_product_type']['checked'])) print ''; - print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_products.png', 0, '', '', $limit); + print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_products.png', 0, $newcardbutton, '', $limit); $topicmail="Information"; $modelmail="product"; @@ -654,7 +672,7 @@ else // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields - $parameters=array('arrayfields'=>$arrayfields); + $parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['p.datec']['checked'])) print_liste_field_titre($arrayfields['p.datec']['label'],$_SERVER["PHP_SELF"],"p.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); @@ -698,6 +716,7 @@ else $product_static->type = $obj->fk_product_type; $product_static->status_buy = $obj->tobuy; $product_static->status = $obj->tosell; + $product_static->status_batch = $obj->tobatch; $product_static->entity = $obj->entity; $product_static->pmp = $obj->pmp; @@ -793,7 +812,7 @@ else if (! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->lire) { $htmltext=$product_fourn->display_price_product_fournisseur(1, 1, 0, 1); - print $form->textwithpicto(price($product_fourn->fourn_unitprice * (1 - $product_fourn->fourn_remise_percent/100) + $product_fourn->fourn_unitcharges - $product_fourn->fourn_remise).' '.$langs->trans("HT"),$htmltext); + print $form->textwithpicto(price($product_fourn->fourn_unitprice * (1 - $product_fourn->fourn_remise_percent/100) - $product_fourn->fourn_remise).' '.$langs->trans("HT"),$htmltext); } else print price($product_fourn->fourn_unitprice).' '.$langs->trans("HT"); } diff --git a/htdocs/product/price.php b/htdocs/product/price.php index a2afb5f9bb8..61b6316ca01 100644 --- a/htdocs/product/price.php +++ b/htdocs/product/price.php @@ -11,6 +11,7 @@ * Copyright (C) 2015 Alexandre Spangaro * Copyright (C) 2015 Marcos García * Copyright (C) 2016 Ferran Marcet + * Copyright (C) 2018 Frédéric France * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1439,7 +1440,12 @@ if ((empty($conf->global->PRODUIT_CUSTOMER_PRICES) || $action=='showlog_default_ // Il doit au moins y avoir la ligne de prix initial. // On l'ajoute donc pour remettre a niveau (pb vieilles versions) - $object->updatePrice($object->price, $object->price_base_type, $user, $newprice_min); + //$object->updatePrice($object->price, $object->price_base_type, $user, $newprice_min); + if (! empty($conf->global->PRODUIT_MULTIPRICES)) { + $object->updatePrice($object->multiprices[1], $object->multiprices_base_type[1], $user, $object->multiprices_tva_tx[1], $object->multiprices_min[1], 1); + } else { + $object->updatePrice($object->price, $object->price_base_type, $user, $object->tva_tx, $object->price_min); + } $result = $db->query($sql); $num = $db->num_rows($result); diff --git a/htdocs/product/stats/facture_fournisseur.php b/htdocs/product/stats/facture_fournisseur.php index 2e6b8d19677..47ca357b0cd 100644 --- a/htdocs/product/stats/facture_fournisseur.php +++ b/htdocs/product/stats/facture_fournisseur.php @@ -20,9 +20,9 @@ */ /** - * \file htdocs/product/stats/facture_fournisseur.php - * \ingroup product service facture - * \brief Page des stats des factures fournisseurs pour un produit + * \file htdocs/product/stats/facture_fournisseur.php + * \ingroup product service facture + * \brief Page of supplier invoice statistics for a product */ require '../../main.inc.php'; @@ -126,8 +126,8 @@ if ($id > 0 || ! empty($ref)) if ($user->rights->fournisseur->facture->lire) { - $sql = "SELECT DISTINCT s.nom as name, s.rowid as socid, s.code_client, f.ref, d.rowid, d.total_ht as total_ht,"; - $sql .= " f.datef, f.paye, f.fk_statut as statut, f.rowid as facid, d.qty"; + $sql = "SELECT DISTINCT s.nom as name, s.rowid as socid, s.code_client, d.rowid, d.total_ht as total_ht,"; + $sql .= " f.rowid as facid, f.ref, f.ref_supplier, f.datef, f.libelle, f.total_ht, f.total_ttc, f.total_tva, f.paye, f.fk_statut as statut, d.qty"; if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user "; $sql .= " FROM " . MAIN_DB_PREFIX . "societe as s"; @@ -217,12 +217,17 @@ if ($id > 0 || ! empty($ref)) while ($i < $num && $i < $conf->liste_limit) { $objp = $db->fetch_object($result); - $var = ! $var; - print ''; - print ''; + print '\n"; $societestatic->fetch($objp->socid); diff --git a/htdocs/product/stock/card.php b/htdocs/product/stock/card.php index 8ab36de3428..1566e5d36c0 100644 --- a/htdocs/product/stock/card.php +++ b/htdocs/product/stock/card.php @@ -117,7 +117,7 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->stock->su if ($result > 0) { setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs'); - header("Location: ".DOL_URL_ROOT.'/product/stock/list.php'); + header("Location: ".DOL_URL_ROOT.'/product/stock/list.php?restore_lastsearch_values=1'); exit; } else @@ -446,7 +446,7 @@ else $totalunit=0; $totalvalue=$totalvaluesell=0; - $sql = "SELECT p.rowid as rowid, p.ref, p.label as produit, p.fk_product_type as type, p.pmp as ppmp, p.price, p.price_ttc, p.entity,"; + $sql = "SELECT p.rowid as rowid, p.ref, p.label as produit, p.tobatch, p.fk_product_type as type, p.pmp as ppmp, p.price, p.price_ttc, p.entity,"; $sql.= " ps.reel as value"; $sql.= " FROM ".MAIN_DB_PREFIX."product_stock as ps, ".MAIN_DB_PREFIX."product as p"; $sql.= " WHERE ps.fk_product = p.rowid"; @@ -487,10 +487,11 @@ else print ''; print "'; diff --git a/htdocs/product/stock/list.php b/htdocs/product/stock/list.php index 58ea5eadc21..29e5439b8e8 100644 --- a/htdocs/product/stock/list.php +++ b/htdocs/product/stock/list.php @@ -43,7 +43,7 @@ $sortorder = GETPOST("sortorder"); if (! $sortfield) $sortfield="e.ref"; if (! $sortorder) $sortorder="ASC"; $page = GETPOST("page"); -if ($page < 0) $page = 0; +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 $offset = $limit * $page; $year = strftime("%Y",time()); @@ -130,6 +130,12 @@ if ($result) if ($search_status) $param.="&search_status=".urlencode($search_status); if ($sall) $param.="&sall=".urlencode($sall); + $newcardbutton=''; + if ($user->rights->stock->creer) + { + $newcardbutton=''.$langs->trans('MenuNewWarehouse').''; + } + print ''; print ''; print ''; @@ -137,7 +143,7 @@ if ($result) print ''; print ''; - print_barre_liste($langs->trans("ListOfWarehouses"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $totalnboflines, 'title_generic.png', 0, '', '', $limit); + print_barre_liste($langs->trans("ListOfWarehouses"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $totalnboflines, 'title_generic.png', 0, $newcardbutton, '', $limit); if ($sall) { diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index e6a2b59e700..ebe1fbe5375 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -55,6 +55,7 @@ $idline = GETPOST('idline'); $sortfield = GETPOST('sortfield','alpha'); $sortorder = GETPOST('sortorder','alpha'); $page = GETPOST('page','int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 if (!$sortfield) { $sortfield = 'p.ref'; @@ -332,7 +333,7 @@ $titletoadd=$langs->trans("Select"); $buttonrecord=$langs->trans("RecordMovement"); $titletoaddnoent=$langs->transnoentitiesnoconv("Select"); $buttonrecordnoent=$langs->transnoentitiesnoconv("RecordMovement"); -print $langs->trans("SelectProductInAndOutWareHouse",$titletoaddnoent,$buttonrecordnoent).'
    '; +print ''.$langs->trans("SelectProductInAndOutWareHouse",$titletoaddnoent,$buttonrecordnoent).'
    '; print '
    '."\n"; $var=true; @@ -445,14 +446,14 @@ print ''; print ''; // Button to record mass movement -$codemove=(isset($_POST["codemove"])?GETPOST("codemove",'alpha'):dol_print_date(dol_now(),'%y%m%d%H%M%S')); +$codemove=(isset($_POST["codemove"])?GETPOST("codemove",'alpha'):dol_print_date(dol_now(),'%Y%m%d%H%M%S')); $labelmovement=GETPOST("label")?GETPOST('label'):$langs->trans("StockTransfer").' '.dol_print_date($now,'%Y-%m-%d %H:%M'); -print '
    '.$langs->trans("PriceQtyMin").''; - print ' '; - print $form->selectPriceBaseType((GETPOST('price_base_type')?GETPOST('price_base_type'):'HT'), "price_base_type"); // We keep 'HT' here, price_base_type is not yet supported for supplier prices - print '
    '.$langs->trans("Currency").''; + $currencycodetouse = GETPOST('multicurrency_code')?GETPOST('multicurrency_code'):(isset($object->fourn_multicurrency_code)?$object->fourn_multicurrency_code:''); + if (empty($currencycodetouse) && $object->fourn_multicurrency_tx == 1) $currencycodetouse=$conf->currency; + print $form->selectMultiCurrency($currencycodetouse, "multicurrency_code", 1); + print '
    '.$langs->trans("CurrencyRate").''; + print '
    '.$langs->trans("PriceQtyMinCurrency").''; + print ' '; + print $form->selectPriceBaseType((GETPOST('multicurrency_price_base_type')?GETPOST('multicurrency_price_base_type'):'HT'), "multicurrency_price_base_type"); // We keep 'HT' here, multicurrency_price_base_type is not yet supported for supplier prices + print '
    ' . $langs->trans("PriceQtyMin") . ''; + print ''; + print ''; + print ' '; + print $form->selectPriceBaseType('', "disabled_price_base_type"); + print '
    ' . $langs->trans("PriceQtyMin") . ''; + print ' '; + print $form->selectPriceBaseType((GETPOST('price_base_type') ? GETPOST('price_base_type') : 'HT'), "price_base_type"); // We keep 'HT' here, price_base_type is not yet supported for supplier prices + print '
    '.$langs->trans("DiscountQtyMin").'
    '; + print $productfourn->fourn_multicurrency_price ? price($productfourn->fourn_multicurrency_price) : ""; + print ''; print price($productfourn->fourn_unitprice); //print $objp->unitprice? price($objp->unitprice) : ($objp->quantity?price($objp->price/$objp->quantity):" "); print ''; + print price($productfourn->fourn_multicurrency_unitprice); + print ''; + print $productfourn->fourn_multicurrency_code ? currency_name($productfourn->fourn_multicurrency_code) : ''; + print ''; print price2num($productfourn->fourn_remise_percent).'%'; @@ -708,18 +840,6 @@ if ($id > 0 || $ref) } print''; - print $productfourn->fourn_unitcharges?price($productfourn->fourn_unitcharges) : ($productfourn->fourn_qty?price($productfourn->fourn_charges/$productfourn->fourn_qty):" "); - print ''; + print ''; if ($user->rights->produit->creer || $user->rights->service->creer) { print ''.img_edit().""; diff --git a/htdocs/product/index.php b/htdocs/product/index.php index 6eebc4d85e2..fd29143613a 100644 --- a/htdocs/product/index.php +++ b/htdocs/product/index.php @@ -272,7 +272,7 @@ print '
    '; * Last modified products */ $max=15; -$sql = "SELECT p.rowid, p.label, p.price, p.ref, p.fk_product_type, p.tosell, p.tobuy, p.fk_price_expression,"; +$sql = "SELECT p.rowid, p.label, p.price, p.ref, p.fk_product_type, p.tosell, p.tobuy, p.tobatch, p.fk_price_expression,"; $sql.= " p.entity,"; $sql.= " p.tms as datem"; $sql.= " FROM ".MAIN_DB_PREFIX."product as p"; @@ -336,7 +336,8 @@ if ($result) $product_static->ref=$objp->ref; $product_static->label = $objp->label; $product_static->type=$objp->fk_product_type; - $product_static->entity = $objp->entity; + $product_static->entity = $objp->entity; + $product_static->status_batch = $objp->tobatch; print $product_static->getNomUrl(1,'',16); print "
    '.dol_trunc($objp->label,32).'
    '; $supplierinvoicestatic->id = $objp->facid; - $supplierinvoicestatic->ref = $objp->facnumber; + $supplierinvoicestatic->ref = $objp->ref; + $supplierinvoicestatic->ref_supplier = $objp->ref_supplier; + $supplierinvoicestatic->libelle = $objp->libelle; + $supplierinvoicestatic->total_ht = $objp->total_ht; + $supplierinvoicestatic->total_ttc = $objp->total_ttc; + $supplierinvoicestatic->total_tva = $objp->total_tva; + + print '
    '; print $supplierinvoicestatic->getNomUrl(1); print "
    "; $productstatic->id=$objp->rowid; - $productstatic->ref = $objp->ref; - $productstatic->label = $objp->produit; + $productstatic->ref = $objp->ref; + $productstatic->label = $objp->produit; $productstatic->type=$objp->type; $productstatic->entity=$objp->entity; + $productstatic->status_batch=$objp->tobatch; print $productstatic->getNomUrl(1,'stock',16); print '
    '; +print '
    '; print ''; - print ''; + print ''; print ''; print ''; print ''; diff --git a/htdocs/product/stock/mouvement.php b/htdocs/product/stock/mouvement.php index e7329f1d44a..6093a252cb6 100644 --- a/htdocs/product/stock/mouvement.php +++ b/htdocs/product/stock/mouvement.php @@ -53,6 +53,8 @@ $msid=GETPOST('msid','int'); $product_id=GETPOST("product_id"); $action=GETPOST('action','aZ09'); $cancel=GETPOST('cancel','alpha'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'movementlist'; + $idproduct = GETPOST('idproduct','int'); $year = GETPOST("year"); $month = GETPOST("month"); @@ -70,18 +72,16 @@ $limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit; $page = GETPOST("page",'int'); $sortfield = GETPOST("sortfield",'alpha'); $sortorder = GETPOST("sortorder",'alpha'); -if ($page < 0) $page = 0; +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 $offset = $limit * $page; if (! $sortfield) $sortfield="m.datem"; if (! $sortorder) $sortorder="DESC"; $pdluoid=GETPOST('pdluoid','int'); -// Initialize context for list -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'movementlist'; - // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new MouvementStock($db); +$hookmanager->initHooks(array('movementlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -101,12 +101,12 @@ $arrayfields=array( 'm.inventorycode'=>array('label'=>$langs->trans("InventoryCodeShort"), 'checked'=>1), 'm.label'=>array('label'=>$langs->trans("LabelMovement"), 'checked'=>1), 'origin'=>array('label'=>$langs->trans("Origin"), 'checked'=>1), - 'm.value'=>array('label'=>$langs->trans("Qty"), 'checked'=>1), - //'m.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500), + 'm.value'=>array('label'=>$langs->trans("Qty"), 'checked'=>1), + 'm.price'=>array('label'=>$langs->trans("UnitPurchaseValue"), 'checked'=>0), + //'m.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500), //'m.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500) ); -$object = new MouvementStock($db); // To be passed as parameter of executeHooks that need /* @@ -414,10 +414,10 @@ $formother=new FormOther($db); $formproduct=new FormProduct($db); if (!empty($conf->projet->enabled)) $formproject=new FormProjets($db); -$sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.fk_product_type as type, p.entity,"; +$sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.tobatch, p.fk_product_type as type, p.entity,"; $sql.= " e.ref as stock, e.rowid as entrepot_id, e.lieu,"; $sql.= " m.rowid as mid, m.value as qty, m.datem, m.fk_user_author, m.label, m.inventorycode, m.fk_origin, m.origintype,"; -$sql.= " m.batch,"; +$sql.= " m.batch, m.price,"; $sql.= " pl.rowid as lotid, pl.eatby, pl.sellby,"; $sql.= " u.login, u.photo, u.lastname, u.firstname"; // Add fields from extrafields @@ -634,7 +634,7 @@ if ($resql) /* */ /* ************************************************************************** */ - if (empty($action) && $id > 0) + if ((empty($action) || $action == 'list') && $id > 0) { print "
    \n"; @@ -810,8 +810,17 @@ if ($resql) print ''; print ''; } + if (! empty($arrayfields['m.price']['checked'])) + { + // Price + print '
    '; + } + // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; + // Fields from hook $parameters=array('arrayfields'=>$arrayfields); $reshook=$hookmanager->executeHooks('printFieldListOption',$parameters); // Note that $action and $object may have been modified by hook @@ -849,8 +858,11 @@ if ($resql) if (! empty($arrayfields['m.label']['checked'])) print_liste_field_titre($arrayfields['m.label']['label'],$_SERVER["PHP_SELF"], "m.label","",$param,"",$sortfield,$sortorder); if (! empty($arrayfields['origin']['checked'])) print_liste_field_titre($arrayfields['origin']['label'],$_SERVER["PHP_SELF"], "","",$param,"",$sortfield,$sortorder); if (! empty($arrayfields['m.value']['checked'])) print_liste_field_titre($arrayfields['m.value']['label'],$_SERVER["PHP_SELF"], "m.value","",$param,'align="right"',$sortfield,$sortorder); + if (! empty($arrayfields['m.price']['checked'])) print_liste_field_titre($arrayfields['m.price']['label'],$_SERVER["PHP_SELF"], "m.price","",$param,'align="right"',$sortfield,$sortorder); + // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; + // Hook fields $parameters=array('arrayfields'=>$arrayfields); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook @@ -878,6 +890,7 @@ if ($resql) $productstatic->label=$objp->produit; $productstatic->type=$objp->type; $productstatic->entity=$objp->entity; + $productstatic->status_batch=$objp->tobatch; $productlot->id = $objp->lotid; $productlot->batch= $objp->batch; @@ -976,6 +989,13 @@ if ($resql) print $objp->qty; print ''; } + if (! empty($arrayfields['m.price']['checked'])) + { + // Price + print ''; + } // Action column print ''; if ($object->element == 'product') { - print ''; - print ''; + print ''; - print ''; + print ''; } - print ''; - print ''; + print ''; + print ''; print ''; // Purchase price print ''; - print ''; + print ''; print ''; if (! empty($conf->projet->enabled)) { print ''; print ''; } print ''; @@ -133,7 +133,7 @@ if (empty($conf) || ! is_object($conf)) print ''; print ''; print ''; print ''; print ''; diff --git a/htdocs/product/stock/tpl/stocktransfer.tpl.php b/htdocs/product/stock/tpl/stocktransfer.tpl.php index 18f6b831cf2..136f0f0702a 100644 --- a/htdocs/product/stock/tpl/stocktransfer.tpl.php +++ b/htdocs/product/stock/tpl/stocktransfer.tpl.php @@ -72,20 +72,20 @@ if (empty($conf) || ! is_object($conf)) print ''; if ($object->element == 'product') { - print ''; - print ''; + print ''; } if ($object->element == 'stock') { - print ''; - print ''; + print ''; } - print ''; print ''; @@ -125,11 +125,11 @@ if (empty($conf) || ! is_object($conf)) // Label $valformovementlabel=(GETPOST("label")?GETPOST("label"):$langs->trans("MovementTransferStock", $productref)); print ''; - print ''; + print ''; print ''; - print ''; + print ''; print ''; print '
    '.$langs->trans("InventoryCode").''.$langs->trans("InventoryCode").''; - print ''; + print ''; print '
    '; + print '  '; + print ''; + print price($objp->price); + print ''; if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined diff --git a/htdocs/product/stock/productlot_card.php b/htdocs/product/stock/productlot_card.php index 8e639c90108..f269e260b25 100644 --- a/htdocs/product/stock/productlot_card.php +++ b/htdocs/product/stock/productlot_card.php @@ -122,9 +122,11 @@ if (empty($reshook)) if ($action == 'update_extras') { - // Fill array 'array_options' with data from update form + $object->oldcopy = dol_clone($object); + + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) diff --git a/htdocs/product/stock/productlot_list.php b/htdocs/product/stock/productlot_list.php index 76d70c1ad10..1d1f6a89478 100644 --- a/htdocs/product/stock/productlot_list.php +++ b/htdocs/product/stock/productlot_list.php @@ -81,6 +81,7 @@ if ($user->societe_id > 0) } // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array +$object = new Productlot($db); $hookmanager->initHooks(array('product_lotlist')); $extrafields = new ExtraFields($db); @@ -119,7 +120,6 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab } // Load object if id or ref is provided as parameter -$object=new Productlot($db); if (($id > 0 || ! empty($ref)) && $action != 'add') { $result=$object->fetch($id,$ref); diff --git a/htdocs/product/stock/replenish.php b/htdocs/product/stock/replenish.php index e51747fd78c..5a61e8b2a98 100644 --- a/htdocs/product/stock/replenish.php +++ b/htdocs/product/stock/replenish.php @@ -158,6 +158,8 @@ if ($action == 'order' && isset($_POST['valid'])) $line->total_ttc = $line->total_ht + $line->total_tva; $line->remise_percent = $obj->remise_percent; $line->ref_fourn = $obj->ref_fourn; + $line->type = $product->type; + $line->fk_unit = $product->fk_unit; $suppliers[$obj->fk_soc]['lines'][] = $line; } } @@ -202,7 +204,13 @@ if ($action == 'order' && isset($_POST['valid'])) $line->remise_percent, 'HT', 0, - $line->info_bits + $line->type, + 0, + false, + null, + null, + 0, + $line->fk_unit ); } if ($result < 0) { diff --git a/htdocs/product/stock/tpl/stockcorrection.tpl.php b/htdocs/product/stock/tpl/stockcorrection.tpl.php index 732fa8c889d..3d2e1c99e9b 100644 --- a/htdocs/product/stock/tpl/stockcorrection.tpl.php +++ b/htdocs/product/stock/tpl/stockcorrection.tpl.php @@ -68,9 +68,9 @@ if (empty($conf) || ! is_object($conf)) print '
    '.$langs->trans("Warehouse").''; - print $formproduct->selectWarehouses((GETPOST("dwid")?GETPOST("dwid",'int'):(GETPOST('id_entrepot')?GETPOST('id_entrepot','int'):'ifone')), 'id_entrepot', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'minwidth100'); + print ''.$langs->trans("Warehouse").''; + print $formproduct->selectWarehouses((GETPOST("dwid")?GETPOST("dwid",'int'):(GETPOST('id_entrepot')?GETPOST('id_entrepot','int'):($object->element=='product' && $object->fk_default_warehouse?$object->fk_default_warehouse:'ifone'))), 'id_entrepot', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'minwidth100'); print '   '.$langs->trans("Product").''; - print $form->select_produits(GETPOST('product_id'), 'product_id', (empty($conf->global->STOCK_SUPPORTS_SERVICES)?'0':''), 20, 0, -1); + print ''.$langs->trans("Product").''; + print $form->select_produits(GETPOST('product_id'), 'product_id', (empty($conf->global->STOCK_SUPPORTS_SERVICES)?'0':''), 20, 0, -1, 2, '', 0, null, 0, 1, 0, 'maxwidth500'); print '   '; print ''.$langs->trans("NumberOfUnit").''.$langs->trans("NumberOfUnit").'
    '.$langs->trans("UnitPurchaseValue").''.$langs->trans("UnitPurchaseValue").''.$langs->trans('Project').''; - $formproject->select_projects(); + $formproject->select_projects(0, '', 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 0, 0, 'maxwidth300'); print '
    '.$langs->trans("MovementLabel").''; - print ''; + print ''; print ''.$langs->trans("InventoryCode").'
    '.$langs->trans("WarehouseSource").''; - print $formproduct->selectWarehouses((GETPOST("dwid")?GETPOST("dwid",'int'):(GETPOST('id_entrepot')?GETPOST('id_entrepot','int'):'ifone')), 'id_entrepot', 'warehouseopen,warehouseinternal', 1); + print ''.$langs->trans("WarehouseSource").''; + print $formproduct->selectWarehouses((GETPOST("dwid")?GETPOST("dwid",'int'):(GETPOST('id_entrepot')?GETPOST('id_entrepot','int'):($object->element=='product' && $object->fk_default_warehouse?$object->fk_default_warehouse:'ifone'))), 'id_entrepot', 'warehouseopen,warehouseinternal', 1); print ''.$langs->trans("Product").''; - print $form->select_produits(GETPOST('product_id'),'product_id',(empty($conf->global->STOCK_SUPPORTS_SERVICES)?'0':'')); + print ''.$langs->trans("Product").''; + print $form->select_produits(GETPOST('product_id'), 'product_id', (empty($conf->global->STOCK_SUPPORTS_SERVICES)?'0':''), 20, 0, -1, 2, '', 0, null, 0, 1, 0, 'maxwidth500'); print ''.$langs->trans("WarehouseTarget").''; + print ''.$langs->trans("WarehouseTarget").''; print $formproduct->selectWarehouses(GETPOST('id_entrepot_destination'), 'id_entrepot_destination', 'warehouseopen,warehouseinternal', 1); print '
    '.$langs->trans("NumberOfUnit").'
    '.$langs->trans("MovementLabel").''.$langs->trans("MovementLabel").''; - print ''; + print ''; print ''.$langs->trans("InventoryCode").''.$langs->trans("InventoryCode").'
    '; diff --git a/htdocs/product/traduction.php b/htdocs/product/traduction.php index fd13aee1ea2..d0740f7cd71 100644 --- a/htdocs/product/traduction.php +++ b/htdocs/product/traduction.php @@ -251,7 +251,7 @@ if ($action == 'edit') print '
    '; print ''; - print ''; + print ''; print ''; $restrictviewformytask=(empty($conf->global->PROJECT_TIME_SHOW_TASK_NOT_ASSIGNED)?1:0); // Get if user is available or not for each day -$holiday = new Holiday($db); - $isavailable=array(); if (! empty($conf->global->MAIN_DEFAULT_WORKING_DAYS)) { @@ -512,7 +510,14 @@ if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This $cssweekend='weekend'; } -print ''; +$tmpday=dol_time_plus_duree($firstdaytoshow, $idw, 'd'); + +$cssonholiday=''; +if (! $isavailable[$daytoparse]['morning'] && ! $isavailable[$daytoparse]['afternoon']) $cssonholiday.='onholidayallday '; +elseif (! $isavailable[$daytoparse]['morning']) $cssonholiday.='onholidaymorning '; +elseif (! $isavailable[$daytoparse]['afternoon']) $cssonholiday.='onholidayafternoon '; + +print ''; print ''; print ''; print "\n"; @@ -522,8 +527,10 @@ $colspan = 8; if ($conf->use_javascript_ajax) { print ''; - print ''; + print ''; @@ -531,12 +538,17 @@ if ($conf->use_javascript_ajax) $idw = $tmparray['wday']; $cssweekend=''; - /*if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. + if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. { $cssweekend='weekend'; - }*/ + } - print ''; + $cssonholiday=''; + if (! $isavailable[$daytoparse]['morning'] && ! $isavailable[$daytoparse]['afternoon']) $cssonholiday.='onholidayallday '; + elseif (! $isavailable[$daytoparse]['morning']) $cssonholiday.='onholidaymorning '; + elseif (! $isavailable[$daytoparse]['afternoon']) $cssonholiday.='onholidayafternoon '; + + print ''; print ' @@ -595,10 +607,15 @@ if (count($tasksarray) > 0) if ($isdiff) { print ''; - print ''; - print ''; + print ''; + print ''; + print ''; + print ''; + print ''; - print ''; + print ''; @@ -624,12 +643,17 @@ if (count($tasksarray) > 0) $idw = $tmparray['wday']; $cssweekend=''; - /*if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. + if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. { $cssweekend='weekend'; - }*/ + } - print ''; + $cssonholiday=''; + if (! $isavailable[$daytoparse]['morning'] && ! $isavailable[$daytoparse]['afternoon']) $cssonholiday.='onholidayallday '; + elseif (! $isavailable[$daytoparse]['morning']) $cssonholiday.='onholidaymorning '; + elseif (! $isavailable[$daytoparse]['afternoon']) $cssonholiday.='onholidayafternoon '; + + print ''; print ' diff --git a/htdocs/projet/activity/perweek.php b/htdocs/projet/activity/perweek.php index d64a5fad6cd..b7d731d786b 100644 --- a/htdocs/projet/activity/perweek.php +++ b/htdocs/projet/activity/perweek.php @@ -314,6 +314,7 @@ $projectstatic=new Project($db); $project = new Project($db); $taskstatic = new Task($db); $thirdpartystatic = new Societe($db); +$holiday = new Holiday($db); $title=$langs->trans("TimeSpent"); @@ -417,6 +418,38 @@ print ''; print '
    '; + +$startday=dol_mktime(12, 0, 0, $startdayarray['first_month'], $startdayarray['first_day'], $startdayarray['first_year']); + +// Get if user is available or not for each day +$isavailable=array(); +if (! empty($conf->global->MAIN_DEFAULT_WORKING_DAYS)) +{ + $tmparray=explode('-', $conf->global->MAIN_DEFAULT_WORKING_DAYS); + if (count($tmparray) >= 2) + { + $numstartworkingday = $tmparray[0]; + $numendworkingday = $tmparray[1]; + } +} + +for ($idw=0; $idw<7; $idw++) +{ + $dayinloopfromfirstdaytoshow = dol_time_plus_duree($firstdaytoshow, $idw, 'd'); // $firstdaytoshow is a date with hours = 0 + $dayinloop = dol_time_plus_duree($startday, $idw, 'd'); + + // Useless because $dayinloopwithouthours should be same than $dayinloopfromfirstdaytoshow + //$tmparray = dol_getdate($dayinloop); + //$dayinloopwithouthours=dol_mktime(0, 0, 0, $tmparray['mon'], $tmparray['mday'], $tmparray['year']); + //print dol_print_date($dayinloop, 'dayhour').' '; + //print dol_print_date($dayinloopwithouthours, 'dayhour').' '; + //print dol_print_date($dayinloopfromfirstdaytoshow, 'dayhour').'
    '; + + $isavailablefordayanduser = $holiday->verifDateHolidayForTimestamp($usertoprocess->id, $dayinloopfromfirstdaytoshow); + $isavailable[$dayinloopfromfirstdaytoshow]=$isavailablefordayanduser; // in projectLinesPerWeek later, we are using $firstdaytoshow and dol_time_plus_duree to loop on each day +} + + $moreforfilter=''; // Filter on categories @@ -448,7 +481,6 @@ if (! empty($moreforfilter)) print ''; } - print '
    '; print '
    '.$langs->trans('Label').'
    '.$langs->trans('Label').'
    '.$langs->trans('Description').''; $doleditor = new DolEditor("desc-$key", $object->multilangs[$key]["description"], '', 160, 'dolibarr_notes', '', false, true, $conf->global->FCKEDITOR_ENABLE_PRODUCTDESC, ROWS_3, '90%'); $doleditor->Create(); diff --git a/htdocs/projet/activity/perday.php b/htdocs/projet/activity/perday.php index 85848eefedc..43720614a2e 100644 --- a/htdocs/projet/activity/perday.php +++ b/htdocs/projet/activity/perday.php @@ -302,7 +302,7 @@ $projectstatic=new Project($db); $project = new Project($db); $taskstatic = new Task($db); $thirdpartystatic = new Societe($db); - +$holiday = new Holiday($db); $prev = dol_getdate($daytoparse - (24 * 3600)); $prev_year = $prev['year']; @@ -487,8 +487,6 @@ print ''.$langs->trans("HourStart").''.$langs->trans("Duration").''.$langs->trans("Duration").''.$langs->trans("Note").'
    '; + print ''; print $langs->trans("Total"); + print ''; //print ' - '.$langs->trans("ExpectedWorkedHours").': '.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).''; print '
     
     
    '; + print ''; print $langs->trans("OtherFilteredTasks"); print ''; + print ''; $timeonothertasks=($totalforeachday[$daytoparse] - $totalforvisibletasks[$daytoparse]); //if ($timeonothertasks) //{ @@ -615,8 +632,10 @@ if (count($tasksarray) > 0) if ($conf->use_javascript_ajax) { print '
    '; + print ''; print $langs->trans("Total"); + print ''; //print ' - '.$langs->trans("ExpectedWorkedHours").': '.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).''; print '
     
     
    '."\n"; @@ -461,7 +493,7 @@ print ''; print ''; print ''; print ''; -for($idw=0;$idw<7;$idw++) +for ($idw=0;$idw<7;$idw++) { print ''; } @@ -485,44 +517,25 @@ print ''; print ''; -$startday=dol_mktime(12, 0, 0, $startdayarray['first_month'], $startdayarray['first_day'], $startdayarray['first_year']); - -// Get if user is available or not for each day -$holiday = new Holiday($db); - -$isavailable=array(); -if (! empty($conf->global->MAIN_DEFAULT_WORKING_DAYS)) -{ - $tmparray=explode('-', $conf->global->MAIN_DEFAULT_WORKING_DAYS); - if (count($tmparray) >= 2) - { - $numstartworkingday = $tmparray[0]; - $numendworkingday = $tmparray[1]; - } -} - for ($idw=0; $idw<7; $idw++) { $dayinloopfromfirstdaytoshow = dol_time_plus_duree($firstdaytoshow, $idw, 'd'); // $firstdaytoshow is a date with hours = 0 $dayinloop = dol_time_plus_duree($startday, $idw, 'd'); - // Useless because $dayinloopwithouthours should be same than $dayinloopfromfirstdaytoshow - //$tmparray = dol_getdate($dayinloop); - //$dayinloopwithouthours=dol_mktime(0, 0, 0, $tmparray['mon'], $tmparray['mday'], $tmparray['year']); - //print dol_print_date($dayinloop, 'dayhour').' '; - //print dol_print_date($dayinloopwithouthours, 'dayhour').' '; - //print dol_print_date($dayinloopfromfirstdaytoshow, 'dayhour').'
    '; - - $isavailablefordayanduser = $holiday->verifDateHolidayForTimestamp($usertoprocess->id, $dayinloopfromfirstdaytoshow); - $isavailable[$dayinloopfromfirstdaytoshow]=$isavailablefordayanduser; // in projectLinesPerWeek later, we are using $firstdaytoshow and dol_time_plus_duree to loop on each day - $cssweekend=''; if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. { $cssweekend='weekend'; } - print ''; + $tmpday=dol_time_plus_duree($firstdaytoshow, $idw, 'd'); + + $cssonholiday=''; + if (! $isavailable[$tmpday]['morning'] && ! $isavailable[$tmpday]['afternoon']) $cssonholiday.='onholidayallday '; + elseif (! $isavailable[$tmpday]['morning']) $cssonholiday.='onholidaymorning '; + elseif (! $isavailable[$tmpday]['afternoon']) $cssonholiday.='onholidayafternoon '; + + print ''; } print ''; print "\n"; @@ -531,8 +544,8 @@ $colspan=7; if ($conf->use_javascript_ajax) { - print ' - '; + print ''; @@ -540,15 +553,22 @@ if ($conf->use_javascript_ajax) for ($idw = 0; $idw < 7; $idw++) { $cssweekend=''; - /*if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. + if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. { $cssweekend='weekend'; - }*/ + } - print ''; + $tmpday=dol_time_plus_duree($firstdaytoshow, $idw, 'd'); + + $cssonholiday=''; + if (! $isavailable[$tmpday]['morning'] && ! $isavailable[$tmpday]['afternoon']) $cssonholiday.='onholidayallday '; + elseif (! $isavailable[$tmpday]['morning']) $cssonholiday.='onholidaymorning '; + elseif (! $isavailable[$tmpday]['afternoon']) $cssonholiday.='onholidayafternoon '; + + print ''; } - print ' - '; + print ''; + print ''; } @@ -625,7 +645,7 @@ if (count($tasksarray) > 0) $cssweekend='weekend'; } - print ''; + $tmpday=dol_time_plus_duree($firstdaytoshow, $idw, 'd'); + + $cssonholiday=''; + if (! $isavailable[$tmpday]['morning'] && ! $isavailable[$tmpday]['afternoon']) $cssonholiday.='onholidayallday '; + elseif (! $isavailable[$tmpday]['morning']) $cssonholiday.='onholidaymorning '; + elseif (! $isavailable[$tmpday]['afternoon']) $cssonholiday.='onholidayafternoon '; + + print ''; } - print ' + print ''; } } diff --git a/htdocs/projet/admin/project.php b/htdocs/projet/admin/project.php index 0252ab58f36..b1cf4d9773b 100644 --- a/htdocs/projet/admin/project.php +++ b/htdocs/projet/admin/project.php @@ -1,11 +1,11 @@ * Copyright (C) 2011-2016 Laurent Destailleur - * Copyright (C) 2011-2012 Juanjo Menent + * Copyright (C) 2011-2015 Juanjo Menent * Copyright (C) 2011-2015 Philippe Grand * Copyright (C) 2013 Florian Henry - * Copyright (C) 2015 Juanjo Menent * Copyright (C) 2015 Marcos García + * Copyright (C) 2018 Ferran Marcet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -59,7 +59,7 @@ if ($action == 'setmainoptions') else dolibarr_del_const($db, "PROJECT_USE_OPPORTUNITIES", $conf->entity); // Warning, the constant saved and used in code is PROJECT_HIDE_TASKS - if (GETPOST('PROJECT_USE_TASKS')) dolibarr_del_const($db, "PROJECT_USE_TASKS", $conf->entity); + if (GETPOST('PROJECT_USE_TASKS')) dolibarr_del_const($db, "PROJECT_HIDE_TASKS", $conf->entity); else dolibarr_set_const($db, "PROJECT_HIDE_TASKS",1,'chaine',0,'',$conf->entity); } @@ -293,7 +293,7 @@ llxHeader("",$langs->trans("ProjectsSetup")); $form=new Form($db); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ProjectsSetup"),$linkback,'title_setup'); $head=project_admin_prepare_head(); diff --git a/htdocs/projet/admin/project_extrafields.php b/htdocs/projet/admin/project_extrafields.php index eb27110e88c..d247408079c 100644 --- a/htdocs/projet/admin/project_extrafields.php +++ b/htdocs/projet/admin/project_extrafields.php @@ -64,7 +64,7 @@ $textobject=$langs->transnoentitiesnoconv("Project"); llxHeader("",$langs->trans("ProjectsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ProjectsSetup"),$linkback,'title_setup'); diff --git a/htdocs/projet/admin/project_task_extrafields.php b/htdocs/projet/admin/project_task_extrafields.php index 9f47376d02d..01a001bb039 100644 --- a/htdocs/projet/admin/project_task_extrafields.php +++ b/htdocs/projet/admin/project_task_extrafields.php @@ -64,7 +64,7 @@ $textobject=$langs->transnoentitiesnoconv("Project"); llxHeader("",$langs->trans("ProjectsSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ProjectsSetup"),$linkback,'title_setup'); $head = project_admin_prepare_head(); diff --git a/htdocs/projet/card.php b/htdocs/projet/card.php index 71be9b5d4a3..00266b74e9c 100644 --- a/htdocs/projet/card.php +++ b/htdocs/projet/card.php @@ -161,6 +161,7 @@ if (empty($reshook)) $object->statut = $status; $object->opp_status = $opp_status; $object->opp_percent = $opp_percent; + $object->bill_time = (GETPOST('bill_time','alpha')=='on'?1:0); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost($extralabels,$object); @@ -261,6 +262,7 @@ if (empty($reshook)) if (isset($_POST['budget_amount'])) $object->budget_amount= price2num(GETPOST('budget_amount','alpha')); if (isset($_POST['opp_status'])) $object->opp_status = $opp_status; if (isset($_POST['opp_percent'])) $object->opp_percent = $opp_percent; + $object->bill_time = (GETPOST('bill_time','alpha')=='on'?1:0); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost($extralabels,$object); @@ -407,7 +409,7 @@ if (empty($reshook)) if ($result > 0) { setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs'); - header("Location: index.php"); + header("Location: list.php?restore_lastsearch_values=1"); exit; } else @@ -444,6 +446,13 @@ if (empty($reshook)) $comefromclone=true; } } + + // Actions to send emails + $trigger_name='PROJECT_SENTBYMAIL'; + $paramname='id'; + $autocopy='MAIN_MAIL_AUTOCOPY_ORDER_TO'; // used to know the automatic BCC to add + $trackid='proj'.$object->id; + include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php'; } @@ -599,6 +608,14 @@ if ($action == 'create' && $user->rights->projet->creer) print ''; print ''; + // Bill time + if (! empty($conf->global->PROJECT_BILL_TIME_SPENT)) + { + print ''; + print ''; + print ''; + } + if ($conf->categorie->enabled) { // Categories print ''; + // Bill time + if (! empty($conf->global->PROJECT_BILL_TIME_SPENT)) + { + print ''; + print ''; + print ''; + } + // Tags-Categories if ($conf->categorie->enabled) { @@ -948,6 +973,14 @@ elseif ($object->id > 0) print nl2br($object->description); print ''; + // Bill time + if (! empty($conf->global->PROJECT_BILL_TIME_SPENT)) + { + print ''; + print ''; + print ''; + } + // Categories if($conf->categorie->enabled) { print ''; print ''; } - else // error + else { - print $elementarray; + if (! is_array($elementarray)) // error + { + print $elementarray; + } } print "
    '.$langs->trans("ProgressDeclared"). print ''.$langs->trans("TimeSpent").'
    ('.$langs->trans("Everybody").')
    '.$langs->trans("TimeSpent").($usertoprocess->firstname?'
    ('.$usertoprocess->firstname.')':'').'
    '.dol_print_date($dayinloopfromfirstdaytoshow, '%a').'
    '.dol_print_date($dayinloopfromfirstdaytoshow, 'dayreduceformat').'
    '.dol_print_date($dayinloopfromfirstdaytoshow, '%a').'
    '.dol_print_date($dayinloopfromfirstdaytoshow, 'dayreduceformat').'
    '; + print '
    '; print $langs->trans("Total"); print ' - '.$langs->trans("ExpectedWorkedHours").': '.price($usertoprocess->weeklyhours, 1, $langs, 0, 0).''; print '
     
     
     
    '; + print ''; $tmpday=dol_time_plus_duree($firstdaytoshow, $idw, 'd'); $timeonothertasks=($totalforeachday[$tmpday] - $totalforvisibletasks[$tmpday]); if ($timeonothertasks) @@ -651,14 +671,21 @@ if (count($tasksarray) > 0) for ($idw = 0; $idw < 7; $idw++) { $cssweekend=''; - /*if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. + if (($idw + 1) < $numstartworkingday || ($idw + 1) > $numendworkingday) // This is a day is not inside the setup of working days, so we use a week-end css. { $cssweekend='weekend'; - }*/ + } - print '
     
     
     
    '.$langs->trans("BillTime").'
    '.$langs->trans("Categories").''; @@ -826,6 +843,14 @@ elseif ($object->id > 0) print ''; print '
    '.$langs->trans("BillTime").'bill_time) ? ' checked="checked"' : '').'">
    '.$langs->trans("BillTime").''.yn($object->bill_time).'
    '.$langs->trans("Categories").''; @@ -1014,11 +1047,21 @@ elseif ($object->id > 0) jQuery("#divtocloseproject").hide(); } - /* Change percent of default percent of new status is higher */ - if (parseFloat(jQuery("#opp_percent").val()) != parseFloat(defaultpercent)) + /* Change percent with default percent (defaultpercent) if new status (defaultpercent) is higher than current (jQuery("#opp_percent").val()) */ + console.log("oldpercent="+oldpercent); + if (oldpercent != \'\' && (parseFloat(defaultpercent) < parseFloat(oldpercent))) { if (jQuery("#opp_percent").val() != \'\' && oldpercent != \'\') jQuery("#oldopppercent").text(\' - '.dol_escape_js($langs->transnoentities("PreviousValue")).': \'+oldpercent+\' %\'); - jQuery("#opp_percent").val(defaultpercent); + if (parseFloat(oldpercent) != 100) { jQuery("#opp_percent").val(oldpercent); } + else { jQuery("#opp_percent").val(defaultpercent); } + } + else + { + if ((parseFloat(jQuery("#opp_percent").val()) < parseFloat(defaultpercent))); + { + if (jQuery("#opp_percent").val() != \'\' && oldpercent != \'\') jQuery("#oldopppercent").text(\' - '.dol_escape_js($langs->transnoentities("PreviousValue")).': \'+oldpercent+\' %\'); + jQuery("#opp_percent").val(defaultpercent); + } } } @@ -1038,7 +1081,7 @@ elseif ($object->id > 0) // modified by hook if (empty($reshook)) { - if ($action != "edit" ) + if ($action != "edit" && $action != 'presend' ) { // Create event @@ -1049,6 +1092,12 @@ elseif ($object->id > 0) print ''; }*/ + // Send + if ($object->statut != 2) + { + print ''; + } + // Modify if ($object->statut != 2 && $user->rights->projet->creer) { @@ -1186,6 +1235,10 @@ elseif ($object->id > 0) print ""; + if (GETPOST('modelselected')) { + $action = 'presend'; + } + if ($action != 'presend') { print '
    '; @@ -1218,6 +1271,14 @@ elseif ($object->id > 0) print '
    '; } + // Presend form + $modelmail='project'; + $defaulttopic='SendProjectRef'; + $diroutput = $conf->projet->dir_output; + $trackid = 'proj'.$object->id; + + include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php'; + // Hook to add more things on page $parameters=array(); $reshook=$hookmanager->executeHooks('mainCardTabAddMore',$parameters,$object,$action); // Note that $action and $object may have been modified by hook diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 8dbce2790fa..ba200ddcfb0 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -64,6 +64,7 @@ class Project extends CommonObject var $user_close_id; var $public; //!< Tell if this is a public or private project var $budget_amount; + var $bill_time; // Is the time spent on project must be invoiced or not var $statuts_short; var $statuts_long; @@ -178,6 +179,7 @@ class Project extends CommonObject $sql.= ", datee"; $sql.= ", opp_amount"; $sql.= ", budget_amount"; + $sql.= ", bill_time"; $sql.= ", entity"; $sql.= ") VALUES ("; $sql.= "'" . $this->db->escape($this->ref) . "'"; @@ -194,6 +196,7 @@ class Project extends CommonObject $sql.= ", " . ($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : 'null'); $sql.= ", " . (strcmp($this->opp_amount,'') ? price2num($this->opp_amount) : 'null'); $sql.= ", " . (strcmp($this->budget_amount,'') ? price2num($this->budget_amount) : 'null'); + $sql.= ", " . ($this->bill_time ? 1 : 0); $sql.= ", ".$conf->entity; $sql.= ")"; @@ -297,6 +300,7 @@ class Project extends CommonObject $sql.= ", opp_amount = " . (strcmp($this->opp_amount, '') ? price2num($this->opp_amount) : "null"); $sql.= ", budget_amount = " . (strcmp($this->budget_amount, '') ? price2num($this->budget_amount) : "null"); $sql.= ", fk_user_modif = " . $user->id; + $sql.= ", bill_time = " . ($this->bill_time ? 1 : 0); $sql.= " WHERE rowid = " . $this->id; dol_syslog(get_class($this)."::update", LOG_DEBUG); @@ -394,7 +398,8 @@ class Project extends CommonObject if (empty($id) && empty($ref)) return -1; $sql = "SELECT rowid, ref, title, description, public, datec, opp_amount, budget_amount,"; - $sql.= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent, note_private, note_public, model_pdf"; + $sql.= " tms, dateo, datee, date_close, fk_soc, fk_user_creat, fk_user_modif, fk_user_close, fk_statut, fk_opp_status, opp_percent,"; + $sql.= " note_private, note_public, model_pdf, bill_time"; $sql.= " FROM " . MAIN_DB_PREFIX . "projet"; if (! empty($id)) { @@ -441,6 +446,7 @@ class Project extends CommonObject $this->opp_percent = $obj->opp_percent; $this->budget_amount = $obj->budget_amount; $this->modelpdf = $obj->model_pdf; + $this->bill_time = (int) $obj->bill_time; $this->db->free($resql); @@ -584,10 +590,10 @@ class Project extends CommonObject $i++; } $this->db->free($result); - - /* Return array */ - return $elements; } + + /* Return array even if empty*/ + return $elements; } else { @@ -642,45 +648,13 @@ class Project extends CommonObject } } - // Delete tasks - if (! $error) - { - $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task_time"; - $sql.= " WHERE fk_task IN (SELECT rowid FROM " . MAIN_DB_PREFIX . "projet_task WHERE fk_projet=" . $this->id . ")"; + // Fetch tasks + $this->getLinesArray($user); - $resql = $this->db->query($sql); - if (!$resql) - { - $this->errors[] = $this->db->lasterror(); - $error++; - } - } - - if (! $error) - { - $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task_extrafields"; - $sql.= " WHERE fk_object IN (SELECT rowid FROM " . MAIN_DB_PREFIX . "projet_task WHERE fk_projet=" . $this->id . ")"; - - $resql = $this->db->query($sql); - if (!$resql) - { - $this->errors[] = $this->db->lasterror(); - $error++; - } - } - - if (! $error) - { - $sql = "DELETE FROM " . MAIN_DB_PREFIX . "projet_task"; - $sql.= " WHERE fk_projet=" . $this->id; - - $resql = $this->db->query($sql); - if (!$resql) - { - $this->errors[] = $this->db->lasterror(); - $error++; - } - } + // Delete tasks + foreach($this->lines as &$task) { + $task->delete($user); + } // Delete project if (! $error) @@ -1015,17 +989,18 @@ class Project extends CommonObject $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; $linkclose.=' class="classfortooltip"'; - if (! is_object($hookmanager)) { - include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; - $hookmanager=new HookManager($this->db); + /*if (! is_object($hookmanager)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('projectdao')); + $parameters=array('id'=>$this->id); + // Note that $action and $object may have been modified by some hooks + $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); + if ($reshook > 0) + $linkclose = $hookmanager->resPrint; + */ } - $hookmanager->initHooks(array('projectdao')); - $parameters=array('id'=>$this->id); - // Note that $action and $object may have been modified by some hooks - $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); - if ($reshook > 0) - $linkclose = $hookmanager->resPrint; - } $picto = 'projectpub'; if (! $this->public) $picto = 'project'; @@ -1040,6 +1015,18 @@ class Project extends CommonObject $result .= $linkend; if ($withpicto != 2) $result.=(($addlabel && $this->title) ? $sep . dol_trunc($this->title, ($addlabel > 1 ? $addlabel : 0)) : ''); + global $action; + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('projectdao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + return $result; } @@ -1141,10 +1128,10 @@ class Project extends CommonObject * * @param User $user User object * @param int $mode 0=All project I have permission on (assigned to me and public), 1=Projects assigned to me only, 2=Will return list of all projects with no test on contacts - * @param int $list 0=Return array,1=Return string list + * @param int $list 0=Return array, 1=Return string list * @param int $socid 0=No filter on third party, id of third party - * @param string $filter additionnal filter on project (statut, ref, ...) - * @return array or string Array of projects id, or string with projects id separated with "," + * @param string $filter additionnal filter on project (statut, ref, ...) + * @return array or string Array of projects id, or string with projects id separated with "," if list is 1 */ function getProjectsAuthorizedForUser($user, $mode=0, $list=0, $socid=0, $filter='') { diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php index eeacf27aaec..28b3c9b12f3 100644 --- a/htdocs/projet/class/task.class.php +++ b/htdocs/projet/class/task.class.php @@ -1696,7 +1696,7 @@ class Task extends CommonObject */ function getLibStatut($mode=0) { - return $this->LibStatut($this->fk_statut,$mode); + return $this->LibStatut($this->fk_statut, $mode); } /** @@ -1706,18 +1706,18 @@ class Task extends CommonObject * @param integer $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto * @return string Label */ - function LibStatut($statut,$mode=0) + function LibStatut($statut, $mode=0) { // list of Statut of the task $this->statuts[0]='Draft'; - $this->statuts[1]='Validated'; + $this->statuts[1]='ToDo'; $this->statuts[2]='Running'; $this->statuts[3]='Finish'; $this->statuts[4]='Transfered'; $this->statuts_short[0]='Draft'; - $this->statuts_short[1]='Validated'; + $this->statuts_short[1]='ToDo'; $this->statuts_short[2]='Running'; - $this->statuts_short[3]='Finish'; + $this->statuts_short[3]='Completed'; $this->statuts_short[4]='Transfered'; global $langs; @@ -1735,7 +1735,7 @@ class Task extends CommonObject if ($statut==0) return img_picto($langs->trans($this->statuts_short[$statut]),'statut0').' '.$langs->trans($this->statuts_short[$statut]); if ($statut==1) return img_picto($langs->trans($this->statuts_short[$statut]),'statut1').' '.$langs->trans($this->statuts_short[$statut]); if ($statut==2) return img_picto($langs->trans($this->statuts_short[$statut]),'statut3').' '.$langs->trans($this->statuts_short[$statut]); - if ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]),'statut4').' '.$langs->trans($this->statuts_short[$statut]); + if ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]),'statut6').' '.$langs->trans($this->statuts_short[$statut]); if ($statut==4) return img_picto($langs->trans($this->statuts_short[$statut]),'statut6').' '.$langs->trans($this->statuts_short[$statut]); if ($statut==5) return img_picto($langs->trans($this->statuts_short[$statut]),'statut5').' '.$langs->trans($this->statuts_short[$statut]); } @@ -1744,7 +1744,7 @@ class Task extends CommonObject if ($statut==0) return img_picto($langs->trans($this->statuts_short[$statut]),'statut0'); if ($statut==1) return img_picto($langs->trans($this->statuts_short[$statut]),'statut1'); if ($statut==2) return img_picto($langs->trans($this->statuts_short[$statut]),'statut3'); - if ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]),'statut4'); + if ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); if ($statut==4) return img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); if ($statut==5) return img_picto($langs->trans($this->statuts_short[$statut]),'statut5'); } @@ -1753,27 +1753,31 @@ class Task extends CommonObject if ($statut==0) return img_picto($langs->trans($this->statuts_short[$statut]),'statut0').' '.$langs->trans($this->statuts[$statut]); if ($statut==1) return img_picto($langs->trans($this->statuts_short[$statut]),'statut1').' '.$langs->trans($this->statuts[$statut]); if ($statut==2) return img_picto($langs->trans($this->statuts_short[$statut]),'statut3').' '.$langs->trans($this->statuts[$statut]); - if ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]),'statut4').' '.$langs->trans($this->statuts[$statut]); + if ($statut==3) return img_picto($langs->trans($this->statuts_short[$statut]),'statut6').' '.$langs->trans($this->statuts[$statut]); if ($statut==4) return img_picto($langs->trans($this->statuts_short[$statut]),'statut6').' '.$langs->trans($this->statuts[$statut]); if ($statut==5) return img_picto($langs->trans($this->statuts_short[$statut]),'statut5').' '.$langs->trans($this->statuts[$statut]); } if ($mode == 5) - { - if ($statut==0) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut0'); - if ($statut==1) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut1'); - if ($statut==2) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut3'); - if ($statut==3) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut4'); - if ($statut==4) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); - if ($statut==5) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut5'); - } - if ($mode == 6) { /*if ($statut==0) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut0'); if ($statut==1) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut1'); if ($statut==2) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut3'); - if ($statut==3) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut4'); + if ($statut==3) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); if ($statut==4) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); - if ($statut==5) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut5');*/ + if ($statut==5) return $langs->trans($this->statuts_short[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut5'); + */ + //return $this->progress.' %'; + return ' '; + } + if ($mode == 6) + { + /*if ($statut==0) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut0'); + if ($statut==1) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut1'); + if ($statut==2) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut3'); + if ($statut==3) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); + if ($statut==4) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut6'); + if ($statut==5) return $langs->trans($this->statuts[$statut]).' '.img_picto($langs->trans($this->statuts_short[$statut]),'statut5'); + */ //return $this->progress.' %'; return ' '; } diff --git a/htdocs/projet/element.php b/htdocs/projet/element.php index 2b753a50468..8b791b7a9c7 100644 --- a/htdocs/projet/element.php +++ b/htdocs/projet/element.php @@ -1137,9 +1137,12 @@ foreach ($listofreferent as $key => $value) print '
     
    "; print "
    \n"; diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php index 6b3613a7b0a..e57eab2d109 100644 --- a/htdocs/projet/list.php +++ b/htdocs/projet/list.php @@ -39,6 +39,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'projectlist'; $title = $langs->trans("Projects"); @@ -95,12 +96,9 @@ $search_eyear = GETPOST('search_eyear','int'); if ($search_status == '') $search_status=-1; // -1 or 1 - -// Initialize context for list -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'projectlist'; - // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Project($db); +$hookmanager->initHooks(array('projectlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -141,7 +139,6 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab } } -$object = new Project($db); /* @@ -195,6 +192,52 @@ if (empty($reshook)) $permtodelete = $user->rights->projet->supprimer; $uploaddir = $conf->projet->dir_output; include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; + + // Close records + if (! $error && $massaction == 'close' && $user->rights->projet->creer) + { + $db->begin(); + + $objecttmp=new $objectclass($db); + $nbok = 0; + foreach($toselect as $toselectid) + { + $result=$objecttmp->fetch($toselectid); + if ($result > 0) + { + $userWrite = $object->restrictedProjectArea($user,'write'); + if ($userWrite > 0 && $objecttmp->statut == 1) { + $result = $objecttmp->setClose($user); + if ($result <= 0) { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } else $nbok++; + } elseif($userWrite <= 0) { + setEventMessages($langs->trans("DontHavePermissionForCloseProject", $objecttmp->ref), null, 'warnings'); + } else { + setEventMessages($langs->trans("DontHaveTheValidateStatus", $objecttmp->ref), null, 'warnings'); + } + } + else + { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } + } + + if (! $error) + { + if ($nbok > 1) setEventMessages($langs->trans("RecordsClosed", $nbok), null, 'mesgs'); + else setEventMessages($langs->trans("RecordsClosed", $nbok), null, 'mesgs'); + $db->commit(); + } + else + { + $db->rollback(); + } + } } @@ -389,10 +432,18 @@ $arrayofmassactions = array( // 'builddoc'=>$langs->trans("PDFMerge"), ); //if($user->rights->societe->creer) $arrayofmassactions['createbills']=$langs->trans("CreateInvoiceForThisCustomer"); +if ($user->rights->projet->creer) $arrayofmassactions['close']=$langs->trans("Close"); if ($user->rights->societe->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete"); if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); + $massactionbutton=$form->selectMassAction('', $arrayofmassactions); +$newcardbutton=''; +if ($user->rights->projet->creer) +{ + $newcardbutton = ''.$langs->trans('NewProject').''; +} + print ''; if ($optioncss != '') print ''; print ''; @@ -404,7 +455,7 @@ print ''; print ''; print ''; -print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_project', 0, '', '', $limit); +print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_project', 0, $newcardbutton, '', $limit); // Show description of content print '
    '; @@ -508,19 +559,19 @@ if (! empty($arrayfields['commercial']['checked'])) // Start date if (! empty($arrayfields['p.dateo']['checked'])) { - print ''; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - $formother->select_year($search_syear?$search_syear:-1,'search_syear',1, 20, 5); + print ''; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + $formother->select_year($search_syear?$search_syear:-1,'search_syear',1, 20, 5, 0, 0, '', 'widthauto valignmiddle'); print ''; } // End date if (! empty($arrayfields['p.datee']['checked'])) { - print ''; - if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; - print ''; - $formother->select_year($search_eyear?$search_eyear:-1,'search_eyear',1, 20, 5); + print ''; + if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print ''; + print ''; + $formother->select_year($search_eyear?$search_eyear:-1,'search_eyear',1, 20, 5, 0, 0, '', 'widthauto valignmiddle'); print ''; } if (! empty($arrayfields['p.public']['checked'])) diff --git a/htdocs/projet/tasks.php b/htdocs/projet/tasks.php index 11367951976..4a5bcdab70b 100644 --- a/htdocs/projet/tasks.php +++ b/htdocs/projet/tasks.php @@ -455,29 +455,25 @@ else if ($id > 0 || ! empty($ref)) } } + print '
    '; - /* - * Actions - */ - print '
    '; - + // Link to create task if ($user->rights->projet->all->creer || $user->rights->projet->creer) { if ($object->public || $userWrite > 0) { - print ''.$langs->trans('AddTask').''; + $linktocreatetask = ''.$langs->trans('AddTask').''; } else { - print ''.$langs->trans('AddTask').''; + $linktocreatetask = ''.$langs->trans('AddTask').''; } } else { - print ''.$langs->trans('AddTask').''; + $linktocreatetask = ''.$langs->trans('AddTask').''; } - print '
    '; print ''; @@ -492,8 +488,9 @@ else if ($id > 0 || ! empty($ref)) $title=$langs->trans("ListOfTasks"); $linktotasks=''.$langs->trans("GoToListOfTimeConsumed").''; + //print_barre_liste($title, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, $linktotasks, $num, $totalnboflines, 'title_generic.png', 0, '', '', 0, 1); - print load_fiche_titre($title,$linktotasks,'title_generic.png'); + print load_fiche_titre($title, $linktotasks.'   '.$linktocreatetask, 'title_generic.png'); // Get list of tasks in tasksarray and taskarrayfiltered // We need all tasks (even not limited to a user because a task to user can have a parent that is not affected to him). @@ -578,7 +575,7 @@ else if ($id > 0 || ! empty($ref)) } else { - if ($nboftaskshown < count($tasksarray)) + if ($nboftaskshown < count($tasksarray) && ! GETPOST('search_user_id','int')) { include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; cleanCorruptedTree($db, 'projet_task', 'fk_task_parent'); diff --git a/htdocs/projet/tasks/comment.php b/htdocs/projet/tasks/comment.php index fb553a1b837..cf6e29d5ef6 100644 --- a/htdocs/projet/tasks/comment.php +++ b/htdocs/projet/tasks/comment.php @@ -97,7 +97,12 @@ if ($id > 0 || ! empty($ref)) { if ($object->fetch($id,$ref) > 0) { - $res=$object->fetch_optionals(); + $result=$object->fetch_optionals(); + + $result=$object->fetchComments(); + if ($result<0){ + setEventMessages($object->error,$object->errors,'errors'); + } $result=$projectstatic->fetch($object->fk_project); if (! empty($projectstatic->socid)) $projectstatic->fetch_thirdparty(); diff --git a/htdocs/projet/tasks/contact.php b/htdocs/projet/tasks/contact.php index a2bb784c062..a2db82fc0ed 100644 --- a/htdocs/projet/tasks/contact.php +++ b/htdocs/projet/tasks/contact.php @@ -285,7 +285,7 @@ if ($id > 0 || ! empty($ref)) //$arrayofuseridoftask=$object->getListContactId('internal'); $head = task_prepare_head($object); - dol_fiche_head($head, 'task_contact', $langs->trans("Task"), -1, 'projecttask'); + dol_fiche_head($head, 'task_contact', $langs->trans("Task"), -1, 'projecttask', 0, '', 'reposition'); $param=(GETPOST('withproject')?'&withproject=1':''); diff --git a/htdocs/projet/tasks/document.php b/htdocs/projet/tasks/document.php index 7fc0b981902..1cb5acb73b5 100644 --- a/htdocs/projet/tasks/document.php +++ b/htdocs/projet/tasks/document.php @@ -224,7 +224,7 @@ if ($object->id > 0) } $head = task_prepare_head($object); - dol_fiche_head($head, 'task_document', $langs->trans("Task"), -1, 'projecttask'); + dol_fiche_head($head, 'task_document', $langs->trans("Task"), -1, 'projecttask', 0, '', 'reposition'); // Files list constructor $filearray=dol_dir_list($upload_dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1); diff --git a/htdocs/projet/tasks/list.php b/htdocs/projet/tasks/list.php index 6ad2e51425f..9e14c462bef 100644 --- a/htdocs/projet/tasks/list.php +++ b/htdocs/projet/tasks/list.php @@ -71,7 +71,8 @@ $search_eyear = GETPOST('search_eyear','int'); $contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'tasklist'; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Task($db); +$hookmanager->initHooks(array('tasklist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -131,8 +132,6 @@ if (is_array($extrafields->attribute_label) && count($extrafields->attribute_lab } } -$object = new Task($db); - /* * Actions @@ -391,6 +390,12 @@ if ($user->rights->societe->supprimer) $arrayofmassactions['predelete']=$langs-> if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); +$newcardbutton=''; +if ($user->rights->projet->creer) +{ + $newcardbutton = ''.$langs->trans('NewTask').''; +} + print ''; if ($optioncss != '') print ''; print ''; @@ -402,7 +407,7 @@ print ''; print ''; print ''; -print_barre_liste($title, $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_project', 0, '', '', $limit); +print_barre_liste($title, $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_project', 0, $newcardbutton, '', $limit); // Show description of content print '
    '; diff --git a/htdocs/projet/tasks/note.php b/htdocs/projet/tasks/note.php index da33b8b7607..f915aaf836a 100644 --- a/htdocs/projet/tasks/note.php +++ b/htdocs/projet/tasks/note.php @@ -202,7 +202,7 @@ if ($object->id > 0) } $head = task_prepare_head($object); - dol_fiche_head($head, 'task_notes', $langs->trans('Task'), -1, 'projecttask'); + dol_fiche_head($head, 'task_notes', $langs->trans('Task'), -1, 'projecttask', 0, '', 'reposition'); $param=(GETPOST('withproject')?'&withproject=1':''); diff --git a/htdocs/projet/tasks/task.php b/htdocs/projet/tasks/task.php index 3d1ccfb8fe1..10f64869257 100644 --- a/htdocs/projet/tasks/task.php +++ b/htdocs/projet/tasks/task.php @@ -125,7 +125,7 @@ if ($action == 'confirm_delete' && $confirm == "yes" && $user->rights->projet->s if ($object->delete($user) > 0) { - header('Location: '.DOL_URL_ROOT.'/projet/tasks.php?id='.$projectstatic->id.($withproject?'&withproject=1':'')); + header('Location: '.DOL_URL_ROOT.'/projet/tasks.php?restore_lastsearch_values=1&id='.$projectstatic->id.($withproject?'&withproject=1':'')); exit; } else @@ -221,7 +221,7 @@ if ($id > 0 || ! empty($ref)) // Tabs for project $tab='tasks'; $head=project_prepare_head($projectstatic); - dol_fiche_head($head, $tab, $langs->trans("Project"), -1, ($projectstatic->public?'projectpub':'project')); + dol_fiche_head($head, $tab, $langs->trans("Project"), -1, ($projectstatic->public?'projectpub':'project'), 0, '', ''); $param=($mode=='mine'?'&mode=mine':''); @@ -352,7 +352,7 @@ if ($id > 0 || ! empty($ref)) print ''; print ''; - dol_fiche_head($head, 'task_task', $langs->trans("Task"),0,'projecttask'); + dol_fiche_head($head, 'task_task', $langs->trans("Task"), 0, 'projecttask', 0, '', ''); print ''; @@ -437,7 +437,7 @@ if ($id > 0 || ! empty($ref)) $param=($withproject?'&withproject=1':''); $linkback=$withproject?''.$langs->trans("BackToList").'':''; - dol_fiche_head($head, 'task_task', $langs->trans("Task"), -1, 'projecttask'); + dol_fiche_head($head, 'task_task', $langs->trans("Task"), -1, 'projecttask', 0, '', 'reposition'); if ($action == 'delete') { @@ -586,7 +586,7 @@ if ($id > 0 || ! empty($ref)) } else { - print ''.$langs->trans('Delete').''; + print ''.$langs->trans('Delete').''; } } else diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index 49e917e4b61..e61d3ceb8ce 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -1,6 +1,6 @@ - * Copyright (C) 2006-2017 Laurent Destailleur + * Copyright (C) 2006-2018 Laurent Destailleur * Copyright (C) 2010-2012 Regis Houssin * Copyright (C) 2011 Juanjo Menent * @@ -30,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; $langs->load('projects'); @@ -38,6 +39,7 @@ $projectid=GETPOST('projectid','int'); $ref=GETPOST('ref','alpha'); $action=GETPOST('action','alpha'); $confirm=GETPOST('confirm','alpha'); +$cancel=GETPOST('cancel','alpha'); $withproject=GETPOST('withproject','int'); $project_ref=GETPOST('project_ref','alpha'); @@ -70,6 +72,7 @@ if (! $sortfield) $sortfield='t.task_date,t.task_datehour,t.rowid'; if (! $sortorder) $sortorder='DESC'; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +//$object = new TaskTime($db); $hookmanager->initHooks(array('projecttaskcard','globalcard')); $object = new Task($db); @@ -77,11 +80,7 @@ $projectstatic = new Project($db); $extrafields_project = new ExtraFields($db); $extrafields_task = new ExtraFields($db); -if ($projectid > 0 || ! empty($ref)) -{ - // fetch optionals attributes and labels - $extralabels_projet=$extrafields_project->fetch_name_optionals_label($projectstatic->table_element); -} +$extralabels_projet=$extrafields_project->fetch_name_optionals_label($projectstatic->table_element); $extralabels_task=$extrafields_task->fetch_name_optionals_label($object->table_element); @@ -89,6 +88,8 @@ $extralabels_task=$extrafields_task->fetch_name_optionals_label($object->table_e * Actions */ +if (GETPOST('cancel','alpha')) { $action=''; } + $parameters=array('socid'=>$socid, 'projectid'=>$projectid); $reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); @@ -137,10 +138,17 @@ if ($action == 'addtimespent' && $user->rights->projet->lire) if (! $error) { - $object->fetch($id, $ref); + if ($id || $ref) + { + $object->fetch($id, $ref); + } + else + { + $object->fetch(GETPOST('taskid','int')); + } $object->fetch_projet(); - if (empty($object->projet->statut)) + if (empty($object->project->statut)) { setEventMessages($langs->trans("ProjectMustBeValidatedFirst"), null, 'errors'); $error++; @@ -175,7 +183,8 @@ if ($action == 'addtimespent' && $user->rights->projet->lire) } else { - $action=''; + if (empty($id)) $action='createtime'; + else $action='createtime'; } } @@ -229,7 +238,7 @@ if ($action == 'updateline' && ! $_POST["cancel"] && $user->rights->projet->lire if ($action == 'confirm_delete' && $confirm == "yes" && $user->rights->projet->lire) { - $object->fetchTimeSpent($_GET['lineid']); + $object->fetchTimeSpent(GETPOST('lineid','int')); // TODO Check that ($task_time->fk_user == $user->id || in_array($task_time->fk_user, $childids)) $result = $object->delTimeSpent($user); @@ -240,6 +249,10 @@ if ($action == 'confirm_delete' && $confirm == "yes" && $user->rights->projet->l $error++; $action=''; } + else + { + setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs'); + } } // Retreive First Task ID of Project if withprojet is on to allow project prev next to work @@ -262,10 +275,9 @@ if (! empty($project_ref) && ! empty($withproject)) // To show all time lines for project $projectidforalltimes=0; -if (GETPOST('projectid')) +if (GETPOST('projectid','none')) { - $projectidforalltimes=GETPOST('projectid','int'); - + $projectidforalltimes=GETPOST('projectid','int'); } @@ -277,6 +289,7 @@ llxHeader("",$langs->trans("Task")); $form = new Form($db); $formother = new FormOther($db); +$formproject = new FormProjets($db); $userstatic = new User($db); if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) @@ -397,48 +410,38 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) dol_fiche_end(); + print '
    '; - /* - * Actions - */ - - if ((empty($id) && empty($ref)) || ! empty($projectidforalltimes)) - { - print '
    '; - + // Link to create time + //if ((empty($id) && empty($ref)) || ! empty($projectidforalltimes)) + //{ if ($user->rights->projet->all->creer || $user->rights->projet->creer) { - if ($object->public || $userWrite > 0) + if ($projectstatic->public || $userWrite > 0) { - print ''.$langs->trans('AddTask').''; + $linktocreatetime = ''.$langs->trans('AddTimeSpent').''; } else { - print ''.$langs->trans('AddTask').''; + $linktocreatetime = ''.$langs->trans('AddTime').''; } } else { - print ''.$langs->trans('AddTask').''; + $linktocreatetime = ''.$langs->trans('AddTime').''; } - - print '
    '; - } - else - { - print '
    '; - } + //} } } if (empty($projectidforalltimes)) { $head=task_prepare_head($object); - dol_fiche_head($head, 'task_time', $langs->trans("Task"), -1, 'projecttask'); + dol_fiche_head($head, 'task_time', $langs->trans("Task"), -1, 'projecttask', 0, '', 'reposition'); if ($action == 'deleteline') { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id.'&lineid='.$_GET["lineid"].($withproject?'&withproject=1':''),$langs->trans("DeleteATimeSpent"),$langs->trans("ConfirmDeleteATimeSpent"),"confirm_delete",'','',1); + print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id>0?"id=".$object->id:'projectid='.$projectstatic->id).'&lineid='.GETPOST("lineid",'int').($withproject?'&withproject=1':''),$langs->trans("DeleteATimeSpent"),$langs->trans("ConfirmDeleteATimeSpent"),"confirm_delete",'','',1); } $param=($withproject?'&withproject=1':''); @@ -526,20 +529,26 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) dol_fiche_end(); + print ''."\n"; + + $title=$langs->trans("ListTaskTimeForTask"); + //print_barre_liste($title, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, $linktotasks, $num, $totalnboflines, 'title_generic.png', 0, '', '', 0, 1); + print load_fiche_titre($title, $linktocreatetime, 'title_generic.png'); /* - * Form to add time spent + * Form to add time spent on task */ - if ($user->rights->projet->lire) - { - print '
    '; + if ($action == 'createtime' && $object->id > 0 && $user->rights->projet->lire) + { + print ''."\n"; print ''; print ''; print ''; print ''; print ''; + print '
    '; print '
    '; print ''; @@ -591,10 +600,15 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) print ''; print ''; - print '
    '; - print ''; + print ''; + print '   '; + print ''; print '
    '; + print ''; + print '
    '; + + print ''; print '
    '; } @@ -602,9 +616,9 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) if ($projectstatic->id > 0) { - if ($action == 'deleteline') + if ($action == 'deleteline' && ! empty($projectidforalltimes)) { - print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id.'&lineid='.$_GET["lineid"].($withproject?'&withproject=1':''),$langs->trans("DeleteATimeSpent"),$langs->trans("ConfirmDeleteATimeSpent"),"confirm_delete",'','',1); + print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id>0?"id=".$object->id:'projectid='.$projectstatic->id).'&lineid='.GETPOST('lineid','int').($withproject?'&withproject=1':''),$langs->trans("DeleteATimeSpent"),$langs->trans("ConfirmDeleteATimeSpent"),"confirm_delete",'','',1); } // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array @@ -623,6 +637,7 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) $arrayfields['t.note']=array('label'=>$langs->trans("Note"), 'checked'=>1); $arrayfields['t.task_duration']=array('label'=>$langs->trans("Duration"), 'checked'=>1); $arrayfields['value'] =array('label'=>$langs->trans("Value"), 'checked'=>1, 'enabled'=>(empty($conf->salaries->enabled)?0:1)); + $arrayfields['valuebilled'] =array('label'=>$langs->trans("AmountInvoiced"), 'checked'=>1, 'enabled'=>(empty($conf->global->PROJECT_BILL_TIME_SPENT)?0:1)); // Extra fields if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { @@ -639,8 +654,11 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) $sql = "SELECT t.rowid, t.fk_task, t.task_date, t.task_datehour, t.task_date_withhour, t.task_duration, t.fk_user, t.note, t.thm,"; $sql .= " pt.ref, pt.label,"; - $sql .= " u.lastname, u.firstname, u.login, u.photo"; - $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."user as u"; + $sql .= " u.lastname, u.firstname, u.login, u.photo,"; + $sql .= " il.fk_facture as invoice_id, il.total_ht"; + $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facturedet as il ON il.rowid = t.invoice_line_id"; + $sql .= ", ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."user as u"; $sql .= " WHERE t.fk_user = u.rowid AND t.fk_task = pt.rowid"; if (empty($projectidforalltimes)) $sql .= " AND t.fk_task =".$object->id; else $sql.= " AND pt.fk_projet IN (".$projectidforalltimes.")"; @@ -673,10 +691,12 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) if (! empty($projectidforalltimes)) { - $title=$langs->trans("ListTaskTimeUserProject"); + print ''."\n"; + + $title=$langs->trans("ListTaskTimeUserProject"); $linktotasks=''.$langs->trans("GoToListOfTasks").''; //print_barre_liste($title, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, $linktotasks, $num, $totalnboflines, 'title_generic.png', 0, '', '', 0, 1); - print load_fiche_titre($title,$linktotasks,'title_generic.png'); + print load_fiche_titre($title,$linktotasks.'   '.$linktocreatetime, 'title_generic.png'); } $i = 0; @@ -694,21 +714,108 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) } + /* + * Form to add time spent + */ + if ($action == 'createtime' && empty($id) && $user->rights->projet->lire) + { + print ''."\n"; + print '
    '; + print ''; + print ''; + print ''; + print ''; + + print ''; + + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print "\n"; + + print ''; + + // Date + print ''; + + // Task + print ''; + + // Contributor + print ''; + + // Note + print ''; + + // Duration - Time spent + print ''; + + // Progress declared + print ''; + + print ''; + + print '
    '.$langs->trans("Date").''.$langs->trans("Task").''.$langs->trans("By").''.$langs->trans("Note").''.$langs->trans("NewTimeSpent").''.$langs->trans("ProgressDeclared").'
    '; + //$newdate=dol_mktime(12,0,0,$_POST["timemonth"],$_POST["timeday"],$_POST["timeyear"]); + $newdate=''; + print $form->select_date($newdate, 'time', ($conf->browser->layout == 'phone'?2:1), 1, 2, "timespent_date", 1, 0, 1); + print ''; + $formproject->selectTasks(-1, GETPOST('taskid','int'), 'taskid', 0, 0, 1, 1, 0, 0, 'maxwidth300', $projectstatic->id, ''); + print ''; + print img_object('','user','class="hideonsmartphone"'); + $contactsofproject=$projectstatic->getListContactId('internal'); + if (count($contactsofproject)>0) + { + if (in_array($user->id, $userid=$contactsofproject)) $userid = $user->id; + else $userid=$contactsofproject[0]; + if ($projectstatic->public) $contactsofproject = array(); + print $form->select_dolusers((GETPOST('userid')?GETPOST('userid'):$userid), 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'maxwidth200'); + } + else + { + print img_error($langs->trans('FirstAddRessourceToAllocateTime')).$langs->trans('FirstAddRessourceToAllocateTime'); + } + print ''; + print ''; + print ''; + print $form->select_duration('timespent_duration', ($_POST['timespent_duration']?$_POST['timespent_duration']:''), 0, 'text'); + print ''; + print $formother->select_percent(GETPOST('progress')?GETPOST('progress'):$object->progress,'progress'); + print ''; + print ''; + print '   '; + print ''; + print '
    '; + + print '
    '; + } + + $arrayofselected=is_array($toselect)?$toselect:array(); - $params=''; + $param=''; if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit; - if ($search_note != '') $params.= '&search_note='.urlencode($search_note); - if ($search_duration != '') $params.= '&search_field2='.urlencode($search_duration); - if ($optioncss != '') $param.='&optioncss='.$optioncss; + if ($search_month > 0) $param.= '&search_month='.urlencode($search_month); + if ($search_year > 0) $param.= '&search_year='.urlencode($search_year); + if ($search_user > 0) $param.= '&search_user='.urlencode($search_user); + if ($search_task_ref != '') $param.= '&search_task_ref='.urlencode($search_task_ref); + if ($search_task_label != '') $param.= '&search_task_label='.urlencode($search_task_label); + if ($search_note != '') $param.= '&search_note='.urlencode($search_note); + if ($search_duration != '') $param.= '&search_field2='.urlencode($search_duration); + if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); /* // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; */ - if ($id) $params.='&id='.$id; - if ($projectid) $params.='&projectid='.$projectid; - if ($withproject) $params.='&withproject='.$withproject; + if ($id) $param.='&id='.urlencode($id); + if ($projectid) $param.='&projectid='.urlencode($projectid); + if ($withproject) $param.='&withproject='.urlencode($withproject); $arrayofmassactions = array( @@ -776,9 +883,11 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) if (! empty($arrayfields['t.note']['checked'])) print ''; // Duration if (! empty($arrayfields['t.task_duration']['checked'])) print ''; - // Value in currency + // Value in main currency if (! empty($arrayfields['value']['checked'])) print ''; - /* + // Value billed + if (! empty($arrayfields['valuebilled']['checked'])) print ''; + /* // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; */ @@ -794,16 +903,17 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) print ''."\n"; print ''; - if (! empty($arrayfields['t.task_date']['checked'])) print_liste_field_titre($arrayfields['t.task_date']['label'],$_SERVER['PHP_SELF'],'t.task_date,t.task_datehour,t.rowid','',$params,'',$sortfield,$sortorder); + if (! empty($arrayfields['t.task_date']['checked'])) print_liste_field_titre($arrayfields['t.task_date']['label'],$_SERVER['PHP_SELF'],'t.task_date,t.task_datehour,t.rowid','',$param,'',$sortfield,$sortorder); if ((empty($id) && empty($ref)) || ! empty($projectidforalltimes)) // Not a dedicated task { - if (! empty($arrayfields['t.task_ref']['checked'])) print_liste_field_titre($arrayfields['t.task_ref']['label'],$_SERVER['PHP_SELF'],'pt.ref','',$params,'',$sortfield,$sortorder); - if (! empty($arrayfields['t.task_label']['checked'])) print_liste_field_titre($arrayfields['t.task_label']['label'],$_SERVER['PHP_SELF'],'pt.label','',$params,'',$sortfield,$sortorder); + if (! empty($arrayfields['t.task_ref']['checked'])) print_liste_field_titre($arrayfields['t.task_ref']['label'],$_SERVER['PHP_SELF'],'pt.ref','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['t.task_label']['checked'])) print_liste_field_titre($arrayfields['t.task_label']['label'],$_SERVER['PHP_SELF'],'pt.label','',$param,'',$sortfield,$sortorder); } - if (! empty($arrayfields['author']['checked'])) print_liste_field_titre($arrayfields['author']['label'],$_SERVER['PHP_SELF'],'','',$params,'',$sortfield,$sortorder); - if (! empty($arrayfields['t.note']['checked'])) print_liste_field_titre($arrayfields['t.note']['label'],$_SERVER['PHP_SELF'],'t.note','',$params,'',$sortfield,$sortorder); - if (! empty($arrayfields['t.task_duration']['checked'])) print_liste_field_titre($arrayfields['t.task_duration']['label'],$_SERVER['PHP_SELF'],'t.task_duration','',$params,'align="right"',$sortfield,$sortorder); - if (! empty($arrayfields['value']['checked'])) print_liste_field_titre($arrayfields['value']['label'],$_SERVER['PHP_SELF'],'','',$params,'align="right"',$sortfield,$sortorder); + if (! empty($arrayfields['author']['checked'])) print_liste_field_titre($arrayfields['author']['label'],$_SERVER['PHP_SELF'],'','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['t.note']['checked'])) print_liste_field_titre($arrayfields['t.note']['label'],$_SERVER['PHP_SELF'],'t.note','',$param,'',$sortfield,$sortorder); + if (! empty($arrayfields['t.task_duration']['checked'])) print_liste_field_titre($arrayfields['t.task_duration']['label'],$_SERVER['PHP_SELF'],'t.task_duration','',$param,'align="right"',$sortfield,$sortorder); + if (! empty($arrayfields['value']['checked'])) print_liste_field_titre($arrayfields['value']['label'],$_SERVER['PHP_SELF'],'','',$param,'align="right"',$sortfield,$sortorder); + if (! empty($arrayfields['valuebilled']['checked'])) print_liste_field_titre($arrayfields['valuebilled']['label'],$_SERVER['PHP_SELF'],'il.total_ht','',$param,'align="right"',$sortfield,$sortorder); /* // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; @@ -952,6 +1062,18 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) $totalarray['totalvalue'] += $value; } + // Value billed + if (! empty($arrayfields['valuebilled']['checked'])) + { + print ''; + $valuebilled = price2num($task_time->total_ht); + if (isset($task_time->total_ht)) print price($valuebilled, 1, $langs, 1, -1, -1, $conf->currency); + print ''; + if (! $i) $totalarray['nbfield']++; + if (! $i) $totalarray['totalvaluefield']=$totalarray['nbfield']; + $totalarray['totalvaluebilled'] += $valuebilled; + } + /* // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; @@ -975,13 +1097,14 @@ if (($id > 0 || ! empty($ref)) || $projectidforalltimes > 0) { if ($task_time->fk_user == $user->id || in_array($task_time->fk_user, $childids) || $user->rights->projet->all->creer) { + //$param = ($projectidforalltimes?'projectid='.$projectidforalltimes.'&':'').'.($withproject?'&withproject=1':''); print ' '; - print 'rowid.($withproject?'&withproject=1':'').'">'; + print 'rowid.$param.'">'; print img_edit(); print ''; print ' '; - print 'rowid.($withproject?'&withproject=1':'').'">'; + print 'rowid.$param.'">'; print img_delete(); print ''; } diff --git a/htdocs/public/agenda/agendaexport.php b/htdocs/public/agenda/agendaexport.php index 81a67204fdb..c8f7f05ff23 100644 --- a/htdocs/public/agenda/agendaexport.php +++ b/htdocs/public/agenda/agendaexport.php @@ -69,14 +69,16 @@ if (GETPOST("format",'alpha')) $format=GETPOST("format",'apha'); if (GETPOST("type",'apha')) $type=GETPOST("type",'alpha'); $filters=array(); -if (GETPOST("year",'int')) $filters['year']=GETPOST("year",'int'); -if (GETPOST("id",'int')) $filters['id']=GETPOST("id",'int'); -if (GETPOST("idfrom",'int')) $filters['idfrom']=GETPOST("idfrom",'int'); -if (GETPOST("idto",'int')) $filters['idto']=GETPOST("idto",'int'); -if (GETPOST("project",'apha')) $filters['project']=GETPOST("project",'apha'); -if (GETPOST("logina",'apha')) $filters['logina']=GETPOST("logina",'apha'); -if (GETPOST("logint",'apha')) $filters['logint']=GETPOST("logint",'apha'); -if (GETPOST("notolderthan",'int')) $filters['notolderthan']=GETPOST("notolderthan","int"); +if (GETPOST("year",'int')) $filters['year']=GETPOST("year",'int'); +if (GETPOST("id",'int')) $filters['id']=GETPOST("id",'int'); +if (GETPOST("idfrom",'int')) $filters['idfrom']=GETPOST("idfrom",'int'); +if (GETPOST("idto",'int')) $filters['idto']=GETPOST("idto",'int'); +if (GETPOST("project",'apha')) $filters['project']=GETPOST("project",'apha'); +if (GETPOST("logina",'apha')) $filters['logina']=GETPOST("logina",'apha'); +if (GETPOST("logint",'apha')) $filters['logint']=GETPOST("logint",'apha'); +if (GETPOST("notactiontype",'apha')) $filters['notactiontype']=GETPOST("notactiontype",'apha'); +if (GETPOST("actiontype",'apha')) $filters['actiontype']=GETPOST("actiontype",'apha'); +if (GETPOST("notolderthan",'int')) $filters['notolderthan']=GETPOST("notolderthan","int"); else $filters['notolderthan']=$conf->global->MAIN_AGENDA_EXPORT_PAST_DELAY; // Check config @@ -116,6 +118,7 @@ foreach ($filters as $key => $value) if ($key == 'project') $filename.='-project'.$value; if ($key == 'logina') $filename.='-logina'.$value; // Author if ($key == 'logint') $filename.='-logint'.$value; // Assigned to + if ($key == 'notactiontype') $filename.='-notactiontype'.$value; } // Add extension if ($format == 'vcal') { $shortfilename.='.vcs'; $filename.='.vcs'; } diff --git a/htdocs/public/demo/index.php b/htdocs/public/demo/index.php index 61685ed8267..61919c54b1a 100644 --- a/htdocs/public/demo/index.php +++ b/htdocs/public/demo/index.php @@ -178,8 +178,6 @@ foreach ($modulesdir as $dir) $filename[$i]= $modName; $orders[$i] = $objMod->family."_".$j; // Tri par famille puis numero module //print "x".$modName." ".$orders[$i]."\n
    "; - if (isset($categ[$objMod->special])) $categ[$objMod->special]++; // Array of all different modules categories - else $categ[$objMod->special]=1; $dirmod[$i] = $dirroot; $j++; $i++; @@ -329,7 +327,7 @@ foreach ($demoprofiles as $profilearray) print '
    '; } - print '
    '."\n"; + print ''."\n"; print ''."\n"; print ''."\n"; print ''."\n"; @@ -340,7 +338,7 @@ foreach ($demoprofiles as $profilearray) print ''."\n"; print ''."\n"; - print '
    '."\n"; + print '
    '."\n"; print '
    '; diff --git a/htdocs/public/members/new.php b/htdocs/public/members/new.php index 978c646a524..1ba869796ed 100644 --- a/htdocs/public/members/new.php +++ b/htdocs/public/members/new.php @@ -142,12 +142,17 @@ function llxFooterVierge() // Action called when page is submitted if ($action == 'add') { + $error = 0; + $urlback=''; + + $db->begin(); + // test if login already exists if (empty($conf->global->ADHERENT_LOGIN_NOT_REQUIRED)) { if(! GETPOST('login')) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Login"))."
    \n"; } $sql = "SELECT login FROM ".MAIN_DB_PREFIX."adherent WHERE login='".$db->escape(GETPOST('login'))."'"; @@ -158,52 +163,52 @@ if ($action == 'add') } if ($num !=0) { - $error+=1; + $error++; $langs->load("errors"); $errmsg .= $langs->trans("ErrorLoginAlreadyExists")."
    \n"; } if (!isset($_POST["pass1"]) || !isset($_POST["pass2"]) || $_POST["pass1"] == '' || $_POST["pass2"] == '' || $_POST["pass1"]!=$_POST["pass2"]) { - $error+=1; + $error++; $langs->load("errors"); $errmsg .= $langs->trans("ErrorPasswordsMustMatch")."
    \n"; } if (! GETPOST("email")) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("EMail"))."
    \n"; } } if (GETPOST('type') <= 0) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Type"))."
    \n"; } if (! in_array(GETPOST('morphy'),array('mor','phy'))) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv('Nature'))."
    \n"; } if (empty($_POST["lastname"])) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Lastname"))."
    \n"; } if (empty($_POST["firstname"])) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Firstname"))."
    \n"; } if (GETPOST("email") && ! isValidEmail(GETPOST("email"))) { - $error+=1; + $error++; $langs->load("errors"); $errmsg .= $langs->trans("ErrorBadEMail",GETPOST("email"))."
    \n"; } $birthday=dol_mktime($_POST["birthhour"],$_POST["birthmin"],$_POST["birthsec"],$_POST["birthmonth"],$_POST["birthday"],$_POST["birthyear"]); if ($_POST["birthmonth"] && empty($birthday)) { - $error+=1; + $error++; $langs->load("errors"); $errmsg .= $langs->trans("ErrorBadDateFormat")."
    \n"; } @@ -211,7 +216,7 @@ if ($action == 'add') { if (GETPOST("morphy") == 'mor' && GETPOST('budget') <= 0) { - $error+=1; + $error++; $errmsg .= $langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("TurnoverOrBudget"))."
    \n"; } } @@ -256,21 +261,72 @@ if ($action == 'add') if ($result > 0) { require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + $object = $adh; - // Send email to say it has been created and will be validated soon... - if (! empty($conf->global->ADHERENT_AUTOREGISTER_MAIL) && ! empty($conf->global->ADHERENT_AUTOREGISTER_MAIL_SUBJECT)) + $adht = new AdherentType($db); + $adht->fetch($object->typeid); + + if ($object->email) { - $result=$adh->send_an_email($conf->global->ADHERENT_AUTOREGISTER_MAIL,$conf->global->ADHERENT_AUTOREGISTER_MAIL_SUBJECT,array(),array(),array(),"","",0,-1); + $subject = ''; + $msg= ''; + + // Send subscription email + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + $formmail=new FormMail($db); + // Set output language + $outputlangs = new Translate('', $conf); + $outputlangs->setDefaultLang(empty($object->thirdparty->default_lang) ? $mysoc->default_lang : $object->thirdparty->default_lang); + $outputlangs->loadLangs(array("main", "members")); + // Get email content fro mtemplae + $arraydefaultmessage=null; + $labeltouse = $conf->global->ADHERENT_EMAIL_TEMPLATE_AUTOREGISTER; + + if (! empty($labeltouse)) $arraydefaultmessage=$formmail->getEMailTemplate($db, 'member', $user, $outputlangs, 0, 1, $labeltouse); + + if (! empty($labeltouse) && is_object($arraydefaultmessage) && $arraydefaultmessage->id > 0) + { + $subject = $arraydefaultmessage->topic; + $msg = $arraydefaultmessage->content; + } + + $substitutionarray=getCommonSubstitutionArray($outputlangs, 0, null, $object); + complete_substitutions_array($substitutionarray, $outputlangs, $object); + $subjecttosend = make_substitutions($subject, $substitutionarray, $outputlangs); + $texttosend = make_substitutions(dol_concatdesc($msg, $adht->getMailOnValid()), $substitutionarray, $outputlangs); + + if ($subjecttosend && $texttosend) + { + $result=$object->send_an_email($texttosend, $subjecttosend, array(), array(), array(), "", "", 0, -1); + } + /*if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + }*/ } // Send email to the foundation to say a new member subscribed with autosubscribe form if (! empty($conf->global->MAIN_INFO_SOCIETE_MAIL) && ! empty($conf->global->ADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT) && ! empty($conf->global->ADHERENT_AUTOREGISTER_NOTIF_MAIL) ) { + // Define link to login card + $appli=constant('DOL_APPLICATION_TITLE'); + if (! empty($conf->global->MAIN_APPLICATION_TITLE)) + { + $appli=$conf->global->MAIN_APPLICATION_TITLE; + if (preg_match('/\d\.\d/', $appli)) + { + if (! preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) $appli.=" (".DOL_VERSION.")"; // If new title contains a version that is different than core + } + else $appli.=" ".DOL_VERSION; + } + else $appli.=" ".DOL_VERSION; + $to=$adh->makeSubstitution($conf->global->MAIN_INFO_SOCIETE_MAIL); $from=$conf->global->ADHERENT_MAIL_FROM; $mailfile = new CMailFile( - $conf->global->ADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT, + '['.$appli.'] '.$conf->global->ADHERENT_AUTOREGISTER_NOTIF_MAIL_SUBJECT, $to, $from, $adh->makeSubstitution($conf->global->ADHERENT_AUTOREGISTER_NOTIF_MAIL), @@ -297,7 +353,7 @@ if ($action == 'add') } else $urlback=$_SERVER["PHP_SELF"]."?action=added"; - if (! empty($conf->global->MEMBER_NEWFORM_PAYONLINE)) + if (! empty($conf->global->MEMBER_NEWFORM_PAYONLINE) && $conf->global->MEMBER_NEWFORM_PAYONLINE != '-1') { if ($conf->global->MEMBER_NEWFORM_PAYONLINE == 'all') { @@ -308,7 +364,7 @@ if ($action == 'add') { if (! empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { - $urlback.='&securekey='.urlencode(dol_hash($conf->global->PAYMENT_SECURITY_TOKEN . 'membersubscription' . $adh->ref, 2)); + $urlback.='&securekey='.urlencode(dol_hash($conf->global->PAYMENT_SECURITY_TOKEN . 'membersubscription' . $adh->ref, 2)); } else { @@ -342,7 +398,7 @@ if ($action == 'add') { if (! empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) { - $urlback.='&securekey='.urlencode(dol_hash($conf->global->PAYPAL_SECURITY_TOKEN . 'membersubscription' . $adh->ref, 2)); + $urlback.='&securekey='.urlencode(dol_hash($conf->global->PAYPAL_SECURITY_TOKEN . 'membersubscription' . $adh->ref, 2)); } else { @@ -359,7 +415,7 @@ if ($action == 'add') { if (! empty($conf->global->STRIPE_SECURITY_TOKEN_UNIQUE)) { - $urlback.='&securekey='.urlencode(dol_hash($conf->global->STRIPE_SECURITY_TOKEN . 'membersubscription' . $adh->ref, 2)); + $urlback.='&securekey='.urlencode(dol_hash($conf->global->STRIPE_SECURITY_TOKEN . 'membersubscription' . $adh->ref, 2)); } else { @@ -376,14 +432,25 @@ if ($action == 'add') if (! empty($entity)) $urlback.='&entity='.$entity; dol_syslog("member ".$adh->ref." was created, we redirect to ".$urlback); - Header("Location: ".$urlback); - exit; } else { + $error++; $errmsg .= join('
    ',$adh->errors); } } + + if (! $error) + { + $db->commit(); + + Header("Location: ".$urlback); + exit; + } + else + { + $db->rollback(); + } } // Action called after a submitted was send and member created successfully @@ -418,10 +485,12 @@ $extrafields->fetch_name_optionals_label('adherent'); // fetch optionals attr llxHeaderVierge($langs->trans("NewSubscription")); -print load_fiche_titre($langs->trans("NewSubscription")); +print load_fiche_titre($langs->trans("NewSubscription"), '', '', 0, 0, 'center'); +print '
    '; if (! empty($conf->global->MEMBER_NEWFORM_TEXT)) print $langs->trans($conf->global->MEMBER_NEWFORM_TEXT)."
    \n"; else print $langs->trans("NewSubscriptionDesc",$conf->global->MAIN_INFO_SOCIETE_MAIL)."
    \n"; +print '
    '; dol_htmloutput_errors($errmsg); diff --git a/htdocs/public/opensurvey/studs.php b/htdocs/public/opensurvey/studs.php index 5f4c7a1fb03..742b949e27c 100644 --- a/htdocs/public/opensurvey/studs.php +++ b/htdocs/public/opensurvey/studs.php @@ -60,18 +60,18 @@ $nbcolonnes = substr_count($object->sujet, ',') + 1; $listofvoters=explode(',',$_SESSION["savevoter"]); // Add comment -if (GETPOST('ajoutcomment')) +if (GETPOST('ajoutcomment','alpha')) { if (!$canbemodified) accessforbidden(); $error=0; - if (! GETPOST('comment')) + if (! GETPOST('comment','none')) { $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Comment")), null, 'errors'); } - if (! GETPOST('commentuser')) + if (! GETPOST('commentuser','nohtml')) { $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("User")), null, 'errors'); @@ -79,8 +79,8 @@ if (GETPOST('ajoutcomment')) if (! $error) { - $comment = GETPOST("comment"); - $comment_user = GETPOST('commentuser'); + $comment = GETPOST("comment",'none'); + $comment_user = GETPOST('commentuser','nohtml'); $resql = $object->addComment($comment, $comment_user); @@ -94,7 +94,7 @@ if (GETPOST("boutonp") || GETPOST("boutonp.x") || GETPOST("boutonp_x")) // bout if (!$canbemodified) accessforbidden(); //Si le nom est bien entré - if (GETPOST('nom')) + if (GETPOST('nom','nohtml')) { $nouveauchoix = ''; for ($i=0;$i<$nbcolonnes;$i++) @@ -112,7 +112,7 @@ if (GETPOST("boutonp") || GETPOST("boutonp.x") || GETPOST("boutonp_x")) // bout } } - $nom=substr(GETPOST("nom"),0,64); + $nom=substr(GETPOST("nom",'nohtml'),0,64); // Check if vote already exists $sql = 'SELECT id_users, nom as name'; @@ -739,9 +739,9 @@ if ($comments) if ($object->allow_comments) { print '
    ' .$langs->trans("AddACommentForPoll") . "
    \n"; - print '
    '."\n"; + print '
    '."\n"; print $langs->trans("Name") .': '; - print '   '."\n"; + print '   '."\n"; print '
    '."\n"; print ''."\n"; diff --git a/htdocs/public/paybox/newpayment.php b/htdocs/public/paybox/newpayment.php index bd27554c500..3dc990c30f4 100644 --- a/htdocs/public/paybox/newpayment.php +++ b/htdocs/public/paybox/newpayment.php @@ -43,811 +43,7 @@ require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; // Security check if (empty($conf->paybox->enabled)) accessforbidden('',0,0,1); -$langs->load("main"); -$langs->load("other"); -$langs->load("dict"); -$langs->load("bills"); -$langs->load("companies"); -$langs->load("errors"); -$langs->load("paybox"); - -// Input are: -// type ('invoice','order','contractline'), -// id (object id), -// amount (required if id is empty), -// tag (a free text, required if type is empty) -// currency (iso code) - -$suffix=GETPOST("suffix",'alpha'); -$amount=price2num(GETPOST("amount")); -if (! GETPOST("currency",'alpha')) $currency=$conf->currency; -else $currency=GETPOST("currency",'alpha'); - -if (! GETPOST('action','aZ09')) -{ - if (! GETPOST("amount") && ! GETPOST("source")) - { - dol_print_error('',$langs->trans('ErrorBadParameters')." - amount or source"); - exit; - } - if (is_numeric($amount) && ! GETPOST("tag") && ! GETPOST("source")) - { - dol_print_error('',$langs->trans('ErrorBadParameters')." - tag or source"); - exit; - } - if (GETPOST("source") && ! GETPOST("ref")) - { - dol_print_error('',$langs->trans('ErrorBadParameters')." - ref"); - exit; - } -} - -// Define $urlwithroot -$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); -$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file -//$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current - -$urlok=$urlwithroot.'/public/paybox/paymentok.php?'; -$urlko=$urlwithroot.'/public/paybox/paymentko.php?'; - -// Complete urls -$SOURCE=GETPOST("source",'alpha'); -$ref=$REF=GETPOST('ref','alpha'); -$TAG=GETPOST("tag",'alpha'); -$FULLTAG=GETPOST("fulltag",'alpha'); // fulltag is tag with more informations -$SECUREKEY=GETPOST("securekey"); // Secure key -$FULLTAG.=($FULLTAG?'.':'').'PM=paybox'; - -if (! empty($SOURCE)) -{ - $urlok.='source='.urlencode($SOURCE).'&'; - $urlko.='source='.urlencode($SOURCE).'&'; -} -if (! empty($REF)) -{ - $urlok.='ref='.urlencode($REF).'&'; - $urlko.='ref='.urlencode($REF).'&'; -} -if (!empty($TAG)) -{ - $urlok.='tag='.urlencode($TAG).'&'; - $urlko.='tag='.urlencode($TAG).'&'; -} -if (!empty($FULLTAG)) -{ - $urlok.='fulltag='.urlencode($FULLTAG).'&'; - $urlko.='fulltag='.urlencode($FULLTAG).'&'; -} -$urlok=preg_replace('/&$/','',$urlok); // Remove last & -$urlko=preg_replace('/&$/','',$urlko); // Remove last & - -// Check security token -$valid=true; -if (! empty($conf->global->PAYMENT_SECURITY_TOKEN)) -{ - if (! empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) - { - if ($SOURCE && $REF) $token = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN . $SOURCE . $REF, 2); // Use the source in the hash to avoid duplicates if the references are identical - else $token = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN, 2); - } - else - { - $token = $conf->global->PAYMENT_SECURITY_TOKEN; - } - if ($SECUREKEY != $token) - { - if (empty($conf->global->PAYMENT_SECURITY_ACCEPT_ANY_TOKEN)) $valid=false; // PAYMENT_SECURITY_ACCEPT_ANY_TOKEN is for backward compatibility - else dol_syslog("Warning: PAYMENT_SECURITY_ACCEPT_ANY_TOKEN is on", LOG_WARNING); - } - - if (! $valid) - { - print '
    Bad value for key.
    '; - //print 'SECUREKEY='.$SECUREKEY.' token='.$token.' valid='.$valid; - exit; - } -} - - -/* - * Actions - */ - -if (GETPOST('action','aZ09') == 'dopayment') -{ - $PRICE=price2num(GETPOST("newamount",'alpha'),'MT'); - $email=GETPOST("email"); - - $origfulltag=GETPOST("fulltag",'alpha'); - - $mesg=''; - if (empty($PRICE) || ! is_numeric($PRICE)) $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Amount")); - elseif (empty($email)) $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("YourEMail")); - elseif (! isValidEMail($email)) $mesg=$langs->trans("ErrorBadEMail",$email); - elseif (! $origfulltag) $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("PaymentCode")); - elseif (dol_strlen($urlok) > 150) $mesg='Error urlok too long '.$urlok; - elseif (dol_strlen($urlko) > 150) $mesg='Error urlko too long '.$urlko; - - if (empty($mesg)) - { - dol_syslog("newpayment.php call paybox api and do redirect", LOG_DEBUG); - - print_paybox_redirect($PRICE, $conf->currency, $email, $urlok, $urlko, $FULLTAG); - - session_destroy(); - exit; - } -} - - - -/* - * View - */ - -$head=''; -if (! empty($conf->global->ONLINE_PAYMENT_CSS_URL)) $head=''."\n"; - -$conf->dol_hide_topmenu=1; -$conf->dol_hide_leftmenu=1; - -llxHeader($head, $langs->trans("PaymentForm"), '', '', 0, 0, '', '', '', 'onlinepaymentbody'); - -// Common variables -$creditor=$mysoc->name; -$paramcreditor='ONLINE_PAYMENT_CREDITOR_'.$suffix; -if (! empty($conf->global->$paramcreditor)) $creditor=$conf->global->$paramcreditor; -else if (! empty($conf->global->ONLINE_PAYMENT_CREDITOR)) $creditor=$conf->global->ONLINE_PAYMENT_CREDITOR; - -// Check link validity -if (! empty($SOURCE) && in_array($ref, array('member_ref', 'contractline_ref', 'invoice_ref', 'order_ref', ''))) -{ - $langs->load("errors"); - dol_print_error_email('BADREFINPAYMENTFORM', $langs->trans("ErrorBadLinkSourceSetButBadValueForRef", $SOURCE, $ref)); - llxFooter(); - $db->close(); - exit; -} - -print ''."\n"; -print '
    '; -print '
    '; -print ''; -print ''; -print ''; -print ''; -print "\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print "\n"; - -print ''."\n"; - -// Show logo (search order: logo defined by PAYMENT_LOGO_suffix, then PAYMENT_LOGO, then small company logo, large company logo, theme logo, common logo) -$width=0; -// Define logo and logosmall -$logosmall=$mysoc->logo_small; -$logo=$mysoc->logo; -$paramlogo='PAYMENT_LOGO_'.$suffix; -if (! empty($conf->global->$paramlogo)) $logosmall=$conf->global->$paramlogo; -else if (! empty($conf->global->PAYMENT_LOGO)) $logosmall=$conf->global->PAYMENT_LOGO; -//print ''."\n"; -// Define urllogo -$urllogo=''; -if (! empty($logosmall) && is_readable($conf->mycompany->dir_output.'/logos/thumbs/'.$logosmall)) -{ - $urllogo=DOL_URL_ROOT.'/viewimage.php?modulepart=mycompany&file='.urlencode('thumbs/'.$logosmall); -} -elseif (! empty($logo) && is_readable($conf->mycompany->dir_output.'/logos/'.$logo)) -{ - $urllogo=DOL_URL_ROOT.'/viewimage.php?modulepart=mycompany&file='.urlencode($logo); - $width=96; -} -// Output html code for logo -if ($urllogo) -{ - print ''; - print ''; - print ''."\n"; -} - -// Output introduction text -$text=''; -if (! empty($conf->global->PAYMENT_NEWFORM_TEXT)) -{ - $langs->load("members"); - if (preg_match('/^\((.*)\)$/',$conf->global->PAYMENT_NEWFORM_TEXT,$reg)) $text.=$langs->trans($reg[1])."
    \n"; - else $text.=$conf->global->PAYMENT_NEWFORM_TEXT."
    \n"; - $text=''."\n"; -} -if (empty($text)) -{ - $text.=''."\n"; - $text.=''."\n"; -} -print $text; - -// Output payment summary form -print ''; -} -else -{ - dol_print_error_email('ERRORNEWPAYMENTPAYBOX'); -} - -print ''."\n"; - -print '

    '.$text.'

    '.$langs->trans("WelcomeOnPaymentPage").'
    '.$langs->trans("ThisScreenAllowsYouToPay",$creditor).'

    '; -print ''; -print ''."\n"; - -$found=false; -$error=0; -$var=false; - - - -// Free payment -if (! GETPOST("source") && $valid) -{ - $found=true; - $tag=GETPOST("tag"); - $fulltag=$tag; - - // Creditor - - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // EMail - - print ''."\n"; -} - - -// Payment on customer order -if (GETPOST("source") == 'order' && $valid) -{ - $found=true; - $langs->load("orders"); - - require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; - - $order=new Commande($db); - $result=$order->fetch('',$ref); - if ($result < 0) - { - $mesg=$order->error; - $error++; - } - else - { - $result=$order->fetch_thirdparty($order->socid); - } - - $amount=$order->total_ttc; - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - - $fulltag='IR='.$order->ref.'.TPID='.$order->thirdparty->id; - //$fulltag.='.TP='.strtr($order->thirdparty->name,"-"," "); We disable this because url that will contains FULLTAG must be lower than 150 - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // EMail - - print ''."\n"; -} - - -// Payment on customer invoice -if (GETPOST("source") == 'invoice' && $valid) -{ - $found=true; - $langs->load("bills"); - - require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; - - $invoice=new Facture($db); - $result=$invoice->fetch('',$ref); - if ($result < 0) - { - $mesg=$invoice->error; - $error++; - } - else - { - $result=$invoice->fetch_thirdparty($invoice->socid); - } - - $amount=price2num($invoice->total_ttc - $invoice->getSommePaiement()); - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - - $fulltag='IR='.$invoice->ref.'.TPID='.$invoice->thirdparty->id; - //$fulltag.='.TP='.strtr($invoice->thirdparty->name,"-"," "); We disable this because url that will contains FULLTAG must be lower than 150 - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // EMail - - print ''."\n"; -} - -// Payment on contract line -if (GETPOST("source") == 'contractline' && $valid) -{ - $found=true; - $langs->load("contracts"); - - require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php'; - - $contractline=new ContratLigne($db); - $result=$contractline->fetch('',$ref); - if ($result < 0) - { - $mesg=$contractline->error; - $error++; - } - else - { - if ($contractline->fk_contrat > 0) - { - $contract=new Contrat($db); - $result=$contract->fetch($contractline->fk_contrat); - if ($result > 0) - { - $result=$contract->fetch_thirdparty($contract->socid); - } - else - { - $mesg=$contract->error; - $error++; - } - } - else - { - $mesg='ErrorRecordNotFound'; - $error++; - } - } - - $amount=$contractline->total_ttc; - if ($contractline->fk_product) - { - $product=new Product($db); - $result=$product->fetch($contractline->fk_product); - - // We define price for product (TODO Put this in a method in product class) - if (! empty($conf->global->PRODUIT_MULTIPRICES)) - { - $pu_ht = $product->multiprices[$contract->thirdparty->price_level]; - $pu_ttc = $product->multiprices_ttc[$contract->thirdparty->price_level]; - $price_base_type = $product->multiprices_base_type[$contract->thirdparty->price_level]; - } - else - { - $pu_ht = $product->price; - $pu_ttc = $product->price_ttc; - $price_base_type = $product->price_base_type; - } - - $amount=$pu_ttc; - if (empty($amount)) - { - dol_print_error('','ErrorNoPriceDefinedForThisProduct'); - exit; - } - } - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - - $fulltag='CLR='.$contractline->ref.'.CR='.$contract->ref.'.TPID='.$contract->thirdparty->id; - //$fulltag.='.TP='.strtr($contract->thirdparty->name,"-"," "); We disable this because url that will contains FULLTAG must be lower than 150 - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - $qty=1; - if (GETPOST('qty')) $qty=GETPOST('qty'); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - // Quantity - - $label=$langs->trans("Quantity"); - $qty=1; - $duration=''; - if ($contractline->fk_product) - { - if ($product->isService() && $product->duration_value > 0) - { - $label=$langs->trans("Duration"); - - // TODO Put this in a global method - if ($product->duration_value > 1) - { - $dur=array("h"=>$langs->trans("Hours"),"d"=>$langs->trans("DurationDays"),"w"=>$langs->trans("DurationWeeks"),"m"=>$langs->trans("DurationMonths"),"y"=>$langs->trans("DurationYears")); - } - else - { - $dur=array("h"=>$langs->trans("Hour"),"d"=>$langs->trans("DurationDay"),"w"=>$langs->trans("DurationWeek"),"m"=>$langs->trans("DurationMonth"),"y"=>$langs->trans("DurationYear")); - } - $duration=$product->duration_value.' '.$dur[$product->duration_unit]; - } - } - print ''; - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // EMail - - print ''."\n"; - -} - -// Payment on member subscription -if (GETPOST("source") == 'membersubscription' && $valid) -{ - $found=true; - $langs->load("members"); - - require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; - require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php'; - - $member=new Adherent($db); - $result=$member->fetch('',$ref); - if ($result < 0) - { - $mesg=$member->error; - $error++; - } - else - { - $subscription=new Subscription($db); - } - - $amount=$subscription->total_ttc; - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - - $fulltag='MID='.$member->id; - //$fulltag.='.M='.dol_trunc(strtr($member->getFullName($langs),"-"," "),12); We disable this because url that will contains FULLTAG must be lower than 150 - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - if ($member->last_subscription_date || $member->last_subscription_amount) - { - // Last subscription date - - print ''."\n"; - - // Last subscription amount - - print ''."\n"; - - if (empty($amount) && ! GETPOST('newamount','alpha')) $_GET['newamount']=$member->last_subscription_amount; - } - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // EMail - - print ''."\n"; -} - - - - -if (! $found && ! $mesg) $mesg=$langs->trans("ErrorBadParameters"); - -if ($mesg) print ''."\n"; - -print '
    '.$langs->trans("ThisIsInformationOnPayment").' :
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("YourEMail"); - print ' ('.$langs->trans("ToComplete").')'; - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("ThirdParty"); - print ''.$order->thirdparty->name.''; - - // Object - - $text=''.$langs->trans("PaymentOrderRef",$order->ref).''; - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("YourEMail"); - print ' ('.$langs->trans("ToComplete").')'; - $email=$order->thirdparty->email; - $email=(GETPOST("email")?GETPOST("email"):(isValidEmail($email)?$email:'')); - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("ThirdParty"); - print ''.$invoice->thirdparty->name.''; - - // Object - - $text=''.$langs->trans("PaymentInvoiceRef",$invoice->ref).''; - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("YourEMail"); - print ' ('.$langs->trans("ToComplete").')'; - $email=$invoice->thirdparty->email; - $email=(GETPOST("email")?GETPOST("email"):(isValidEmail($email)?$email:'')); - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("ThirdParty"); - print ''.$contract->thirdparty->name.''; - - // Object - - $text=''.$langs->trans("PaymentRenewContractId",$contract->ref,$contractline->ref).''; - if ($contractline->fk_product) - { - $text.='
    '.$product->ref.($product->label?' - '.$product->label:''); - } - if ($contractline->description) $text.='
    '.dol_htmlentitiesbr($contractline->description); - //if ($contractline->date_fin_validite) { - // $text.='
    '.$langs->trans("DateEndPlanned").': '; - // $text.=dol_print_date($contractline->date_fin_validite); - //} - if ($contractline->date_fin_validite) - { - $text.='
    '.$langs->trans("ExpiredSince").': '.dol_print_date($contractline->date_fin_validite); - } - - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$label.''.($duration?$duration:$qty).''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("YourEMail"); - print ' ('.$langs->trans("ToComplete").')'; - $email=$contract->thirdparty->email; - $email=(GETPOST("email")?GETPOST("email"):(isValidEmail($email)?$email:'')); - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("Member"); - print ''; - if ($member->morphy == 'mor' && ! empty($member->societe)) print $member->societe; - else print $member->getFullName($langs); - print ''; - - // Object - - $text=''.$langs->trans("PaymentSubscription").''; - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$langs->trans("LastSubscriptionDate"); - print ''.dol_print_date($member->last_subscription_date,'day'); - print '
    '.$langs->trans("LastSubscriptionAmount"); - print ''.price($member->last_subscription_amount); - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - $valtoshow=''; - if (empty($amount) || ! is_numeric($amount)) - { - $valtoshow=price2num(GETPOST("newamount",'alpha'),'MT'); - // force default subscription amount to value defined into constant... - if (empty($valtoshow)) - { - if (! empty($conf->global->MEMBER_NEWFORM_EDITAMOUNT)) { - if (! empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { - $valtoshow = $conf->global->MEMBER_NEWFORM_AMOUNT; - } - } - else { - if (! empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { - $amount = $conf->global->MEMBER_NEWFORM_AMOUNT; - } - } - } - } - if (empty($amount) || ! is_numeric($amount)) - { - //$valtoshow=price2num(GETPOST("newamount",'alpha'),'MT'); - if (! empty($conf->global->MEMBER_MIN_AMOUNT) && $valtoshow) $valtoshow=max($conf->global->MEMBER_MIN_AMOUNT,$valtoshow); - print ''; - print ''; - } - else { - $valtoshow=$amount; - if (! empty($conf->global->MEMBER_MIN_AMOUNT) && $valtoshow) $valtoshow=max($conf->global->MEMBER_MIN_AMOUNT,$valtoshow); - print ''.price($valtoshow).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("YourEMail"); - $email=$member->email; - $email=(GETPOST("email")?GETPOST("email"):(isValidEmail($email)?$email:'')); - if (empty($email)) print ' ('.$langs->trans("ToComplete").')'; - print '

    '.$mesg.'
    '."\n"; -print "\n"; - -if ($found && ! $error) // We are in a management option and no error -{ - print '
    '; - //print '
    '.$langs->trans("YouWillBeRedirectedOnPayBox").'...
    '."\n"; -print '
    '."\n"; -print '
    '."\n"; -print '
    '; - - -htmlPrintOnlinePaymentFooter($mysoc,$langs,1,$suffix); - - -llxFooter('', 'public'); - -$db->close(); +$newurl = $_SERVER['REQUEST_URI']; +$newurl = preg_replace('/\/paybox\/newpayment/', '/payment/newpayment', $newurl); +header("Location: ".$newurl.(preg_match('/\?/', $newurl)?'&':'?').'paymentmethod=paybox'); +exit; diff --git a/htdocs/public/paybox/paymentok.php b/htdocs/public/paybox/paymentok.php index 63c15b035f2..9e711ade51b 100644 --- a/htdocs/public/paybox/paymentok.php +++ b/htdocs/public/paybox/paymentok.php @@ -157,7 +157,7 @@ if (! empty($conf->global->ONLINE_PAYMENT_SENDEMAIL)) if (! empty($tmptag['MEM'])) { $langs->load("members"); - $url=$urlwithroot."/adherents/card_subscriptions.php?rowid=".$tmptag['MEM']; + $url=$urlwithroot."/adherents/subscription.php?rowid=".$tmptag['MEM']; $content.=$langs->trans("PaymentSubscription")."
    \n"; $content.=$langs->trans("MemberId").': '.$tmptag['MEM']."
    \n"; $content.=$langs->trans("Link").': '.$url.''."
    \n"; diff --git a/htdocs/public/payment/newpayment.php b/htdocs/public/payment/newpayment.php index 74ffcfd120a..ae7821b8841 100644 --- a/htdocs/public/payment/newpayment.php +++ b/htdocs/public/payment/newpayment.php @@ -41,17 +41,12 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php'; // Security check // No check on module enabled. Done later according to $validpaymentmethod -$langs->load("main"); -$langs->load("other"); -$langs->load("dict"); -$langs->load("bills"); -$langs->load("companies"); -$langs->load("errors"); -$langs->load("paybox"); // File with generic data +$langs->loadLangs(array("main","other","dict","bills","companies","errors","paybox","paypal")); // File with generic data $action=GETPOST('action','aZ09'); @@ -89,7 +84,7 @@ if (! $action) } -$paymentmethod=''; +$paymentmethod=GETPOST('paymentmethod','alphanohtml')?GETPOST('paymentmethod','alphanohtml'):''; // Empty in most cases. Defined when a payment mode is forced $validpaymentmethod=array(); // Detect $paymentmethod @@ -160,10 +155,8 @@ $urlko=preg_replace('/&$/','',$urlko); // Remove last & // Find valid payment methods -if (! empty($conf->paypal->enabled)) +if ((empty($paymentmethod) || $paymentmethod == 'paypal') && ! empty($conf->paypal->enabled)) { - $langs->load("paypal"); - require_once DOL_DOCUMENT_ROOT.'/paypal/lib/paypal.lib.php'; require_once DOL_DOCUMENT_ROOT.'/paypal/lib/paypalfunctions.lib.php'; @@ -191,7 +184,7 @@ if (! empty($conf->paypal->enabled)) $validpaymentmethod['paypal']='valid'; } -if (! empty($conf->paybox->enabled)) +if ((empty($paymentmethod) || $paymentmethod == 'paybox') && ! empty($conf->paybox->enabled)) { $langs->load("paybox"); @@ -200,7 +193,7 @@ if (! empty($conf->paybox->enabled)) $validpaymentmethod['paybox']='valid'; } -if (! empty($conf->stripe->enabled)) +if ((empty($paymentmethod) || $paymentmethod == 'stripe') && ! empty($conf->stripe->enabled)) { $langs->load("stripe"); @@ -244,7 +237,11 @@ if (! empty($conf->global->PAYMENT_SECURITY_TOKEN)) } } - +if (! empty($paymentmethod) && empty($validpaymentmethod[$paymentmethod])) +{ + print 'Payment module for payment method '.$paymentmethod.' is not active'; + exit; +} if (empty($validpaymentmethod)) { print 'No active payment module (Paypal, Stripe, Paybox, ...)'; @@ -264,6 +261,7 @@ else if (! empty($conf->global->$paramcreditor)) $creditor=$conf->global->$param * Actions */ +// Action dopayment is called after choosing the payment mode if ($action == 'dopayment') { if ($paymentmethod == 'paypal') @@ -272,22 +270,31 @@ if ($action == 'dopayment') $PAYPAL_PAYMENT_TYPE='Sale'; $origfulltag=GETPOST("fulltag",'alpha'); - $shipToName=GETPOST("shipToName"); - $shipToStreet=GETPOST("shipToStreet"); - $shipToCity=GETPOST("shipToCity"); - $shipToState=GETPOST("shipToState"); - $shipToCountryCode=GETPOST("shipToCountryCode"); - $shipToZip=GETPOST("shipToZip"); - $shipToStreet2=GETPOST("shipToStreet2"); - $phoneNum=GETPOST("phoneNum"); - $email=GETPOST("email"); + $shipToName=GETPOST("shipToName",'alpha'); + $shipToStreet=GETPOST("shipToStreet",'alpha'); + $shipToCity=GETPOST("shipToCity",'alpha'); + $shipToState=GETPOST("shipToState",'alpha'); + $shipToCountryCode=GETPOST("shipToCountryCode",'alpha'); + $shipToZip=GETPOST("shipToZip",'alpha'); + $shipToStreet2=GETPOST("shipToStreet2",'alpha'); + $phoneNum=GETPOST("phoneNum",'alpha'); + $email=GETPOST("email",'alpha'); $desc=GETPOST("desc",'alpha'); + $thirdparty_id=GETPOST('thirdparty_id', 'int'); $mesg=''; - if (empty($PAYPAL_API_PRICE) || ! is_numeric($PAYPAL_API_PRICE)) $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Amount")); + if (empty($PAYPAL_API_PRICE) || ! is_numeric($PAYPAL_API_PRICE)) + { + $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Amount")); + $action=''; + } //elseif (empty($EMAIL)) $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("YourEMail")); //elseif (! isValidEMail($EMAIL)) $mesg=$langs->trans("ErrorBadEMail",$EMAIL); - elseif (! $origfulltag) $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("PaymentCode")); + elseif (! $origfulltag) + { + $mesg=$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("PaymentCode")); + $action=''; + } //var_dump($_POST); if (empty($mesg)) @@ -310,6 +317,7 @@ if ($action == 'dopayment') dol_syslog("PAYPAL_API_KO: $PAYPAL_API_KO", LOG_DEBUG); dol_syslog("PAYPAL_API_PRICE: $PAYPAL_API_PRICE", LOG_DEBUG); dol_syslog("PAYPAL_API_DEVISE: $PAYPAL_API_DEVISE", LOG_DEBUG); + // All those fields may be empty when making a payment for a free amount for example dol_syslog("shipToName: $shipToName", LOG_DEBUG); dol_syslog("shipToStreet: $shipToStreet", LOG_DEBUG); dol_syslog("shipToCity: $shipToCity", LOG_DEBUG); @@ -327,16 +335,18 @@ if ($action == 'dopayment') //$_SESSION["FinalPaymentAmt"]=$PAYPAL_API_PRICE; // A redirect is added if API call successfull - print_paypal_redirect($PAYPAL_API_PRICE,$PAYPAL_API_DEVISE,$PAYPAL_PAYMENT_TYPE,$PAYPAL_API_OK,$PAYPAL_API_KO, $FULLTAG); + $mesg = print_paypal_redirect($PAYPAL_API_PRICE,$PAYPAL_API_DEVISE,$PAYPAL_PAYMENT_TYPE,$PAYPAL_API_OK,$PAYPAL_API_KO, $FULLTAG); - exit; + // If we are here, it means the Paypal redirect was not done, so we show error message + $action = ''; } } if ($paymentmethod == 'paybox') { $PRICE=price2num(GETPOST("newamount"),'MT'); - $email=GETPOST("email"); + $email=GETPOST("email",'alpha'); + $thirdparty_id=GETPOST('thirdparty_id', 'int'); $origfulltag=GETPOST("fulltag",'alpha'); @@ -377,47 +387,125 @@ if ($action == 'dopayment') // Called when choosing Stripe mode, after the 'dopayment' -if ($action == 'charge') +if ($action == 'charge' && ! empty($conf->stripe->enabled)) { + $amountstripe = $amount; + // Correct the amount according to unit of currency // See https://support.stripe.com/questions/which-zero-decimal-currencies-does-stripe-support $arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'); - if (! in_array($currency, $arrayzerounitcurrency)) $amount=$amount * 100; + if (! in_array($currency, $arrayzerounitcurrency)) $amountstripe=$amountstripe * 100; dol_syslog("POST keys : ".join(',', array_keys($_POST)), LOG_DEBUG, 0, '_stripe'); dol_syslog("POST values: ".join(',', $_POST), LOG_DEBUG, 0, '_stripe'); $stripeToken = GETPOST("stripeToken",'alpha'); - $email = GETPOST("stripeEmail",'alpha'); + $email = GETPOST("email",'alpha'); + $thirdparty_id=GETPOST('thirdparty_id', 'int'); // Note that for payment following online registration for members, this is empty because thirdparty is created once payment is confirmed by paymentok.php $vatnumber = GETPOST('vatnumber','alpha'); dol_syslog("stripeToken = ".$stripeToken, LOG_DEBUG, 0, '_stripe'); dol_syslog("email = ".$email, LOG_DEBUG, 0, '_stripe'); + dol_syslog("thirdparty_id = ".$thirdparty_id, LOG_DEBUG, 0, '_stripe'); dol_syslog("vatnumber = ".$vatnumber, LOG_DEBUG, 0, '_stripe'); $error = 0; try { - dol_syslog("Create customer card profile", LOG_DEBUG, 0, '_stripe'); - $customer = \Stripe\Customer::create(array( - 'email' => $email, - 'description' => ($email?'Customer card profile for '.$email:null), - 'metadata' => array('ipaddress'=>$_SERVER['REMOTE_ADDR']), - 'business_vat_id' => ($vatnumber?$vatnumber:null), - 'source' => $stripeToken // source can be a token OR array('object'=>'card', 'exp_month'=>xx, 'exp_year'=>xxxx, 'number'=>xxxxxxx, 'cvc'=>xxx, 'name'=>'Cardholder's full name', zip ?) - )); - // Return $customer = array('id'=>'cus_XXXX', ...) + $metadata = array( + 'dol_version'=>DOL_VERSION, + 'dol_entity'=>$conf->entity, + 'ipaddress'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR']) + ); + if (! empty($dol_id)) $metadata["dol_id"] = $dol_id; + if (! empty($dol_type)) $metadata["dol_type"] = $dol_type; + if (! empty($thirdparty_id)) $metadata["dol_thirdparty_id"] = $thirdparty_id; - dol_syslog("Create charge", LOG_DEBUG, 0, '_stripe'); - $charge = \Stripe\Charge::create(array( - 'customer' => $customer->id, - 'amount' => price2num($amount, 'MU'), - 'currency' => $currency, - 'description' => 'Stripe payment: '.$FULLTAG, - 'metadata' => array("FULLTAG" => $FULLTAG, 'Recipient' => $mysoc->name), - 'statement_descriptor' => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 6, 'right', 'UTF-8', 1).' '.$FULLTAG, 22, 'right', 'UTF-8', 1) // 22 chars that appears on bank receipt - )); - // Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...) + if ($thirdparty_id > 0) + { + dol_syslog("Search existing Stripe customer profile for thirdparty_id=".$thirdparty_id, LOG_DEBUG, 0, '_stripe'); + + $service = 'StripeTest'; + $servicestatus = 0; + if (! empty($conf->global->STRIPE_LIVE) && ! GETPOST('forcesandbox','alpha')) + { + $service = 'StripeLive'; + $servicestatus = 1; + } + $stripeacc = null; // No Oauth/connect use for public pages + + $thirdparty = new Societe($db); + $thirdparty->fetch($thirdparty_id); + + include_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; + $stripe = new Stripe($db); + $customer = $stripe->customerStripe($thirdparty, $stripeacc, $servicestatus, 1); + + $card = $customer->sources->create(array("source" => $stripeToken, "metadata" => $metadata)); + + if (empty($card)) + { + $error++; + dol_syslog('Failed to create card record', LOG_WARNING, 0, '_stripe'); + setEventMessages('Failed to create card record', null, 'errors'); + $action=''; + } + else + { + dol_syslog("Create charge on card ".$card->id, LOG_DEBUG, 0, '_stripe'); + $charge = \Stripe\Charge::create(array( + 'amount' => price2num($amountstripe, 'MU'), + 'currency' => $currency, + 'capture' => true, // Charge immediatly + 'description' => 'Stripe payment: '.$FULLTAG, + 'metadata' => array("FULLTAG" => $FULLTAG, 'Recipient' => $mysoc->name, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR'])), + 'customer' => $customer->id, + 'source' => $card, + 'statement_descriptor' => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 6, 'right', 'UTF-8', 1).' '.$FULLTAG, 22, 'right', 'UTF-8', 1) // 22 chars that appears on bank receipt + )); + // Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...) + if (empty($charge)) + { + $error++; + dol_syslog('Failed to charge card', LOG_WARNING, 0, '_stripe'); + setEventMessages('Failed to charge card', null, 'errors'); + $action=''; + } + } + } + else + { + dol_syslog("Create anonymous customer card profile", LOG_DEBUG, 0, '_stripe'); + $customer = \Stripe\Customer::create(array( + 'email' => $email, + 'description' => ($email?'Anonymous customer for '.$email:'Anonymous customer'), + 'metadata' => $metadata, + 'business_vat_id' => ($vatnumber?$vatnumber:null), + 'source' => $stripeToken // source can be a token OR array('object'=>'card', 'exp_month'=>xx, 'exp_year'=>xxxx, 'number'=>xxxxxxx, 'cvc'=>xxx, 'name'=>'Cardholder's full name', zip ?) + )); + // Return $customer = array('id'=>'cus_XXXX', ...) + + // The customer was just created with a source, so we can make a charge + // with no card defined, the source just used for customer creation will be used. + dol_syslog("Create charge", LOG_DEBUG, 0, '_stripe'); + $charge = \Stripe\Charge::create(array( + 'customer' => $customer->id, + 'amount' => price2num($amountstripe, 'MU'), + 'currency' => $currency, + 'capture' => true, // Charge immediatly + 'description' => 'Stripe payment: '.$FULLTAG, + 'metadata' => array("FULLTAG" => $FULLTAG, 'Recipient' => $mysoc->name, 'dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR'])), + 'statement_descriptor' => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 6, 'right', 'UTF-8', 1).' '.$FULLTAG, 22, 'right', 'UTF-8', 1) // 22 chars that appears on bank receipt + )); + // Return $charge = array('id'=>'ch_XXXX', 'status'=>'succeeded|pending|failed', 'failure_code'=>, 'failure_message'=>...) + if (empty($charge)) + { + $error++; + dol_syslog('Failed to charge card', LOG_WARNING, 0, '_stripe'); + setEventMessages('Failed to charge card', null, 'errors'); + $action=''; + } + } } catch(\Stripe\Error\Card $e) { // Since it's a decline, \Stripe\Error\Card will be caught $body = $e->getJsonBody(); @@ -431,8 +519,8 @@ if ($action == 'charge') print('Message is:' . $err['message'] . "\n"); $error++; - setEventMessages($e->getMessage(), null, 'errors'); dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); + setEventMessages($e->getMessage(), null, 'errors'); $action=''; } catch (\Stripe\Error\RateLimit $e) { // Too many requests made to the API too quickly @@ -525,11 +613,11 @@ if ($source && in_array($ref, array('member_ref', 'contractline_ref', 'invoice_r // Show sandbox warning -if (! empty($conf->paypal->enabled) && (! empty($conf->global->PAYPAL_API_SANDBOX) || GETPOST('forcesandbox','alpha'))) // We can force sand box with param 'forcesandbox' +if ((empty($paymentmethod) || $paymentmethod == 'paypal') && ! empty($conf->paypal->enabled) && (! empty($conf->global->PAYPAL_API_SANDBOX) || GETPOST('forcesandbox','alpha'))) // We can force sand box with param 'forcesandbox' { dol_htmloutput_mesg($langs->trans('YouAreCurrentlyInSandboxMode','Paypal'),'','warning'); } -if (! empty($conf->stripe->enabled) && (empty($conf->global->STRIPE_LIVE) || GETPOST('forcesandbox','alpha'))) +if ((empty($paymentmethod) || $paymentmethod == 'stripe') && ! empty($conf->stripe->enabled) && (empty($conf->global->STRIPE_LIVE) || GETPOST('forcesandbox','alpha'))) { dol_htmloutput_mesg($langs->trans('YouAreCurrentlyInSandboxMode','Stripe'),'','warning'); } @@ -781,8 +869,9 @@ if ($source == 'order') { print ''."\n"; } + if (is_object($order->thirdparty)) print ''."\n"; print ''."\n"; - print ''."\n"; + print ''."\n"; $labeldesc=$langs->trans("Order").' '.$order->ref; if (GETPOST('desc','alpha')) $labeldesc=GETPOST('desc','alpha'); print ''."\n"; @@ -850,7 +939,7 @@ if ($source == 'invoice') print ''."\n"; // Amount - print ''.$langs->trans("Amount"); + print ''.$langs->trans("PaymentAmount"); if (empty($amount) && empty($object->paye)) print ' ('.$langs->trans("ToComplete").')'; print ''; if (empty($object->paye)) @@ -915,8 +1004,9 @@ if ($source == 'invoice') { print ''."\n"; } + if (is_object($invoice->thirdparty)) print ''."\n"; print ''."\n"; - print ''."\n"; + print ''."\n"; $labeldesc=$langs->trans("Invoice").' '.$invoice->ref; if (GETPOST('desc','alpha')) $labeldesc=GETPOST('desc','alpha'); print ''."\n"; @@ -1122,8 +1212,9 @@ if ($source == 'contractline') { print ''."\n"; } + if (is_object($contract->thirdparty)) print ''."\n"; print ''."\n"; - print ''."\n"; + print ''."\n"; $labeldesc=$langs->trans("Contract").' '.$contract->ref; if (GETPOST('desc','alpha')) $labeldesc=GETPOST('desc','alpha'); print ''."\n"; @@ -1147,8 +1238,8 @@ if ($source == 'membersubscription') } else { + $member->fetch_thirdparty(); $object = $member; - $subscription=new Subscription($db); } @@ -1285,6 +1376,7 @@ if ($source == 'membersubscription') { print ''."\n"; } + if (is_object($member->thirdparty)) print ''."\n"; print ''."\n"; $labeldesc = $langs->trans("PaymentSubscription"); if (GETPOST('desc','alpha')) $labeldesc=GETPOST('desc','alpha'); @@ -1313,19 +1405,19 @@ if ($action != 'dopayment') { // Buttons for all payments registration methods - if (! empty($conf->paybox->enabled)) + if ((empty($paymentmethod) || $paymentmethod == 'paybox') && ! empty($conf->paybox->enabled)) { // If STRIPE_PICTO_FOR_PAYMENT is 'cb' we show a picto of a crdit card instead of paybox print '
    '; } - if (! empty($conf->stripe->enabled)) + if ((empty($paymentmethod) || $paymentmethod == 'stripe') && ! empty($conf->stripe->enabled)) { // If STRIPE_PICTO_FOR_PAYMENT is 'cb' we show a picto of a crdit card instead of stripe print '
    '; } - if (! empty($conf->paypal->enabled)) + if ((empty($paymentmethod) || $paymentmethod == 'paypal') && ! empty($conf->paypal->enabled)) { if (empty($conf->global->PAYPAL_API_INTEGRAL_OR_PAYPALONLY)) $conf->global->PAYPAL_API_INTEGRAL_OR_PAYPALONLY='integral'; @@ -1371,7 +1463,7 @@ if (preg_match('/^dopayment/',$action)) /* print ''; - - -print '
    '; - - - -htmlPrintOnlinePaymentFooter($mysoc,$langs,1,$suffix); - -llxFooter('', 'public'); - -$db->close(); +$newurl = $_SERVER['REQUEST_URI']; +$newurl = preg_replace('/\/paypal\/newpayment/', '/payment/newpayment', $newurl); +header("Location: ".$newurl.(preg_match('/\?/', $newurl)?'&':'?').'paymentmethod=paypal'); +exit; diff --git a/htdocs/public/paypal/paymentok.php b/htdocs/public/paypal/paymentok.php index 80ec1d8ec2b..1e6adb7ca51 100644 --- a/htdocs/public/paypal/paymentok.php +++ b/htdocs/public/paypal/paymentok.php @@ -212,7 +212,7 @@ if ($PAYPALTOKEN) if (! empty($tmptag['MEM'])) { $langs->load("members"); - $url=$urlwithroot."/adherents/card_subscriptions.php?rowid=".$tmptag['MEM']; + $url=$urlwithroot."/adherents/subscription.php?rowid=".$tmptag['MEM']; $content.=$langs->trans("PaymentSubscription")."
    \n"; $content.=$langs->trans("MemberId").': '.$tmptag['MEM']."
    \n"; $content.=$langs->trans("Link").': '.$url.''."
    \n"; diff --git a/htdocs/public/stripe/ipn.php b/htdocs/public/stripe/ipn.php new file mode 100644 index 00000000000..19f9b8c80bc --- /dev/null +++ b/htdocs/public/stripe/ipn.php @@ -0,0 +1,331 @@ +. + */ + +define("NOLOGIN",1); // This means this output page does not require to be logged. +define("NOCSRFCHECK",1); // We accept to go on this page from external web site. + +$entity=(! empty($_GET['entity']) ? (int) $_GET['entity'] : (! empty($_POST['entity']) ? (int) $_POST['entity'] : 1)); +if (is_numeric($entity)) define("DOLENTITY", $entity); + +$res=0; +if (! $res && file_exists("../../main.inc.php")) $res=@include("../../main.inc.php"); // to work if your module directory is into a subdir of root htdocs directory +if (! $res) die("Include of main fails"); + +if (empty($conf->stripe->enabled)) accessforbidden('',0,0,1); +require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; +require_once DOL_DOCUMENT_ROOT.'/includes/stripe/init.php'; +require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/ccountry.class.php'; +require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + +// You can find your endpoint's secret in your webhook settings +if (isset($_GET['connect'])){ + if (isset($_GET['test'])) + { + $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_CONNECT_KEY; + $service = 'StripeTest'; + } + else + { + $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_CONNECT_KEY; + $service = 'StripeLive'; + } +} +else { + if (isset($_GET['test'])) + { + $endpoint_secret = $conf->global->STRIPE_TEST_WEBHOOK_KEY; + $service = 'StripeTest'; + } + else + { + $endpoint_secret = $conf->global->STRIPE_LIVE_WEBHOOK_KEY; + $service = 'StripeLive'; + } +} +$payload = @file_get_contents("php://input"); +$sig_header = $_SERVER["HTTP_STRIPE_SIGNATURE"]; +$event = null; + +$error = 0; + +try { + $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret); +} +catch(\UnexpectedValueException $e) { + // Invalid payload + http_response_code(400); // PHP 5.4 or greater + exit(); +} catch(\Stripe\Error\SignatureVerification $e) { + // Invalid signature + http_response_code(400); // PHP 5.4 or greater + exit(); +} + +// Do something with $event + +http_response_code(200); // PHP 5.4 or greater +$langs->load("main"); +$user = new User($db); +$user->fetch(5); +$user->getrights(); + +if (! empty($conf->multicompany->enabled) && ! empty($conf->stripeconnect->enabled)) { + $sql = "SELECT entity"; + $sql.= " FROM ".MAIN_DB_PREFIX."oauth_token"; + $sql.= " WHERE service = '".$db->escape($service)."' and tokenstring = '%".$db->escape($event->account)."%'"; + + dol_syslog(get_class($db) . "::fetch", LOG_DEBUG); + $result = $db->query($sql); + if ($result) + { + if ($db->num_rows($result)) + { + $obj = $db->fetch_object($result); + $key=$obj->entity; + } + else {$key=1; + } + } + else {$key=1; + } + $ret=$mc->switchEntity($key); + if (! $res && file_exists("../../main.inc.php")) $res=@include("../../main.inc.php"); + if (! $res) die("Include of main fails"); +} + +// list of action +$stripe=new Stripe($db); +if ($event->type == 'payout.created') { + $error=0; + + $result=dolibarr_set_const($db, $service."_NEXTPAYOUT", date('Y-m-d H:i:s',$event->data->object->arrival_date), 'chaine', 0, '', $conf->entity); + + if ($result > 0) + { + // TODO Use CMail and translation + $body = "Un virement de ".price2num($event->data->object->amount/100)." ".$event->data->object->currency." est attendu sur votre compte le ".date('d-m-Y H:i:s',$event->data->object->arrival_date); + $subject = '[NOTIFICATION] Virement programmée'; + $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; // TODO convert in dolibarr standard + mail(''.$conf->global->MAIN_INFO_SOCIETE_MAIL.'', $subject, $body, $headers); + return 1; + } + else + { + $error++; + return -1; + } +} +elseif ($event->type == 'payout.paid') { + global $conf; + $error=0; + $result=dolibarr_set_const($db, $service."_NEXTPAYOUT",null,'chaine',0,'',$conf->entity); + if ($result) + { + $langs->load("errors"); + + $dateo = dol_now(); + $label = $event->data->object->description; + $amount= $event->data->object->amount/100; + $amount_to= $event->data->object->amount/100; + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + + $accountfrom=new Account($db); + $accountfrom->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS); + + $accountto=new Account($db); + $accountto->fetch($conf->global->STRIPE_BANK_ACCOUNT_FOR_BANKTRANFERS); + + if ($accountto->currency_code != $accountfrom->currency_code) { + $error++; + setEventMessages($langs->trans("ErrorTransferBetweenDifferentCurrencyNotPossible"), null, 'errors'); + } + + if ($accountto->id != $accountfrom->id) + { + + $bank_line_id_from=0; + $bank_line_id_to=0; + $result=0; + + // By default, electronic transfert from bank to bank + $typefrom='PRE'; + $typeto='VIR'; + + if (! $error) $bank_line_id_from = $accountfrom->addline($dateo, $typefrom, $label, -1*price2num($amount), '', '', $user); + if (! ($bank_line_id_from > 0)) $error++; + if ((! $error) && ($accountto->currency_code == $accountfrom->currency_code)) $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount), '', '', $user); + if ((! $error) && ($accountto->currency_code != $accountfrom->currency_code)) $bank_line_id_to = $accountto->addline($dateo, $typeto, $label, price2num($amount_to), '', '', $user); + if (! ($bank_line_id_to > 0)) $error++; + + if (! $error) $result=$accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, DOL_URL_ROOT.'/compta/bank/ligne.php?rowid=', '(banktransfert)', 'banktransfert'); + if (! ($result > 0)) $error++; + if (! $error) $result=$accountto->add_url_line($bank_line_id_to, $bank_line_id_from, DOL_URL_ROOT.'/compta/bank/ligne.php?rowid=', '(banktransfert)', 'banktransfert'); + if (! ($result > 0)) $error++; + } + + // TODO Use CMail and translation + $body = "Un virement de ".price2num($event->data->object->amount/100)." ".$event->data->object->currency." a ete effectue sur votre compte le ".date('d-m-Y H:i:s',$event->data->object->arrival_date); + $subject = '[NOTIFICATION] Virement effectué'; + $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + mail(''.$conf->global->MAIN_INFO_SOCIETE_MAIL.'', $subject, $body, $headers); + + return 1; + } + else + { + $error++; + return -1; + } +} +elseif ($event->type == 'charge.succeeded') { + + //TODO: create fees + +} +elseif ($event->type == 'customer.source.created') { + + //TODO: save customer's source + +} +elseif ($event->type == 'customer.source.updated') { + + //TODO: update customer's source + +} +elseif ($event->type == 'customer.source.delete') { + + //TODO: delete customer's source + +} +elseif ($event->type == 'charge.failed') { + + $subject = 'Your payment has been received: '.$event->data->object->id.''; + $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + //mail('ptibogxiv@msn.com', $subject, 'test', $headers); + +} +elseif (($event->type == 'source.chargeable') && ($event->data->object->type == 'three_d_secure') && ($event->data->object->three_d_secure->authenticated==true)) { + + $stripe=new Stripe($db); + $charge=$stripe->CreatePaymentStripe($event->data->object->amount/100,$event->data->object->currency,$event->data->object->metadata->source,$event->data->object->metadata->idsource,$event->data->object->id,$event->data->object->metadata->customer,$stripe->getStripeAccount($service)); + + if (isset($charge->id) && $charge->statut=='error'){ + $msg=$charge->message; + $code=$charge->code; + $error++; + } + elseif (isset($charge->id) && $charge->statut=='success' && $event->data->object->metadata->source=='order') { + $order=new Commande($db); + $order->fetch($event->data->object->metadata->idsource); + $invoice = new Facture($db); + $idinv=$invoice->createFromOrder($order); + + if ($idinv > 0) + { + $result=$invoice->validate($user); + if ($result > 0) { + $invoice->fetch($idinv); + $paiement = $invoice->getSommePaiement(); + $creditnotes=$invoice->getSumCreditNotesUsed(); + $deposits=$invoice->getSumDepositsUsed(); + $ref=$invoice->ref; + $ifverif=$invoice->socid; + $currency=$invoice->multicurrency_code; + $total=price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits,'MT'); + }else{ + $msg=$invoice->error; + $error++; + } + }else{ + $msg=$invoice->error; + $error++; + } + } + + if (!$error){ + $datepaye = dol_now(); + $paiementcode ="CB"; + $amounts=array(); + $amounts[$invoice->id] = $total; + $multicurrency_amounts=array(); + //$multicurrency_amounts[$item] = $total; + $paiement = new Paiement($db); + $paiement->datepaye = $datepaye; + $paiement->amounts = $amounts; // Array with all payments dispatching + $paiement->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching + $paiement->paiementid = dol_getIdFromCode($db,$paiementcode,'c_paiement'); + $paiement->num_paiement = $charge->message; + $paiement->note = ''; + } + + if (! $error){ + $paiement_id=$paiement->create($user, 0); + + if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE) && count($invoice->lines)){ + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id','aZ09')) $newlang = GETPOST('lang_id','aZ09'); + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $invoice->thirdparty->default_lang; + if (! empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + $model=$invoice->modelpdf; + $ret = $invoice->fetch($invoice->id); // Reload to get new records + + $invoice->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + if ($paiement_id < 0){ + $msg=$paiement->errors; + $error++; + }else{ + if ($event->data->object->metadata->source=='order') { + $order->classifyBilled($user); + } + } + } + + if (! $error){ + $label='(CustomerInvoicePayment)'; + if (GETPOST('type') == 2) $label='(CustomerInvoicePaymentBack)'; + $paiement->addPaymentToBank($user,'payment',$label,$conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS,'',''); + if ($result < 0) + { + $msg=$paiement->errors; + $error++; + } + $invoice->set_paid($user); + } + + $body = ""; + $subject = 'Facture '.$invoice->ref; + $headers = 'From: "'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'" <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; + //mail('ptibogxiv@msn.com', $subject, $body, $headers); TODO convert in dolibarr standard +} +elseif ($event->type == 'customer.deleted') { + $db->begin(); + $sql = "DELETE FROM ".MAIN_DB_PREFIX."societe_account WHERE key_account = '".$event->data->object->id."' and site='stripe' "; + dol_syslog(get_class($this) . "::delete sql=" . $sql, LOG_DEBUG); + $db->query($sql); + $db->commit(); +} + diff --git a/htdocs/public/stripe/newpayment.php b/htdocs/public/stripe/newpayment.php index 36cd64b4f01..5357f99dd3d 100644 --- a/htdocs/public/stripe/newpayment.php +++ b/htdocs/public/stripe/newpayment.php @@ -38,1233 +38,15 @@ if (is_numeric($entity)) define("DOLENTITY", $entity); require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/stripe/config.php'; -/* included into config.php require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/stripe/lib/stripe.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/includes/stripe/init.php'; -*/ +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; // Security check if (empty($conf->stripe->enabled)) accessforbidden('',0,0,1); -$langs->load("main"); -$langs->load("companies"); -$langs->load("other"); -$langs->load("paybox"); // File with generic data -$langs->load("paypal"); -$langs->load("stripe"); - -$action=GETPOST('action','alpha'); - -// Input are: -// type ('invoice','order','contractline'), -// id (object id), -// amount (required if id is empty), -// tag (a free text, required if type is empty) -// currency (iso code) - -$suffix=GETPOST("suffix",'alpha'); -$amount=price2num(GETPOST("amount")); -if (! GETPOST("currency",'alpha')) $currency=$conf->currency; -else $currency=GETPOST("currency",'alpha'); - -if (! $action) -{ - if (! GETPOST("amount") && ! GETPOST("source")) - { - dol_print_error('',$langs->trans('ErrorBadParameters')." - amount or source"); - exit; - } - if (is_numeric($amount) && ! GETPOST("tag") && ! GETPOST("source")) - { - dol_print_error('',$langs->trans('ErrorBadParameters')." - tag or source"); - exit; - } - if (GETPOST("source") && ! GETPOST("ref")) - { - dol_print_error('',$langs->trans('ErrorBadParameters')." - ref"); - exit; - } -} - -// Define $urlwithroot -//$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root)); -//$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file -$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current - -$urlok=$urlwithroot.'/public/stripe/paymentok.php?'; -$urlko=$urlwithroot.'/public/stripe/paymentko.php?'; - -// Complete urls for post treatment -$SOURCE=GETPOST("source",'alpha'); -$ref=$REF=GETPOST('ref','alpha'); -$TAG=GETPOST("tag",'alpha'); -$FULLTAG=GETPOST("fulltag",'alpha'); // fulltag is tag with more informations -$SECUREKEY=GETPOST("securekey"); // Secure key -if (! preg_match('/'.preg_quote('PM=stripe','/').'/', $FULLTAG)) $FULLTAG.=($FULLTAG?'.':'').'PM=stripe'; - -if (! empty($SOURCE)) -{ - $urlok.='source='.urlencode($SOURCE).'&'; - $urlko.='source='.urlencode($SOURCE).'&'; -} -if (! empty($REF)) -{ - $urlok.='ref='.urlencode($REF).'&'; - $urlko.='ref='.urlencode($REF).'&'; -} -if (! empty($TAG)) -{ - $urlok.='tag='.urlencode($TAG).'&'; - $urlko.='tag='.urlencode($TAG).'&'; -} -if (! empty($FULLTAG)) -{ - $urlok.='fulltag='.urlencode($FULLTAG).'&'; - $urlko.='fulltag='.urlencode($FULLTAG).'&'; -} -if (! empty($SECUREKEY)) -{ - $urlok.='securekey='.urlencode($SECUREKEY).'&'; - $urlko.='securekey='.urlencode($SECUREKEY).'&'; -} -if (! empty($entity)) -{ - $urlok.='entity='.urlencode($entity).'&'; - $urlko.='entity='.urlencode($entity).'&'; -} -$urlok=preg_replace('/&$/','',$urlok); // Remove last & -$urlko=preg_replace('/&$/','',$urlko); // Remove last & - -// Check parameters -/* -$STRIPE_API_OK=""; -if ($urlok) $STRIPE_API_OK=$urlok; -$STRIPE_API_KO=""; -if ($urlko) $STRIPE_API_KO=$urlko; -if (empty($STRIPE_API_USER)) -{ - dol_print_error('',"Paypal setup param STRIPE_API_USER not defined"); - return -1; -} -if (empty($STRIPE_API_PASSWORD)) -{ - dol_print_error('',"Paypal setup param STRIPE_API_PASSWORD not defined"); - return -1; -} -if (empty($STRIPE_API_SIGNATURE)) -{ - dol_print_error('',"Paypal setup param STRIPE_API_SIGNATURE not defined"); - return -1; -} -*/ - - -// Check security token -$valid=true; -if (! empty($conf->global->STRIPE_SECURITY_TOKEN)) -{ - if (! empty($conf->global->STRIPE_SECURITY_TOKEN_UNIQUE)) - { - if ($SOURCE && $REF) $token = dol_hash($conf->global->STRIPE_SECURITY_TOKEN . $SOURCE . $REF, 2); // Use the source in the hash to avoid duplicates if the references are identical - else $token = dol_hash($conf->global->STRIPE_SECURITY_TOKEN, 2); - } - else - { - $token = $conf->global->STRIPE_SECURITY_TOKEN; - } - if ($SECUREKEY != $token) - { - if (empty($conf->global->PAYMENT_SECURITY_ACCEPT_ANY_TOKEN)) $valid=false; // PAYMENT_SECURITY_ACCEPT_ANY_TOKEN is for backward compatibility - else dol_syslog("Warning: PAYMENT_SECURITY_ACCEPT_ANY_TOKEN is on", LOG_WARNING); - } - - if (! $valid) - { - print '
    Bad value for key.
    '; - //print 'SECUREKEY='.$SECUREKEY.' token='.$token.' valid='.$valid; - exit; - } -} - -// Common variables -$creditor=$mysoc->name; -$paramcreditor='ONLINE_PAYMENT_CREDITOR_'.$suffix; -if (! empty($conf->global->$paramcreditor)) $creditor=$conf->global->$paramcreditor; -else if (! empty($conf->global->ONLINE_PAYMENT_CREDITOR)) $creditor=$conf->global->ONLINE_PAYMENT_CREDITOR; - - - -/* - * Actions - */ - -if ($action == 'dopayment') // We click on button Create payment -{ - if (GETPOST('newamount','alpha')) $amount = price2num(GETPOST('newamount','alpha'),'MT'); - else - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Amount")), null, 'errors'); - $action = ''; - } -} - -if ($action == 'charge') -{ - // Correct the amount according to unit of currency - // See https://support.stripe.com/questions/which-zero-decimal-currencies-does-stripe-support - $arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'); - if (! in_array($currency, $arrayzerounitcurrency)) $amount=$amount * 100; - - dol_syslog("POST keys : ".join(',', array_keys($_POST)), LOG_DEBUG, 0, '_stripe'); - dol_syslog("POST values: ".join(',', $_POST), LOG_DEBUG, 0, '_stripe'); - - $stripeToken = GETPOST("stripeToken",'alpha'); - $email = GETPOST("stripeEmail",'alpha'); - $vatnumber = GETPOST('vatnumber','alpha'); - - dol_syslog("stripeToken = ".$stripeToken, LOG_DEBUG, 0, '_stripe'); - dol_syslog("email = ".$email, LOG_DEBUG, 0, '_stripe'); - dol_syslog("vatnumber = ".$vatnumber, LOG_DEBUG, 0, '_stripe'); - - $error = 0; - - try { - dol_syslog("Create customer card profile", LOG_DEBUG, 0, '_stripe'); - $customer = \Stripe\Customer::create(array( - 'email' => $email, - 'description' => ($email?'Customer card profile for '.$email:null), - 'metadata' => array('ipaddress'=>$_SERVER['REMOTE_ADDR']), - 'business_vat_id' => ($vatnumber?$vatnumber:null), - 'source' => $stripeToken // source can be a token OR array('object'=>'card', 'exp_month'=>xx, 'exp_year'=>xxxx, 'number'=>xxxxxxx, 'cvc'=>xxx, 'name'=>'Cardholder's full name', zip ?) - )); - // TODO Add 'business_vat_id' ? - - dol_syslog("Create charge", LOG_DEBUG, 0, '_stripe'); - $charge = \Stripe\Charge::create(array( - 'customer' => $customer->id, // Will reuse default source of this customer card profile - 'amount' => price2num($amount, 'MU'), - 'currency' => $currency, - 'description' => 'Stripe payment: '.$FULLTAG, - 'metadata' => array("FULLTAG" => $FULLTAG, 'Recipient' => $mysoc->name), - 'statement_descriptor' => dol_trunc(dol_trunc(dol_string_unaccent($mysoc->name), 6, 'right', 'UTF-8', 1).' '.$FULLTAG, 22, 'right', 'UTF-8', 1) // 22 chars that appears on bank receipt - )); - } catch(\Stripe\Error\Card $e) { - // Since it's a decline, \Stripe\Error\Card will be caught - $body = $e->getJsonBody(); - $err = $body['error']; - - print('Status is:' . $e->getHttpStatus() . "\n"); - print('Type is:' . $err['type'] . "\n"); - print('Code is:' . $err['code'] . "\n"); - // param is '' in this case - print('Param is:' . $err['param'] . "\n"); - print('Message is:' . $err['message'] . "\n"); - - $error++; - setEventMessages($e->getMessage(), null, 'errors'); - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - $action=''; - } catch (\Stripe\Error\RateLimit $e) { - // Too many requests made to the API too quickly - $error++; - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - setEventMessages($e->getMessage(), null, 'errors'); - $action=''; - } catch (\Stripe\Error\InvalidRequest $e) { - // Invalid parameters were supplied to Stripe's API - $error++; - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - setEventMessages($e->getMessage(), null, 'errors'); - $action=''; - } catch (\Stripe\Error\Authentication $e) { - // Authentication with Stripe's API failed - // (maybe you changed API keys recently) - $error++; - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - setEventMessages($e->getMessage(), null, 'errors'); - $action=''; - } catch (\Stripe\Error\ApiConnection $e) { - // Network communication with Stripe failed - $error++; - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - setEventMessages($e->getMessage(), null, 'errors'); - $action=''; - } catch (\Stripe\Error\Base $e) { - // Display a very generic error to the user, and maybe send - // yourself an email - $error++; - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - setEventMessages($e->getMessage(), null, 'errors'); - $action=''; - } catch (Exception $e) { - // Something else happened, completely unrelated to Stripe - $error++; - dol_syslog($e->getMessage(), LOG_WARNING, 0, '_stripe'); - setEventMessages($e->getMessage(), null, 'errors'); - $action=''; - } - - $_SESSION["onlinetoken"] = $stripeToken; - $_SESSION["FinalPaymentAmt"] = $amount; - $_SESSION["currencyCodeType"] = $currency; - $_SESSION["paymentType"] = ''; - $_SESSION['ipaddress'] = $_SERVER['REMOTE_ADDR']; // Payer ip - $_SESSION['payerID'] = is_object($customer)?$customer->id:''; - $_SESSION['TRANSACTIONID'] = is_object($charge)?$charge->id:''; - - dol_syslog("Action charge stripe result=".$error." ip=".$_SESSION['ipaddress'], LOG_DEBUG, 0, '_stripe'); - dol_syslog("onlinetoken=".$_SESSION["onlinetoken"]." FinalPaymentAmt=".$_SESSION["FinalPaymentAmt"]." currencyCodeType=".$_SESSION["currencyCodeType"]." payerID=".$_SESSION['payerID']." TRANSACTIONID=".$_SESSION['TRANSACTIONID'], LOG_DEBUG, 0, '_stripe'); - dol_syslog("FULLTAG=".$FULLTAG, LOG_DEBUG, 0, '_stripe'); - dol_syslog("Now call the redirect to paymentok or paymentko", LOG_DEBUG, 0, '_stripe'); - - if ($error) - { - header("Location: ".$urlko); - exit; - } - else - { - header("Location: ".$urlok); - exit; - } - -} - - -/* - * View - */ - -$head=''; -if (! empty($conf->global->ONLINE_PAYMENT_CSS_URL)) $head=''."\n"; - -$conf->dol_hide_topmenu=1; -$conf->dol_hide_leftmenu=1; - -llxHeader($head, $langs->trans("PaymentForm"), '', '', 0, 0, '', '', '', 'onlinepaymentbody'); - -// Check link validity -if (! empty($SOURCE) && in_array($ref, array('member_ref', 'contractline_ref', 'invoice_ref', 'order_ref', ''))) -{ - $langs->load("errors"); - dol_print_error_email('BADREFINPAYMENTFORM', $langs->trans("ErrorBadLinkSourceSetButBadValueForRef", $SOURCE, $ref)); - llxFooter(); - $db->close(); - exit; -} - -if (empty($conf->global->STRIPE_LIVE) || GETPOST('forcesandbox','alpha')) -{ - dol_htmloutput_mesg($langs->trans('YouAreCurrentlyInSandboxMode'),'','warning'); -} - -print ''."\n"; -print '
    '."\n"; -print '
    '."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''; -print ''; -print "\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print ''."\n"; -print "\n"; - -print ''."\n"; - -// Show logo (search order: logo defined by PAYMENT_LOGO_suffix, then PAYMENT_LOGO, then small company logo, large company logo, theme logo, common logo) -$width=0; -// Define logo and logosmall -$logosmall=$mysoc->logo_small; -$logo=$mysoc->logo; -$paramlogo='PAYMENT_LOGO_'.$suffix; -if (! empty($conf->global->$paramlogo)) $logosmall=$conf->global->$paramlogo; -else if (! empty($conf->global->PAYMENT_LOGO)) $logosmall=$conf->global->PAYMENT_LOGO; -//print ''."\n"; -// Define urllogo -$urllogo=''; -if (! empty($logosmall) && is_readable($conf->mycompany->dir_output.'/logos/thumbs/'.$logosmall)) -{ - $urllogo=DOL_URL_ROOT.'/viewimage.php?modulepart=mycompany&file='.urlencode('thumbs/'.$logosmall); -} -elseif (! empty($logo) && is_readable($conf->mycompany->dir_output.'/logos/'.$logo)) -{ - $urllogo=DOL_URL_ROOT.'/viewimage.php?modulepart=mycompany&file='.urlencode($logo); - $width=96; -} -// Output html code for logo -if ($urllogo) -{ - print ''; - print ''; - print ''."\n"; -} - -// Output introduction text -$text=''; -if (! empty($conf->global->PAYMENT_NEWFORM_TEXT)) -{ - $langs->load("members"); - if (preg_match('/^\((.*)\)$/',$conf->global->PAYMENT_NEWFORM_TEXT,$reg)) $text.=$langs->trans($reg[1])."
    \n"; - else $text.=$conf->global->PAYMENT_NEWFORM_TEXT."
    \n"; - $text=''."\n"; -} -if (empty($text)) -{ - $text.=''."\n"; - $text.=''."\n"; -} -print $text; - -// Output payment summary form -print ''."\n"; - -print '

    '.$text.'

    '.$langs->trans("WelcomeOnPaymentPage").'
    '.$langs->trans("ThisScreenAllowsYouToPay",$creditor).'

    '; -print ''; -print ''."\n"; - -$found=false; -$error=0; -$var=false; - -// Free payment -if (! GETPOST("source")) -{ - $found=true; - $tag=GETPOST("tag"); - $fulltag=$tag; - - // Creditor - print ''."\n"; - - // Amount - print ''."\n"; - - // Tag - - print ''."\n"; - - // We do not add fields shipToName, shipToStreet, shipToCity, shipToState, shipToCountryCode, shipToZip, shipToStreet2, phoneNum - // as they don't exists (buyer is unknown, tag is free). -} - - -// Payment on customer order -if (GETPOST("source") == 'order') -{ - $found=true; - $langs->load("orders"); - - require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; - - $order=new Commande($db); - $result=$order->fetch('',$ref); - if ($result < 0) - { - $mesg=$order->error; - $error++; - } - else - { - $result=$order->fetch_thirdparty($order->socid); - } - - if ($action != 'dopayment') // Do not change amount if we just click on first dopayment - { - $amount=$order->total_ttc; - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - } - - $fulltag='ORD='.$order->ref.'.CUS='.$order->thirdparty->id; - //$fulltag.='.NAM='.strtr($order->thirdparty->name,"-"," "); - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // Shipping address - $shipToName=$order->thirdparty->name; - $shipToStreet=$order->thirdparty->address; - $shipToCity=$order->thirdparty->town; - $shipToState=$order->thirdparty->state_code; - $shipToCountryCode=$order->thirdparty->country_code; - $shipToZip=$order->thirdparty->zip; - $shipToStreet2=''; - $phoneNum=$order->thirdparty->phone; - if ($shipToName && $shipToStreet && $shipToCity && $shipToCountryCode && $shipToZip) - { - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - } - else - { - print ''."\n"; - } - print ''."\n"; - print ''."\n"; - print 'ref.'">'."\n"; -} - - -// Payment on customer invoice -if (GETPOST("source") == 'invoice') -{ - $found=true; - $langs->load("bills"); - - require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; - - $invoice=new Facture($db); - $result=$invoice->fetch('',$ref); - if ($result < 0) - { - $mesg=$invoice->error; - $error++; - } - else - { - $result=$invoice->fetch_thirdparty($invoice->socid); - } - - if ($action != 'dopayment') // Do not change amount if we just click on first dopayment - { - $amount=price2num($invoice->total_ttc - $invoice->getSommePaiement()); - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - } - - $fulltag='INV='.$invoice->ref.'.CUS='.$invoice->thirdparty->id; - //$fulltag.='.NAM='.strtr($invoice->thirdparty->name,"-"," "); - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // Shipping address - $shipToName=$invoice->thirdparty->name; - $shipToStreet=$invoice->thirdparty->address; - $shipToCity=$invoice->thirdparty->town; - $shipToState=$invoice->thirdparty->state_code; - $shipToCountryCode=$invoice->thirdparty->country_code; - $shipToZip=$invoice->thirdparty->zip; - $shipToStreet2=''; - $phoneNum=$invoice->thirdparty->phone; - if ($shipToName && $shipToStreet && $shipToCity && $shipToCountryCode && $shipToZip) - { - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - } - else - { - print ''."\n"; - } - print ''."\n"; - print ''."\n"; - print 'ref.'">'."\n"; -} - -// Payment on contract line -if (GETPOST("source") == 'contractline') -{ - $found=true; - $langs->load("contracts"); - - require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php'; - - $contractline=new ContratLigne($db); - $result=$contractline->fetch('',$ref); - if ($result < 0) - { - $mesg=$contractline->error; - $error++; - } - else - { - if ($contractline->fk_contrat > 0) - { - $contract=new Contrat($db); - $result=$contract->fetch($contractline->fk_contrat); - if ($result > 0) - { - $result=$contract->fetch_thirdparty($contract->socid); - } - else - { - $mesg=$contract->error; - $error++; - } - } - else - { - $mesg='ErrorRecordNotFound'; - $error++; - } - } - - if ($action != 'dopayment') // Do not change amount if we just click on first dopayment - { - $amount=$contractline->total_ttc; - if ($contractline->fk_product) - { - $product=new Product($db); - $result=$product->fetch($contractline->fk_product); - - // We define price for product (TODO Put this in a method in product class) - if (! empty($conf->global->PRODUIT_MULTIPRICES)) - { - $pu_ht = $product->multiprices[$contract->thirdparty->price_level]; - $pu_ttc = $product->multiprices_ttc[$contract->thirdparty->price_level]; - $price_base_type = $product->multiprices_base_type[$contract->thirdparty->price_level]; - } - else - { - $pu_ht = $product->price; - $pu_ttc = $product->price_ttc; - $price_base_type = $product->price_base_type; - } - - $amount=$pu_ttc; - if (empty($amount)) - { - dol_print_error('','ErrorNoPriceDefinedForThisProduct'); - exit; - } - } - - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - } - - $fulltag='COL='.$contractline->ref.'.CON='.$contract->ref.'.CUS='.$contract->thirdparty->id.'.DAT='.dol_print_date(dol_now(),'%Y%m%d%H%M'); - //$fulltag.='.NAM='.strtr($contract->thirdparty->name,"-"," "); - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - $qty=1; - if (GETPOST('qty')) $qty=GETPOST('qty'); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - // Quantity - - $label=$langs->trans("Quantity"); - $qty=1; - $duration=''; - if ($contractline->fk_product) - { - if ($product->isService() && $product->duration_value > 0) - { - $label=$langs->trans("Duration"); - - // TODO Put this in a global method - if ($product->duration_value > 1) - { - $dur=array("h"=>$langs->trans("Hours"),"d"=>$langs->trans("DurationDays"),"w"=>$langs->trans("DurationWeeks"),"m"=>$langs->trans("DurationMonths"),"y"=>$langs->trans("DurationYears")); - } - else - { - $dur=array("h"=>$langs->trans("Hour"),"d"=>$langs->trans("DurationDay"),"w"=>$langs->trans("DurationWeek"),"m"=>$langs->trans("DurationMonth"),"y"=>$langs->trans("DurationYear")); - } - $duration=$product->duration_value.' '.$dur[$product->duration_unit]; - } - } - print ''; - print ''."\n"; - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // Shipping address - $shipToName=$contract->thirdparty->name; - $shipToStreet=$contract->thirdparty->address; - $shipToCity=$contract->thirdparty->town; - $shipToState=$contract->thirdparty->state_code; - $shipToCountryCode=$contract->thirdparty->country_code; - $shipToZip=$contract->thirdparty->zip; - $shipToStreet2=''; - $phoneNum=$contract->thirdparty->phone; - if ($shipToName && $shipToStreet && $shipToCity && $shipToCountryCode && $shipToZip) - { - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - } - else - { - print ''."\n"; - } - print ''."\n"; - print ''."\n"; - print 'ref.'">'."\n"; -} - -// Payment on member subscription -if (GETPOST("source") == 'membersubscription') -{ - $found=true; - $langs->load("members"); - - require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; - require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php'; - - $member=new Adherent($db); - $result=$member->fetch('',$ref); - if ($result < 0) - { - $mesg=$member->error; - $error++; - } - else - { - $subscription=new Subscription($db); - } - - if ($action != 'dopayment') // Do not change amount if we just click on first dopayment - { - $amount=$subscription->total_ttc; - if (GETPOST("amount",'int')) $amount=GETPOST("amount",'int'); - $amount=price2num($amount); - } - - $fulltag='MEM='.$member->id.'.DAT='.dol_print_date(dol_now(),'%Y%m%d%H%M'); - if (! empty($TAG)) { $tag=$TAG; $fulltag.='.TAG='.$TAG; } - $fulltag=dol_string_unaccent($fulltag); - - // Creditor - - print ''."\n"; - - // Debitor - - print ''."\n"; - - if ($member->last_subscription_date || $member->last_subscription_amount) - { - // Last subscription date - - print ''."\n"; - - // Last subscription amount - - print ''."\n"; - - if (empty($amount) && ! GETPOST('newamount','alpha')) $_GET['newamount']=$member->last_subscription_amount; - } - - // Amount - - print ''."\n"; - - // Tag - - print ''."\n"; - - // Shipping address - $shipToName=$member->getFullName($langs); - $shipToStreet=$member->address; - $shipToCity=$member->town; - $shipToState=$member->state_code; - $shipToCountryCode=$member->country_code; - $shipToZip=$member->zip; - $shipToStreet2=''; - $phoneNum=$member->phone; - if ($shipToName && $shipToStreet && $shipToCity && $shipToCountryCode && $shipToZip) - { - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - } - else - { - print ''."\n"; - } - print ''."\n"; - print ''."\n"; -} - - - -if (! $found && ! $mesg) $mesg=$langs->trans("ErrorBadParameters"); - -if ($mesg) print ''."\n"; - -print '
    '.$langs->trans("ThisIsInformationOnPayment").' :
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("ThirdParty"); - print ''.$order->thirdparty->name.''; - - // Object - - $text=''.$langs->trans("PaymentOrderRef",$order->ref).''; - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("ThirdParty"); - print ''.$invoice->thirdparty->name.''; - - // Object - - $text=''.$langs->trans("PaymentInvoiceRef",$invoice->ref).''; - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("ThirdParty"); - print ''.$contract->thirdparty->name.''; - - // Object - - $text=''.$langs->trans("PaymentRenewContractId",$contract->ref,$contractline->ref).''; - if ($contractline->fk_product) - { - $text.='
    '.$product->ref.($product->label?' - '.$product->label:''); - } - if ($contractline->description) $text.='
    '.dol_htmlentitiesbr($contractline->description); - //if ($contractline->date_fin_validite) { - // $text.='
    '.$langs->trans("DateEndPlanned").': '; - // $text.=dol_print_date($contractline->date_fin_validite); - //} - if ($contractline->date_fin_validite) - { - $text.='
    '.$langs->trans("ExpiredSince").': '.dol_print_date($contractline->date_fin_validite); - } - - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$label.''.($duration?$duration:$qty).''; - print ''; - print '
    '.$langs->trans("Amount"); - if (empty($amount)) print ' ('.$langs->trans("ToComplete").')'; - print ''; - if (empty($amount) || ! is_numeric($amount)) - { - print ''; - print ''; - } - else { - print ''.price($amount).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '
    '.$langs->trans("Creditor"); - print ''.$creditor.''; - print ''; - print '
    '.$langs->trans("Member"); - print ''; - if ($member->morphy == 'mor' && ! empty($member->societe)) print $member->societe; - else print $member->getFullName($langs); - print ''; - - // Object - - $text=''.$langs->trans("PaymentSubscription").''; - print '
    '.$langs->trans("Designation"); - print ''.$text; - print ''; - print ''; - print '
    '.$langs->trans("LastSubscriptionDate"); - print ''.dol_print_date($member->last_subscription_date,'day'); - print '
    '.$langs->trans("LastSubscriptionAmount"); - print ''.price($member->last_subscription_amount); - print '
    '.$langs->trans("Amount"); - if (empty($amount)) - { - print ' ('.$langs->trans("ToComplete"); - if (! empty($conf->global->MEMBER_EXT_URL_SUBSCRIPTION_INFO)) print ' - '.$langs->trans("SeeHere").''; - print ')'; - } - print ''; - $valtoshow=''; - if (empty($amount) || ! is_numeric($amount)) - { - $valtoshow=price2num(GETPOST("newamount",'alpha'),'MT'); - // force default subscription amount to value defined into constant... - if (empty($valtoshow)) - { - if (! empty($conf->global->MEMBER_NEWFORM_EDITAMOUNT)) { - if (! empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { - $valtoshow = $conf->global->MEMBER_NEWFORM_AMOUNT; - } - } - else { - if (! empty($conf->global->MEMBER_NEWFORM_AMOUNT)) { - $amount = $conf->global->MEMBER_NEWFORM_AMOUNT; - } - } - } - } - if (empty($amount) || ! is_numeric($amount)) - { - //$valtoshow=price2num(GETPOST("newamount",'alpha'),'MT'); - if (! empty($conf->global->MEMBER_MIN_AMOUNT) && $valtoshow) $valtoshow=max($conf->global->MEMBER_MIN_AMOUNT,$valtoshow); - print ''; - print ''; - } - else { - $valtoshow=$amount; - if (! empty($conf->global->MEMBER_MIN_AMOUNT) && $valtoshow) $valtoshow=max($conf->global->MEMBER_MIN_AMOUNT,$valtoshow); - print ''.price($valtoshow).''; - print ''; - print ''; - } - // Currency - print ' '.$langs->trans("Currency".$currency).''; - print ''; - print '
    '.$langs->trans("PaymentCode"); - print ''.$fulltag.''; - print ''; - print ''; - print '

    '.$mesg.'
    '."\n"; -print "\n"; - - -if ($action != 'dopayment') -{ - if ($found && ! $error) // We are in a management option and no error - { - print '
    '; - } - else - { - dol_print_error_email('ERRORNEWPAYMENTSTRIPE'); - } -} - - -print '
    '."\n"; -print '
    '."\n"; -print '
    '."\n"; -print '
    '; - - -// Add more content on page for some services -if (preg_match('/^dopayment/',$action)) -{ - // Simple checkout - /* - print ''; - */ - - // Personalized checkout - print ''; - - print ' - -
    -
    '; - - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''."\n"; - print ''; - print ''."\n"; - print ''."\n"; - print ''; - - print ' - -
    - -
    - -
    - -
    - - - -
    -
    - - -
    - -
    - - - - - - '; -} - - - -htmlPrintOnlinePaymentFooter($mysoc,$langs,1,$suffix); - -llxFooter('', 'public'); - -$db->close(); - +$newurl = $_SERVER['REQUEST_URI']; +$newurl = preg_replace('/\/stripe\/newpayment/', '/stripe/newpayment', $newurl); +header("Location: ".$newurl.(preg_match('/\?/', $newurl)?'&':'?').'paymentmethod=stripe'); +exit; diff --git a/htdocs/public/stripe/paymentko.php b/htdocs/public/stripe/paymentko.php index a2462904b07..ff1abfae9e4 100644 --- a/htdocs/public/stripe/paymentko.php +++ b/htdocs/public/stripe/paymentko.php @@ -1,6 +1,6 @@ -* + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or diff --git a/htdocs/public/stripe/paymentok.php b/htdocs/public/stripe/paymentok.php index d0ce82d1ca8..52b3dac8a7f 100644 --- a/htdocs/public/stripe/paymentok.php +++ b/htdocs/public/stripe/paymentok.php @@ -158,7 +158,7 @@ if ($ispaymentok) if (! empty($tmptag['MEM'])) { $langs->load("members"); - $url=$urlwithroot."/adherents/card_subscriptions.php?rowid=".$tmptag['MEM']; + $url=$urlwithroot."/adherents/subscription.php?rowid=".$tmptag['MEM']; $content.=$langs->trans("PaymentSubscription")."
    \n"; $content.=$langs->trans("MemberId").': '.$tmptag['MEM']."
    \n"; $content.=$langs->trans("Link").': '.$url.''."
    \n"; diff --git a/htdocs/public/ticketsup/create_ticket.php b/htdocs/public/ticketsup/create_ticket.php new file mode 100644 index 00000000000..a0cb6f787ac --- /dev/null +++ b/htdocs/public/ticketsup/create_ticket.php @@ -0,0 +1,366 @@ + + * 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * Display public form to add new ticket + * + * @package ticketsup + */ +if (!defined('NOREQUIREUSER')) { + define('NOREQUIREUSER', '1'); +} + +//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1'); +//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); +//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', '1'); +} + +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +// If there is no menu to show +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +// If we don't need to load the html.form.class.php +//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); +define("NOLOGIN", 1); // This means this output page does not require to be logged. +define("NOCSRFCHECK", 1); // We accept to go on this page from external web site. + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/lib/ticketsup.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; +require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("mails"); +$langs->load("ticketsup"); + +// Get parameters +$id = GETPOST('id', 'int'); +$msg_id = GETPOST('msg_id', 'int'); + +$action = GETPOST('action', 'alpha'); + +$object = new Ticketsup($db); + +/* + * Add file in email form + */ +if (GETPOST('addfile') && !GETPOST('add_ticket')) { + ////$res = $object->fetch('',GETPOST('track_id')); + ////if($res > 0) + ////{ + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + + // Set tmp directory TODO Use a dedicated directory for temp mails files + $vardir = $conf->ticketsup->dir_output; + $upload_dir_tmp = $vardir . '/temp'; + if (!dol_is_dir($upload_dir_tmp)) { + dol_mkdir($upload_dir_tmp); + } + dol_add_file_process($upload_dir_tmp, 0, 0, 'addedfile'); + $action = 'create_ticket'; + ////} +} + +/* + * Remove file in email form + */ +if (GETPOST('removedfile') && !GETPOST('add_ticket')) { + ////$res = $object->fetch('',GETPOST('track_id')); + ////if($res > 0) + ////{ + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + + // Set tmp directory + $vardir = $conf->ticketsup->dir_output . '/'; + $upload_dir_tmp = $vardir . '/temp'; + + // TODO Delete only files that was uploaded from email form + dol_remove_file_process($_POST['removedfile'], 0); + $action = 'create_ticket'; + ////} +} +if ($action == 'create_ticket' && GETPOST('add_ticket')) { + $error = 0; + $origin_email = GETPOST('email', 'alpha'); + if (empty($origin_email)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Email"))); + $action = ''; + } else { + // Search company saved with email + $searched_companies = $object->searchSocidByEmail($origin_email, '0'); + + // Chercher un contact existant avec cette adresse email + // Le premier contact trouvé est utilisé pour déterminer le contact suivi + $contacts = $object->searchContactByEmail($origin_email); + + // Option to require email exists to create ticket + if (!empty($conf->global->TICKETS_EMAIL_MUST_EXISTS) && !$contacts[0]->socid) { + $error++; + array_push($object->errors, $langs->trans("ErrorEmailMustExistToCreateTicket")); + $action = ''; + } + } + + if (!GETPOST("subject")) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject"))); + $action = ''; + } elseif (!GETPOST("message")) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message"))); + $action = ''; + } + + // Check email address + if (!isValidEmail($origin_email)) { + $error++; + array_push($object->errors, $langs->trans("ErrorBadEmailAddress", $langs->transnoentities("email"))); + $action = ''; + } + + if (!$error) { + $object->db->begin(); + + $object->track_id = generate_random_id(16); + + $object->subject = GETPOST("subject"); + $object->message = GETPOST("message"); + $object->origin_email = $origin_email; + + $object->type_code = GETPOST("type_code"); + $object->category_code = GETPOST("category_code"); + $object->severity_code = GETPOST("severity_code"); + if (is_array($searched_companies)) { + $object->fk_soc = $searched_companies[0]->id; + } + + if (is_array($contacts) and count($contacts) > 0) { + $object->fk_soc = $contacts[0]->socid; + $usertoassign = $contacts[0]->id; + } + + if (!empty($conf->global->TICKETS_EXTRAFIELDS_PUBLIC) && $conf->global->TICKETS_EXTRAFIELDS_PUBLIC == "1") { + $extrafields = new ExtraFields($db); + $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object); + } + + // Generate new ref + $object->ref = $object->getDefaultRef(); + if (!is_object($user)) { + $user = new User($db); + } + $id = $object->create($user, 1); // Disable trigger for email (send by this page) + if ($id <= 0) { + $error++; + $errors = ($object->error ? array($object->error) : $object->errors); + array_push($object->errors, $object->error ? array($object->error) : $object->errors); + $action = 'create_ticket'; + } + + if (!$error && $id > 0) { + if ($usertoassign > 0) { + $object->add_contact($usertoassign, "SUPPORTCLI", 'external', $notrigger = 0); + } + + $object->db->commit(); + + $res = $object->fetch($id); + if ($res) { + // Create form object + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + $formmail = new FormMail($db); + + // Init to avoid errors + $filepath = array(); + $filename = array(); + $mimetype = array(); + + $attachedfiles = $formmail->get_attached_files(); + $filepath = $attachedfiles['paths']; + $filename = $attachedfiles['names']; + $mimetype = $attachedfiles['mimes']; + + // Send email to customer + $subject = '[' . $conf->global->MAIN_INFO_SOCIETE_NOM . '] ' . $langs->transnoentities('TicketNewEmailSubject'); + $message .= ($conf->global->TICKETS_MESSAGE_MAIL_NEW ? $conf->global->TICKETS_MESSAGE_MAIL_NEW : $langs->transnoentities('TicketNewEmailBody')) . "\n\n"; + $message .= $langs->transnoentities('TicketNewEmailBodyInfosTicket') . "\n"; + + $url_public_ticket = ($conf->global->TICKETS_URL_PUBLIC_INTERFACE ? $conf->global->TICKETS_URL_PUBLIC_INTERFACE . '/' : dol_buildpath('/ticketsup/public/view.php', 2)) . '?track_id=' . $object->track_id; + $infos_new_ticket = $langs->transnoentities('TicketNewEmailBodyInfosTrackId', '' . $object->track_id . '') . "\n"; + $infos_new_ticket .= $langs->transnoentities('TicketNewEmailBodyInfosTrackUrl') . "\n\n"; + + $message .= dol_nl2br($infos_new_ticket); + $message .= $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE ? $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE : $langs->transnoentities('TicketMessageMailSignatureText'); + + $sendto = GETPOST('email'); + + $from = $conf->global->MAIN_INFO_SOCIETE_NOM . '<' . $conf->global->TICKETS_NOTIFICATION_EMAIL_FROM . '>'; + $replyto = $from; + + $message = dol_nl2br($message); + + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $sendto, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1); + if ($mailfile->error) { + setEventMessage($mailfile->error, 'errors'); + } else { + $result = $mailfile->sendfile(); + } + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + + /* Send email to admin */ + $sendto = $conf->global->TICKETS_NOTIFICATION_EMAIL_TO; + $subject = '[' . $conf->global->MAIN_INFO_SOCIETE_NOM . '] ' . $langs->transnoentities('TicketNewEmailSubjectAdmin'); + $message_admin = $langs->transnoentities('TicketNewEmailBodyAdmin', $object->track_id) . "\n\n"; + $message_admin .= '
    • ' . $langs->trans('Title') . ' : ' . $object->subject . '
    • '; + $message_admin .= '
    • ' . $langs->trans('Type') . ' : ' . $object->type_label . '
    • '; + $message_admin .= '
    • ' . $langs->trans('Category') . ' : ' . $object->category_label . '
    • '; + $message_admin .= '
    • ' . $langs->trans('Severity') . ' : ' . $object->severity_label . '
    • '; + $message_admin .= '
    • ' . $langs->trans('From') . ' : ' . $object->origin_email . '
    • '; + if (is_array($object->array_options) && count($object->array_options) > 0) { + foreach ($object->array_options as $key => $value) { + $message_admin .= '
    • ' . $langs->trans($key) . ' : ' . $value . '
    • '; + } + } + $message_admin .= '
    '; + $message_admin .= '

    ' . $langs->trans('Message') . ' :
    ' . $object->message . '

    '; + $message_admin .= '

    ' . $langs->trans('SeeThisTicketIntomanagementInterface') . '

    '; + + $from = $conf->global->MAIN_INFO_SOCIETE_NOM . '<' . $conf->global->TICKETS_NOTIFICATION_EMAIL_FROM . '>'; + $replyto = $from; + + $message_admin = dol_nl2br($message_admin); + + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1); + if ($mailfile->error) { + setEventMessage($mailfile->error, 'errors'); + } else { + $result = $mailfile->sendfile(); + } + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + + // Copy files into ticket directory + $destdir = $conf->ticketsup->dir_output . '/' . $object->track_id; + if (!dol_is_dir($destdir)) { + dol_mkdir($destdir); + } + foreach ($filename as $i => $val) { + dol_move($filepath[$i], $destdir . '/' . $filename[$i], 0, 1); + $formmail->remove_attached_files($i); + } + } + + setEventMessage($langs->trans('YourTicketSuccessfullySaved')); + $action = "infos_success"; + } else { + $object->db->rollback(); + setEventMessage($object->errors, 'errors'); + $action = 'create_ticket'; + } + } else { + setEventMessage($object->errors, 'errors'); + } +} + +/*************************************************** + * PAGE + * + ****************************************************/ + +$arrayofjs = array(); +$arrayofcss = array('/opensurvey/css/style.css', '/ticketsup/css/styles.css', '/ticketsup/css/bg.css.php'); +llxHeaderTicket($langs->trans("CreateTicket"), "", 0, 0, $arrayofjs, $arrayofcss); + +$form = new Form($db); +$formticket = new FormTicketsup($db); + +if (!$conf->global->TICKETS_ENABLE_PUBLIC_INTERFACE) { + print '
    ' . $langs->trans('TicketPublicInterfaceForbidden') . '
    '; + $db->close(); + exit(); +} + +print '
    '; + +if ($action != "infos_success") { + $formticket->withfromsocid = isset($socid) ? $socid : $user->societe_id; + $formticket->withtitletopic = 1; + $formticket->withcompany = 0; + $formticket->withusercreate = 1; + $formticket->fk_user_create = 0; + $formticket->withemail = 1; + $formticket->ispublic = 1; + $formticket->withfile = 2; + if (!empty($conf->global->TICKETS_EXTRAFIELDS_PUBLIC)) { + $formticket->withextrafields = $conf->global->TICKETS_EXTRAFIELDS_PUBLIC; + } + $formticket->action = 'create_ticket'; + + $formticket->param = array('returnurl' => $_SERVER['PHP_SELF']); + + if (empty($defaultref)) { + $defaultref = ''; + } + + print load_fiche_titre($langs->trans('NewTicket'), '', 'ticketsup-32@ticketsup', 0); + + print '
    ' . $langs->trans('TicketPublicInfoCreateTicket') . '
    '; + $formticket->showForm(); +} else { + print '
    ' . $langs->trans('MesgInfosPublicTicketCreatedWithTrackId', '' . $object->track_id . ''); + print '
    '; + print $langs->trans('PleaseRememberThisId'); +} +print '
    '; + +/*************************************************** + * LINKED OBJECT BLOCK + * + * Put here code to view linked object + ****************************************************/ +//$somethingshown=$object->showLinkedObjectBlock(); + +// End of page +$db->close(); +llxFooter(''); diff --git a/htdocs/public/ticketsup/img/bg_ticket.png b/htdocs/public/ticketsup/img/bg_ticket.png new file mode 100644 index 00000000000..344950d68f3 Binary files /dev/null and b/htdocs/public/ticketsup/img/bg_ticket.png differ diff --git a/htdocs/public/ticketsup/index.php b/htdocs/public/ticketsup/index.php new file mode 100644 index 00000000000..8352282c4af --- /dev/null +++ b/htdocs/public/ticketsup/index.php @@ -0,0 +1,88 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/public/index.php + * \ingroup ticketsup + * \brief Public file to add and manage ticket + */ + +//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER','1'); +//if (! defined('NOREQUIREDB')) define('NOREQUIREDB','1'); +//if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC','1'); +//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN','1'); +if (!defined('NOCSRFCHECK')) { + define('NOCSRFCHECK', '1'); +} +// Do not check anti CSRF attack test +//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK','1'); // Do not check style html tag into posted data +//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL','1'); // Do not check anti POST attack test +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +// If there is no need to load and show top and left menu +//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML','1'); // If we don't need to load the html.form.class.php +//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX','1'); +if (!defined("NOLOGIN")) { + define("NOLOGIN", '1'); +} +// If this page is public (can be called outside logged session) + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/lib/ticketsup.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("ticketsup"); +$langs->load("errors"); + +// Get parameters +$track_id = GETPOST('track_id', 'alpha'); +$action = GETPOST('action', 'alpha'); + +/*************************************************** + * VIEW + * + ****************************************************/ +$form = new Form($db); +$formticket = new FormTicketsup($db); + +$arrayofjs = array(); +$arrayofcss = array('/ticketsup/css/styles.css', '/ticketsup/css/bg.css.php'); +llxHeaderTicket($langs->trans("Tickets"), "", 0, 0, $arrayofjs, $arrayofcss); + +if (!$conf->global->TICKETS_ENABLE_PUBLIC_INTERFACE) { + print '
    ' . $langs->trans('TicketPublicInterfaceForbidden') . '
    '; +} else { + print '
    '; + print '

    ' . ($conf->global->TICKETS_PUBLIC_TEXT_HOME ? $conf->global->TICKETS_PUBLIC_TEXT_HOME : $langs->trans("TicketPublicDesc")) . '

    '; + print ''; + print '
    '; +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/public/ticketsup/list.php b/htdocs/public/ticketsup/list.php new file mode 100644 index 00000000000..34413168bc0 --- /dev/null +++ b/htdocs/public/ticketsup/list.php @@ -0,0 +1,685 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/public/index.php + * \ingroup ticketsup + * \brief Public file to add and manage ticket + */ + +if (!defined('NOCSRFCHECK')) { + define('NOCSRFCHECK', '1'); +} +// Do not check anti CSRF attack test +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +// If there is no need to load and show top and left menu +if (!defined("NOLOGIN")) { + define("NOLOGIN", '1'); +} +// If this page is public (can be called outside logged session) + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/lib/ticketsup.lib.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("ticketsup"); + +// Get parameters +$track_id = GETPOST('track_id', 'alpha'); +$action = GETPOST('action', 'alpha', 3); +$email = GETPOST('email', 'alpha'); + +if (GETPOST('btn_view_ticket_list')) { + unset($_SESSION['track_id_customer']); + unset($_SESSION['email_customer']); +} +if (isset($_SESSION['track_id_customer'])) { + $track_id = $_SESSION['track_id_customer']; +} +if (isset($_SESSION['email_customer'])) { + $email = $_SESSION['email_customer']; +} + +$object = new ActionsTicketsup($db); + +if ($action == "view_ticketlist") { + $error = 0; + $display_ticket_list = false; + if (!strlen($track_id)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("TicketTrackId"))); + $action = ''; + } + + if (!strlen($email)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Email"))); + $action = ''; + } else { + if (!isValidEmail($email)) { + $error++; + array_push($object->errors, $langs->trans("ErrorEmailInvalid")); + $action = ''; + } + } + + if (!$error) { + $ret = $object->fetch('', $track_id); + if ($ret && $object->dao->id > 0) { + // vérifie si l'adresse email est bien dans les contacts du ticket + $contacts = $object->dao->liste_contact(-1, 'external'); + foreach ($contacts as $contact) { + if ($contact['email'] == $email) { + $display_ticket_list = true; + $_SESSION['email_customer'] = $email; + $_SESSION['track_id_customer'] = $track_id; + break; + } else { + $display_ticket_list = false; + } + } + + if ($object->dao->fk_soc > 0) { + $object->dao->fetch_thirdparty(); + } + + if ($email == $object->dao->origin_email || $email == $object->dao->thirdparty->email) { + $display_ticket_list = true; + $_SESSION['email_customer'] = $email; + $_SESSION['track_id_customer'] = $track_id; + } + } else { + $error++; + array_push($object->errors, $langs->trans("ErrorTicketNotFound", $track_id)); + $action = ''; + } + } + + if ($error) { + setEventMessage($object->errors, 'errors'); + $action = ''; + } +} +$object->doActions($action); + +/*************************************************** + * VIEW + * + ****************************************************/ + +$form = new Form($db); +$user_assign = new User($db); +$user_create = new User($db); +$formticket = new FormTicketsup($db); + +$arrayofjs = array(); +$arrayofcss = array('/ticketsup/css/styles.css', '/ticketsup/css/bg.css.php'); +llxHeaderTicket($langs->trans("Tickets"), "", 0, 0, $arrayofjs, $arrayofcss); + +if (!$conf->global->TICKETS_ENABLE_PUBLIC_INTERFACE) { + print '
    ' . $langs->trans('TicketPublicInterfaceForbidden') . '
    '; + $db->close(); + exit(); +} + +print '
    '; + +if ($action == "view_ticketlist") { + if ($display_ticket_list) { + // Filters + $search_fk_status = GETPOST("search_fk_status", 'alpha'); + $search_subject = GETPOST("search_subject"); + $search_type = GETPOST("search_type", 'alpha'); + $search_category = GETPOST("search_category", 'alpha'); + $search_severity = GETPOST("search_severity", 'alpha'); + $search_fk_user_create = GETPOST("search_fk_user_create", 'int'); + $search_fk_user_assign = GETPOST("search_fk_user_assign", 'int'); + + // Store current page url + $url_page_current = dol_buildpath('/ticketsup/public/list.php', 1); + + // Do we click on purge search criteria ? + if (GETPOST("button_removefilter_x")) { + $search_fk_status = ''; + $search_subject = ''; + $search_type = ''; + $search_category = ''; + $search_severity = ''; + $search_fk_user_create = ''; + $search_fk_user_assign = ''; + } + + // fetch optionals attributes and labels + $extrafields = new ExtraFields($db); + $extralabels = $extrafields->fetch_name_optionals_label('ticketsup'); + $search_array_options = $extrafields->getOptionalsFromPost($extralabels, '', 'search_'); + + $filter = array(); + $param = ''; + + // Definition of fields for list + $arrayfields = array( + 't.datec' => array('label' => $langs->trans("Date"), 'checked' => 1), + 't.date_read' => array('label' => $langs->trans("TicketReadOn"), 'checked' => 0), + 't.date_close' => array('label' => $langs->trans("TicketCloseOn"), 'checked' => 0), + 't.ref' => array('label' => $langs->trans("Ref"), 'checked' => 1), + 't.fk_statut' => array('label' => $langs->trans("Statut"), 'checked' => 1), + 't.subject' => array('label' => $langs->trans("Subject"), 'checked' => 1), + 'type.code' => array('label' => $langs->trans("Type"), 'checked' => 1), + 'category.code' => array('label' => $langs->trans("Category"), 'checked' => 1), + 'severity.code' => array('label' => $langs->trans("Severity"), 'checked' => 1), + 't.progress' => array('label' => $langs->trans("Progression"), 'checked' => 0), + //'t.fk_contract' => array('label' => $langs->trans("Contract"), 'checked' => 0), + 't.fk_user_create' => array('label' => $langs->trans("Author"), 'checked' => 1), + 't.fk_user_assign' => array('label' => $langs->trans("AuthorAssign"), 'checked' => 0), + + //'t.entity'=>array('label'=>$langs->trans("Entity"), 'checked'=>1, 'enabled'=>(! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode))), + //'t.datec' => array('label' => $langs->trans("DateCreation"), 'checked' => 0, 'position' => 500), + //'t.tms' => array('label' => $langs->trans("DateModificationShort"), 'checked' => 0, 'position' => 2) + //'t.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000), + ); + + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + foreach ($extrafields->attribute_label as $key => $val) { + if ($extrafields->attribute_type[$key] != 'separate') { + $arrayfields["ef." . $key] = array('label' => $extrafields->attribute_label[$key], 'checked' => $extrafields->attribute_list[$key], 'position' => $extrafields->attribute_pos[$key], 'enabled' => $extrafields->attribute_perms[$key]); + } + } + } + if (!empty($search_subject)) { + $filter['t.subject'] = $search_subject; + $param .= '&search_subject=' . $search_subject; + } + if (!empty($search_type)) { + $filter['t.type_code'] = $search_type; + $param .= '&search_type=' . $search_type; + } + if (!empty($search_category)) { + $filter['t.category_code'] = $search_category; + $param .= '&search_category=' . $search_category; + } + if (!empty($search_severity)) { + $filter['t.severity_code'] = $search_severity; + $param .= '&search_severity=' . $search_severity; + } + if (!empty($search_fk_user_assign)) { + // -1 value = all so no filter + if ($search_fk_user_assign > 0) { + $filter['t.fk_user_assign'] = $search_fk_user_assign; + $param .= '&search_fk_user_assign=' . $search_fk_user_assign; + } + } + if (!empty($search_fk_user_create)) { + // -1 value = all so no filter + if ($search_fk_user_create > 0) { + $filter['t.fk_user_create'] = $search_fk_user_create; + $param .= '&search_fk_user_create=' . $search_fk_user_create; + } + } + + if ((isset($search_fk_status) && $search_fk_status != '') && $search_fk_status != '-1' && $search_fk_status != 'non_closed') { + $filter['t.fk_statut'] = $search_fk_status; + $param .= '&search_fk_status=' . $search_fk_status; + } + + if (isset($search_fk_status) && $search_fk_status == 'non_closed') { + $filter['t.fk_statut'] = array(0, 1, 3, 4, 5, 6); + $param .= '&search_fk_status=non_closed'; + } + + require DOL_DOCUMENT_ROOT . '/core/actions_changeselectedfields.inc.php'; + + $sortfield = GETPOST("sortfield", 'alpha'); + $sortorder = GETPOST("sortorder", 'alpha'); + + if (!$sortfield) { + $sortfield = 't.datec'; + } + + if (!$sortorder) { + $sortorder = 'DESC'; + } + + $limit = $conf->liste_limit; + + $page = GETPOST("page", 'int'); + if ($page == -1) { + $page = 0; + } + $offset = $limit * $page; + $pageprev = $page - 1; + $pagenext = $page + 1; + + // Request SQL + $sql = "SELECT"; + $sql .= " t.rowid,"; + $sql .= " t.ref,"; + $sql .= " t.track_id,"; + $sql .= " t.fk_soc,"; + $sql .= " t.fk_project,"; + $sql .= " t.origin_email,"; + $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,"; + $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,"; + $sql .= " t.subject,"; + $sql .= " t.message,"; + $sql .= " t.fk_statut,"; + $sql .= " t.resolution,"; + $sql .= " t.progress,"; + $sql .= " t.timing,"; + $sql .= " t.type_code,"; + $sql .= " t.category_code,"; + $sql .= " t.severity_code,"; + $sql .= " t.datec,"; + $sql .= " t.date_read,"; + $sql .= " t.date_close,"; + $sql .= " t.tms"; + $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label"; + // Add fields for extrafields + foreach ($extrafields->attribute_list as $key => $val) { + $sql .= ($extrafields->attribute_type[$key] != 'separate' ? ",ef." . $key . ' as options_' . $key : ''); + } + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup as t"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_type as type ON type.code=t.type_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_category as category ON category.code=t.category_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_severity as severity ON severity.code=t.severity_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON s.rowid=t.fk_soc"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as uc ON uc.rowid=t.fk_user_create"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as ua ON ua.rowid=t.fk_user_assign"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "element_contact as ec ON ec.element_id=t.rowid"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_type_contact as tc ON ec.fk_c_type_contact=tc.rowid"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople sp ON ec.fk_socpeople=sp.rowid"; + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "ticketsup_extrafields as ef on (t.rowid = ef.fk_object)"; + } + $sql .= " WHERE t.entity IN (" . getEntity('ticketsup') . ")"; + $sql .= " AND tc.source = 'external'"; + $sql .= " AND tc.element='" . $object->dao->element . "'"; + $sql .= " AND tc.active=1"; + $sql .= " AND (sp.email='" . $db->escape($_SESSION['email_customer']) . "'"; + $sql .= " OR s.email='" . $db->escape($_SESSION['email_customer']) . "'"; + $sql .= " OR t.origin_email='" . $db->escape($_SESSION['email_customer']) . "')"; + // Manage filter + if (!empty($filter)) { + foreach ($filter as $key => $value) { + if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year + $sql .= ' AND ' . $key . ' = \'' . $value . '\''; + } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code')) { + $sql .= " AND " . $key . " = '" . $db->escape($value) ."'"; + } elseif ($key == 't.fk_statut') { + if (is_array($value) && count($value) > 0) { + $sql .= 'AND ' . $key . ' IN (' . implode(',', $value) . ')'; + } else { + $sql .= ' AND ' . $key . ' = ' . $db->escape($value); + } + } else { + $sql .= ' AND ' . $key . ' LIKE \'%' . $value . '%\''; + } + } + } + $sql .= " GROUP BY t.track_id"; + $sql .= " ORDER BY " . $sortfield . ' ' . $sortorder; + + $resql = $db->query($sql); + if ($resql) { + $num_total = $db->num_rows($resql); + if (!empty($limit)) { + $sql .= ' ' . $db->plimit($limit + 1, $offset); + } + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + print_barre_liste($langs->trans('TicketList'), $page, 'public/list.php', $param, $sortfield, $sortorder, '', $num, $num_total, 'ticketsup-32@ticketsup'); + + /* + * Search bar + */ + print '
    ' . "\n"; + print ''; + print ''; + print ''; + print ''; + + $varpage = empty($contextpage) ? $url_page_current : $contextpage; + $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields + + print ''; + + print ''; + if (!empty($arrayfields['t.datec']['checked'])) { + print_liste_field_titre($arrayfields['t.datec']['label'], $url_page_current, 't.datec', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.date_read']['checked'])) { + print_liste_field_titre($arrayfields['t.date_read']['label'], $url_page_current, 't.date_read', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.date_close']['checked'])) { + print_liste_field_titre($arrayfields['t.date_close']['label'], $url_page_current, 't.date_close', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.ref']['checked'])) { + print_liste_field_titre($arrayfields['t.ref']['label'], $url_page_current, 't.ref', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.fk_statut']['checked'])) { + print_liste_field_titre($arrayfields['t.fk_statut']['label'], $url_page_current, 't.fk_statut', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.subject']['checked'])) { + print_liste_field_titre($arrayfields['t.subject']['label']); + } + if (!empty($arrayfields['type.code']['checked'])) { + print_liste_field_titre($arrayfields['type.code']['label'], $url_page_current, 'type.code', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['category.code']['checked'])) { + print_liste_field_titre($arrayfields['category.code']['label'], $url_page_current, 'category.code', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['severity.code']['checked'])) { + print_liste_field_titre($arrayfields['severity.code']['label'], $url_page_current, 'severity.code', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.progress']['checked'])) { + print_liste_field_titre($arrayfields['t.progress']['label'], $url_page_current, 't.progress', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.fk_user_create']['checked'])) { + print_liste_field_titre($arrayfields['t.fk_user_create']['label'], $url_page_current, 't.fk_user_create', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.fk_user_assign']['checked'])) { + print_liste_field_titre($arrayfields['t.fk_user_assign']['label'], $url_page_current, 't.fk_user_assign', '', $param, '', $sortfield, $sortorder); + } + if (!empty($arrayfields['t.tms']['checked'])) { + print_liste_field_titre($arrayfields['t.tms']['label'], $url_page_current, 't.tms', '', $param, '', $sortfield, $sortorder); + } + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + foreach ($extrafields->attribute_label as $key => $val) { + if (!empty($arrayfields["ef." . $key]['checked'])) { + $align = $extrafields->getAlignFlag($key); + print_liste_field_titre($extralabels[$key], $url_page_current, "ef." . $key, "", $param, ($align ? 'align="' . $align . '"' : ''), $sortfield, $sortorder); + } + } + } + print_liste_field_titre($selectedfields, $url_page_current, "", '', '', 'align="right"', $sortfield, $sortorder, 'maxwidthsearch '); + print ''; + + /* + * Filter bar + */ + $formTicket = new FormTicketsup($db); + + print ''; + + if (!empty($arrayfields['t.datec']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.date_read']['checked'])) { + print ''; + } + if (!empty($arrayfields['t.date_close']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.ref']['checked'])) { + print ''; + } + + // Status + if (!empty($arrayfields['t.fk_statut']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.subject']['checked'])) { + print ''; + } + + if (!empty($arrayfields['type.code']['checked'])) { + print ''; + } + + if (!empty($arrayfields['category.code']['checked'])) { + print ''; + } + + if (!empty($arrayfields['severity.code']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.progress']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.fk_user_create']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.fk_user_assign']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.tms']['checked'])) { + print ''; + } + + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + foreach ($extrafields->attribute_label as $key => $val) { + if (!empty($arrayfields["ef." . $key]['checked'])) { + print ''; + } + } + } + + print ''; + print ''; + + $var = true; + while ($obj = $db->fetch_object($resql)) { + $var = !$var; + print ""; + + // Date ticket + if (!empty($arrayfields['t.datec']['checked'])) { + print ''; + } + + // Date read + if (!empty($arrayfields['t.date_read']['checked'])) { + print ''; + } + + // Date close + if (!empty($arrayfields['t.date_close']['checked'])) { + print ''; + } + + // ref + if (!empty($arrayfields['t.ref']['checked'])) { + print ''; + } + + // Statut + if (!empty($arrayfields['t.fk_statut']['checked'])) { + print ''; + } + + // Subject + if (!empty($arrayfields['t.subject']['checked'])) { + print ''; + } + + // Type + if (!empty($arrayfields['type.code']['checked'])) { + print ''; + } + + // Category + if (!empty($arrayfields['category.code']['checked'])) { + print ''; + } + + // Severity + if (!empty($arrayfields['severity.code']['checked'])) { + print ''; + } + + // Progression + if (!empty($arrayfields['t.progress']['checked'])) { + print ''; + } + + // Message author + if (!empty($arrayfields['t.fk_user_create']['checked'])) { + print ''; + } + + // Assigned author + if (!empty($arrayfields['t.fk_user_assign']['checked'])) { + print ''; + } + + if (!empty($arrayfields['t.tms']['checked'])) { + print ''; + } + + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + foreach ($extrafields->attribute_label as $key => $val) { + if (!empty($arrayfields["ef." . $key]['checked'])) { + print 'getAlignFlag($key); + if ($align) { + print ' align="' . $align . '"'; + } + print '>'; + $tmpkey = 'options_' . $key; + print $extrafields->showOutputField($key, $obj->$tmpkey, '', 1); + print ''; + } + } + } + print ''; + $i++; + print ''; + } + + print '
    '; + $selected = ($search_fk_status != "non_closed" ? $search_fk_status : ''); + $object->printSelectStatus($selected); + print ''; + print ''; + print ''; + $formTicket->selectTypesTickets($search_type, 'search_type', '', 2, 1, 1); + print ''; + $formTicket->selectCategoriesTickets($search_category, 'search_category', '', 2, 1, 1); + print ''; + $formTicket->selectSeveritiesTickets($search_severity, 'search_severity', '', 2, 1, 1); + print ''; + print ''; + print ''; + print '
    '; + print dol_print_date($obj->datec, 'dayhour'); + print ''; + print dol_print_date($obj->date_read, 'dayhour'); + print ''; + print dol_print_date($obj->date_close, 'dayhour'); + print ''; + print $obj->ref; + print ''; + $object->fk_statut = $obj->fk_statut; + print $object->getLibStatut(2); + print ''; + print '' . $obj->subject . ''; + print ''; + print $obj->type_label; + print ''; + print $obj->category_label; + print ''; + print $obj->severity_label; + print ''; + print $obj->progress; + print ''; + if ($obj->fk_user_create) { + $user_create->firstname = (!empty($obj->user_create_firstname) ? $obj->user_create_firstname : ''); + $user_create->name = (!empty($obj->user_create_lastname) ? $obj->user_create_lastname : ''); + $user_create->id = (!empty($obj->fk_user_create) ? $obj->fk_user_create : ''); + print $user_create->getFullName(); + } else { + print $langs->trans('Email'); + } + print ''; + if ($obj->fk_user_assign) { + $user_assign->firstname = (!empty($obj->user_assign_firstname) ? $obj->user_assign_firstname : ''); + $user_assign->lastname = (!empty($obj->user_assign_lastname) ? $obj->user_assign_lastname : ''); + $user_assign->id = (!empty($obj->fk_user_assign) ? $obj->fk_user_assign : ''); + print $user_assign->getFullName(); + } else { + print $langs->trans('None'); + } + print '' . dol_print_date($obj->tms, 'dayhour') . '
    '; + print '
    '; + + print '"; + print ''; + } + } + } else { + print ''; + } +} else { + print '

    ' . $langs->trans("TicketPublicMsgViewLogIn") . '

    '; + + print '
    '; + print '
    '; + print ''; + print ''; + print ''; + + print '

    '; + print ''; + print '

    '; + + print '

    '; + print ''; + print '

    '; + + print '

    '; + print ''; + print "

    \n"; + + print "
    \n"; + print "
    \n"; +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/public/ticketsup/view.php b/htdocs/public/ticketsup/view.php new file mode 100644 index 00000000000..c0069d9f6ee --- /dev/null +++ b/htdocs/public/ticketsup/view.php @@ -0,0 +1,319 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/public/index.php + * \ingroup ticketsup + * \brief Public file to add and manage ticket + */ + +if (!defined('NOCSRFCHECK')) { + define('NOCSRFCHECK', '1'); +} +// Do not check anti CSRF attack test +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +// If there is no need to load and show top and left menu +if (!defined("NOLOGIN")) { + define("NOLOGIN", '1'); +} +// If this page is public (can be called outside logged session) + +require '../../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticketsup/lib/ticketsup.lib.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("ticketsup"); + +// Get parameters +$track_id = GETPOST('track_id', 'alpha'); +$action = GETPOST('action', 'alpha', 3); +$email = GETPOST('email', 'alpha'); + +if (GETPOST('btn_view_ticket')) { + unset($_SESSION['email_customer']); +} +if (isset($_SESSION['email_customer'])) { + $email = $_SESSION['email_customer']; +} + +$object = new ActionsTicketsup($db); + +if ($action == "view_ticket" || $action == "add_message" || $action == "close" || $action == "confirm_public_close" || $action == "new_public_message") { + $error = 0; + $display_ticket = false; + if (!strlen($track_id)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("TicketTrackId"))); + $action = ''; + } + + if (!strlen($email)) { + $error++; + array_push($object->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Email"))); + $action = ''; + } else { + if (!isValidEmail($email)) { + $error++; + array_push($object->errors, $langs->trans("ErrorEmailInvalid")); + $action = ''; + } + } + + if (!$error) { + $ret = $object->fetch('', $track_id); + if ($ret && $object->dao->id > 0) { + // vérifie si l'adresse email est bien dans les contacts du ticket + $contacts = $object->dao->liste_contact(-1, 'external'); + foreach ($contacts as $contact) { + if ($contact['email'] == $email) { + $display_ticket = true; + $_SESSION['email_customer'] = $email; + break; + } else { + $display_ticket = false; + } + } + + if ($object->dao->fk_soc > 0) { + $object->dao->fetch_thirdparty(); + } + + if ($email == $object->dao->origin_email || $email == $object->dao->thirdparty->email) { + $display_ticket = true; + $_SESSION['email_customer'] = $email; + } + } else { + $error++; + array_push($object->errors, $langs->trans("ErrorTicketNotFound", $track_id)); + $action = ''; + } + } + + if ($error) { + setEventMessage($object->errors, 'errors'); + $action = ''; + } +} +$object->doActions($action); + +/*************************************************** + * VIEW + * + ****************************************************/ + +$form = new Form($db); +$formticket = new FormTicketsup($db); + +$arrayofjs = array(); +$arrayofcss = array('/ticketsup/css/styles.css', '/ticketsup/css/bg.css.php'); +llxHeaderTicket($langs->trans("Tickets"), "", 0, 0, $arrayofjs, $arrayofcss); + +if (!$conf->global->TICKETS_ENABLE_PUBLIC_INTERFACE) { + print '
    ' . $langs->trans('TicketPublicInterfaceForbidden') . '
    '; + $db->close(); + exit(); +} + +print '
    '; + +if ($action == "view_ticket" || $action == "add_message" || $action == "close" || $action == "confirm_public_close") { + if ($display_ticket) { + // Confirmation close + if ($action == 'close') { + $ret = $form->form_confirm($_SERVER["PHP_SELF"] . "?track_id=" . $track_id, $langs->trans("CloseATicket"), $langs->trans("ConfirmCloseAticket"), "confirm_public_close", '', '', 1); + if ($ret == 'html') { + print '
    '; + } + } + + print '
    '; + + print ''; + + // Ref + print ''; + + // Tracking ID + print ''; + + // Subject + print ''; + + // Statut + print ''; + + // Type + print ''; + + // Category + print ''; + + // Severity + print ''; + + // Creation date + print ''; + + // Author + print ''; + + // Read date + if (!empty($object->dao->date_read)) { + print ''; + } + + // Close date + if (!empty($object->dao->date_close)) { + print ''; + } + + // User assigned + print ''; + + // Progression + print ''; + + print '
    ' . $langs->trans("Ref") . ''; + print $object->dao->ref; + print '
    ' . $langs->trans("TicketTrackId") . ''; + print $object->dao->track_id; + print '
    ' . $langs->trans("Subject") . ''; + print $object->dao->subject; + print '
    ' . $langs->trans("Status") . ''; + print $object->dao->getLibStatut(2); + print '
    ' . $langs->trans("Type") . ''; + print $object->dao->type_label; + print '
    ' . $langs->trans("Category") . ''; + print $object->dao->category_label; + print '
    ' . $langs->trans("Severity") . ''; + print $object->dao->severity_label; + print '
    ' . $langs->trans("DateCreation") . ''; + print dol_print_date($object->dao->datec, 'dayhour'); + print '
    ' . $langs->trans("Author") . ''; + if ($object->dao->fk_user_create > 0) { + $langs->load("users"); + $fuser = new User($db); + $fuser->fetch($object->dao->fk_user_create); + print $fuser->getFullName($langs); + } else { + print $object->dao->origin_email; + } + + print '
    ' . $langs->trans("TicketReadOn") . ''; + print dol_print_date($object->dao->date_read, 'dayhour'); + print '
    ' . $langs->trans("TicketCloseOn") . ''; + print dol_print_date($object->dao->date_close, 'dayhour'); + print '
    ' . $langs->trans("UserAssignedTo") . ''; + if ($object->dao->fk_user_assign > 0) { + $fuser = new User($db); + $fuser->fetch($object->dao->fk_user_assign); + print $fuser->getFullName($langs, 1); + } else { + print $langs->trans('None'); + } + print '
    ' . $langs->trans("Progression") . ''; + print ($object->dao->progress > 0 ? $object->dao->progress : '0') . '%'; + print '
    '; + + print '
    '; + + print '
    '; + + if ($action == 'add_message') { + print load_fiche_titre($langs->trans('TicketAddMessage'), '', 'messages@ticketsup'); + + $formticket = new FormTicketsup($db); + + $formticket->action = "new_public_message"; + $formticket->track_id = $object->dao->track_id; + $formticket->id = $object->dao->id; + + $formticket->param = array('fk_user_create' => '-1'); + + $formticket->withfile = 2; + $formticket->showMessageForm('100%'); + } else { + print '
    '; + print ''; + print ''; + print ''; + print ''; + print ''; + print "
    \n"; + + print '
    '; + // List ticket + print ''; + + if ($object->dao->fk_statut < 8) { + // New message + print ''; + + // Close ticket + if ($object->dao->fk_statut > 0 && $object->dao->fk_statut < 8) { + print ''; + } + } + + print '
    '; + } + + // Message list + print load_fiche_titre($langs->trans('TicketMessagesList'), '', 'messages@ticketsup'); + $object->viewTicketMessages(false); + + print '
    '; + + // Logs list + print load_fiche_titre($langs->trans('TicketHistory'), '', 'history@ticketsup'); + $object->viewTicketLogs(false); + } else { + print ''; + } +} else { + print '

    ' . $langs->trans("TicketPublicMsgViewLogIn") . '

    '; + + print '
    '; + print '
    '; + print ''; + print ''; + + print '

    '; + print ''; + print '

    '; + + print '

    '; + print ''; + print '

    '; + + print '

    '; + print ''; + print "

    \n"; + + print "
    \n"; + print "
    \n"; +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/resource/list.php b/htdocs/resource/list.php index 190659628f2..927975e9c26 100644 --- a/htdocs/resource/list.php +++ b/htdocs/resource/list.php @@ -17,8 +17,8 @@ /** * \file resource/index.php - * \ingroup resource - * \brief Page to manage resource objects + * \ingroup resource + * \brief Page to manage resource objects */ @@ -43,8 +43,8 @@ $sortfield = GETPOST('sortfield','alpha'); // Initialize context for list $contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'resourcelist'; +// Initialize technical objects $object = new Dolresource($db); - $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -84,7 +84,7 @@ foreach ($search_array_options as $key => $val) if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage; -$hookmanager->initHooks(array('resource_list')); +$hookmanager->initHooks(array('resourcelist')); if (empty($sortorder)) $sortorder="ASC"; if (empty($sortfield)) $sortfield="t.ref"; @@ -196,7 +196,13 @@ if($ret == -1) { dol_print_error($db,$object->error); exit; } else { - print_barre_liste($pagetitle, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $ret+1, $nbtotalofrecords,'title_generic.png', 0, '', '', $limit); + $newcardbutton=''; + if ($user->rights->resource->write) + { + $newcardbutton=''.$langs->trans('MenuResourceAdd').''; + } + + print_barre_liste($pagetitle, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $ret+1, $nbtotalofrecords,'title_generic.png', 0, $newcardbutton, '', $limit); } $moreforfilter = ''; diff --git a/htdocs/societe/admin/contact_extrafields.php b/htdocs/societe/admin/contact_extrafields.php index f97ceedcca3..4facdd11ea1 100644 --- a/htdocs/societe/admin/contact_extrafields.php +++ b/htdocs/societe/admin/contact_extrafields.php @@ -64,7 +64,7 @@ $help_url='EN:Module Third Parties setup|FR:Paramétrage_du_module_Tiers'; llxHeader('',$langs->trans("CompanySetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CompanySetup"),$linkback,'title_setup'); diff --git a/htdocs/societe/admin/societe.php b/htdocs/societe/admin/societe.php index ed4704471d0..7866ffbf0be 100644 --- a/htdocs/societe/admin/societe.php +++ b/htdocs/societe/admin/societe.php @@ -275,7 +275,7 @@ $form=new Form($db); $help_url='EN:Module Third Parties setup|FR:Paramétrage_du_module_Tiers|ES:Configuración_del_módulo_terceros'; llxHeader('',$langs->trans("CompanySetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CompanySetup"),$linkback,'title_setup'); diff --git a/htdocs/societe/admin/societe_extrafields.php b/htdocs/societe/admin/societe_extrafields.php index 8826f1e4750..06dc57677f5 100644 --- a/htdocs/societe/admin/societe_extrafields.php +++ b/htdocs/societe/admin/societe_extrafields.php @@ -65,7 +65,7 @@ $help_url='EN:Module Third Parties setup|FR:Paramétrage_du_module_Tiers'; llxHeader('',$langs->trans("CompanySetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("CompanySetup"),$linkback,'title_setup'); diff --git a/htdocs/societe/canvas/actions_card_common.class.php b/htdocs/societe/canvas/actions_card_common.class.php index 452a9cc6a61..37082f4140b 100644 --- a/htdocs/societe/canvas/actions_card_common.class.php +++ b/htdocs/societe/canvas/actions_card_common.class.php @@ -43,34 +43,6 @@ abstract class ActionsCardCommon var $errors=array(); - /** - * Instantiation of DAO class - * - * @return int 0 - * @deprecated Using getInstanceDao should not be used. - */ - private function getInstanceDao() - { - dol_syslog(__METHOD__ . " is deprecated", LOG_WARNING); - - if (! is_object($this->object)) - { - $modelclassfile = dol_buildpath('/'.$this->dirmodule.'/canvas/'.$this->canvas.'/dao_'.$this->targetmodule.'_'.$this->canvas.'.class.php'); - if (file_exists($modelclassfile)) - { - // Include dataservice class (model) - $ret = require_once $modelclassfile; - if ($ret) - { - // Instantiate dataservice class (model) - $modelclassname = 'Dao'.ucfirst($this->targetmodule).ucfirst($this->canvas); - $this->object = new $modelclassname($this->db); - } - } - } - return 0; - } - /** * Get object from id or ref and save it into this->object * @@ -87,271 +59,6 @@ abstract class ActionsCardCommon $this->object = $object; } - /** - * doActions of a canvas is not the doActions of the hook - * @deprecated Use the doActions of hooks instead of this. - * - * @param int $action Action code - * @return void - */ - function doActions(&$action) - { - global $conf, $user, $langs; - - if ($_POST["getcustomercode"]) - { - // We defined value code_client - $_POST["code_client"]="Acompleter"; - } - - if ($_POST["getsuppliercode"]) - { - // We defined value code_fournisseur - $_POST["code_fournisseur"]="Acompleter"; - } - - // Add new third party - if ((! $_POST["getcustomercode"] && ! $_POST["getsuppliercode"]) - && ($action == 'add' || $action == 'update')) - { - require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; - $error=0; - - if (GETPOST("private") == 1) - { - $this->object->particulier = GETPOST("private"); - - $this->object->name = empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)?trim($_POST["firstname"].' '.$_POST["lastname"]):trim($_POST["lastname"].' '.$_POST["firstname"]); - $this->object->civility_id = $_POST["civility_id"]; - // Add non official properties - $this->object->name_bis = $_POST["lastname"]; - $this->object->firstname = $_POST["firstname"]; - } - else - { - $this->object->name = $_POST["nom"]; - } - - $this->object->address = $_POST["adresse"]; - $this->object->zip = $_POST["zipcode"]; - $this->object->town = $_POST["town"]; - $this->object->country_id = $_POST["country_id"]; - $this->object->state_id = $_POST["state_id"]; - $this->object->phone = $_POST["tel"]; - $this->object->fax = $_POST["fax"]; - $this->object->email = trim($_POST["email"]); - $this->object->url = $_POST["url"]; - $this->object->idprof1 = $_POST["idprof1"]; - $this->object->idprof2 = $_POST["idprof2"]; - $this->object->idprof3 = $_POST["idprof3"]; - $this->object->idprof4 = $_POST["idprof4"]; - $this->object->prefix_comm = $_POST["prefix_comm"]; - $this->object->code_client = $_POST["code_client"]; - $this->object->code_fournisseur = $_POST["code_fournisseur"]; - $this->object->capital = $_POST["capital"]; - $this->object->barcode = $_POST["barcode"]; - $this->object->canvas = GETPOST("canvas"); - - $this->object->tva_assuj = $_POST["assujtva_value"]; - - // Local Taxes - $this->object->localtax1_assuj = $_POST["localtax1assuj_value"]; - $this->object->localtax2_assuj = $_POST["localtax2assuj_value"]; - $this->object->tva_intra = $_POST["tva_intra"]; - - $this->object->forme_juridique_code = $_POST["forme_juridique_code"]; - $this->object->effectif_id = $_POST["effectif_id"]; - if (GETPOST("private") == 1) - { - $this->object->typent_id = dol_getIdFromCode($db,'TE_PRIVATE','c_typent'); - } - else - { - $this->object->typent_id = $_POST["typent_id"]; - } - $this->object->client = $_POST["client"]; - $this->object->fournisseur = $_POST["fournisseur"]; - - $this->object->commercial_id = $_POST["commercial_id"]; - $this->object->default_lang = $_POST["default_lang"]; - - // Check parameters - if (empty($_POST["cancel"])) - { - if (! empty($this->object->email) && ! isValidEMail($this->object->email)) - { - $error = 1; - $langs->load("errors"); - $this->error = $langs->trans("ErrorBadEMail",$this->object->email); - $action = ($action == 'add' ? 'create' : 'edit'); - } - if (! empty($this->object->url) && ! isValidUrl($this->object->url)) - { - $error = 1; - $langs->load("errors"); - $this->error = $langs->trans("ErrorBadUrl",$this->object->url); - $action = ($action == 'add' ? 'create' : 'edit'); - } - if ($this->object->fournisseur && ! $conf->fournisseur->enabled) - { - $error = 1; - $langs->load("errors"); - $this->error = $langs->trans("ErrorSupplierModuleNotEnabled"); - $action = ($action == 'add' ? 'create' : 'edit'); - } - } - - if (! $error) - { - if ($action == 'add') - { - $this->db->begin(); - - if (empty($this->object->client)) $this->object->code_client=''; - if (empty($this->object->fournisseur)) $this->object->code_fournisseur=''; - - $result = $this->object->create($user); - if ($result >= 0) - { - if ($this->object->particulier) - { - dol_syslog(get_class($this)."::doActions This thirdparty is a personal people",LOG_DEBUG); - $contact=new Contact($this->db); - - $contact->civility_id = $this->object->civility_id; - $contact->name = $this->object->name_bis; - $contact->firstname = $this->object->firstname; - $contact->address = $this->object->address; - $contact->zip = $this->object->zip; - $contact->town = $this->object->town; - $contact->country_id = $this->object->country_id; - $contact->socid = $this->object->id; // fk_soc - $contact->status = 1; - $contact->email = $this->object->email; - $contact->priv = 0; - - $result=$contact->create($user); - } - } - else - { - $this->errors=$this->object->errors; - } - - if ($result >= 0) - { - $this->db->commit(); - - if ( $this->object->client == 1 ) - { - header("Location: ".DOL_URL_ROOT."/comm/card.php?socid=".$this->object->id); - return; - } - else - { - if ( $this->object->fournisseur == 1 ) - { - header("Location: ".DOL_URL_ROOT."/fourn/card.php?socid=".$this->object->id); - return; - } - else - { - header("Location: ".$_SERVER["PHP_SELF"]."?socid=".$this->object->id); - return; - } - } - } - else - { - $this->db->rollback(); - - $this->errors=$this->object->errors; - $action = 'create'; - } - } - - if ($action == 'update') - { - if ($_POST["cancel"]) - { - header("Location: ".$_SERVER["PHP_SELF"]."?socid=".$this->object->id); - exit; - } - - $oldsoccanvas = clone $this->object; - - // To avoid setting code if third party is not concerned. But if it had values, we keep them. - if (empty($this->object->client) && empty($oldsoccanvas->code_client)) $this->object->code_client=''; - if (empty($this->object->fournisseur) && empty($oldsoccanvas->code_fournisseur)) $this->object->code_fournisseur=''; - - $result = $this->object->update($this->object->id, $user, 1, $oldsoccanvas->codeclient_modifiable(), $oldsoccanvas->codefournisseur_modifiable()); - if ($result >= 0) - { - header("Location: ".$_SERVER["PHP_SELF"]."?socid=".$this->object->id); - exit; - } - else - { - $reload = 0; - $this->errors = $this->object->errors; - $action = "edit"; - } - } - } - } - - if ($action == 'confirm_delete' && GETPOST("confirm") == 'yes') - { - $result = $this->object->delete($this->object->id); - - if ($result >= 0) - { - header("Location: ".DOL_URL_ROOT."/societe/list.php?delsoc=".$this->object->name.""); - exit; - } - else - { - $reload = 0; - $this->errors=$this->object->errors; - $action = ''; - } - } - - /* - * Generate document - */ - if ($action == 'builddoc') // En get ou en post - { - if (is_numeric(GETPOST('model'))) - { - $this->error=$langs->trans("ErrorFieldRequired",$langs->transnoentities("Model")); - } - else - { - require_once DOL_DOCUMENT_ROOT.'/core/modules/societe/modules_societe.class.php'; - - $this->object->fetch_thirdparty(); - - // Define output language - $outputlangs = $langs; - $newlang=''; - if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09'); - if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang=$this->object->default_lang; - if (! empty($newlang)) - { - $outputlangs = new Translate("",$conf); - $outputlangs->setDefaultLang($newlang); - } - $result=thirdparty_doc_create($this->db, $this->object->id, '', GETPOST('model','alpha'), $outputlangs); - if ($result <= 0) - { - dol_print_error($this->db,$result); - exit; - } - } - } - } - /** * Assign custom values for canvas (for example into this->tpl to be used by templates) * diff --git a/htdocs/societe/canvas/company/actions_card_company.class.php b/htdocs/societe/canvas/company/actions_card_company.class.php index 19f9a2fbdb8..419a5ecb73e 100644 --- a/htdocs/societe/canvas/company/actions_card_company.class.php +++ b/htdocs/societe/canvas/company/actions_card_company.class.php @@ -67,23 +67,6 @@ class ActionsCardCompany extends ActionsCardCommon } - /** - * doActions of a canvas is not the doActions of the hook - * @deprecated Use the doActions of hooks instead of this. - * - * @param string $action Type of action - * @param int $id Id of object - * @return int <0 if KO, >0 if OK - */ - function doActions(&$action, $id) - { - $ret = $this->getObject($id); - - $return = parent::doActions($action); - - return $return; - } - /** * Assign custom values for canvas (for example into this->tpl to be used by templates) * diff --git a/htdocs/societe/canvas/company/tpl/card_view.tpl.php b/htdocs/societe/canvas/company/tpl/card_view.tpl.php index 3865e9b95a0..24ddfbaaaac 100644 --- a/htdocs/societe/canvas/company/tpl/card_view.tpl.php +++ b/htdocs/societe/canvas/company/tpl/card_view.tpl.php @@ -185,7 +185,7 @@ for ($i=1; $i<=4; $i++) { trans('RIB'); ?> rights->societe->creer) { ?> - control->tpl['image_edit']; ?> + control->tpl['image_edit']; ?>   diff --git a/htdocs/societe/canvas/individual/tpl/card_view.tpl.php b/htdocs/societe/canvas/individual/tpl/card_view.tpl.php index 359f4ec7b44..fd9b3487f50 100644 --- a/htdocs/societe/canvas/individual/tpl/card_view.tpl.php +++ b/htdocs/societe/canvas/individual/tpl/card_view.tpl.php @@ -144,7 +144,7 @@ dol_fiche_head($head, 'card', $langs->trans("ThirdParty"),0,'company'); trans('RIB'); ?> rights->societe->creer) { ?> - control->tpl['image_edit']; ?> + control->tpl['image_edit']; ?>   diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index 362834b316d..bc16a30d7da 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -10,6 +10,8 @@ * Copyright (C) 2015 Jean-François Ferry * Copyright (C) 2015 Marcos García * Copyright (C) 2015 Raphaël Doursenaud + * Copyright (C) 2018 Nicolas ZABOURI + * Copyright (C) 2018 Ferran Marcet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,11 +46,7 @@ require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; if (! empty($conf->adherent->enabled)) require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; -$langs->load("companies"); -$langs->load("commercial"); -$langs->load("bills"); -$langs->load("banks"); -$langs->load("users"); +$langs->loadLangs(array("companies","commercial","bills","banks","users")); if (! empty($conf->categorie->enabled)) $langs->load("categories"); if (! empty($conf->incoterm->enabled)) $langs->load("incoterm"); if (! empty($conf->notification->enabled)) $langs->load("mails"); @@ -73,7 +71,9 @@ $extralabels=$extrafields->fetch_name_optionals_label($object->table_element); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('thirdpartycard','globalcard')); -if ($action == 'view' && $object->fetch($socid)<=0) +if ($socid > 0) $object->fetch($socid); + +if (! ($object->id > 0) && $action == 'view') { $langs->load("errors"); print($langs->trans('ErrorRecordNotFound')); @@ -117,8 +117,6 @@ if (empty($reshook)) if ($action == 'confirm_merge' && $confirm == 'yes' && $user->rights->societe->creer) { - $object->fetch($socid); - $error = 0; $soc_origin_id = GETPOST('soc_origin', 'int'); $soc_origin = new Societe($db); @@ -149,7 +147,7 @@ if (empty($reshook)) $listofproperties=array( 'address', 'zip', 'town', 'state_id', 'country_id', 'phone', 'phone_pro', 'fax', 'email', 'skype', 'url', 'barcode', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', - 'tva_intra', 'effectif_id', 'forme_juridique', 'remise_percent', 'mode_reglement_supplier_id', 'cond_reglement_supplier_id', 'name_bis', + 'tva_intra', 'effectif_id', 'forme_juridique', 'remise_percent', 'remise_supplier_percent', 'mode_reglement_supplier_id', 'cond_reglement_supplier_id', 'name_bis', 'stcomm_id', 'outstanding_limit', 'price_level', 'parent', 'default_lang', 'ref', 'ref_ext', 'import_key', 'fk_incoterms', 'fk_multicurrency', 'code_client', 'code_fournisseur', 'code_compta', 'code_compta_fournisseur', 'model_pdf', 'fk_projet' @@ -179,9 +177,15 @@ if (empty($reshook)) // Merge categories $static_cat = new Categorie($db); - $custcats = $static_cat->containing($soc_origin->id, 'customer', 'id'); + + $custcats_ori = $static_cat->containing($soc_origin->id, 'customer', 'id'); + $custcats = $static_cat->containing($object->id, 'customer', 'id'); + $custcats = array_merge($custcats,$custcats_ori); $object->setCategories($custcats, 'customer'); - $suppcats = $static_cat->containing($soc_origin->id, 'supplier', 'id'); + + $suppcats_ori = $static_cat->containing($soc_origin->id, 'supplier', 'id'); + $suppcats = $static_cat->containing($object->id, 'supplier', 'id'); + $suppcats = array_merge($suppcats,$suppcats_ori); $object->setCategories($suppcats, 'supplier'); // If thirdparty has a new code that is same than origin, we clean origin code to avoid duplicate key from database unique keys. @@ -209,7 +213,7 @@ if (empty($reshook)) $objects = array( 'Adherent' => '/adherents/class/adherent.class.php', 'Societe' => '/societe/class/societe.class.php', - 'Categorie' => '/categories/class/categorie.class.php', + //'Categorie' => '/categories/class/categorie.class.php', 'ActionComm' => '/comm/action/class/actioncomm.class.php', 'Propal' => '/comm/propal/class/propal.class.php', 'Commande' => '/commande/class/commande.class.php', @@ -327,9 +331,11 @@ if (empty($reshook)) if ($action == 'update_extras') { $object->fetch($socid); + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute','none')); if ($ret < 0) $error++; if (! $error) @@ -511,6 +517,8 @@ if (empty($reshook)) { if ($action == 'add') { + $error = 0; + $db->begin(); if (empty($object->client)) $object->code_client=''; @@ -531,13 +539,32 @@ if (empty($reshook)) } } + // Links with users + $salesreps = GETPOST('commercial', 'array'); + $result = $object->setSalesRep($salesreps); + if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + } + // Customer categories association $custcats = GETPOST('custcats', 'array'); - $object->setCategories($custcats, 'customer'); + $result = $object->setCategories($custcats, 'customer'); + if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + } // Supplier categories association $suppcats = GETPOST('suppcats', 'array'); - $object->setCategories($suppcats, 'supplier'); + $result = $object->setCategories($suppcats, 'supplier'); + if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + } // Logo/Photo save $dir = $conf->societe->multidir_output[$conf->entity]."/".$object->id."/logos/"; @@ -566,7 +593,7 @@ if (empty($reshook)) } } else - { + { switch($_FILES['photo']['error']) { case 1: //uploaded file exceeds the upload_max_filesize directive in php.ini @@ -593,7 +620,7 @@ if (empty($reshook)) $error++; } - if ($result >= 0) + if ($result >= 0 && ! $error) { $db->commit(); @@ -622,6 +649,8 @@ if (empty($reshook)) if ($action == 'update') { + $error = 0; + if (GETPOST('cancel','alpha')) { if (! empty($backtopage)) @@ -647,16 +676,36 @@ if (empty($reshook)) setEventMessages($object->error, $object->errors, 'errors'); $error++; } + + // Links with users + $salesreps = GETPOST('commercial', 'array'); + $result = $object->setSalesRep($salesreps); + if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + } + // Prevent thirdparty's emptying if a user hasn't rights $user->rights->categorie->lire (in such a case, post of 'custcats' is not defined) - if (!empty($user->rights->categorie->lire)) + if (! $error && !empty($user->rights->categorie->lire)) { // Customer categories association $categories = GETPOST( 'custcats', 'array' ); - $object->setCategories($categories, 'customer'); + $result = $object->setCategories($categories, 'customer'); + if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + } // Supplier categories association $categories = GETPOST('suppcats', 'array'); - $object->setCategories($categories, 'supplier'); + $result = $object->setCategories($categories, 'supplier'); + if ($result < 0) + { + $error++; + setEventMessages($object->error, $object->errors, 'errors'); + } } // Logo/Photo save @@ -686,13 +735,18 @@ if (empty($reshook)) } else { - // Create small thumbs for company (Ratio is near 16/9) - // Used on logon for example - $imgThumbSmall = vignette($newfile, $maxwidthsmall, $maxheightsmall, '_small', $quality); + // Create thumbs + $object->addThumbs($newfile); - // Create mini thumbs for company (Ratio is near 16/9) - // Used on menu or for setup page for example - $imgThumbMini = vignette($newfile, $maxwidthmini, $maxheightmini, '_mini', $quality); + // Index file in database + if (! empty($conf->global->THIRDPARTY_LOGO_ALLOW_EXTERNAL_DOWNLOAD)) + { + require_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php'; + // the dir dirname($newfile) is directory of logo, so we should have only one file at once into index, so we delete indexes for the dir + deleteFilesIntoDatabaseIndex(dirname($newfile), '', '', 'uploaded', 1); + // now we index the uploaded logo file + addFileIntoDatabaseIndex(dirname($newfile), basename($newfile), '', 'uploaded', 1); + } } } } @@ -764,7 +818,7 @@ if (empty($reshook)) if ($result > 0) { - header("Location: ".DOL_URL_ROOT."/societe/list.php?delsoc=".urlencode($object->name)); + header("Location: ".DOL_URL_ROOT."/societe/list.php?restore_lastsearch_values=1&delsoc=".urlencode($object->name)); exit; } else @@ -790,8 +844,10 @@ if (empty($reshook)) $result = $object->setIncoterms(GETPOST('incoterm_id', 'int'), GETPOST('location_incoterms', 'alpha')); } - // Actions to send emails $id=$socid; + $object->fetch($socid); + + // Actions to send emails $trigger_name='COMPANY_SENTBYMAIL'; $paramname='socid'; $mode='emailfromthirdparty'; @@ -1145,7 +1201,7 @@ else // Address print ''.fieldLabel('Address','address').''; - print ''; @@ -1224,7 +1280,7 @@ else // Vat is used print ''.fieldLabel('VATIsUsed','assujtva_value').''; print ''; - print $form->selectyesno('assujtva_value',(isset($conf->global->THIRDPARTY_DEFAULT_USEVAT)?$conf->global->THIRDPARTY_DEFAULT_USEVAT:1),1); // Assujeti par defaut en creation + print $form->selectyesno('assujtva_value', GETPOSTISSET('assujtva_value')?GETPOST('assujtva_value','int'):1, 1); // Assujeti par defaut en creation print ''; print ''.fieldLabel('VATIntra','intra_vat').''; print ''; @@ -1322,7 +1378,8 @@ else print ''; print ''.fieldLabel('AllocateCommercial','commercial_id').''; print ''; - print $form->select_dolusers((! empty($object->commercial_id)?$object->commercial_id:$user->id),'commercial_id',1); // Add current user by default + $userlist = $form->select_dolusers('', '', 0, null, 0, '', '', 0, 0, 0, '', 0, '', '', 0, 1); + print $form->multiselectarray('commercial', $userlist, GETPOST('commercial', 'array'), null, null, null, null, "90%"); print ''; } @@ -1586,6 +1643,7 @@ else print ''; print ''; print ''; + print ''; if ($modCodeClient->code_auto || $modCodeFournisseur->code_auto) print ''; @@ -1647,12 +1705,12 @@ else } else if ($object->codeclient_modifiable()) { - print ''; + print ''; } else { print $object->code_client; - print ''; + print ''; } print ''; $s=$modCodeClient->getToolTip($langs,$object,0); @@ -1893,6 +1951,16 @@ else print ''.fieldLabel('Capital','capital').''; print ' '.$langs->trans("Currency".$conf->currency).''; + // Assign a Name + print ''; + print ''.fieldLabel('AllocateCommercial','commercial_id').''; + print ''; + $userlist = $form->select_dolusers('', '', 0, null, 0, '', '', 0, 0, 0, '', 0, '', '', 0, 1); + $arrayselected = GETPOST('commercial', 'array'); + if (empty($arrayselected)) $arrayselected = $object->getSalesRepresentatives($user, 1); + print $form->multiselectarray('commercial', $userlist, $arrayselected, null, null, null, null, "90%"); + print ''; + // Default language if (! empty($conf->global->MAIN_MULTILANGS)) { @@ -2135,111 +2203,113 @@ else } //if ($j % 2 == 1) print ''; - // VAT is used - print ''; - print $langs->trans('VATIsUsed'); - print ''; - print yn($object->tva_assuj); - print ''; - print ''; + + // This fields are used to know VAT to include in an invoice when the thirdparty is making a sale, so when it is a supplier. + // We don't need them into customer profile. + // Except for spain and localtax where localtax depends on buyer and not seller + + if ($object->fournisseur) + { + // VAT is used + print ''; + print $form->textwithpicto($langs->trans('VATIsUsed'),$langs->trans('VATIsUsedWhenSelling')); + print ''; + print yn($object->tva_assuj); + print ''; + print ''; + } // Local Taxes - //TODO: Place into a function to control showing by country or study better option - if($mysoc->localtax1_assuj=="1" && $mysoc->localtax2_assuj=="1") - { - print ''.$langs->transcountry("LocalTax1IsUsed",$mysoc->country_code).''; - print yn($object->localtax1_assuj); - print ''.$langs->transcountry("LocalTax2IsUsed",$mysoc->country_code).''; - print yn($object->localtax2_assuj); - print ''; + if ($object->fournisseur || $mysoc->country_code=='ES') + { + if($mysoc->localtax1_assuj=="1" && $mysoc->localtax2_assuj=="1") + { + print ''.$langs->transcountry("LocalTax1IsUsed",$mysoc->country_code).''; + print yn($object->localtax1_assuj); + print ''.$langs->transcountry("LocalTax2IsUsed",$mysoc->country_code).''; + print yn($object->localtax2_assuj); + print ''; - if($object->localtax1_assuj=="1" && (! isOnlyOneLocalTax(1))) - { - print '
    '; - print ''; - print ''; - print ''.$langs->transcountry("TypeLocaltax1", $mysoc->country_code).' id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; - if($action == 'editRE') - { - print ''; - $formcompany->select_localtax(1,$object->localtax1_value, "lt1"); - print ''; - } - else - { - print ''.$object->localtax1_value.''; - } - print ''; - } - if($object->localtax2_assuj=="1" && (! isOnlyOneLocalTax(2))) - { - print '
    '; - print ''; - print ''; - print ''.$langs->transcountry("TypeLocaltax2", $mysoc->country_code).'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; - if($action == 'editIRPF'){ - print ''; - $formcompany->select_localtax(2,$object->localtax2_value, "lt2"); - print ''; - }else{ - print ''.$object->localtax2_value.''; - } - print ''; - } - } - elseif($mysoc->localtax1_assuj=="1" && $mysoc->localtax2_assuj!="1") - { - print ''.$langs->transcountry("LocalTax1IsUsed",$mysoc->country_code).''; - print yn($object->localtax1_assuj); - print ''; - if($object->localtax1_assuj=="1" && (! isOnlyOneLocalTax(1))) - { - print '
    '; - print ''; - print ''; - print ' '.$langs->transcountry("TypeLocaltax1", $mysoc->country_code).'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; - if($action == 'editRE'){ - print ''; - $formcompany->select_localtax(1,$object->localtax1_value, "lt1"); - print ''; - }else{ - print ''.$object->localtax1_value.''; - } - print ''; + if($object->localtax1_assuj=="1" && (! isOnlyOneLocalTax(1))) + { + print '
    '; + print ''; + print ''; + print ''.$langs->transcountry("TypeLocaltax1", $mysoc->country_code).' id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; + if($action == 'editRE') + { + print ''; + $formcompany->select_localtax(1,$object->localtax1_value, "lt1"); + print ''; + } + else + { + print ''.$object->localtax1_value.''; + } + print ''; + } + if($object->localtax2_assuj=="1" && (! isOnlyOneLocalTax(2))) + { + print '
    '; + print ''; + print ''; + print ''.$langs->transcountry("TypeLocaltax2", $mysoc->country_code).'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; + if($action == 'editIRPF'){ + print ''; + $formcompany->select_localtax(2,$object->localtax2_value, "lt2"); + print ''; + }else{ + print ''.$object->localtax2_value.''; + } + print ''; + } + } + elseif($mysoc->localtax1_assuj=="1" && $mysoc->localtax2_assuj!="1") + { + print ''.$langs->transcountry("LocalTax1IsUsed",$mysoc->country_code).''; + print yn($object->localtax1_assuj); + print ''; + if($object->localtax1_assuj=="1" && (! isOnlyOneLocalTax(1))) + { + print '
    '; + print ''; + print ''; + print ' '.$langs->transcountry("TypeLocaltax1", $mysoc->country_code).'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; + if($action == 'editRE'){ + print ''; + $formcompany->select_localtax(1,$object->localtax1_value, "lt1"); + print ''; + }else{ + print ''.$object->localtax1_value.''; + } + print ''; - } - } - elseif($mysoc->localtax2_assuj=="1" && $mysoc->localtax1_assuj!="1") - { - print ''.$langs->transcountry("LocalTax2IsUsed",$mysoc->country_code).''; - print yn($object->localtax2_assuj); - print ''; - if($object->localtax2_assuj=="1" && (! isOnlyOneLocalTax(2))) - { + } + } + elseif($mysoc->localtax2_assuj=="1" && $mysoc->localtax1_assuj!="1") + { + print ''.$langs->transcountry("LocalTax2IsUsed",$mysoc->country_code).''; + print yn($object->localtax2_assuj); + print ''; + if($object->localtax2_assuj=="1" && (! isOnlyOneLocalTax(2))) + { - print '
    '; - print ''; - print ''; - print ' '.$langs->transcountry("TypeLocaltax2", $mysoc->country_code).' id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; - if($action == 'editIRPF'){ - print ''; - $formcompany->select_localtax(2,$object->localtax2_value, "lt2"); - print ''; - }else{ - print ''.$object->localtax2_value.''; - } - print ''; + print '
    '; + print ''; + print ''; + print ' '.$langs->transcountry("TypeLocaltax2", $mysoc->country_code).' id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'),1).''; + if($action == 'editIRPF'){ + print ''; + $formcompany->select_localtax(2,$object->localtax2_value, "lt2"); + print ''; + }else{ + print ''.$object->localtax2_value.''; + } + print ''; - } - } - /* - if ($mysoc->country_code=='ES' && $mysoc->localtax2_assuj!="1" && ! empty($conf->fournisseur->enabled) && $object->fournisseur==1) - { - print ''.$langs->transcountry("LocalTax2IsUsed",$mysoc->country_code).''; - print yn($object->localtax2_assuj); - print ''; - } - */ + } + } + } // VAT Code print ''; diff --git a/htdocs/societe/class/api_thirdparties.class.php b/htdocs/societe/class/api_thirdparties.class.php index fa52c73f597..1cdfabf7fca 100644 --- a/htdocs/societe/class/api_thirdparties.class.php +++ b/htdocs/societe/class/api_thirdparties.class.php @@ -83,8 +83,14 @@ class Thirdparties extends DolibarrApi throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); } - $filterabsolutediscount = "fk_facture_source IS NULL OR (fk_facture_source IS NOT NULL AND (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%'))"; - $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')"; + if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + } else { + $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')"; + $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')"; + } + $absolute_discount = $this->company->getAvailableDiscounts('', $filterabsolutediscount); $absolute_creditnote = $this->company->getAvailableDiscounts('', $filtercreditnote); $this->company->absolute_discount = price2num($absolute_discount, 'MT'); @@ -308,7 +314,7 @@ class Thirdparties extends DolibarrApi $listofproperties=array( 'address', 'zip', 'town', 'state_id', 'country_id', 'phone', 'phone_pro', 'fax', 'email', 'skype', 'url', 'barcode', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', - 'tva_intra', 'effectif_id', 'forme_juridique', 'remise_percent', 'mode_reglement_supplier_id', 'cond_reglement_supplier_id', 'name_bis', + 'tva_intra', 'effectif_id', 'forme_juridique', 'remise_percent', 'remise_supplier_percent', 'mode_reglement_supplier_id', 'cond_reglement_supplier_id', 'name_bis', 'stcomm_id', 'outstanding_limit', 'price_level', 'parent', 'default_lang', 'ref', 'ref_ext', 'import_key', 'fk_incoterms', 'fk_multicurrency', 'code_client', 'code_fournisseur', 'code_compta', 'code_compta_fournisseur', 'model_pdf', 'fk_projet' diff --git a/htdocs/societe/class/companybankaccount.class.php b/htdocs/societe/class/companybankaccount.class.php index 428f5b96f42..187f7c5ec65 100644 --- a/htdocs/societe/class/companybankaccount.class.php +++ b/htdocs/societe/class/companybankaccount.class.php @@ -72,7 +72,7 @@ class CompanyBankAccount extends Account $now = dol_now(); $error = 0; // Correct default_rib to be sure to have always one default - $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe_rib where fk_soc = ".$this->socid." AND default_rib = 1"; + $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe_rib where fk_soc = ".$this->socid." AND default_rib = 1 AND type = 'ban'"; $result = $this->db->query($sql); if ($result) { @@ -81,8 +81,8 @@ class CompanyBankAccount extends Account if (empty($this->default_rib) && $numrows == 0) $this->default_rib = 1; } - $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_rib (fk_soc, datec)"; - $sql.= " VALUES (".$this->socid.", '".$this->db->idate($now)."')"; + $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_rib (fk_soc, type, datec)"; + $sql.= " VALUES (".$this->socid.", 'ban', '".$this->db->idate($now)."')"; $resql=$this->db->query($sql); if ($resql) { @@ -197,18 +197,18 @@ class CompanyBankAccount extends Account * Load record from database * * @param int $id Id of record - * @param int $socid Id of company. If this is filled, function will return the default RIB of company + * @param int $socid Id of company. If this is filled, function will return the first default RIB of company * @return int <0 if KO, >0 if OK */ function fetch($id, $socid=0) { if (empty($id) && empty($socid)) return -1; - $sql = "SELECT rowid, fk_soc, bank, number, code_banque, code_guichet, cle_rib, bic, iban_prefix as iban, domiciliation, proprio,"; + $sql = "SELECT rowid, type, fk_soc, bank, number, code_banque, code_guichet, cle_rib, bic, iban_prefix as iban, domiciliation, proprio,"; $sql.= " owner_address, default_rib, label, datec, tms as datem, rum, frstrecur"; $sql.= " FROM ".MAIN_DB_PREFIX."societe_rib"; if ($id) $sql.= " WHERE rowid = ".$id; - if ($socid) $sql.= " WHERE fk_soc = ".$socid." AND default_rib = 1"; + if ($socid) $sql.= " WHERE fk_soc = ".$socid." AND default_rib = 1 AND type ='ban'"; $resql = $this->db->query($sql); if ($resql) @@ -220,6 +220,7 @@ class CompanyBankAccount extends Account $this->ref = $obj->fk_soc.'-'.$obj->label; // Generate an artificial ref $this->id = $obj->rowid; + $this->type = $obj->type; $this->socid = $obj->fk_soc; $this->bank = $obj->bank; $this->code_banque = $obj->code_banque; @@ -321,7 +322,7 @@ class CompanyBankAccount extends Account } /** - * Set RIB as Default + * Set a BAN as Default * * @param int $rib RIB id * @return int 0 if KO, 1 if OK @@ -345,13 +346,13 @@ class CompanyBankAccount extends Account $this->db->begin(); - $sql2 = "UPDATE ".MAIN_DB_PREFIX."societe_rib SET default_rib = 0 "; - $sql2.= "WHERE fk_soc = ".$obj->fk_soc; + $sql2 = "UPDATE ".MAIN_DB_PREFIX."societe_rib SET default_rib = 0"; + $sql2.= " WHERE type = 'ban' AND fk_soc = ".$obj->fk_soc; dol_syslog(get_class($this).'::setAsDefault', LOG_DEBUG); $result2 = $this->db->query($sql2); - $sql3 = "UPDATE ".MAIN_DB_PREFIX."societe_rib SET default_rib = 1 "; - $sql3.= "WHERE rowid = ".$obj->id; + $sql3 = "UPDATE ".MAIN_DB_PREFIX."societe_rib SET default_rib = 1"; + $sql3.= " WHERE rowid = ".$obj->id; dol_syslog(get_class($this).'::setAsDefault', LOG_DEBUG); $result3 = $this->db->query($sql3); diff --git a/htdocs/societe/class/companypaymentmode.class.php b/htdocs/societe/class/companypaymentmode.class.php new file mode 100644 index 00000000000..6a6dfa4d38c --- /dev/null +++ b/htdocs/societe/class/companypaymentmode.class.php @@ -0,0 +1,564 @@ + + * Copyright (C) ---Put here your own copyright and developer email--- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/companypaymentmode.class.php + * \ingroup monmodule + * \brief This file is a CRUD class file for CompanyPaymentMode (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + +/** + * Class for CompanyPaymentMode + */ +class CompanyPaymentMode extends CommonObject +{ + /** + * @var string ID to identify managed object + */ + public $element = 'companypaymentmode'; + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'societe_rib'; + /** + * @var int Does companypaymentmode support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + */ + public $ismultientitymanaged = 2; + /** + * @var int Does companypaymentmode support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + /** + * @var string String with name of icon for companypaymentmode. Must be the part after the 'object_' into object_companypaymentmode.png + */ + public $picto = 'generic'; + + + /** + * 'type' if the field format. + * 'label' the translation key. + * 'enabled' is a condition when the field must be managed. + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only. Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'position' is the sort order of field. + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). + * 'help' is a string visible as a tooltip on field + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'default' is a default value for creation (can still be replaced by the global setup of default values) + * 'showoncombobox' if field must be shown into the label of combobox + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' =>array('type'=>'integer', 'label'=>'Rowid', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>10), + 'fk_soc' =>array('type'=>'integer', 'label'=>'Fk soc', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>15), + 'label' =>array('type'=>'varchar(30)', 'label'=>'Label', 'enabled'=>1, 'visible'=>-2, 'position'=>30), + 'bank' =>array('type'=>'varchar(255)', 'label'=>'Bank', 'enabled'=>1, 'visible'=>-2, 'position'=>35), + 'code_banque' =>array('type'=>'varchar(128)', 'label'=>'Code banque', 'enabled'=>1, 'visible'=>-2, 'position'=>40), + 'code_guichet' =>array('type'=>'varchar(6)', 'label'=>'Code guichet', 'enabled'=>1, 'visible'=>-2, 'position'=>45), + 'number' =>array('type'=>'varchar(255)', 'label'=>'Number', 'enabled'=>1, 'visible'=>-2, 'position'=>50), + 'cle_rib' =>array('type'=>'varchar(5)', 'label'=>'Cle rib', 'enabled'=>1, 'visible'=>-2, 'position'=>55), + 'bic' =>array('type'=>'varchar(20)', 'label'=>'Bic', 'enabled'=>1, 'visible'=>-2, 'position'=>60), + 'iban_prefix' =>array('type'=>'varchar(34)', 'label'=>'Iban prefix', 'enabled'=>1, 'visible'=>-2, 'position'=>65), + 'domiciliation' =>array('type'=>'varchar(255)', 'label'=>'Domiciliation', 'enabled'=>1, 'visible'=>-2, 'position'=>70), + 'proprio' =>array('type'=>'varchar(60)', 'label'=>'Proprio', 'enabled'=>1, 'visible'=>-2, 'position'=>75), + 'owner_address' =>array('type'=>'text', 'label'=>'Owner address', 'enabled'=>1, 'visible'=>-2, 'position'=>80), + 'default_rib' =>array('type'=>'tinyint(4)', 'label'=>'Default rib', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>85), + 'rum' =>array('type'=>'varchar(32)', 'label'=>'Rum', 'enabled'=>1, 'visible'=>-2, 'position'=>90), + 'date_rum' =>array('type'=>'date', 'label'=>'Date rum', 'enabled'=>1, 'visible'=>-2, 'position'=>95), + 'frstrecur' =>array('type'=>'varchar(16)', 'label'=>'Frstrecur', 'enabled'=>1, 'visible'=>-2, 'position'=>100), + 'type' =>array('type'=>'varchar(32)', 'label'=>'Type', 'enabled'=>1, 'visible'=>-2, 'position'=>110), + 'last_four' =>array('type'=>'varchar(4)', 'label'=>'Last four', 'enabled'=>1, 'visible'=>-2, 'position'=>115), + 'card_type' =>array('type'=>'varchar(255)', 'label'=>'Card type', 'enabled'=>1, 'visible'=>-2, 'position'=>120), + 'cvn' =>array('type'=>'varchar(255)', 'label'=>'Cvn', 'enabled'=>1, 'visible'=>-2, 'position'=>125), + 'exp_date_month' =>array('type'=>'integer', 'label'=>'Exp date month', 'enabled'=>1, 'visible'=>-2, 'position'=>130), + 'exp_date_year' =>array('type'=>'integer', 'label'=>'Exp date year', 'enabled'=>1, 'visible'=>-2, 'position'=>135), + 'country_code' =>array('type'=>'varchar(10)', 'label'=>'Country code', 'enabled'=>1, 'visible'=>-2, 'position'=>140), + 'approved' =>array('type'=>'integer', 'label'=>'Approved', 'enabled'=>1, 'visible'=>-2, 'position'=>145), + 'email' =>array('type'=>'varchar(255)', 'label'=>'Email', 'enabled'=>1, 'visible'=>-2, 'position'=>150), + 'max_total_amount_of_all_payments' =>array('type'=>'double(24,8)', 'label'=>'Max total amount of all payments', 'enabled'=>1, 'visible'=>-2, 'position'=>155), + 'preapproval_key' =>array('type'=>'varchar(255)', 'label'=>'Preapproval key', 'enabled'=>1, 'visible'=>-2, 'position'=>160), + 'total_amount_of_all_payments' =>array('type'=>'double(24,8)', 'label'=>'Total amount of all payments', 'enabled'=>1, 'visible'=>-2, 'position'=>165), + 'stripe_card_ref' =>array('type'=>'varchar(128)', 'label'=>'Stripe card ref', 'enabled'=>1, 'visible'=>-2, 'position'=>170), + 'status' =>array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>175), + 'starting_date' =>array('type'=>'date', 'label'=>'Starting date', 'enabled'=>1, 'visible'=>-2, 'position'=>180), + 'ending_date' =>array('type'=>'date', 'label'=>'Ending date', 'enabled'=>1, 'visible'=>-2, 'position'=>185), + 'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>20), + 'tms' =>array('type'=>'timestamp', 'label'=>'Tms', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>25), + 'import_key' =>array('type'=>'varchar(14)', 'label'=>'Import key', 'enabled'=>1, 'visible'=>-2, 'position'=>105), + //'aaa' =>array('type'=>'date', 'label'=>'Ending date', 'enabled'=>0, 'visible'=>-2, 'position'=>185), + ); + public $rowid; + public $fk_soc; + public $label; + public $bank; + public $code_banque; + public $code_guichet; + public $number; + public $cle_rib; + public $bic; + public $iban_prefix; + public $domiciliation; + public $proprio; + public $owner_address; + public $default_rib; + public $rum; + public $date_rum; + public $frstrecur; + public $type; + public $last_four; + public $card_type; + public $cvn; + public $exp_date_month; + public $exp_date_year; + public $country_code; + public $approved; + public $email; + public $max_total_amount_of_all_payments; + public $preapproval_key; + public $total_amount_of_all_payments; + public $stripe_card_ref; + public $status; + public $starting_date; + public $ending_date; + public $datec; + public $tms; + public $import_key; + // END MODULEBUILDER PROPERTIES + + + + // If this object has a subtable with lines + + /** + * @var int Name of subtable line + */ + //public $table_element_line = 'companypaymentmodedet'; + /** + * @var int Field with ID of parent key if this field has a parent + */ + //public $fk_element = 'fk_companypaymentmode'; + /** + * @var int Name of subtable class that manage subtable lines + */ + //public $class_element_line = 'CompanyPaymentModeline'; + /** + * @var array Array of child tables (child tables to delete before deleting a record) + */ + //protected $childtables=array('companypaymentmodedet'); + /** + * @var CompanyPaymentModeLine[] Array of subtable lines + */ + //public $lines = array(); + + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf; + + $this->db = $db; + + if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible']=0; + if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled']=0; + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create(User $user, $notrigger = false) + { + $idpayment = $this->createCommon($user, $notrigger); + + return $idpayment; + } + + /** + * Clone and object into another one + * + * @param User $user User that creates + * @param int $fromid Id of object to clone + * @return mixed New object created, <0 if KO + */ + public function createFromClone(User $user, $fromid) + { + global $hookmanager, $langs; + $error = 0; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $object = new self($this->db); + + $this->db->begin(); + + // Load source object + $object->fetchCommon($fromid); + // Reset some properties + unset($object->id); + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + $object->ref = "copy_of_".$object->ref; + $object->title = $langs->trans("CopyOf")." ".$object->title; + // ... + + // Create clone + $object->context['createfromclone'] = 'createfromclone'; + $result = $object->createCommon($user); + if ($result < 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + // End + if (!$error) { + $this->db->commit(); + return $object; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @param int $socid Id of company to get first default payment mode + * @param string $type Filter on type ('ban', 'card', ...) + * @return int <0 if KO, 0 if not found, >0 if OK + */ + public function fetch($id, $ref = null, $socid = 0, $type = '') + { + if ($socid) $sql.= " AND fk_soc = ".$this->db->escape($socid)." AND default_rib = 1"; + if ($type) $sql.= " AND type = '".$this->db->escape($type)."'"; + + $result = $this->fetchCommon($id, $ref, $morewhere); + if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines(); + return $result; + } + + /** + * Load object lines in memory from the database + * + * @return int <0 if KO, 0 if not found, >0 if OK + */ + /*public function fetchLines() + { + $this->lines=array(); + + // Load lines with object CompanyPaymentModeLine + + return count($this->lines)?1:0; + }*/ + + /** + * Update object into database + * + * @param User $user User that modifies + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update(User $user, $notrigger = false) + { + return $this->updateCommon($user, $notrigger); + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param bool $notrigger false=launch triggers after, true=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete(User $user, $notrigger = false) + { + return $this->deleteCommon($user, $notrigger); + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) + { + global $db, $conf, $langs; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; + + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("CompanyPaymentMode") . ''; + $label.= '
    '; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref; + + $url = dol_buildpath('/monmodule/companypaymentmode_card.php',1).'?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowCompanyPaymentMode"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = '
    '; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + return $result; + } + + /** + * Set a Payment mode as Default + * + * @param int $id Payment mode ID + * @param string $alltypes 1=The default is for all payment types instead of per type + * @return int 0 if KO, 1 if OK + */ + function setAsDefault($id=0, $alltypes=0) + { + $sql1 = "SELECT rowid as id, fk_soc, type FROM ".MAIN_DB_PREFIX."societe_rib"; + $sql1.= " WHERE rowid = ".($id?$id:$this->id); + + dol_syslog(get_class($this).'::setAsDefault', LOG_DEBUG); + $result1 = $this->db->query($sql1); + if ($result1) + { + if ($this->db->num_rows($result1) == 0) + { + return 0; + } + else + { + $obj = $this->db->fetch_object($result1); + + $type = ''; + if (empty($alltypes)) $type = $obj->type; + + $this->db->begin(); + + $sql2 = "UPDATE ".MAIN_DB_PREFIX."societe_rib SET default_rib = 0"; + $sql2.= " WHERE default_rib <> 0 AND fk_soc = ".$obj->fk_soc; + if ($type) $sql2.= " AND type = '".$this->db->escape($type)."'"; + dol_syslog(get_class($this).'::setAsDefault', LOG_DEBUG); + $result2 = $this->db->query($sql2); + + $sql3 = "UPDATE ".MAIN_DB_PREFIX."societe_rib SET default_rib = 1"; + $sql3.= " WHERE rowid = ".$obj->id; + if ($type) $sql3.= " AND type = '".$this->db->escape($type)."'"; + dol_syslog(get_class($this).'::setAsDefault', LOG_DEBUG); + $result3 = $this->db->query($sql3); + + if (!$result2 || !$result3) + { + dol_print_error($this->db); + $this->db->rollback(); + return -1; + } + else + { + $this->db->commit(); + return 1; + } + } + } + else + { + dol_print_error($this->db); + return -1; + } + } + + /** + * Retourne le libelle du status d'un user (actif, inactif) + * + * @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto + * @return string Label of status + */ + function getLibStatut($mode=0) + { + return $this->LibStatut($this->status,$mode); + } + + /** + * Return the status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + static function LibStatut($status,$mode=0) + { + global $langs; + + if ($mode == 0) + { + $prefix=''; + if ($status == 1) return $langs->trans('Enabled'); + if ($status == 0) return $langs->trans('Disabled'); + } + if ($mode == 1) + { + if ($status == 1) return $langs->trans('Enabled'); + if ($status == 0) return $langs->trans('Disabled'); + } + if ($mode == 2) + { + if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled'); + if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5').' '.$langs->trans('Disabled'); + } + if ($mode == 3) + { + if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4'); + if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5'); + } + if ($mode == 4) + { + if ($status == 1) return img_picto($langs->trans('Enabled'),'statut4').' '.$langs->trans('Enabled'); + if ($status == 0) return img_picto($langs->trans('Disabled'),'statut5').' '.$langs->trans('Disabled'); + } + if ($mode == 5) + { + if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4'); + if ($status == 0) return $langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut5'); + } + if ($mode == 6) + { + if ($status == 1) return $langs->trans('Enabled').' '.img_picto($langs->trans('Enabled'),'statut4'); + if ($status == 0) return $langs->trans('Disabled').' '.img_picto($langs->trans('Disabled'),'statut5'); + } + } + + /** + * Charge les informations d'ordre info dans l'objet commande + * + * @param int $id Id of order + * @return void + */ + function info($id) + { + $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; + $sql.= ' fk_user_creat, fk_user_modif'; + $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; + $sql.= ' WHERE t.rowid = '.$id; + $result=$this->db->query($sql); + if ($result) + { + if ($this->db->num_rows($result)) + { + $obj = $this->db->fetch_object($result); + $this->id = $obj->rowid; + if ($obj->fk_user_author) + { + $cuser = new User($this->db); + $cuser->fetch($obj->fk_user_author); + $this->user_creation = $cuser; + } + + if ($obj->fk_user_valid) + { + $vuser = new User($this->db); + $vuser->fetch($obj->fk_user_valid); + $this->user_validation = $vuser; + } + + if ($obj->fk_user_cloture) + { + $cluser = new User($this->db); + $cluser->fetch($obj->fk_user_cloture); + $this->user_cloture = $cluser; + } + + $this->date_creation = $this->db->jdate($obj->datec); + $this->date_modification = $this->db->jdate($obj->datem); + $this->date_validation = $this->db->jdate($obj->datev); + } + + $this->db->free($result); + + } + else + { + dol_print_error($this->db); + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->initAsSpecimenCommon(); + } + +} diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 3ca69fdb6fd..62cb8956169 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -12,6 +12,7 @@ * Copyright (C) 2013 Peter Fontaine * Copyright (C) 2014-2015 Marcos García * Copyright (C) 2015 Raphaël Doursenaud + * Copyright (C) 2017 Rui Strecht * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -116,6 +117,13 @@ class Societe extends CommonObject var $state_code; var $state; + /** + * Id of region + * @var int + */ + var $region_code; + var $region; + /** * State code * @var string @@ -234,6 +242,7 @@ class Societe extends CommonObject var $forme_juridique; var $remise_percent; + var $remise_supplier_percent; var $mode_reglement_supplier_id; var $cond_reglement_supplier_id; var $fk_prospectlevel; @@ -763,9 +772,10 @@ class Societe extends CommonObject // Clean parameters $this->id = $id; + $this->entity = ((isset($this->entity) && is_numeric($this->entity))?$this->entity:$conf->entity); $this->name = $this->name?trim($this->name):trim($this->nom); $this->nom = $this->name; // For backward compatibility - $this->name_alias = trim($this->name_alias); + $this->name_alias = trim($this->name_alias); $this->ref_ext = trim($this->ref_ext); $this->address = $this->address?trim($this->address):trim($this->address); $this->zip = $this->zip?trim($this->zip):trim($this->zip); @@ -780,9 +790,9 @@ class Societe extends CommonObject $this->fax = preg_replace("/\./","",$this->fax); $this->email = trim($this->email); $this->skype = trim($this->skype); - $this->url = $this->url?clean_url($this->url,0):''; - $this->note_private = trim($this->note_private); - $this->note_public = trim($this->note_public); + $this->url = $this->url?clean_url($this->url,0):''; + $this->note_private = trim($this->note_private); + $this->note_public = trim($this->note_public); $this->idprof1 = trim($this->idprof1); $this->idprof2 = trim($this->idprof2); $this->idprof3 = trim($this->idprof3); @@ -887,7 +897,8 @@ class Societe extends CommonObject dol_syslog(get_class($this)."::update verify ok or not done"); $sql = "UPDATE ".MAIN_DB_PREFIX."societe SET "; - $sql .= "nom = '" . $this->db->escape($this->name) ."'"; // Required + $sql .= "entity = " . $this->entity; + $sql .= ",nom = '" . $this->db->escape($this->name) ."'"; // Required $sql .= ",name_alias = '" . $this->db->escape($this->name_alias) ."'"; $sql .= ",ref_ext = " .(! empty($this->ref_ext)?"'".$this->db->escape($this->ref_ext) ."'":"null"); $sql .= ",address = '" . $this->db->escape($this->address) ."'"; @@ -990,10 +1001,10 @@ class Societe extends CommonObject $sql .= ", code_fournisseur = ".(! empty($this->code_fournisseur)?"'".$this->db->escape($this->code_fournisseur)."'":"null"); $sql .= ", code_compta_fournisseur = ".(! empty($this->code_compta_fournisseur)?"'".$this->db->escape($this->code_compta_fournisseur)."'":"null"); } - $sql .= ", fk_user_modif = ".(! empty($user->id)?"'".$user->id."'":"null"); + $sql .= ", fk_user_modif = ".($user->id > 0 ? $user->id:"null"); $sql .= ", fk_multicurrency = ".(int) $this->fk_multicurrency; - $sql .= ', multicurrency_code = \''.$this->db->escape($this->multicurrency_code)."'"; - $sql .= " WHERE rowid = '" . $id ."'"; + $sql .= ", multicurrency_code = '".$this->db->escape($this->multicurrency_code)."'"; + $sql .= " WHERE rowid = " . (int) $id; $resql=$this->db->query($sql); if ($resql) @@ -1121,10 +1132,11 @@ class Societe extends CommonObject * @param string $idprof4 Prof id 4 of third party (Warning, this can return several records) * @param string $idprof5 Prof id 5 of third party (Warning, this can return several records) * @param string $idprof6 Prof id 6 of third party (Warning, this can return several records) - * @param string $email Email (Warning, this can return several records) + * @param string $email Email of third party (Warning, this can return several records) + * @param string $ref_alias Name_alias of third party (Warning, this can return several records) * @return int >0 if OK, <0 if KO or if two records found for same ref or idprof, 0 if not found. */ - function fetch($rowid, $ref='', $ref_ext='', $ref_int='', $idprof1='',$idprof2='',$idprof3='',$idprof4='',$idprof5='',$idprof6='', $email='') + function fetch($rowid, $ref='', $ref_ext='', $ref_int='', $idprof1='',$idprof2='',$idprof3='',$idprof4='',$idprof5='',$idprof6='', $email='', $ref_alias='') { global $langs; global $conf; @@ -1143,7 +1155,7 @@ class Societe extends CommonObject $sql .= ', s.fk_forme_juridique as forme_juridique_code'; $sql .= ', s.webservices_url, s.webservices_key'; $sql .= ', s.code_client, s.code_fournisseur, s.code_compta, s.code_compta_fournisseur, s.parent, s.barcode'; - $sql .= ', s.fk_departement, s.fk_pays as country_id, s.fk_stcomm, s.remise_client, s.mode_reglement, s.cond_reglement, s.fk_account, s.tva_assuj'; + $sql .= ', s.fk_departement, s.fk_pays as country_id, s.fk_stcomm, s.remise_client, s.remise_supplier, s.mode_reglement, s.cond_reglement, s.fk_account, s.tva_assuj'; $sql .= ', s.mode_reglement_supplier, s.cond_reglement_supplier, s.localtax1_assuj, s.localtax1_value, s.localtax2_assuj, s.localtax2_value, s.fk_prospectlevel, s.default_lang, s.logo'; $sql .= ', s.fk_shipping_method'; $sql .= ', s.outstanding_limit, s.import_key, s.canvas, s.fk_incoterms, s.location_incoterms'; @@ -1166,17 +1178,18 @@ class Societe extends CommonObject $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON s.fk_incoterms = i.rowid'; $sql .= ' WHERE s.entity IN ('.getEntity($this->element).')'; - if ($rowid) $sql .= ' AND s.rowid = '.$rowid; - if ($ref) $sql .= " AND s.nom = '".$this->db->escape($ref)."'"; - if ($ref_ext) $sql .= " AND s.ref_ext = '".$this->db->escape($ref_ext)."'"; - if ($ref_int) $sql .= " AND s.ref_int = '".$this->db->escape($ref_int)."'"; - if ($idprof1) $sql .= " AND s.siren = '".$this->db->escape($idprof1)."'"; - if ($idprof2) $sql .= " AND s.siret = '".$this->db->escape($idprof2)."'"; - if ($idprof3) $sql .= " AND s.ape = '".$this->db->escape($idprof3)."'"; - if ($idprof4) $sql .= " AND s.idprof4 = '".$this->db->escape($idprof4)."'"; - if ($idprof5) $sql .= " AND s.idprof5 = '".$this->db->escape($idprof5)."'"; - if ($idprof6) $sql .= " AND s.idprof6 = '".$this->db->escape($idprof6)."'"; - if ($email) $sql .= " AND s.email = '".$this->db->escape($email)."'"; + if ($rowid) $sql .= ' AND s.rowid = '.$rowid; + if ($ref) $sql .= " AND s.nom = '".$this->db->escape($ref)."'"; + if ($ref_alias) $sql .= " AND s.nom_alias = '".$this->db->escape($ref_alias)."'"; + if ($ref_ext) $sql .= " AND s.ref_ext = '".$this->db->escape($ref_ext)."'"; + if ($ref_int) $sql .= " AND s.ref_int = '".$this->db->escape($ref_int)."'"; + if ($idprof1) $sql .= " AND s.siren = '".$this->db->escape($idprof1)."'"; + if ($idprof2) $sql .= " AND s.siret = '".$this->db->escape($idprof2)."'"; + if ($idprof3) $sql .= " AND s.ape = '".$this->db->escape($idprof3)."'"; + if ($idprof4) $sql .= " AND s.idprof4 = '".$this->db->escape($idprof4)."'"; + if ($idprof5) $sql .= " AND s.idprof5 = '".$this->db->escape($idprof5)."'"; + if ($idprof6) $sql .= " AND s.idprof6 = '".$this->db->escape($idprof6)."'"; + if ($email) $sql .= " AND s.email = '".$this->db->escape($email)."'"; $resql=$this->db->query($sql); if ($resql) @@ -1275,6 +1288,7 @@ class Societe extends CommonObject $this->prefix_comm = $obj->prefix_comm; $this->remise_percent = $obj->remise_client; + $this->remise_supplier_percent = $obj->remise_supplier; $this->mode_reglement_id = $obj->mode_reglement; $this->cond_reglement_id = $obj->cond_reglement; $this->mode_reglement_supplier_id = $obj->mode_reglement_supplier; @@ -1315,7 +1329,6 @@ class Societe extends CommonObject $result = 1; - // Retreive all extrafield // fetch optionals attributes and labels $this->fetch_optionals(); } @@ -1659,16 +1672,78 @@ class Societe extends CommonObject } } + /** + * Definit la societe comme un client + * + * @param float $remise Valeur en % de la remise + * @param string $note Note/Motif de modification de la remise + * @param User $user Utilisateur qui definie la remise + * @return int <0 if KO, >0 if OK + */ + function set_remise_supplier($remise, $note, User $user) + { + global $conf, $langs; + + // Nettoyage parametres + $note=trim($note); + if (! $note) + { + $this->error=$langs->trans("ErrorFieldRequired",$langs->trans("NoteReason")); + return -2; + } + + dol_syslog(get_class($this)."::set_remise_supplier ".$remise.", ".$note.", ".$user->id); + + if ($this->id) + { + $this->db->begin(); + + $now=dol_now(); + + // Positionne remise courante + $sql = "UPDATE ".MAIN_DB_PREFIX."societe "; + $sql.= " SET remise_supplier = '".$this->db->escape($remise)."'"; + $sql.= " WHERE rowid = " . $this->id; + $resql=$this->db->query($sql); + if (! $resql) + { + $this->db->rollback(); + $this->error=$this->db->error(); + return -1; + } + + // Ecrit trace dans historique des remises + $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe_remise_supplier"; + $sql.= " (entity, datec, fk_soc, remise_supplier, note, fk_user_author)"; + $sql.= " VALUES (".$conf->entity.", '".$this->db->idate($now)."', ".$this->id.", '".$this->db->escape($remise)."',"; + $sql.= " '".$this->db->escape($note)."',"; + $sql.= " ".$user->id; + $sql.= ")"; + + $resql=$this->db->query($sql); + if (! $resql) + { + $this->db->rollback(); + $this->error=$this->db->lasterror(); + return -1; + } + + $this->db->commit(); + return 1; + } + } + /** * Add a discount for third party * - * @param float $remise Amount of discount - * @param User $user User adding discount - * @param string $desc Reason of discount - * @param float $tva_tx VAT rate + * @param float $remise Amount of discount + * @param User $user User adding discount + * @param string $desc Reason of discount + * @param float $tva_tx VAT rate + * @param int $discount_type 0 => customer discount, 1 => supplier discount * @return int <0 if KO, id of discount record if OK */ - function set_remise_except($remise, User $user, $desc, $tva_tx=0) + function set_remise_except($remise, User $user, $desc, $tva_tx=0, $discount_type=0) { global $langs; @@ -1694,11 +1769,13 @@ class Societe extends CommonObject $discount = new DiscountAbsolute($this->db); $discount->fk_soc=$this->id; + $discount->discount_type=$discount_type; $discount->amount_ht=price2num($remise,'MT'); $discount->amount_tva=price2num($remise*$tva_tx/100,'MT'); $discount->amount_ttc=price2num($discount->amount_ht+$discount->amount_tva,'MT'); $discount->tva_tx=price2num($tva_tx,'MT'); $discount->description=$desc; + $result=$discount->create($user); if ($result > 0) { @@ -1716,17 +1793,18 @@ class Societe extends CommonObject /** * Renvoie montant TTC des reductions/avoirs en cours disponibles de la societe * - * @param User $user Filtre sur un user auteur des remises - * @param string $filter Filtre autre - * @param integer $maxvalue Filter on max value for discount + * @param User $user Filtre sur un user auteur des remises + * @param string $filter Filtre autre + * @param integer $maxvalue Filter on max value for discount + * @param int $discount_type 0 => customer discount, 1 => supplier discount * @return int <0 if KO, Credit note amount otherwise */ - function getAvailableDiscounts($user='',$filter='',$maxvalue=0) + function getAvailableDiscounts($user='',$filter='',$maxvalue=0,$discount_type=0) { require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php'; $discountstatic=new DiscountAbsolute($this->db); - $result=$discountstatic->getAvailableDiscounts($this,$user,$filter,$maxvalue); + $result=$discountstatic->getAvailableDiscounts($this,$user,$filter,$maxvalue,$discount_type); if ($result >= 0) { return $result; @@ -1742,9 +1820,10 @@ class Societe extends CommonObject * Return array of sales representatives * * @param User $user Object user + * @param int $mode 0=Array with properties, 1=Array of id. * @return array Array of sales representatives of third party */ - function getSalesRepresentatives(User $user) + function getSalesRepresentatives(User $user, $mode=0) { global $conf; @@ -1772,14 +1851,22 @@ class Societe extends CommonObject while ($i < $num) { $obj = $this->db->fetch_object($resql); - $reparray[$i]['id']=$obj->rowid; - $reparray[$i]['lastname']=$obj->lastname; - $reparray[$i]['firstname']=$obj->firstname; - $reparray[$i]['email']=$obj->email; - $reparray[$i]['statut']=$obj->statut; - $reparray[$i]['entity']=$obj->entity; - $reparray[$i]['login']=$obj->login; - $reparray[$i]['photo']=$obj->photo; + + if (empty($mode)) + { + $reparray[$i]['id']=$obj->rowid; + $reparray[$i]['lastname']=$obj->lastname; + $reparray[$i]['firstname']=$obj->firstname; + $reparray[$i]['email']=$obj->email; + $reparray[$i]['statut']=$obj->statut; + $reparray[$i]['entity']=$obj->entity; + $reparray[$i]['login']=$obj->login; + $reparray[$i]['photo']=$obj->photo; + } + else + { + $reparray[]=$obj->rowid; + } $i++; } return $reparray; @@ -1968,7 +2055,7 @@ class Societe extends CommonObject else if ($option == 'ban') { $label.= '' . $langs->trans("ShowBan") . ''; - $linkstart = 'db); } - $hookmanager->initHooks(array('societedao')); + $hookmanager->initHooks(array('thirdpartydao')); $parameters=array('id'=>$this->id); $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ } $linkstart.=$linkclose.'>'; $linkend=''; @@ -2043,6 +2131,18 @@ class Societe extends CommonObject if ($withpicto != 2) $result.=($maxlen?dol_trunc($name,$maxlen):$name); $result.=$linkend; + global $action; + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('thirdpartydao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + return $result; } @@ -2364,7 +2464,7 @@ class Societe extends CommonObject function get_all_rib() { require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php'; - $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe_rib WHERE fk_soc = ".$this->id; + $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe_rib WHERE type='ban' AND fk_soc = ".$this->id; $result = $this->db->query($sql); if (!$result) { $this->error++; @@ -2596,21 +2696,16 @@ class Societe extends CommonObject if (! empty($conf->global->SOCIETE_CODECOMPTA_ADDON)) { - $file=''; + $res=false; $dirsociete=array_merge(array('/core/modules/societe/'), $conf->modules_parts['societe']); foreach ($dirsociete as $dirroot) { - if (file_exists(DOL_DOCUMENT_ROOT.'/'.$dirroot.$conf->global->SOCIETE_CODECOMPTA_ADDON.".php")) - { - $file=$dirroot.$conf->global->SOCIETE_CODECOMPTA_ADDON.".php"; - break; - } + $res=dol_include_once($dirroot.$conf->global->SOCIETE_CODECOMPTA_ADDON.'.php'); + if ($res) break; } - if (! empty($file)) + if ($res) { - dol_include_once($file); - $classname = $conf->global->SOCIETE_CODECOMPTA_ADDON; $mod = new $classname; @@ -2782,27 +2877,23 @@ class Societe extends CommonObject $chaine=trim($this->idprof1); $chaine=preg_replace('/(\s)/','',$chaine); + if (!is_numeric($chaine)) return -1; if (dol_strlen($chaine) != 9) return -1; - $sum = 0; + // on prend chaque chiffre un par un + // si son index (position dans la chaîne en commence à 0 au premier caractère) est impair + // on double sa valeur et si cette dernière est supérieure à 9, on lui retranche 9 + // on ajoute cette valeur à la somme totale - for ($i = 0 ; $i < 10 ; $i = $i+2) + for ($index = 0; $index < 9; $index ++) { - $sum = $sum + substr($this->idprof1, (8 - $i), 1); + $number = (int) $siren[$index]; + if (($index % 2) != 0) { if (($number *= 2) > 9) $number -= 9; } + $sum += $number; } - for ($i = 1 ; $i < 9 ; $i = $i+2) - { - $ps = 2 * substr($this->idprof1, (8 - $i), 1); - - if ($ps > 9) - { - $ps = substr($ps, 0,1) + substr($ps, 1, 1); - } - $sum = $sum + $ps; - } - - if (substr($sum, -1) != 0) return -1; + // le numéro est valide si la somme des chiffres est multiple de 10 + if (($sum % 10) != 0) return -1; } // Verifie SIRET si pays FR @@ -2811,7 +2902,23 @@ class Societe extends CommonObject $chaine=trim($this->idprof2); $chaine=preg_replace('/(\s)/','',$chaine); + if (!is_numeric($chaine)) return -1; if (dol_strlen($chaine) != 14) return -1; + + // on prend chaque chiffre un par un + // si son index (position dans la chaîne en commence à 0 au premier caractère) est pair + // on double sa valeur et si cette dernière est supérieure à 9, on lui retranche 9 + // on ajoute cette valeur à la somme totale + + for ($index = 0; $index < 14; $index ++) + { + $number = (int) $chaine[$index]; + if (($index % 2) == 0) { if (($number *= 2) > 9) $number -= 9; } + $sum += $number; + } + + // le numéro est valide si la somme des chiffres est multiple de 10 + if (($sum % 10) != 0) return -1; } //Verify CIF/NIF/NIE if pays ES @@ -2831,10 +2938,10 @@ class Societe extends CommonObject //Check NIF if (preg_match('/(^[0-9]{8}[A-Z]{1}$)/', $string)) - if ($num[8] == substr('TRWAGMYFPDXBNJZSQVHLCKE', substr($string, 0, 8) % 23, 1)) - return 1; - else - return -1; + if ($num[8] == substr('TRWAGMYFPDXBNJZSQVHLCKE', substr($string, 0, 8) % 23, 1)) + return 1; + else + return -1; //algorithm checking type code CIF $sum = $num[2] + $num[4] + $num[6]; @@ -2844,36 +2951,56 @@ class Societe extends CommonObject //Chek special NIF if (preg_match('/^[KLM]{1}/', $string)) - if ($num[8] == chr(64 + $n) || $num[8] == substr('TRWAGMYFPDXBNJZSQVHLCKE', substr($string, 1, 8) % 23, 1)) - return 1; - else - return -1; + if ($num[8] == chr(64 + $n) || $num[8] == substr('TRWAGMYFPDXBNJZSQVHLCKE', substr($string, 1, 8) % 23, 1)) + return 1; + else + return -1; //Check CIF if (preg_match('/^[ABCDEFGHJNPQRSUVW]{1}/', $string)) - if ($num[8] == chr(64 + $n) || $num[8] == substr($n, strlen($n) - 1, 1)) - return 2; - else - return -2; + if ($num[8] == chr(64 + $n) || $num[8] == substr($n, strlen($n) - 1, 1)) + return 2; + else + return -2; //Check NIE T if (preg_match('/^[T]{1}/', $string)) - if ($num[8] == preg_match('/^[T]{1}[A-Z0-9]{8}$/', $string)) - return 3; - else - return -3; + if ($num[8] == preg_match('/^[T]{1}[A-Z0-9]{8}$/', $string)) + return 3; + else + return -3; //Check NIE XYZ if (preg_match('/^[XYZ]{1}/', $string)) - if ($num[8] == substr('TRWAGMYFPDXBNJZSQVHLCKE', substr(str_replace(array('X','Y','Z'), array('0','1','2'), $string), 0, 8) % 23, 1)) - return 3; - else - return -3; + if ($num[8] == substr('TRWAGMYFPDXBNJZSQVHLCKE', substr(str_replace(array('X','Y','Z'), array('0','1','2'), $string), 0, 8) % 23, 1)) + return 3; + else + return -3; //Can not be verified return -4; } + //Verify NIF if country is PT + //Returns: 1 if NIF ok, -1 if NIF bad, 0 if unexpected bad + if ($idprof == 1 && $soc->country_code == 'PT') + { + $string=trim($this->idprof1); + $string=preg_replace('/(\s)/','',$string); + + for ($i = 0; $i < 9; $i ++) { + $num[$i] = substr($string, $i, 1); + } + + //Check NIF + if (preg_match('/(^[0-9]{9}$)/', $string)) { + return 1; + } + else { + return -1; + } + } + return $ok; } @@ -2895,20 +3022,33 @@ class Societe extends CommonObject $hookmanager->initHooks(array('idprofurl')); $parameters=array('idprof'=>$idprof, 'company'=>$thirdparty); $reshook=$hookmanager->executeHooks('getIdProfUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks - if (empty($reshook)) - { - if (! empty($conf->global->MAIN_DISABLEPROFIDRULES)) return ''; + if (empty($reshook)) { + if (! empty($conf->global->MAIN_DISABLEPROFIDRULES)) { + return ''; + } // TODO Move links to validate professional ID into a dictionary table "country" + "link" - if ($idprof == 1 && $thirdparty->country_code == 'FR') $url='http://www.societe.com/cgi-bin/search?champs='.$thirdparty->idprof1; // See also http://avis-situation-sirene.insee.fr/ - //if ($idprof == 1 && ($thirdparty->country_code == 'GB' || $thirdparty->country_code == 'UK')) $url='http://www.companieshouse.gov.uk/WebCHeck/findinfolink/'; // Link no more valid - if ($idprof == 1 && $thirdparty->country_code == 'ES') $url='http://www.e-informa.es/servlet/app/portal/ENTP/screen/SProducto/prod/ETIQUETA_EMPRESA/nif/'.$thirdparty->idprof1; - if ($idprof == 1 && $thirdparty->country_code == 'IN') $url='http://www.tinxsys.com/TinxsysInternetWeb/dealerControllerServlet?tinNumber='.$thirdparty->idprof1.';&searchBy=TIN&backPage=searchByTin_Inter.jsp'; + if ($idprof == 1 && $thirdparty->country_code == 'FR') { + $url='http://www.societe.com/cgi-bin/search?champs='.$thirdparty->idprof1; // See also http://avis-situation-sirene.insee.fr/ + } + if ($idprof == 1 && ($thirdparty->country_code == 'GB' || $thirdparty->country_code == 'UK')) { + $url='https://beta.companieshouse.gov.uk/company/'.$thirdparty->idprof1; + } + if ($idprof == 1 && $thirdparty->country_code == 'ES') { + $url='http://www.e-informa.es/servlet/app/portal/ENTP/screen/SProducto/prod/ETIQUETA_EMPRESA/nif/'.$thirdparty->idprof1; + } + if ($idprof == 1 && $thirdparty->country_code == 'IN') { + $url='http://www.tinxsys.com/TinxsysInternetWeb/dealerControllerServlet?tinNumber='.$thirdparty->idprof1.';&searchBy=TIN&backPage=searchByTin_Inter.jsp'; + } + if ($idprof == 1 && $thirdparty->country_code == 'PT') { + $url='http://www.nif.pt/'.$thirdparty->idprof1; + } - if ($url) return ''.$langs->trans("Check").''; + if ($url) { + return ''.$langs->trans("Check").''; + } } - else - { + else { return $hookmanager->resPrint; } @@ -3005,6 +3145,17 @@ class Societe extends CommonObject return $isacompany; } + /** + * Return if a company is inside the EEC (European Economic Community) + * + * @return boolean true = country inside EEC, false = country outside EEC + */ + function isInEEC() + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; + return isInEEC($this); + } + /** * Charge la liste des categories fournisseurs * @@ -3067,6 +3218,8 @@ class Societe extends CommonObject { global $user,$langs; + dol_syslog(get_class($this)."::create_from_member", LOG_DEBUG); + $name = $socname?$socname:$member->societe; if (empty($name)) $name=$member->getFullName($langs); @@ -3099,7 +3252,6 @@ class Societe extends CommonObject $sql.= " SET fk_soc=".$this->id; $sql.= " WHERE rowid=".$member->id; - dol_syslog(get_class($this)."::create_from_member", LOG_DEBUG); $resql=$this->db->query($sql); if ($resql) { @@ -3140,15 +3292,7 @@ class Societe extends CommonObject $this->zip=empty($conf->global->MAIN_INFO_SOCIETE_ZIP)?'':$conf->global->MAIN_INFO_SOCIETE_ZIP; $this->town=empty($conf->global->MAIN_INFO_SOCIETE_TOWN)?'':$conf->global->MAIN_INFO_SOCIETE_TOWN; $this->state_id=empty($conf->global->MAIN_INFO_SOCIETE_STATE)?'':$conf->global->MAIN_INFO_SOCIETE_STATE; - - /* Disabled: we don't want any SQL request into method setMySoc. This method set object from env only. - If we need label, label must be loaded by output that need it from id (label depends on output language) - require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php'; - if (!empty($conf->global->MAIN_INFO_SOCIETE_STATE)) { - $this->state_id= $conf->global->MAIN_INFO_SOCIETE_STATE; - $this->state = getState($this->state_id); - } - */ + $this->region_code=empty($conf->global->MAIN_INFO_SOCIETE_REGION)?'':$conf->global->MAIN_INFO_SOCIETE_REGION; $this->note_private=empty($conf->global->MAIN_INFO_SOCIETE_NOTE)?'':$conf->global->MAIN_INFO_SOCIETE_NOTE; @@ -3767,6 +3911,8 @@ class Societe extends CommonObject $to_add = $categories; } + $error = 0; + // Process foreach ($to_del as $del) { if ($c->fetch($del) > 0) { @@ -3774,12 +3920,67 @@ class Societe extends CommonObject } } foreach ($to_add as $add) { - if ($c->fetch($add) > 0) { - $c->add_type($this, $type_text); + if ($c->fetch($add) > 0) + { + $result = $c->add_type($this, $type_text); + if ($result < 0) + { + $error++; + $this->error = $c->error; + $this->errors = $c->errors; + break; + } } } - return 1; + return $error ? -1 : 1; + } + + /** + * Sets sales representatives of the thirdparty + * + * @param int[]|int $salesrep User ID or array of user IDs + * @return int <0 if KO, >0 if OK + */ + public function setSalesRep($salesrep) + { + global $user; + + // Handle single user + if (!is_array($salesrep)) { + $salesrep = array($salesrep); + } + + // Get current users + $existing = $this->getSalesRepresentatives($user, 1); + + // Diff + if (is_array($existing)) { + $to_del = array_diff($existing, $salesrep); + $to_add = array_diff($salesrep, $existing); + } else { + $to_del = array(); // Nothing to delete + $to_add = $salesrep; + } + + $error = 0; + + // Process + foreach ($to_del as $del) { + $this->del_commercial($user, $del); + } + foreach ($to_add as $add) { + $result = $this->add_commercial($user, $add); + if ($result < 0) + { + $error++; + $this->error = $c->error; + $this->errors = $c->errors; + break; + } + } + + return $error ? -1 : 1; } diff --git a/htdocs/website/class/websiteaccount.class.php b/htdocs/societe/class/societeaccount.class.php similarity index 85% rename from htdocs/website/class/websiteaccount.class.php rename to htdocs/societe/class/societeaccount.class.php index f24847a4601..6b2199925be 100644 --- a/htdocs/website/class/websiteaccount.class.php +++ b/htdocs/societe/class/societeaccount.class.php @@ -20,9 +20,9 @@ */ /** - * \file class/websiteaccount.class.php - * \ingroup website - * \brief This file is a CRUD class file for WebsiteAccount (Create/Read/Update/Delete) + * \file class/societeaccount.class.php + * \ingroup societe + * \brief This file is a CRUD class file for SocieteAccount (Create/Read/Update/Delete) */ // Put here all includes required by your class file @@ -31,24 +31,24 @@ require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php'; //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; /** - * Class for WebsiteAccount + * Class for SocieteAccount */ -class WebsiteAccount extends CommonObject +class SocieteAccount extends CommonObject { /** * @var string ID to identify managed object */ - public $element = 'websiteaccount'; + public $element = 'societeaccount'; /** * @var string Name of table without prefix where object is stored */ - public $table_element = 'website_account'; + public $table_element = 'societe_account'; /** - * @var array Does websiteaccount support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + * @var array Does societeaccount support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe */ public $ismultientitymanaged = 0; /** - * @var string String with name of icon for websiteaccount. Must be the part after the 'object_' into object_myobject.png + * @var string String with name of icon for societeaccount. Must be the part after the 'object_' into object_myobject.png */ public $picto = 'lock'; @@ -76,12 +76,15 @@ class WebsiteAccount extends CommonObject */ public $fields=array( 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>'Id',), - 'login' => array('type'=>'varchar(64)', 'label'=>'Login', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>'Login',), + 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'default'=>1), + 'key_account' => array('type'=>'varchar(128)', 'label'=>'KeyAccount', 'visible'=>-1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>'Key account',), + 'login' => array('type'=>'varchar(64)', 'label'=>'Login', 'visible'=>1, 'enabled'=>1, 'position'=>10), 'pass_encoding' => array('type'=>'varchar(24)', 'label'=>'PassEncoding', 'visible'=>0, 'enabled'=>1, 'position'=>30), 'pass_crypted' => array('type'=>'varchar(128)', 'label'=>'Password', 'visible'=>1, 'enabled'=>1, 'position'=>31, 'notnull'=>1), 'pass_temp' => array('type'=>'varchar(128)', 'label'=>'Temp', 'visible'=>0, 'enabled'=>0, 'position'=>32, 'notnull'=>-1,), 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>1, 'position'=>40, 'notnull'=>-1, 'index'=>1), - 'fk_website' => array('type'=>'integer:Website:website/class/website.class.php', 'label'=>'WebSite', 'visible'=>1, 'enabled'=>1, 'position'=>41, 'notnull'=>1, 'index'=>1), + 'site' => array('type'=>'varchar(128)', 'label'=>'Site', 'visible'=>-1, 'enabled'=>1, 'position'=>41), + 'fk_website' => array('type'=>'integer:Website:website/class/website.class.php', 'label'=>'WebSite', 'visible'=>1, 'enabled'=>1, 'position'=>42, 'notnull'=>1, 'index'=>1), 'date_last_login' => array('type'=>'datetime', 'label'=>'LastConnexion', 'visible'=>2, 'enabled'=>1, 'position'=>50, 'notnull'=>0,), 'date_previous_login' => array('type'=>'datetime', 'label'=>'PreviousConnexion', 'visible'=>2, 'enabled'=>1, 'position'=>51, 'notnull'=>0,), //'note_public' => array('type'=>'text', 'label'=>'NotePublic', 'visible'=>-1, 'enabled'=>1, 'position'=>45, 'notnull'=>-1,), @@ -94,9 +97,13 @@ class WebsiteAccount extends CommonObject 'status' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>1000, 'notnull'=>1, 'index'=>1, 'default'=>1, 'arrayofkeyval'=>array('1'=>'Active','0'=>'Disabled')), ); public $rowid; - public $login; + public $entity; + public $key_account; public $pass_encoding; public $pass_crypted; + public $pass_temp; + public $fk_soc; + public $site; public $date_last_login; public $date_previous_login; public $note_private; @@ -106,7 +113,6 @@ class WebsiteAccount extends CommonObject public $fk_user_modif; public $import_key; public $status; - public $fk_soc; // END MODULEBUILDER PROPERTIES @@ -117,21 +123,21 @@ class WebsiteAccount extends CommonObject /** * @var int Name of subtable line */ - //public $table_element_line = 'website_accountdet'; + //public $table_element_line = 'societe_accountdet'; /** * @var int Field with ID of parent key if this field has a parent */ - //public $fk_element = 'fk_website_account'; + //public $fk_element = 'fk_societe_account'; /** * @var int Name of subtable class that manage subtable lines */ - //public $class_element_line = 'WebsiteAccountline'; + //public $class_element_line = 'societeAccountline'; /** * @var array Array of child tables (child tables to delete before deleting a record) */ - //protected $childtables=array('website_accountdet'); + //protected $childtables=array('societe_accountdet'); /** - * @var WebsiteAccountLine[] Array of subtable lines + * @var societeAccountLine[] Array of subtable lines */ //public $lines = array(); @@ -235,11 +241,47 @@ class WebsiteAccount extends CommonObject { $this->lines=array(); - // Load lines with object WebsiteAccountLine + // Load lines with object societeAccountLine return count($this->lines)?1:0; } + /** + * Try to find the external customer id of a thirdparty for an another site/system. + * + * @param int $id Id of third party + * @param string $site Site (example: 'stripe', '...') + * @param int $status Status (0=test, 1=live) + * @return string Stripe customer ref 'cu_xxxxxxxxxxxxx' or '' + */ + public function getCustomerAccount($id, $site, $status=0) + { + global $conf; + + $sql = "SELECT sa.key_account as key_account, sa.entity"; + $sql.= " FROM " . MAIN_DB_PREFIX . "societe_account as sa"; + $sql.= " WHERE sa.fk_soc = " . $id; + $sql.= " AND sa.entity IN (".getEntity('societe').")"; + $sql.= " AND sa.site = '".$this->db->escape($site)."' AND sa.status = ".((int) $status); + $sql.= " AND key_account IS NOT NULL AND key_account <> ''"; + //$sql.= " ORDER BY sa.key_account DESC"; + + dol_syslog(get_class($this) . "::getCustomerAccount Try to find the system customer id of thirdparty id=".$id." (exemple: cus_.... for stripe)", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + if ($this->db->num_rows($result)) { + $obj = $this->db->fetch_object($result); + $key = $obj->key_account; + } else { + $key = ''; + } + } else { + $key = ''; + } + + return $key; + } + /** * Update object into database * @@ -287,12 +329,12 @@ class WebsiteAccount extends CommonObject $this->ref = $this->login; - $label = '' . $langs->trans("WebsiteAccount") . ''; + $label = '' . $langs->trans("SocieteAccount") . ''; $label.= '
    '; $label.= '' . $langs->trans('Login') . ': ' . $this->ref; //$label.= '' . $langs->trans('WebSite') . ': ' . $this->ref; - $url = dol_buildpath('/website/websiteaccount_card.php',1).'?id='.$this->id; + $url = dol_buildpath('/societe/societeaccount_card.php',1).'?id='.$this->id; if ($option != 'nolink') { @@ -307,7 +349,7 @@ class WebsiteAccount extends CommonObject { if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { - $label=$langs->trans("ShowWebsiteAccount"); + $label=$langs->trans("ShowsocieteAccount"); $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; } $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; @@ -462,40 +504,4 @@ class WebsiteAccount extends CommonObject { $this->initAsSpecimenCommon(); } - - - /** - * Action executed by scheduler - * CAN BE A CRON TASK - * - * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) - */ - public function doScheduledJob() - { - global $conf, $langs; - - $this->output = ''; - $this->error=''; - - dol_syslog(__METHOD__, LOG_DEBUG); - - // ... - - return 0; - } } - -/** - * Class WebsiteAccountLine. You can also remove this and generate a CRUD class for lines objects. - */ -/* -class WebsiteAccountLine -{ - // @var int ID - public $id; - // @var mixed Sample line property 1 - public $prop1; - // @var mixed Sample line property 2 - public $prop2; -} -*/ \ No newline at end of file diff --git a/htdocs/societe/commerciaux.php b/htdocs/societe/commerciaux.php deleted file mode 100644 index d6add5eacd5..00000000000 --- a/htdocs/societe/commerciaux.php +++ /dev/null @@ -1,300 +0,0 @@ - - * Copyright (C) 2010 Laurent Destailleur - * Copyright (C) 2010-2011 Regis Houssin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * \file htdocs/societe/commerciaux.php - * \ingroup societe - * \brief Page of links to sales representatives - */ - -require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; - -$langs->load("companies"); -$langs->load("commercial"); -$langs->load("customers"); -$langs->load("suppliers"); -$langs->load("banks"); - -// Security check -$socid = GETPOST('socid', 'int'); -if ($user->societe_id) $socid=$user->societe_id; -$result = restrictedArea($user, 'societe','',''); - -$hookmanager->initHooks(array('salesrepresentativescard','globalcard')); - -/* - * Actions - */ - -if (! empty($socid) && $_GET["commid"]) -{ - $action = 'add'; - - if ($user->rights->societe->creer) - { - $object = new Societe($db); - $object->fetch($socid); - - $parameters=array('id'=>$_GET["commid"]); - $reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks - if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); - - if (empty($reshook)) $object->add_commercial($user, $_GET["commid"]); - - header("Location: ".$_SERVER["PHP_SELF"]."?socid=".$object->id); - exit; - } - else - { - header("Location: ".$_SERVER["PHP_SELF"]."?socid=".$socid); - exit; - } -} - -if (! empty($socid) && $_GET["delcommid"]) -{ - $action = 'delete'; - - if ($user->rights->societe->creer) - { - $object = new Societe($db); - $object->fetch($socid); - - $parameters=array('id'=>$_GET["delcommid"]); - $reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks - if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); - - if (empty($reshook)) $object->del_commercial($user, $_GET["delcommid"]); - - header("Location: commerciaux.php?socid=".$object->id); - exit; - } - else - { - header("Location: ".$_SERVER["PHP_SELF"]."?socid=".$socid); - exit; - } -} - - -/* - * View - */ - -$help_url='EN:Module_Third_Parties|FR:Module_Tiers|ES:Empresas'; -llxHeader('',$langs->trans("ThirdParty"),$help_url); - -$form = new Form($db); - -if (! empty($socid)) -{ - $object = new Societe($db); - $result=$object->fetch($socid); - - $action='view'; - - $head=societe_prepare_head2($object); - - dol_fiche_head($head, 'salesrepresentative', $langs->trans("ThirdParty"), -1, 'company'); - - $linkback = ''.$langs->trans("BackToList").''; - - dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); - - print '
    '; - - print '
    '; - print ''; - - print ''; - print 'global->SOCIETE_USEPREFIX)?' colspan="3"':'').'>'; - print $object->code_client; - if ($object->check_codeclient() <> 0) print ' '.$langs->trans("WrongCustomerCode"); - print ''; - if (! empty($conf->global->SOCIETE_USEPREFIX)) // Old not used prefix field - { - print ''; - } - print ''; - print ''; - - // Liste les commerciaux - print ''; - print '"; - - print '
    '.$langs->trans('CustomerCode').''.$langs->trans('Prefix').''.$object->prefix_comm.'
    '.$langs->trans("SalesRepresentatives").''; - - $sql = "SELECT DISTINCT u.rowid, u.login, u.fk_soc, u.lastname, u.firstname, u.statut, u.entity, u.photo"; - $sql .= " FROM ".MAIN_DB_PREFIX."user as u"; - $sql .= " , ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - if (! empty($conf->multicompany->enabled) && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) - { - $sql.= ", ".MAIN_DB_PREFIX."usergroup_user as ug"; - } - $sql .= " WHERE sc.fk_soc =".$object->id; - $sql .= " AND sc.fk_user = u.rowid"; - if (! empty($conf->multicompany->enabled) && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) - { - $sql.= " AND ((ug.fk_user = sc.fk_user"; - $sql.= " AND ug.entity = ".$conf->entity.")"; - $sql.= " OR u.admin = 1)"; - } - else - $sql.= " AND u.entity IN (0,".$conf->entity.")"; - - $sql .= " ORDER BY u.lastname ASC "; - - dol_syslog('societe/commerciaux.php::list salesman sql = '.$sql,LOG_DEBUG); - $resql = $db->query($sql); - if ($resql) - { - $num = $db->num_rows($resql); - $i = 0; - - $tmpuser = new User($db); - - while ($i < $num) - { - $obj = $db->fetch_object($resql); - - $parameters=array('socid'=>$object->id); - $reshook=$hookmanager->executeHooks('formObjectOptions',$parameters,$obj,$action); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; - if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); - - $tmpuser->id = $obj->rowid; - $tmpuser->firstname = $obj->firstname; - $tmpuser->lastname = $obj->lastname; - $tmpuser->statut = $obj->statut; - $tmpuser->login = $obj->login; - $tmpuser->entity = $obj->entity; - $tmpuser->societe_id = $obj->fk_soc; - $tmpuser->photo = $obj->photo; - print $tmpuser->getNomUrl(-1); - - /*print ''; - print img_object($langs->trans("ShowUser"),"user").' '; - print dolGetFirstLastname($obj->firstname, $obj->lastname)."\n"; - print '';*/ - print ' '; - if ($user->rights->societe->creer) - { - print ''; - print img_delete(); - print ''; - } - print '
    '; - $i++; - } - - $db->free($resql); - } - else - { - dol_print_error($db); - } - if($i == 0) { print $langs->trans("NoSalesRepresentativeAffected"); } - - print "
    '; - print "
    \n"; - - dol_fiche_end(); - - - if ($user->rights->societe->creer && $user->rights->societe->client->voir) - { - /* - * Liste - * - */ - - $langs->load("users"); - $title=$langs->trans("ListOfUsers"); - - $sql = "SELECT DISTINCT u.rowid, u.lastname, u.firstname, u.login, u.email, u.statut, u.fk_soc, u.photo"; - $sql.= " FROM ".MAIN_DB_PREFIX."user as u"; - if (! empty($conf->multicompany->enabled) && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) - { - $sql.= ", ".MAIN_DB_PREFIX."usergroup_user as ug"; - $sql.= " WHERE ((ug.fk_user = u.rowid"; - $sql.= " AND ug.entity = ".$conf->entity.")"; - $sql.= " OR u.admin = 1)"; - } - else - $sql.= " WHERE u.entity IN (0,".$conf->entity.")"; - if (! empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX)) $sql.= " AND u.statut<>0 "; - $sql.= " ORDER BY u.lastname ASC "; - - $resql = $db->query($sql); - if ($resql) - { - $num = $db->num_rows($resql); - $i = 0; - - print load_fiche_titre($title); - - // Lignes des titres - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print "\n"; - - $var=True; - $tmpuser=new User($db); - - while ($i < $num) - { - $obj = $db->fetch_object($resql); - - print "'; - print ''; - print ''; - print ''; - - print ''."\n"; - $i++; - } - - print "
    '.$langs->trans("Name").''.$langs->trans("Login").''.$langs->trans("Status").' 
    "; - $tmpuser->id=$obj->rowid; - $tmpuser->firstname=$obj->firstname; - $tmpuser->lastname=$obj->lastname; - $tmpuser->statut=$obj->statut; - $tmpuser->login=$obj->login; - $tmpuser->email=$obj->email; - $tmpuser->societe_id=$obj->fk_soc; - $tmpuser->photo=$obj->photo; - print $tmpuser->getNomUrl(-1); - print ''.$obj->login.''.$tmpuser->getLibStatut(2).''.$langs->trans("Add").'
    "; - $db->free($resql); - } - else - { - dol_print_error($db); - } - } - -} - -llxFooter(); -$db->close(); diff --git a/htdocs/societe/consumption.php b/htdocs/societe/consumption.php index ffb4cac77fa..1a6a4be6199 100644 --- a/htdocs/societe/consumption.php +++ b/htdocs/societe/consumption.php @@ -491,6 +491,7 @@ if ($sql_select) print img_object($langs->trans("ShowReduc"),'reduc').' '; if ($objp->description == '(DEPOSIT)') $txt=$langs->trans("Deposit"); elseif ($objp->description == '(EXCESS RECEIVED)') $txt=$langs->trans("ExcessReceived"); + elseif ($objp->description == '(EXCESS PAID)') $txt=$langs->trans("ExcessPaid"); //else $txt=$langs->trans("Discount"); print $txt; ?> @@ -510,6 +511,12 @@ if ($sql_select) $discount->fetch($objp->fk_remise_except); echo ($txt?' - ':'').$langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0)); } + elseif ($objp->description == '(EXCESS PAID)' && $objp->fk_remise_except > 0) + { + $discount=new DiscountAbsolute($db); + $discount->fetch($objp->fk_remise_except); + echo ($txt?' - ':'').$langs->transnoentities("DiscountFromExcessPaid",$discount->getNomUrl(0)); + } elseif ($objp->description == '(DEPOSIT)' && $objp->fk_remise_except > 0) { $discount=new DiscountAbsolute($db); diff --git a/htdocs/societe/contact.php b/htdocs/societe/contact.php index 32575beb8c7..297ff3e8a56 100644 --- a/htdocs/societe/contact.php +++ b/htdocs/societe/contact.php @@ -66,7 +66,7 @@ $extrafields = new ExtraFields($db); $extralabels=$extrafields->fetch_name_optionals_label($object->table_element); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('thirdpartycard','globalcard')); +$hookmanager->initHooks(array('thirdpartycontact','globalcard')); if ($action == 'view' && $object->fetch($socid)<=0) { diff --git a/htdocs/societe/document.php b/htdocs/societe/document.php index be549d6cac5..2cab1da971f 100644 --- a/htdocs/societe/document.php +++ b/htdocs/societe/document.php @@ -69,6 +69,10 @@ if ($id > 0 || ! empty($ref)) $courrier_dir = $conf->societe->multidir_output[$object->entity] . "/courrier/" . get_exdir($object->id,0,0,0,$object,'thirdparty'); } +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('thirdpartydocument','globalcard')); + + /* * Actions diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index 47852c71403..448f526df4a 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -9,6 +9,7 @@ * Copyright (C) 2016 Ferran Marcet * Copyright (C) 2017 Rui Strecht * Copyright (C) 2017 Juanjo Menent + * Copyright (C) 2018 Nicolas ZABOURI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +45,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'thirdpartylist'; // Security check $socid = GETPOST('socid','int'); @@ -105,18 +107,13 @@ $offset = $limit * $page; $pageprev = $page - 1; $pagenext = $page + 1; -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage='thirdpartylist'; -/*if ($search_type == '1,3') { $contextpage='customerlist'; $type='c'; } -if ($search_type == '2,3') { $contextpage='prospectlist'; $type='p'; } -if ($search_type == '4') { $contextpage='supplierlist'; $type='f'; } -*/ if ($type == 'c') { $contextpage='customerlist'; if ($search_type=='') $search_type='1,3'; } if ($type == 'p') { $contextpage='prospectlist'; if ($search_type=='') $search_type='2,3'; } if ($type == 'f') { $contextpage='supplierlist'; if ($search_type=='') $search_type='4'; } // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new Societe($db); +$hookmanager->initHooks(array('thirdpartylist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -137,6 +134,7 @@ $fieldstosearchall = array( 's.siren'=>"ProfId1", 's.siret'=>"ProfId2", 's.ape'=>"ProfId3", + 's.phone'=>"Phone", ); if (($tmp = $langs->transnoentities("ProfId4".$mysoc->country_code)) && $tmp != "ProfId4".$mysoc->country_code && $tmp != '-') $fieldstosearchall['s.idprof4']='ProfId4'; if (($tmp = $langs->transnoentities("ProfId5".$mysoc->country_code)) && $tmp != "ProfId5".$mysoc->country_code && $tmp != '-') $fieldstosearchall['s.idprof5']='ProfId5'; @@ -397,6 +395,7 @@ $sql.= " s.email, s.phone, s.url, s.siren as idprof1, s.siret as idprof2, s.ape $sql.= " s.tms as date_update, s.datec as date_creation,"; $sql.= " s.code_compta,s.code_compta_fournisseur,"; $sql.= " typent.code as typent_code,"; +$sql.= " country.code as country_code,"; $sql.= " state.code_departement as state_code, state.nom as state_name,"; $sql.= " region.code_region as region_code, region.nom as region_name"; // We'll need these fields in order to filter by sale (including the case where the user can only see his prospects) @@ -570,6 +569,23 @@ if ($user->rights->societe->supprimer) $arrayofmassactions['predelete']=$langs-> if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); +$newcardbutton=''; +if ($user->rights->societe->creer) +{ + $typefilter=''; + $label='MenuNewThirdParty'; + + if(! empty($type)) + { + $typefilter = '&type='.$type; + if($type == 'p') $label='MenuNewProspect'; + if($type == 'c') $label='MenuNewCustomer'; + if($type == 'f') $label='NewSupplier'; + } + + $newcardbutton = ''.$langs->trans($label).''; +} + print '
    '; if ($optioncss != '') print ''; print ''; @@ -578,7 +594,7 @@ print ''; print ''; print ''; -print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, '', '', $limit); +print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, $newcardbutton, '', $limit); $langs->load("other"); $textprofid=array(); @@ -998,7 +1014,7 @@ while ($i < min($num, $limit)) $savalias = $obj->name_alias; if (! empty($arrayfields['s.name_alias']['checked'])) $companystatic->name_alias=''; print ''; - print $companystatic->getNomUrl(1,'',100); + print $companystatic->getNomUrl(1, '', 100, 0, 1); print "\n"; $companystatic->name_alias = $savalias; if (! $i) $totalarray['nbfield']++; @@ -1089,7 +1105,7 @@ while ($i < min($num, $limit)) } if (! empty($arrayfields['s.phone']['checked'])) { - print "".$obj->phone."\n"; + print "".dol_print_phone($obj->phone, $obj->country_code, 0, $obj->rowid)."\n"; if (! $i) $totalarray['nbfield']++; } if (! empty($arrayfields['s.url']['checked'])) diff --git a/htdocs/societe/note.php b/htdocs/societe/note.php index d0766bc24d8..e31a07e8e65 100644 --- a/htdocs/societe/note.php +++ b/htdocs/societe/note.php @@ -43,6 +43,9 @@ if ($id > 0) $object->fetch($id); $permissionnote=$user->rights->societe->creer; // Used by the include of actions_setnotes.inc.php +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('thirdpartynote','globalcard')); + /* * Actions diff --git a/htdocs/societe/paymentmodes.php b/htdocs/societe/paymentmodes.php new file mode 100644 index 00000000000..a63d55c5e8f --- /dev/null +++ b/htdocs/societe/paymentmodes.php @@ -0,0 +1,1598 @@ + + * Copyright (C) 2003 Jean-Louis Bergamo + * Copyright (C) 2004-2018 Laurent Destailleur + * Copyright (C) 2005-2009 Regis Houssin + * Copyright (C) 2013 Peter Fontaine + * Copyright (C) 2015-2016 Marcos García + * Copyright (C) 2017 Ferran Marcet + * Copyright (C) 2018 ptibogxiv + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/societe/paymentmodes.php + * \ingroup societe + * \brief Tab of payment modes for the customer + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/companypaymentmode.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societeaccount.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/prelevement/class/bonprelevement.class.php'; +require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; + +$langs->loadLangs(array("companies","commercial","banks","bills",'paypal','stripe')); + + +// Security check +$socid = GETPOST("socid","int"); +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'societe','',''); + +$id=GETPOST("id","int"); +$source=GETPOST("source","alpha"); +$ribid=GETPOST("ribid","int"); +$action=GETPOST("action", 'alpha', 3); +$cancel=GETPOST('cancel', 'alpha'); + +$object = new Societe($db); +$object->fetch($socid); + +$companybankaccount = new CompanyBankAccount($db); +$companypaymentmode = new CompanyPaymentMode($db); +$prelevement = new BonPrelevement($db); + +$extrafields = new ExtraFields($db); + +// fetch optionals attributes and labels +$extralabels=$extrafields->fetch_name_optionals_label($object->table_element); + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('thirdpartybancard','globalcard')); + + +if (! empty($conf->stripe->enabled)) +{ + $service = 'StripeTest'; + $servicestatus = 0; + if (! empty($conf->global->STRIPE_LIVE) && ! GETPOST('forcesandbox','alpha')) + { + $service = 'StripeLive'; + $servicestatus = 1; + } + + $stripe = new Stripe($db); + $stripeacc = $stripe->getStripeAccount($service); // Get Stripe OAuth connect account (no network access here) + $stripecu = $stripe->getStripeCustomerAccount($object->id, $servicestatus); // Get remote Stripe customer 'cus_...' (no network access here) +} + + + +/* + * Actions + */ + +if ($cancel) +{ + $action=''; +} + +$parameters=array('id'=>$socid, 'objcanvas'=>$objcanvas); +$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +if (empty($reshook)) +{ + if ($cancel) + { + $action=''; + if (! empty($backtopage)) + { + header("Location: ".$backtopage); + exit; + } + } + + if ($action == 'update') + { + // Modification + if (! GETPOST('label','alpha') || ! GETPOST('bank','alpha')) + { + if (! GETPOST('label','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors'); + if (! GETPOST('bank','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BankName")), null, 'errors'); + $action='edit'; + $error++; + } + if ($companybankaccount->needIBAN() == 1) + { + if (! GETPOST('iban')) + { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("IBAN")), null, 'errors'); + $action='edit'; + $error++; + } + if (! GETPOST('bic')) + { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BIC")), null, 'errors'); + $action='edit'; + $error++; + } + } + + $companybankaccount->fetch($id); + if (! $error) + { + $companybankaccount->socid = $object->id; + + $companybankaccount->bank = GETPOST('bank','alpha'); + $companybankaccount->label = GETPOST('label','alpha'); + $companybankaccount->courant = GETPOST('courant','alpha'); + $companybankaccount->clos = GETPOST('clos','alpha'); + $companybankaccount->code_banque = GETPOST('code_banque','alpha'); + $companybankaccount->code_guichet = GETPOST('code_guichet','alpha'); + $companybankaccount->number = GETPOST('number','alpha'); + $companybankaccount->cle_rib = GETPOST('cle_rib','alpha'); + $companybankaccount->bic = GETPOST('bic','alpha'); + $companybankaccount->iban = GETPOST('iban','alpha'); + $companybankaccount->domiciliation = GETPOST('domiciliation','alpha'); + $companybankaccount->proprio = GETPOST('proprio','alpha'); + $companybankaccount->owner_address = GETPOST('owner_address','alpha'); + $companybankaccount->frstrecur = GETPOST('frstrecur','alpha'); + $companybankaccount->rum = GETPOST('rum','alpha'); + if (empty($companybankaccount->rum)) + { + $companybankaccount->rum = $prelevement->buildRumNumber($object->code_client, $companybankaccount->datec, $companybankaccount->id); + $companybankaccount->date_rum = dol_now(); + } + + $result = $companybankaccount->update($user); + if (! $result) + { + setEventMessages($companybankaccount->error, $companybankaccount->errors, 'errors'); + } + else + { + // If this account is the default bank account, we disable others + if ($companybankaccount->default_rib) + { + $companybankaccount->setAsDefault($id); // This will make sure there is only one default rib + } + + $url=$_SERVER["PHP_SELF"].'?socid='.$object->id; + header('Location: '.$url); + exit; + } + } + } + + if ($action == 'updatecard') + { + // Modification + if (! GETPOST('label','alpha') || ! GETPOST('proprio','alpha') || ! GETPOST('cardnumber','alpha') || ! GETPOST('exp_date_month','alpha') || ! GETPOST('exp_date_year','alpha') || ! GETPOST('cvn','alpha')) + { + if (! GETPOST('label','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors'); + if (! GETPOST('proprio','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("NameOnCard")), null, 'errors'); + if (! GETPOST('cardnumber','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CardNumber")), null, 'errors'); + if (! (GETPOST('exp_date_month','alpha') > 0) || ! (GETPOST('exp_date_year','alpha') > 0)) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ExpiryDate")), null, 'errors'); + if (! GETPOST('cvn','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CVN")), null, 'errors'); + $action='createcard'; + $error++; + } + + $companypaymentmode->fetch($id); + if (! $error) + { + $companypaymentmode->fk_soc = $object->id; + + $companypaymentmode->bank = GETPOST('bank','alpha'); + $companypaymentmode->label = GETPOST('label','alpha'); + $companypaymentmode->number = GETPOST('cardnumber','alpha'); + $companypaymentmode->last_four = substr(GETPOST('cardnumber','alpha'), -4); + $companypaymentmode->proprio = GETPOST('proprio','alpha'); + $companypaymentmode->exp_date_month = GETPOST('exp_date_month','int'); + $companypaymentmode->exp_date_year = GETPOST('exp_date_year','int'); + $companypaymentmode->cvn = GETPOST('cvn','alpha'); + $companypaymentmode->country_code = $object->country_code; + + $companypaymentmode->stripe_card_ref = GETPOST('stripe_card_ref','alpha'); + + $result = $companypaymentmode->update($user); + if (! $result) + { + setEventMessages($companypaymentmode->error, $companypaymentmode->errors, 'errors'); + } + else + { + // If this account is the default bank account, we disable others + if ($companypaymentmode->default_rib) + { + $companypaymentmode->setAsDefault($id); // This will make sure there is only one default rib + } + + $url=$_SERVER["PHP_SELF"].'?socid='.$object->id; + header('Location: '.$url); + exit; + } + } + } + + if ($action == 'add') + { + $error=0; + + if (! GETPOST('label','alpha') || ! GETPOST('bank','alpha')) + { + if (! GETPOST('label','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors'); + if (! GETPOST('bank','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BankName")), null, 'errors'); + $action='create'; + $error++; + } + + if (! $error) + { + // Ajout + $companybankaccount = new CompanyBankAccount($db); + + $companybankaccount->socid = $object->id; + + $companybankaccount->bank = GETPOST('bank','alpha'); + $companybankaccount->label = GETPOST('label','alpha'); + $companybankaccount->courant = GETPOST('courant','alpha'); + $companybankaccount->clos = GETPOST('clos','alpha'); + $companybankaccount->code_banque = GETPOST('code_banque','alpha'); + $companybankaccount->code_guichet = GETPOST('code_guichet','alpha'); + $companybankaccount->number = GETPOST('number','alpha'); + $companybankaccount->cle_rib = GETPOST('cle_rib','alpha'); + $companybankaccount->bic = GETPOST('bic','alpha'); + $companybankaccount->iban = GETPOST('iban','alpha'); + $companybankaccount->domiciliation = GETPOST('domiciliation','alpha'); + $companybankaccount->proprio = GETPOST('proprio','alpha'); + $companybankaccount->owner_address = GETPOST('owner_address','alpha'); + $companybankaccount->frstrecur = GETPOST('frstrecur'); + $companybankaccount->rum = GETPOST('rum','alpha'); + $companybankaccount->datec = dol_now(); + $companybankaccount->status = 1; + + $db->begin(); + + // This test can be done only once properties were set + if ($companybankaccount->needIBAN() == 1) + { + if (! GETPOST('iban')) + { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("IBAN")), null, 'errors'); + $action='create'; + $error++; + } + if (! GETPOST('bic')) + { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BIC")), null, 'errors'); + $action='create'; + $error++; + } + } + + if (! $error) + { + $result = $companybankaccount->create($user); + if ($result < 0) + { + $error++; + setEventMessages($companybankaccount->error, $companybankaccount->errors, 'errors'); + $action='create'; // Force chargement page création + } + + if (empty($companybankaccount->rum)) + { + $companybankaccount->rum = $prelevement->buildRumNumber($object->code_client, $companybankaccount->datec, $companybankaccount->id); + $companybankaccount->date_rum = dol_now(); + } + } + + if (! $error) + { + $result = $companybankaccount->update($user); // This will set the UMR number. + if ($result < 0) + { + $error++; + setEventMessages($companybankaccount->error, $companybankaccount->errors, 'errors'); + $action='create'; + } + } + + if (! $error) + { + $db->commit(); + + $url=$_SERVER["PHP_SELF"].'?socid='.$object->id; + header('Location: '.$url); + exit; + } + else + { + $db->rollback(); + } + } + } + + if ($action == 'addcard') + { + $error=0; + + if (! GETPOST('label','alpha') || ! GETPOST('proprio','alpha') || ! GETPOST('cardnumber','alpha') || ! GETPOST('exp_date_month','alpha') || ! GETPOST('exp_date_year','alpha') || ! GETPOST('cvn','alpha')) + { + if (! GETPOST('label','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors'); + if (! GETPOST('proprio','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("NameOnCard")), null, 'errors'); + if (! GETPOST('cardnumber','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CardNumber")), null, 'errors'); + if (! (GETPOST('exp_date_month','alpha') > 0) || ! (GETPOST('exp_date_year','alpha') > 0)) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ExpiryDate")), null, 'errors'); + if (! GETPOST('cvn','alpha')) setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CVN")), null, 'errors'); + $action='createcard'; + $error++; + } + + if (! $error) + { + // Ajout + $companypaymentmode = new CompanyPaymentMode($db); + + $companypaymentmode->fk_soc = $object->id; + $companypaymentmode->bank = GETPOST('bank','alpha'); + $companypaymentmode->label = GETPOST('label','alpha'); + $companypaymentmode->number = GETPOST('cardnumber','alpha'); + $companypaymentmode->last_four = substr(GETPOST('cardnumber','alpha'), -4); + $companypaymentmode->proprio = GETPOST('proprio','alpha'); + $companypaymentmode->exp_date_month = GETPOST('exp_date_month','int'); + $companypaymentmode->exp_date_year = GETPOST('exp_date_year','int'); + $companypaymentmode->cvn = GETPOST('cvn','alpha'); + $companypaymentmode->datec = dol_now(); + $companypaymentmode->default_rib = 0; + $companypaymentmode->type = 'card'; + $companypaymentmode->country_code = $object->country_code; + $companypaymentmode->status = $servicestatus; + + $companypaymentmode->stripe_card_ref = GETPOST('stripe_card_ref','alpha'); + + $db->begin(); + + if (! $error) + { + $result = $companypaymentmode->create($user); + if ($result < 0) + { + $error++; + setEventMessages($companypaymentmode->error, $companypaymentmode->errors, 'errors'); + $action='createcard'; // Force chargement page création + } + } + + if (! $error) + { + $db->commit(); + + $url=$_SERVER["PHP_SELF"].'?socid='.$object->id; + header('Location: '.$url); + exit; + } + else + { + $db->rollback(); + } + } + } + + if ($action == 'setasbankdefault' && GETPOST('ribid','int') > 0) + { + $companybankaccount = new CompanyBankAccount($db); + $res = $companybankaccount->setAsDefault(GETPOST('ribid','int')); + if ($res) + { + $url=DOL_URL_ROOT.'/societe/paymentmodes.php?socid='.$object->id; + header('Location: '.$url); + exit; + } + else + { + setEventMessages($db->lasterror, null, 'errors'); + } + } + + if ($action == 'confirm_deletecard' && GETPOST('confirm','alpha') == 'yes') + { + $companypaymentmode = new CompanyPaymentMode($db); + if ($companypaymentmode->fetch($ribid?$ribid:$id)) + { + $result = $companypaymentmode->delete($user); + if ($result > 0) + { + $url = $_SERVER['PHP_SELF']."?socid=".$object->id; + header('Location: '.$url); + exit; + } + else + { + setEventMessages($companypaymentmode->error, $companypaymentmode->errors, 'errors'); + } + } + else + { + setEventMessages($companypaymentmode->error, $companypaymentmode->errors, 'errors'); + } + } + if ($action == 'confirm_delete' && GETPOST('confirm','alpha') == 'yes') + { + $companybankaccount = new CompanyBankAccount($db); + if ($companybankaccount->fetch($ribid?$ribid:$id)) + { + $result = $companybankaccount->delete($user); + if ($result > 0) + { + $url = $_SERVER['PHP_SELF']."?socid=".$object->id; + header('Location: '.$url); + exit; + } + else + { + setEventMessages($companybankaccount->error, $companybankaccount->errors, 'errors'); + } + } + else + { + setEventMessages($companybankaccount->error, $companybankaccount->errors, 'errors'); + } + } + + $savid=$id; + + // Actions to build doc + if ($action == 'builddocrib') + { + $action = 'builddoc'; + $moreparams = array( + 'use_companybankid'=>GETPOST('companybankid'), + 'force_dir_output'=>$conf->societe->multidir_output[$object->entity].'/'.dol_sanitizeFileName($object->id) + ); + $_POST['lang_id'] = GETPOST('lang_idrib'.GETPOST('companybankid')); + $_POST['model'] = GETPOST('modelrib'.GETPOST('companybankid')); + } + + $id = $socid; + $upload_dir = $conf->societe->multidir_output[$object->entity]; + $permissioncreate=$user->rights->societe->creer; + include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; + + $id = $savid; + + // Action for stripe + if (! empty($conf->stripe->enabled) && class_exists('Stripe')) + { + if ($action == 'synccustomertostripe') + { + if ($object->client == 0) + { + $error++; + setEventMessages('ThisThirdpartyIsNotACustomer', null, 'errors'); + } + else + { + // Creation of Stripe customer + update of societe_account + $cu = $stripe->customerStripe($object, $stripeacc, $servicestatus, 1); + if (! $cu) + { + $error++; + setEventMessages($stripe->error, $stripe->errors, 'errors'); + } + else + { + $stripecu = $cu->id; + } + } + } + if ($action == 'synccardtostripe') + { + $companypaymentmode = new CompanyPaymentMode($db); + $companypaymentmode->fetch($id); + + if ($companypaymentmode->type != 'card') + { + $error++; + setEventMessages('ThisPaymentModeIsNotACard', null, 'errors'); + } + else + { + // Get the Stripe customer + $cu = $stripe->customerStripe($object, $stripeacc, $servicestatus); + if (! $cu) + { + $error++; + setEventMessages($stripe->error, $stripe->errors, 'errors'); + } + + if (! $error) + { + // Creation of Stripe card + update of societe_account + $card = $stripe->cardStripe($cu, $companypaymentmode, $stripeacc, $servicestatus, 1); + if (! $card) + { + $error++; + setEventMessages($stripe->error, $stripe->errors, 'errors'); + } + else + { + $stripecard = $card->id; + } + } + } + } + + if ($action == 'setkey_account') + { + $error = 0; + + $newcu = GETPOST('key_account', 'alpha'); + + $db->begin(); + + $sql = 'UPDATE '.MAIN_DB_PREFIX."societe_account"; + $sql.= " SET key_account = '".$db->escape(GETPOST('key_account', 'alpha'))."'"; + $sql.= " WHERE site = 'stripe' AND fk_soc = ".$object->id." AND status = ".$servicestatus." AND entity = ".$conf->entity; // Keep = here for entity. Only 1 record must be modified ! + + $resql = $db->query($sql); + $num = $db->num_rows($resql); + if (empty($num)) + { + $societeaccount = new SocieteAccount($db); + $societeaccount->fk_soc = $object->id; + $societeaccount->login = ''; + $societeaccount->pass_encoding = ''; + $societeaccount->site = 'stripe'; + $societeaccount->status = $servicestatus; + $societeaccount->key_account = $newcu; + $result = $societeaccount->create($user); + if ($result < 0) + { + $error++; + } + } + + if (! $error) + { + $stripecu = $newcu; + $db->commit(); + } + else + { + $db->rollback(); + } + } + if ($action == 'setlocalassourcedefault') + { + try { + $companypaymentmode->setAsDefault($id); + + $url=DOL_URL_ROOT.'/societe/paymentmodes.php?socid='.$object->id; + header('Location: '.$url); + exit; + } + catch(Exception $e) + { + $error++; + setEventMessages($e->getMessage(), null, 'errors'); + } + } + elseif ($action == 'setassourcedefault') + { + try { + $cu=$stripe->customerStripe($object, $stripeacc, $servicestatus); + $cu->default_source = (string) $source; + $result = $cu->save(); + + $url=DOL_URL_ROOT.'/societe/paymentmodes.php?socid='.$object->id; + header('Location: '.$url); + exit; + } + catch(Exception $e) + { + $error++; + setEventMessages($e->getMessage(), null, 'errors'); + } + } + elseif ($action == 'deletecard') + { + try { + $cu=$stripe->customerStripe($object, $stripeacc, $servicestatus); + $card=$cu->sources->retrieve("$source"); + if ($card) + { + // $card->detach(); Does not work with card_, only with src_ + if (method_exists($card, 'detach')) $card->detach(); + else $card->delete(); + } + + $url=DOL_URL_ROOT.'/societe/paymentmodes.php?socid='.$object->id; + header('Location: '.$url); + exit; + } + catch(Exception $e) + { + $error++; + setEventMessages($e->getMessage(), null, 'errors'); + } + } + } +} + + + +/* + * View + */ + +$form = new Form($db); +$formother = new FormOther($db); +$formfile = new FormFile($db); + +llxHeader(); + +$head=societe_prepare_head($object); + +// Show sandbox warning +/*if (! empty($conf->paypal->enabled) && (! empty($conf->global->PAYPAL_API_SANDBOX) || GETPOST('forcesandbox','alpha'))) // We can force sand box with param 'forcesandbox' +{ + dol_htmloutput_mesg($langs->trans('YouAreCurrentlyInSandboxMode','Paypal'),'','warning'); +}*/ +if (! empty($conf->stripe->enabled) && (empty($conf->global->STRIPE_LIVE) || GETPOST('forcesandbox','alpha'))) +{ + dol_htmloutput_mesg($langs->trans('YouAreCurrentlyInSandboxMode','Stripe'),'','warning'); +} + +// Load Bank account +if (! $id) +{ + $companybankaccount->fetch(0, $object->id); + $companypaymentmode->fetch(0, null, $object->id, 'card'); +} +else +{ + $companybankaccount->fetch($id); + $companypaymentmode->fetch($id); +} +if (empty($companybankaccount->socid)) $companybankaccount->socid=$object->id; + +if ($socid && ($action == 'edit' || $action == 'editcard') && $user->rights->societe->creer) +{ + print ''; + print ''; + $actionforadd='update'; + if ($action == 'editcard') $actionforadd='updatecard'; + print ''; + print ''; +} +if ($socid && ($action == 'create' || $action == 'createcard') && $user->rights->societe->creer) +{ + print ''; + print ''; + $actionforadd='add'; + if ($action == 'createcard') $actionforadd='addcard'; + print ''; +} + + +// View +if ($socid && $action != 'edit' && $action != 'create' && $action != 'editcard' && $action != 'createcard') +{ + dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"), -1, 'company'); + + // Confirm delete ban + if ($action == 'delete') + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?socid=".$object->id."&ribid=".($ribid?$ribid:$id), $langs->trans("DeleteARib"), $langs->trans("ConfirmDeleteRib", $companybankaccount->getRibLabel()), "confirm_delete", '', 0, 1); + } + // Confirm delete card + if ($action == 'deletecard') + { + print $form->formconfirm($_SERVER["PHP_SELF"]."?socid=".$object->id."&ribid=".($ribid?$ribid:$id), $langs->trans("DeleteACard"), $langs->trans("ConfirmDeleteCard", $companybankaccount->getRibLabel()), "confirm_deletecard", '', 0, 1); + } + + $linkback = ''.$langs->trans("BackToList").''; + + dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); + + + if (! empty($conf->global->SOCIETE_USEPREFIX)) // Old not used prefix field + { + print ''.$langs->trans('Prefix').''.$object->prefix_comm.''; + } + + //if ($conf->agenda->enabled && $user->rights->agenda->myactions->read) $elementTypeArray['action']=$langs->transnoentitiesnoconv('Events'); + + print '
    '; + + print '
    '; + print ''; + + if ($object->client) + { + print ''; + $sql = "SELECT count(*) as nb from ".MAIN_DB_PREFIX."facture where fk_soc = ".$socid; + $resql=$db->query($sql); + if (!$resql) dol_print_error($db); + + $obj = $db->fetch_object($resql); + $nbFactsClient = $obj->nb; + $thirdTypeArray['customer']=$langs->trans("customer"); + if ($conf->propal->enabled && $user->rights->propal->lire) $elementTypeArray['propal']=$langs->transnoentitiesnoconv('Proposals'); + if ($conf->commande->enabled && $user->rights->commande->lire) $elementTypeArray['order']=$langs->transnoentitiesnoconv('Orders'); + if ($conf->facture->enabled && $user->rights->facture->lire) $elementTypeArray['invoice']=$langs->transnoentitiesnoconv('Invoices'); + if ($conf->contrat->enabled && $user->rights->contrat->lire) $elementTypeArray['contract']=$langs->transnoentitiesnoconv('Contracts'); + } + + if (! empty($conf->stripe->enabled)) + { + $permissiontowrite = $user->rights->societe->creer; + // Stripe customer key 'cu_....' stored into llx_societe_account + print ''; + } + + print '
    '; + print $langs->trans('CustomerCode').''; + print $object->code_client; + if ($object->check_codeclient() <> 0) print ' ('.$langs->trans("WrongCustomerCode").')'; + print '
    '; + //print $langs->trans('StripeCustomerId'); + print $form->editfieldkey("StripeCustomerId", 'key_account', $stripecu, $object, $permissiontowrite, 'string', '', 0, 2, 'socid'); + print ''; + //print $stripecu; + print $form->editfieldval("StripeCustomerId", 'key_account', $stripecu, $object, $permissiontowrite, 'string', '', null, null, '', 2, '', 'socid'); + if ($stripecu) + { + $url='https://dashboard.stripe.com/test/customers/'.$stripecu; + if ($servicestatus) + { + $url='https://dashboard.stripe.com/customers/'.$stripecu; + } + print ' '.img_picto($langs->trans('ShowInStripe'), 'object_globe').''; + } + print ''; + if (empty($stripecu)) + { + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + } + print '
    '; + print '
    '; + + print '
    '; + + // List of Stripe payment modes + if (! (empty($conf->stripe->enabled))) + { + $morehtmlright=''; + if (! empty($conf->global->STRIPE_ALLOW_LOCAL_CARD)) + { + $morehtmlright=''.$langs->trans("Add").''; + } + print load_fiche_titre($langs->trans('StripePaymentModes').($stripeacc?' (Stripe connection with Stripe OAuth Connect account '.$stripeacc.')':' (Stripe connection with keys from Stripe module setup)'), $morehtmlright, ''); + + $listofsources = array(); + if (is_object($stripe)) + { + try { + $customerstripe=$stripe->customerStripe($object, $stripeacc, $servicestatus); + if ($customerstripe->id) { + $listofsources=$customerstripe->sources->data; + } + } + catch(Exception $e) + { + dol_syslog("Error when searching/loading Stripe customer for thirdparty id =".$object->id); + } + } + + print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table + print ''."\n"; + print ''; + if (! empty($conf->global->STRIPE_ALLOW_LOCAL_CARD)) + { + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print "\n"; + + $nbremote = 0; + $nblocal = 0; + $arrayofstripecard = array(); + + // Show local sources + if (! empty($conf->global->STRIPE_ALLOW_LOCAL_CARD)) + { + //$societeaccount = new SocieteAccount($db); + $companypaymentmodetemp = new CompanyPaymentMode($db); + + $sql='SELECT rowid FROM '.MAIN_DB_PREFIX."societe_rib"; + $sql.=" WHERE type in ('card', 'paypal')"; + $sql.=" AND fk_soc = ".$object->id; + + $resql = $db->query($sql); + if ($resql) + { + $num_rows = $db->num_rows($resql); + if ($num_rows) + { + $i=0; + while ($i < $num_rows) + { + $nblocal++; + + $obj = $db->fetch_object($resql); + if ($obj) + { + $companypaymentmodetemp->fetch($obj->rowid); + + $arrayofstripecard[$companypaymentmodetemp->stripe_card_ref]=$companypaymentmodetemp->stripe_card_ref; + + print ''; + print ''; + print ''; + print ''; + print ''; + // Default + print ''; + print ''; + print ''; + print ''; + } + $i++; + } + } + } + else dol_print_error($db); + } + + // Show remote sources (not already shown as local source) + if (is_array($listofsources) && count($listofsources)) + { + foreach ($listofsources as $src) + { + if (! empty($arrayofstripecard[$src->id])) continue; // Already in previous list + + $nbremote++; + + print ''; + // Local ID + if (! empty($conf->global->STRIPE_ALLOW_LOCAL_CARD)) + { + print ''; + } + print ''; + print ''; + // Default + print ''; + print ''; + print ''; + + print ''; + } + } + + if ($nbremote == 0 && $nblocal == 0) + { + print ''; + } + print "
    '.$langs->trans('LocalID').''.$langs->trans('StripeID').''.$langs->trans('Type').''.$langs->trans('Informations').''.$langs->trans('Default').''.$langs->trans('Note').'
    '; + print $companypaymentmodetemp->id; + print ''; + print $companypaymentmodetemp->stripe_card_ref; + /*if ($companypaymentmodetemp->stripe_card_ref) + { + $url='https://dashboard.stripe.com/test/card/'.$companypaymentmodetemp->stripe_card_ref; + if ($servicestatus) + { + $url='https://dashboard.stripe.com/card/'.$companypaymentmodetemp->stripe_card_ref; + } + print ' '.img_picto($langs->trans('ShowInStripe'), 'object_globe').''; + }*/ + print ''; + print img_credit_card($companypaymentmodetemp->type); + print ''; + if ($companypaymentmodetemp->last_four) print '....'.$companypaymentmodetemp->last_four; + if ($companypaymentmodetemp->exp_date_month || $companypaymentmodetemp->exp_date_year) print ' - '.sprintf("%02d", $companypaymentmodetemp->exp_date_month).'/'.$companypaymentmodetemp->exp_date_year.''; + print ''; + if ($companypaymentmodetemp->country_code) + { + $img=picto_from_langcode($companypaymentmodetemp->country_code); + print $img?$img.' ':''; + print getCountry($companypaymentmodetemp->country_code,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; + print ''; + if (empty($companypaymentmodetemp->default_rib)) + { + print ''; + print img_picto($langs->trans("Default"),'off'); + print ''; + } else { + print img_picto($langs->trans("Default"),'on'); + } + print ''; + if (empty($companypaymentmodetemp->stripe_card_ref)) print $langs->trans("Local"); + else print $langs->trans("LocalAndRemote"); + print ''; + if ($user->rights->societe->creer) + { + if ($stripecu && empty($companypaymentmodetemp->stripe_card_ref)) + { + print ''.$langs->trans("CreateCardOnStripe").''; + } + + print ''; + print img_picto($langs->trans("Modify"),'edit'); + print ''; + print ' '; + print ''; + print img_picto($langs->trans("Delete"), 'delete'); + print ''; + } + print '
    '; + print ''; + print $src->id; + print ''; + if ($src->object=='card') + { + print img_credit_card($src->brand); + } + elseif ($src->object=='source' && $src->type=='card') + { + print img_credit_card($src->card->brand); + } + elseif ($src->object=='source' && $src->type=='sepa_debit') + { + print ''; + } + + print''; + if ($src->object=='card') + { + print '....'.$src->last4.' - '.$src->exp_month.'/'.$src->exp_year.''; + print ''; + if ($src->country) + { + $img=picto_from_langcode($src->country); + print $img?$img.' ':''; + print getCountry($src->country,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; + } + elseif ($src->object=='source' && $src->type=='card') + { + print $src->owner->name.'
    ....'.$src->card->last4.' - '.$src->card->exp_month.'/'.$src->card->exp_year.''; + print '
    '; + + if ($src->card->country) + { + $img=picto_from_langcode($src->card->country); + print $img?$img.' ':''; + print getCountry($src->card->country,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; + } + elseif ($src->object=='source' && $src->type=='sepa_debit') + { + print 'info sepa'; + print ''; + if ($src->sepa_debit->country) + { + $img=picto_from_langcode($src->sepa_debit->country); + print $img?$img.' ':''; + print getCountry($src->sepa_debit->country,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; + } + print ''; + if (($customerstripe->default_source != $src->id)) + { + print ''; + print img_picto($langs->trans("Default"),'off'); + print ''; + } else { + print img_picto($langs->trans("Default"),'on'); + } + print ''; + print $langs->trans("Remote"); + print ''; + if ($user->rights->societe->creer) + { + print ''; + print img_picto($langs->trans("Delete"), 'delete'); + print ''; + } + print '
    '.$langs->trans("None").'
    "; + print "
    "; + } + + + // List of bank accounts + print '
    '; + + $morehtmlright=''.$langs->trans("Add").''; + + print load_fiche_titre($langs->trans("BankAccounts"), $morehtmlright, ''); + + $rib_list = $object->get_all_rib(); + $var = false; + if (is_array($rib_list)) + { + print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table + print ''; + + print ''; + print_liste_field_titre("LabelRIB"); + print_liste_field_titre("Bank"); + print_liste_field_titre("RIB"); + print_liste_field_titre("IBAN"); + print_liste_field_titre("BIC"); + if (! empty($conf->prelevement->enabled)) + { + print print_liste_field_titre("RUM"); + print print_liste_field_titre("WithdrawMode"); + } + print_liste_field_titre("DefaultRIB", '', '', '', '', 'align="center"'); + print_liste_field_titre('', '', '', '', '', 'align="center"'); + print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch '); + print "\n"; + + foreach ($rib_list as $rib) + { + print ''; + // Label + print ''; + // Bank name + print ''; + // Account number + print ''; + // IBAN + print ''; + // BIC + print ''; + + if (! empty($conf->prelevement->enabled)) + { + // RUM + //print ''; + print ''; + + // FRSTRECUR + print ''; + } + + // Default + print ''; + + // Generate doc + print ''; + + // Edit/Delete + print ''; + + print ''; + } + + if (count($rib_list) == 0) + { + $colspan=8; + if (! empty($conf->prelevement->enabled)) $colspan+=2; + print ''; + } + + print '
    '.$rib->label.''.$rib->bank.''; + $string=''; + foreach ($rib->getFieldsToShow() as $val) { + + if ($val == 'BankCode') { + $string .= $rib->code_banque.' '; + } elseif ($val == 'BankAccountNumber') { + $string .= $rib->number.' '; + } elseif ($val == 'DeskCode') { + $string .= $rib->code_guichet.' '; + } elseif ($val == 'BankAccountNumberKey') { + $string .= $rib->cle_rib.' '; + /* Already output after + }elseif ($val == 'BIC') { + $string .= $rib->bic.' '; + }elseif ($val == 'IBAN') { + $string .= $rib->iban.' ';*/ + } + } + if (! empty($rib->label) && $rib->number) { + if (! checkBanForAccount($rib)) { + $string.= ' '.img_picto($langs->trans("ValueIsNotValid"),'warning'); + } else { + $string.= ' '.img_picto($langs->trans("ValueIsValid"),'info'); + } + } + + print $string; + print ''.$rib->iban; + if (! empty($rib->iban)) { + if (! checkIbanForAccount($rib)) { + print ' '.img_picto($langs->trans("IbanNotValid"),'warning'); + } else { + print ' '.img_picto($langs->trans("IbanValid"),'info'); + } + } + print ''.$rib->bic; + if (! empty($rib->bic)) { + if (! checkSwiftForAccount($rib)) { + print ' '.img_picto($langs->trans("SwiftNotValid"),'warning'); + } else { + print ' '.img_picto($langs->trans("SwiftValid"),'info'); + } + } + print ''.$prelevement->buildRumNumber($object->code_client, $rib->datec, $rib->id).''.$rib->rum.''.$rib->frstrecur.''; + if (!$rib->default_rib) { + print ''; + print img_picto($langs->trans("Disabled"),'off'); + print ''; + } else { + print img_picto($langs->trans("Enabled"),'on'); + } + print ''; + + $buttonlabel = $langs->trans("BuildDoc"); + $forname='builddocrib'.$rib->id; + + include_once DOL_DOCUMENT_ROOT.'/core/modules/bank/modules_bank.php'; + $modellist=ModeleBankAccountDoc::liste_modeles($db); + + $out = ''; + if (is_array($modellist) && count($modellist)) + { + $out.= '
    '; + $out.= ''; + $out.= ''; + $out.= ''; + $out.= ''; + + if (is_array($modellist) && count($modellist) == 1) // If there is only one element + { + $arraykeys=array_keys($modellist); + $modelselected=$arraykeys[0]; + } + if (! empty($conf->global->BANKADDON_PDF)) $modelselected = $conf->global->BANKADDON_PDF; + + $out.= $form->selectarray('modelrib'.$rib->id, $modellist, $modelselected, $showempty, 0, 0, '', 0, 0, 0, '', 'minwidth100'); + $out.= ajax_combobox('modelrib'.$rib->id); + + // Language code (if multilang) + if ($conf->global->MAIN_MULTILANGS) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; + $formadmin=new FormAdmin($db); + $defaultlang=$codelang?$codelang:$langs->getDefaultLang(); + $morecss='maxwidth150'; + if (! empty($conf->browser->phone)) $morecss='maxwidth100'; + $out.= $formadmin->select_language($defaultlang, 'lang_idrib'.$rib->id, 0, 0, 0, 0, 0, $morecss); + } + // Button + $genbutton = 'dol_no_mouse_hover) && $modulepart != 'unpaid') + { + $langs->load("errors"); + $genbutton.= ' '.img_warning($langs->transnoentitiesnoconv("WarningNoDocumentModelActivated")); + } + if (! $allowgenifempty && ! is_array($modellist) && empty($modellist) && empty($conf->dol_no_mouse_hover) && $modulepart != 'unpaid') $genbutton=''; + if (empty($modellist) && ! $showempty && $modulepart != 'unpaid') $genbutton=''; + $out.= $genbutton; + $out.= '
    '; + } + print $out; + print '
    '; + if ($user->rights->societe->creer) + { + print ''; + print img_picto($langs->trans("Modify"),'edit'); + print ''; + + print ' '; + + print ''; + print img_picto($langs->trans("Delete"),'delete'); + print ''; + } + print '
    '.$langs->trans("NoBANRecord").'
    '; + print '
    '; + } else { + dol_print_error($db); + } + + dol_fiche_end(); + + + if (empty($conf->global->SOCIETE_DISABLE_BUILDDOC)) + { + print '
    '; + print ''; // ancre + + /* + * Documents generes + */ + $filedir=$conf->societe->multidir_output[$object->entity].'/'.$object->id; + $urlsource=$_SERVER["PHP_SELF"]."?socid=".$object->id; + $genallowed=$user->rights->societe->lire; + $delallowed=$user->rights->societe->creer; + + $var=true; + + print $formfile->showdocuments('company', $object->id, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 0, 0, 0, 28, 0, 'entity='.$object->entity, 0, '', $object->default_lang); + + print '
    '; + + + print '
    '; + + print '
    '; + } + /* + include_once DOL_DOCUMENT_ROOT.'/core/modules/bank/modules_bank.php'; + $modellist=ModeleBankAccountDoc::liste_modeles($db); + //print ''; + if (is_array($modellist) && count($modellist) == 1) // If there is only one element + { + $arraykeys=array_keys($modellist); + $modelselected=$arraykeys[0]; + } + $out.= $form->selectarray('model', $modellist, $modelselected, 0, 0, 0, '', 0, 0, 0, '', 'minwidth100'); + $out.= ajax_combobox('model'); + //print $out; + $buttonlabel=$langs->trans("Generate"); + $genbutton = ''; // TODO Add link to generate doc + */ +} + +// Edit BAN +if ($socid && $action == 'edit' && $user->rights->societe->creer) +{ + dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"),0,'company'); + + $linkback = ''.$langs->trans("BackToList").''; + + dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); + + print '
    '; + + print '
    '; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + // Show fields of bank account + foreach ($companybankaccount->getFieldsToShow(1) as $val) { + + $require=false; + if ($val == 'BankCode') { + $name = 'code_banque'; + $size = 8; + $content = $companybankaccount->code_banque; + } elseif ($val == 'DeskCode') { + $name = 'code_guichet'; + $size = 8; + $content = $companybankaccount->code_guichet; + } elseif ($val == 'BankAccountNumber') { + $name = 'number'; + $size = 18; + $content = $companybankaccount->number; + } elseif ($val == 'BankAccountNumberKey') { + $name = 'cle_rib'; + $size = 3; + $content = $companybankaccount->cle_rib; + } elseif ($val == 'IBAN') { + $name = 'iban'; + $size = 30; + $content = $companybankaccount->iban; + if ($companybankaccount->needIBAN()) $require=true; + } elseif ($val == 'BIC') { + $name = 'bic'; + $size = 12; + $content = $companybankaccount->bic; + if ($companybankaccount->needIBAN()) $require=true; + } + + print ''.$langs->trans($val).''; + print ''; + print ''; + } + + print '"; + + print ''; + print ''; + print "\n"; + + print '"; + + print '
    '.$langs->trans("LabelRIB").'
    '.$langs->trans("BankName").'
    '.$langs->trans("BankAccountDomiciliation").''; + print '
    '.$langs->trans("BankAccountOwner").'
    '.$langs->trans("BankAccountOwnerAddress").''; + print '
    '; + + if ($conf->prelevement->enabled) + { + print '
    '; + + print ''; + + if (empty($companybankaccount->rum)) $companybankaccount->rum = $prelevement->buildRumNumber($object->code_client, $companybankaccount->datec, $companybankaccount->id); + + // RUM + print ''; + print ''; + + print ''; + + print '
    '.$langs->trans("RUM").'
    '.$langs->trans("WithdrawMode").''; + $tblArraychoice = array("FRST" => $langs->trans("FRST"), "RECUR" => $langs->trans("RECUR")); + print $form->selectarray("frstrecur", $tblArraychoice, dol_escape_htmltag(GETPOST('frstrecur','alpha')?GETPOST('frstrecur','alpha'):$companybankaccount->frstrecur), 0); + print '
    '; + } + + print '
    '; + + dol_fiche_end(); + + print '
    '; + print ''; + print '     '; + print ''; + print '
    '; +} + +// Edit Card +if ($socid && $action == 'editcard' && $user->rights->societe->creer) +{ + dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"),0,'company'); + + $linkback = ''.$langs->trans("BackToList").''; + + dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); + + print '
    '; + + print '
    '; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print '"; + print ''; + + print '
    '.$langs->trans("Label").'
    '.$langs->trans("NameOnCard").'
    '.$langs->trans("CardNumber").'
    '.$langs->trans("ExpiryDate").''; + print $formother->select_month($companypaymentmode->exp_date_month, 'exp_date_month', 1); + print $formother->select_year($companypaymentmode->exp_date_year, 'exp_date_year', 1, 5, 10, 0, 0, '', 'marginleftonly'); + print '
    '.$langs->trans("CVN").'
    '.$langs->trans("StripeID")." ('card_....')
    '; + print '
    '; + + dol_fiche_end(); + + print '
    '; + print ''; + print '     '; + print ''; + print '
    '; +} + + +// Create BAN +if ($socid && $action == 'create' && $user->rights->societe->creer) +{ + dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"),0,'company'); + + $linkback = ''.$langs->trans("BackToList").''; + + dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); + + print '
    '; + + print '
    '; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + // Show fields of bank account + foreach ($companybankaccount->getFieldsToShow(1) as $val) { + + $require=false; + if ($val == 'BankCode') { + $name = 'code_banque'; + $size = 8; + } elseif ($val == 'DeskCode') { + $name = 'code_guichet'; + $size = 8; + } elseif ($val == 'BankAccountNumber') { + $name = 'number'; + $size = 18; + } elseif ($val == 'BankAccountNumberKey') { + $name = 'cle_rib'; + $size = 3; + } elseif ($val == 'IBAN') { + $name = 'iban'; + $size = 30; + if ($companybankaccount->needIBAN()) $require=true; + } elseif ($val == 'BIC') { + $name = 'bic'; + $size = 12; + if ($companybankaccount->needIBAN()) $require=true; + } + + print ''.$langs->trans($val).''; + print ''; + print ''; + } + + print '"; + + print ''; + print ''; + print "\n"; + + print '"; + + print '
    '.$langs->trans("LabelRIB").'
    '.$langs->trans("Bank").'
    '.$langs->trans("BankAccountDomiciliation").''; + print '
    '.$langs->trans("BankAccountOwner").'
    '.$langs->trans("BankAccountOwnerAddress").''; + print '
    '; + + if ($conf->prelevement->enabled) + { + print '
    '; + + print ''; + + // RUM + print ''; + print ''; + + print ''; + + print '
    '.$langs->trans("RUM").'
    '.$langs->trans("RUMWillBeGenerated").'
    '.$langs->trans("WithdrawMode").''; + $tblArraychoice = array("FRST" => $langs->trans("FRST"), "RECUR" => $langs->trans("RECUR")); + print $form->selectarray("frstrecur", $tblArraychoice, (isset($_POST['frstrecur'])?GETPOST('frstrecur'):'FRST'), 0); + print '
    '; + } + + print '
    '; + + dol_fiche_end(); + + dol_set_focus('#label'); + + print '
    '; + print ''; + print '     '; + print ''; + print '
    '; +} + +// Create Card +if ($socid && $action == 'createcard' && $user->rights->societe->creer) +{ + dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"),0,'company'); + + $linkback = ''.$langs->trans("BackToList").''; + + dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); + + print '
    '; + + print '
    '; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print ''; + print ''; + + print '"; + print ''; + + print '
    '.$langs->trans("Label").'
    '.$langs->trans("NameOnCard").'
    '.$langs->trans("CardNumber").'
    '.$langs->trans("ExpiryDate").''; + print $formother->select_month(GETPOST('exp_date_month','int'), 'exp_date_month', 1); + print $formother->select_year(GETPOST('exp_date_year','int'), 'exp_date_year', 1, 5, 10, 0, 0, '', 'marginleftonly'); + print '
    '.$langs->trans("CVN").'
    '.$langs->trans("StripeID")." ('card_....')
    '; + + print '
    '; + + dol_fiche_end(); + + dol_set_focus('#label'); + + print '
    '; + print ''; + print '     '; + print ''; + print '
    '; +} + +if ($socid && ($action == 'edit' || $action == 'editcard') && $user->rights->societe->creer) +{ + print ''; +} +if ($socid && ($action == 'create' || $action == 'createcard') && $user->rights->societe->creer) +{ + print ''; +} + + +llxFooter(); + +$db->close(); diff --git a/htdocs/societe/rib.php b/htdocs/societe/rib.php deleted file mode 100644 index d4263cc0b21..00000000000 --- a/htdocs/societe/rib.php +++ /dev/null @@ -1,932 +0,0 @@ - - * Copyright (C) 2003 Jean-Louis Bergamo - * Copyright (C) 2004-2016 Laurent Destailleur - * Copyright (C) 2005-2009 Regis Houssin - * Copyright (C) 2013 Peter Fontaine - * Copyright (C) 2015-2016 Marcos García - * Copyright (C) 2017 Ferran Marcet - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * \file htdocs/societe/rib.php - * \ingroup societe - * \brief BAN tab for companies - */ - -require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; -require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/prelevement/class/bonprelevement.class.php'; - -$langs->load("companies"); -$langs->load("commercial"); -$langs->load("banks"); -$langs->load("bills"); - -// Security check -$socid = GETPOST("socid","int"); -if ($user->societe_id) $socid=$user->societe_id; -$result = restrictedArea($user, 'societe','',''); - -$id=GETPOST("id","int"); -$ribid=GETPOST("ribid","int"); -$action=GETPOST("action", 'alpha', 3); - -$object = new Societe($db); -$object->fetch($socid); - -$account = new CompanyBankAccount($db); -$prelevement = new BonPrelevement($db); - -$extrafields = new ExtraFields($db); - -// fetch optionals attributes and labels -$extralabels=$extrafields->fetch_name_optionals_label($object->table_element); - -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('thirdpartybancard','globalcard')); - - -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('thirdpartybancard')); - - - -/* - * Actions - */ - -$parameters=array('id'=>$socid, 'objcanvas'=>$objcanvas); -$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks -if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); - -if (empty($reshook)) -{ - if ($cancel) - { - $action=''; - if (! empty($backtopage)) - { - header("Location: ".$backtopage); - exit; - } - } - - - if ($action == 'update' && ! $_POST["cancel"]) - { - // Modification - if (! GETPOST('label')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors'); - $action='edit'; - $error++; - } - if (! GETPOST('bank')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BankName")), null, 'errors'); - $action='edit'; - $error++; - } - if ($account->needIBAN() == 1) - { - if (! GETPOST('iban')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("IBAN")), null, 'errors'); - $action='edit'; - $error++; - } - if (! GETPOST('bic')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BIC")), null, 'errors'); - $action='edit'; - $error++; - } - } - - $account->fetch($id); - if (! $error) - { - $account->socid = $object->id; - - $account->bank = GETPOST('bank','alpha'); - $account->label = GETPOST('label','alpha'); - $account->courant = GETPOST('courant','alpha'); - $account->clos = GETPOST('clos','alpha'); - $account->code_banque = GETPOST('code_banque','alpha'); - $account->code_guichet = GETPOST('code_guichet','alpha'); - $account->number = GETPOST('number','alpha'); - $account->cle_rib = GETPOST('cle_rib','alpha'); - $account->bic = GETPOST('bic','alpha'); - $account->iban = GETPOST('iban','alpha'); - $account->domiciliation = GETPOST('domiciliation','alpha'); - $account->proprio = GETPOST('proprio','alpha'); - $account->owner_address = GETPOST('owner_address','alpha'); - $account->frstrecur = GETPOST('frstrecur','alpha'); - $account->rum = GETPOST('rum','alpha'); - if (empty($account->rum)) - { - $account->rum = $prelevement->buildRumNumber($object->code_client, $account->datec, $account->id); - $account->date_rum = dol_now(); - } - - $result = $account->update($user); - if (! $result) - { - setEventMessages($account->error, $account->errors, 'errors'); - } - else - { - // If this account is the default bank account, we disable others - if ($account->default_rib) - { - $account->setAsDefault($id); // This will make sure there is only one default rib - } - - $url=DOL_URL_ROOT.'/societe/rib.php?socid='.$object->id; - header('Location: '.$url); - exit; - } - } - } - - if ($action == 'add' && ! $_POST["cancel"]) - { - $error=0; - - if (! GETPOST('label')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors'); - $action='create'; - $error++; - } - if (! GETPOST('bank')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BankName")), null, 'errors'); - $action='create'; - $error++; - } - - if (! $error) - { - // Ajout - $account = new CompanyBankAccount($db); - - $account->socid = $object->id; - - $account->bank = GETPOST('bank','alpha'); - $account->label = GETPOST('label','alpha'); - $account->courant = GETPOST('courant','alpha'); - $account->clos = GETPOST('clos','alpha'); - $account->code_banque = GETPOST('code_banque','alpha'); - $account->code_guichet = GETPOST('code_guichet','alpha'); - $account->number = GETPOST('number','alpha'); - $account->cle_rib = GETPOST('cle_rib','alpha'); - $account->bic = GETPOST('bic','alpha'); - $account->iban = GETPOST('iban','alpha'); - $account->domiciliation = GETPOST('domiciliation','alpha'); - $account->proprio = GETPOST('proprio','alpha'); - $account->owner_address = GETPOST('owner_address','alpha'); - $account->frstrecur = GETPOST('frstrecur'); - $account->rum = GETPOST('rum','alpha'); - $account->datec = dol_now(); - - $db->begin(); - - // This test can be done only once properties were set - if ($account->needIBAN() == 1) - { - if (! GETPOST('iban')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("IBAN")), null, 'errors'); - $action='create'; - $error++; - } - if (! GETPOST('bic')) - { - setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BIC")), null, 'errors'); - $action='create'; - $error++; - } - } - - if (! $error) - { - $result = $account->create($user); - if ($result < 0) - { - $error++; - setEventMessages($account->error, $account->errors, 'errors'); - $action='create'; // Force chargement page création - } - - if (empty($account->rum)) - { - $account->rum = $prelevement->buildRumNumber($object->code_client, $account->datec, $account->id); - $account->date_rum = dol_now(); - } - } - - if (! $error) - { - $result = $account->update($user); // This will set the UMR number. - if ($result < 0) - { - $error++; - setEventMessages($account->error, $account->errors, 'errors'); - $action='create'; - } - } - - if (! $error) - { - $db->commit(); - - $url=DOL_URL_ROOT.'/societe/rib.php?socid='.$object->id; - header('Location: '.$url); - exit; - } - else - { - $db->rollback(); - } - } - } - - if ($action == 'setasdefault') - { - $account = new CompanyBankAccount($db); - $res = $account->setAsDefault(GETPOST('ribid','int')); - if ($res) - { - $url=DOL_URL_ROOT.'/societe/rib.php?socid='.$object->id; - header('Location: '.$url); - exit; - } - else - { - setEventMessages($db->lasterror, null, 'errors'); - } - } - - if ($action == 'confirm_delete' && $_GET['confirm'] == 'yes') - { - $account = new CompanyBankAccount($db); - if ($account->fetch($ribid?$ribid:$id)) - { - $result = $account->delete($user); - if ($result > 0) - { - $url = $_SERVER['PHP_SELF']."?socid=".$object->id; - header('Location: '.$url); - exit; - } - else - { - setEventMessages($account->error, $account->errors, 'errors'); - } - } - else - { - setEventMessages($account->error, $account->errors, 'errors'); - } - } - - $savid=$id; - - // Actions to build doc - if ($action == 'builddocrib') - { - $action = 'builddoc'; - $moreparams = array( - 'use_companybankid'=>GETPOST('companybankid'), - 'force_dir_output'=>$conf->societe->multidir_output[$object->entity].'/'.dol_sanitizeFileName($object->id) - ); - $_POST['lang_id'] = GETPOST('lang_idrib'.GETPOST('companybankid')); - $_POST['model'] = GETPOST('modelrib'.GETPOST('companybankid')); - } - $id = $socid; - $upload_dir = $conf->societe->multidir_output[$object->entity]; - $permissioncreate=$user->rights->societe->creer; - include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; - - $id = $savid; -} - - - -/* - * View - */ - -$form = new Form($db); -$formfile = new FormFile($db); - -llxHeader(); - -$head=societe_prepare_head($object); -if (! $id) -{ - $account->fetch(0,$object->id); -} -else -{ - $account->fetch($id); -} -if (empty($account->socid)) $account->socid=$object->id; - -if ($socid && $action == 'edit' && $user->rights->societe->creer) -{ - print '
    '; - print ''; - print ''; - print ''; -} -if ($socid && $action == 'create' && $user->rights->societe->creer) -{ - print ''; - print ''; - print ''; -} - - -// View -if ($socid && $action != 'edit' && $action != "create") -{ - dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"), -1, 'company'); - - // Confirm delete third party - if ($action == 'delete') - { - print $form->formconfirm($_SERVER["PHP_SELF"]."?socid=".$object->id."&ribid=".($ribid?$ribid:$id), $langs->trans("DeleteARib"), $langs->trans("ConfirmDeleteRib", $account->getRibLabel()), "confirm_delete", '', 0, 1); - } - - $linkback = ''.$langs->trans("BackToList").''; - - dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); - - - print load_fiche_titre($langs->trans("DefaultRIB"), '', ''); - - print '
    '; - print '
    '; - - print ''; - - print ''; - print ''; - - print ''; - print ''; - - // Show fields of bank account - foreach($account->getFieldsToShow(1) as $val) - { - if ($val == 'BankCode') { - $content = $account->code_banque; - } elseif ($val == 'DeskCode') { - $content = $account->code_guichet; - } elseif ($val == 'BankAccountNumber') { - $content = $account->number; - if (! empty($account->label) && $account->number) { - if (! checkBanForAccount($account)) { - $content.= ' '.img_picto($langs->trans("ValueIsNotValid"),'warning'); - } else { - $content.= ' '.img_picto($langs->trans("ValueIsValid"),'info'); - } - } - } elseif ($val == 'BankAccountNumberKey') { - $content = $account->cle_rib; - } elseif ($val == 'IBAN') { - $content = $account->iban; - if (! empty($account->iban)) { - if (! checkIbanForAccount($account)) { - $content.= ' '.img_picto($langs->trans("ValueIsNotValid"),'warning'); - } else { - $content.= ' '.img_picto($langs->trans("ValueIsValid"),'info'); - } - } - } elseif ($val == 'BIC') { - $content = $account->bic; - if (! empty($account->bic)) { - if (! checkSwiftForAccount($account)) { - $content.= ' '.img_picto($langs->trans("ValueIsNotValid"),'warning'); - } else { - $content.= ' '.img_picto($langs->trans("ValueIsValid"),'info'); - } - } - } - - print ''; - print ''; - print ''; - } - - print '\n"; - - print '\n"; - - print '\n"; - - print '
    '.$langs->trans("LabelRIB").''.$account->label.'
    '.$langs->trans("BankName").''.$account->bank.'
    '.$langs->trans($val).''.$content.'
    '.$langs->trans("BankAccountDomiciliation").''; - print $account->domiciliation; - print "
    '.$langs->trans("BankAccountOwner").''; - print $account->proprio; - print "
    '.$langs->trans("BankAccountOwnerAddress").''; - print $account->owner_address; - print "
    '; - print '
    '; - - print '
    '; - - // List of bank accounts - - $morehtmlright=''.$langs->trans("Add").''; - - print load_fiche_titre($langs->trans("AllRIB"), $morehtmlright, ''); - - $rib_list = $object->get_all_rib(); - $var = false; - if (is_array($rib_list)) - { - print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table - print ''; - - print ''; - print_liste_field_titre("LabelRIB"); - print_liste_field_titre("Bank"); - print_liste_field_titre("RIB"); - print_liste_field_titre("IBAN"); - print_liste_field_titre("BIC"); - if (! empty($conf->prelevement->enabled)) - { - print print_liste_field_titre("RUM"); - print print_liste_field_titre("WithdrawMode"); - } - print_liste_field_titre("DefaultRIB", '', '', '', '', 'align="center"'); - print_liste_field_titre('', '', '', '', '', 'align="center"'); - print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch '); - print "\n"; - - foreach ($rib_list as $rib) - { - print ''; - // Label - print ''; - // Bank name - print ''; - // Account number - print ''; - // IBAN - print ''; - // BIC - print ''; - - if (! empty($conf->prelevement->enabled)) - { - // RUM - //print ''; - print ''; - - // FRSTRECUR - print ''; - } - - // Default - print ''; - - // Generate doc - print ''; - - // Edit/Delete - print ''; - - print ''; - } - - if (count($rib_list) == 0) - { - $colspan=8; - if (! empty($conf->prelevement->enabled)) $colspan+=2; - print ''; - } - - print '
    '.$rib->label.''.$rib->bank.''; - $string=''; - foreach ($rib->getFieldsToShow() as $val) { - - if ($val == 'BankCode') { - $string .= $rib->code_banque.' '; - } elseif ($val == 'BankAccountNumber') { - $string .= $rib->number.' '; - } elseif ($val == 'DeskCode') { - $string .= $rib->code_guichet.' '; - } elseif ($val == 'BankAccountNumberKey') { - $string .= $rib->cle_rib.' '; - /* Already output after - }elseif ($val == 'BIC') { - $string .= $rib->bic.' '; - }elseif ($val == 'IBAN') { - $string .= $rib->iban.' ';*/ - } - } - if (! empty($rib->label) && $rib->number) { - if (! checkBanForAccount($rib)) { - $string.= ' '.img_picto($langs->trans("ValueIsNotValid"),'warning'); - } else { - $string.= ' '.img_picto($langs->trans("ValueIsValid"),'info'); - } - } - - print $string; - print ''.$rib->iban; - if (! empty($rib->iban)) { - if (! checkIbanForAccount($rib)) { - print ' '.img_picto($langs->trans("IbanNotValid"),'warning'); - } else { - print ' '.img_picto($langs->trans("IbanValid"),'info'); - } - } - print ''.$rib->bic; - if (! empty($rib->bic)) { - if (! checkSwiftForAccount($rib)) { - print ' '.img_picto($langs->trans("SwiftNotValid"),'warning'); - } else { - print ' '.img_picto($langs->trans("SwiftValid"),'info'); - } - } - print ''.$prelevement->buildRumNumber($object->code_client, $rib->datec, $rib->id).''.$rib->rum.''.$rib->frstrecur.''; - if (!$rib->default_rib) { - print ''; - print img_picto($langs->trans("Disabled"),'off'); - print ''; - } else { - print img_picto($langs->trans("Enabled"),'on'); - } - print ''; - - $buttonlabel = $langs->trans("BuildDoc"); - $forname='builddocrib'.$rib->id; - - include_once DOL_DOCUMENT_ROOT.'/core/modules/bank/modules_bank.php'; - $modellist=ModeleBankAccountDoc::liste_modeles($db); - - $out = ''; - if (is_array($modellist) && count($modellist)) - { - $out.= ''; - $out.= ''; - $out.= ''; - $out.= ''; - $out.= ''; - - if (is_array($modellist) && count($modellist) == 1) // If there is only one element - { - $arraykeys=array_keys($modellist); - $modelselected=$arraykeys[0]; - } - if (! empty($conf->global->BANKADDON_PDF)) $modelselected = $conf->global->BANKADDON_PDF; - - $out.= $form->selectarray('modelrib'.$rib->id, $modellist, $modelselected, $showempty, 0, 0, '', 0, 0, 0, '', 'minwidth100'); - $out.= ajax_combobox('modelrib'.$rib->id); - - // Language code (if multilang) - if ($conf->global->MAIN_MULTILANGS) - { - include_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; - $formadmin=new FormAdmin($db); - $defaultlang=$codelang?$codelang:$langs->getDefaultLang(); - $morecss='maxwidth150'; - if (! empty($conf->browser->phone)) $morecss='maxwidth100'; - $out.= $formadmin->select_language($defaultlang, 'lang_idrib'.$rib->id, 0, 0, 0, 0, 0, $morecss); - } - // Button - $genbutton = 'dol_no_mouse_hover) && $modulepart != 'unpaid') - { - $langs->load("errors"); - $genbutton.= ' '.img_warning($langs->transnoentitiesnoconv("WarningNoDocumentModelActivated")); - } - if (! $allowgenifempty && ! is_array($modellist) && empty($modellist) && empty($conf->dol_no_mouse_hover) && $modulepart != 'unpaid') $genbutton=''; - if (empty($modellist) && ! $showempty && $modulepart != 'unpaid') $genbutton=''; - $out.= $genbutton; - $out.= ''; - } - print $out; - print ''; - if ($user->rights->societe->creer) - { - print ''; - print img_picto($langs->trans("Modify"),'edit'); - print ''; - - print ' '; - - print ''; - print img_picto($langs->trans("Delete"),'delete'); - print ''; - } - print '
    '.$langs->trans("NoBANRecord").'
    '; - print '
    '; - } else { - dol_print_error($db); - } - - dol_fiche_end(); - - - /* - if ($socid && $action != 'edit' && $action != 'create') - { - // Barre d'actions - print '
    '; - - if ($user->rights->societe->creer) - { - print ''.$langs->trans("Add").''; - } - - print '
    '; - } - */ - - - - if (empty($conf->global->SOCIETE_DISABLE_BUILDDOC)) - { - print '
    '; - print ''; // ancre - - /* - * Documents generes - */ - $filedir=$conf->societe->multidir_output[$object->entity].'/'.$object->id; - $urlsource=$_SERVER["PHP_SELF"]."?socid=".$object->id; - $genallowed=$user->rights->societe->lire; - $delallowed=$user->rights->societe->creer; - - $var=true; - - print $formfile->showdocuments('company', $object->id, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 0, 0, 0, 28, 0, 'entity='.$object->entity, 0, '', $object->default_lang); - - print '
    '; - - - print '
    '; - - print '
    '; - } - /* - include_once DOL_DOCUMENT_ROOT.'/core/modules/bank/modules_bank.php'; - $modellist=ModeleBankAccountDoc::liste_modeles($db); - //print ''; - if (is_array($modellist) && count($modellist) == 1) // If there is only one element - { - $arraykeys=array_keys($modellist); - $modelselected=$arraykeys[0]; - } - $out.= $form->selectarray('model', $modellist, $modelselected, 0, 0, 0, '', 0, 0, 0, '', 'minwidth100'); - $out.= ajax_combobox('model'); - //print $out; - $buttonlabel=$langs->trans("Generate"); - $genbutton = ''; // TODO Add link to generate doc - */ -} - -// Edit -if ($socid && $action == 'edit' && $user->rights->societe->creer) -{ - dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"),0,'company'); - - $linkback = ''.$langs->trans("BackToList").''; - - dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); - - print '
    '; - - print '
    '; - print ''; - - print ''; - print ''; - - print ''; - print ''; - - // Show fields of bank account - foreach ($account->getFieldsToShow(1) as $val) { - - $require=false; - if ($val == 'BankCode') { - $name = 'code_banque'; - $size = 8; - $content = $account->code_banque; - } elseif ($val == 'DeskCode') { - $name = 'code_guichet'; - $size = 8; - $content = $account->code_guichet; - } elseif ($val == 'BankAccountNumber') { - $name = 'number'; - $size = 18; - $content = $account->number; - } elseif ($val == 'BankAccountNumberKey') { - $name = 'cle_rib'; - $size = 3; - $content = $account->cle_rib; - } elseif ($val == 'IBAN') { - $name = 'iban'; - $size = 30; - $content = $account->iban; - if ($account->needIBAN()) $require=true; - } elseif ($val == 'BIC') { - $name = 'bic'; - $size = 12; - $content = $account->bic; - if ($account->needIBAN()) $require=true; - } - - print ''.$langs->trans($val).''; - print ''; - print ''; - } - - print '"; - - print ''; - print ''; - print "\n"; - - print '"; - - print '
    '.$langs->trans("LabelRIB").'
    '.$langs->trans("BankName").'
    '.$langs->trans("BankAccountDomiciliation").''; - print '
    '.$langs->trans("BankAccountOwner").'
    '.$langs->trans("BankAccountOwnerAddress").''; - print '
    '; - - if ($conf->prelevement->enabled) - { - print '
    '; - - print ''; - - if (empty($account->rum)) $account->rum = $prelevement->buildRumNumber($object->code_client, $account->datec, $account->id); - - // RUM - print ''; - print ''; - - print ''; - - print '
    '.$langs->trans("RUM").'
    '.$langs->trans("WithdrawMode").''; - $tblArraychoice = array("FRST" => $langs->trans("FRST"), "RECUR" => $langs->trans("RECUR")); - print $form->selectarray("frstrecur", $tblArraychoice, dol_escape_htmltag(GETPOST('frstrecur')?GETPOST('frstrecur'):$account->frstrecur), 0); - print '
    '; - } - - print '
    '; - - dol_fiche_end(); - - print '
    '; - print ''; - print '     '; - print ''; - print '
    '; -} - - -// Create -if ($socid && $action == 'create' && $user->rights->societe->creer) -{ - dol_fiche_head($head, 'rib', $langs->trans("ThirdParty"),0,'company'); - - $linkback = ''.$langs->trans("BackToList").''; - - dol_banner_tab($object, 'socid', $linkback, ($user->societe_id?0:1), 'rowid', 'nom'); - - print '
    '; - - print '
    '; - print ''; - - print ''; - print ''; - - print ''; - print ''; - - // Show fields of bank account - foreach ($account->getFieldsToShow(1) as $val) { - - $require=false; - if ($val == 'BankCode') { - $name = 'code_banque'; - $size = 8; - } elseif ($val == 'DeskCode') { - $name = 'code_guichet'; - $size = 8; - } elseif ($val == 'BankAccountNumber') { - $name = 'number'; - $size = 18; - } elseif ($val == 'BankAccountNumberKey') { - $name = 'cle_rib'; - $size = 3; - } elseif ($val == 'IBAN') { - $name = 'iban'; - $size = 30; - if ($account->needIBAN()) $require=true; - } elseif ($val == 'BIC') { - $name = 'bic'; - $size = 12; - if ($account->needIBAN()) $require=true; - } - - print ''.$langs->trans($val).''; - print ''; - print ''; - } - - print '"; - - print ''; - print ''; - print "\n"; - - print '"; - - print '
    '.$langs->trans("LabelRIB").'
    '.$langs->trans("Bank").'
    '.$langs->trans("BankAccountDomiciliation").''; - print '
    '.$langs->trans("BankAccountOwner").'
    '.$langs->trans("BankAccountOwnerAddress").''; - print '
    '; - - if ($conf->prelevement->enabled) - { - print '
    '; - - print ''; - - // RUM - print ''; - print ''; - - print ''; - - print '
    '.$langs->trans("RUM").'
    '.$langs->trans("RUMWillBeGenerated").'
    '.$langs->trans("WithdrawMode").''; - $tblArraychoice = array("FRST" => $langs->trans("FRST"), "RECUR" => $langs->trans("RECUR")); - print $form->selectarray("frstrecur", $tblArraychoice, (isset($_POST['frstrecur'])?GETPOST('frstrecur'):'FRST'), 0); - print '
    '; - } - - print '
    '; - - dol_fiche_end(); - - print '
    '; - print ''; - print '     '; - print ''; - print '
    '; -} - -if ($socid && $action == 'edit' && $user->rights->societe->creer) -{ - print ''; -} -if ($socid && $action == 'create' && $user->rights->societe->creer) -{ - print ''; -} - - -llxFooter(); - -$db->close(); diff --git a/htdocs/societe/societecontact.php b/htdocs/societe/societecontact.php index 439586468c4..6b3e833abb0 100644 --- a/htdocs/societe/societecontact.php +++ b/htdocs/societe/societecontact.php @@ -45,6 +45,9 @@ $result = restrictedArea($user, 'societe', $id,''); $object = new Societe($db); +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('contactthirdparty','globalcard')); + /* * Actions @@ -222,7 +225,8 @@ if ($id > 0 || ! empty($ref)) $sql.= " t.libelle as type, t.subscription"; $sql.= " FROM ".MAIN_DB_PREFIX."adherent as d"; $sql.= ", ".MAIN_DB_PREFIX."adherent_type as t"; - $sql.= " WHERE d.fk_soc=".$id; + $sql.= " WHERE d.fk_soc = ".$id; + $sql.= " AND d.fk_adherent_type = t.rowid"; dol_syslog("get list sql=".$sql); $resql = $db->query($sql); diff --git a/htdocs/societe/tpl/linesalesrepresentative.tpl.php b/htdocs/societe/tpl/linesalesrepresentative.tpl.php index 0714a547f22..2f325f3001f 100644 --- a/htdocs/societe/tpl/linesalesrepresentative.tpl.php +++ b/htdocs/societe/tpl/linesalesrepresentative.tpl.php @@ -24,18 +24,7 @@ if (empty($conf) || ! is_object($conf)) // Sale representative print ''; -print '
    '; print $langs->trans('SalesRepresentatives'); -print ''; -if ($user->rights->societe->creer && $user->rights->societe->client->voir) -{ - print ''.img_edit('',1).''; -} -else -{ - print ' '; -} -print '
    '; print ''; print ''; @@ -44,7 +33,6 @@ $nbofsalesrepresentative=count($listsalesrepresentatives); if ($nbofsalesrepresentative > 0) { $userstatic=new User($db); - $i=0; foreach($listsalesrepresentatives as $val) { $userstatic->id=$val['id']; @@ -56,27 +44,7 @@ if ($nbofsalesrepresentative > 0) $userstatic->email=$val['email']; $userstatic->entity=$val['entity']; print $userstatic->getNomUrl(-1); - $i++; - if ($i < $nbofsalesrepresentative) - { - print ' '; - if ($i >= 3) // We print only number - { - $userstatic->id=0; - $userstatic->login=''; - $userstatic->lastname=''; - $userstatic->firstname=''; - $userstatic->statut=0; - $userstatic->photo=''; - $userstatic->email=''; - $userstatic->entity=0; - print ''; - print $userstatic->getNomUrl(-1, 'nolink', 0, 1); - print '+'.($nbofsalesrepresentative - $i); - print ''; - break; - } - } + print ' '; } } else print ''.$langs->trans("NoSalesRepresentativeAffected").''; diff --git a/htdocs/societe/website.php b/htdocs/societe/website.php index 9e57176250a..1f3e69a674c 100644 --- a/htdocs/societe/website.php +++ b/htdocs/societe/website.php @@ -59,16 +59,13 @@ $object=new Societe($db); $objectwebsiteaccount=new WebsiteAccount($db); $extrafields = new ExtraFields($db); $diroutputmassaction=$conf->website->dir_output . '/temp/massgeneration/'.$user->id; -$hookmanager->initHooks(array('websiteaccountlist')); // Note that conf->hooks_modules contains array +$hookmanager->initHooks(array('websitethirdpartylist')); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extralabels = $extrafields->fetch_name_optionals_label('websiteaccount'); $search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); unset($objectwebsiteaccount->fields['fk_soc']); // Remove this field, we are already on the thirdparty -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array('websitethirdparty')); - // Initialize array of search criterias $search_all=trim(GETPOST("search_all",'alpha')); $search=array(); diff --git a/htdocs/stripe/admin/stripe.php b/htdocs/stripe/admin/stripe.php index de3c4db8987..a60d608c0de 100644 --- a/htdocs/stripe/admin/stripe.php +++ b/htdocs/stripe/admin/stripe.php @@ -2,6 +2,7 @@ /* Copyright (C) 2017 Alexandre Spangaro * Copyright (C) 2017 Olivier Geffroy * Copyright (C) 2017 Saasprov + * Copyright (C) 2018 ptibogxiv * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +28,7 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/stripe/lib/stripe.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; $servicename='Stripe'; @@ -44,45 +46,72 @@ $action = GETPOST('action','alpha'); if ($action == 'setvalue' && $user->admin) { $db->begin(); - - $result=dolibarr_set_const($db, "STRIPE_LIVE",GETPOST('STRIPE_LIVE','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "STRIPE_TEST_PUBLISHABLE_KEY",GETPOST('STRIPE_TEST_PUBLISHABLE_KEY','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "STRIPE_TEST_SECRET_KEY",GETPOST('STRIPE_TEST_SECRET_KEY','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "STRIPE_LIVE_PUBLISHABLE_KEY",GETPOST('STRIPE_LIVE_PUBLISHABLE_KEY','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "STRIPE_LIVE_SECRET_KEY",GETPOST('STRIPE_LIVE_SECRET_KEY','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "ONLINE_PAYMENT_CREDITOR",GETPOST('ONLINE_PAYMENT_CREDITOR','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "ONLINE_PAYMENT_CSS_URL",GETPOST('ONLINE_PAYMENT_CSS_URL','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "ONLINE_PAYMENT_MESSAGE_FORM",GETPOST('ONLINE_PAYMENT_MESSAGE_FORM','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "ONLINE_PAYMENT_MESSAGE_OK",GETPOST('ONLINE_PAYMENT_MESSAGE_OK','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "ONLINE_PAYMENT_MESSAGE_KO",GETPOST('ONLINE_PAYMENT_MESSAGE_KO','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "ONLINE_PAYMENT_SENDEMAIL",GETPOST('ONLINE_PAYMENT_SENDEMAIL'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; + $result = dolibarr_set_const($db, "STRIPE_LIVE", GETPOST('STRIPE_LIVE', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + if (empty($conf->stripeconnect->enabled)) { + $result = dolibarr_set_const($db, "STRIPE_TEST_PUBLISHABLE_KEY", GETPOST('STRIPE_TEST_PUBLISHABLE_KEY', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_TEST_SECRET_KEY", GETPOST('STRIPE_TEST_SECRET_KEY', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_TEST_WEBHOOK_KEY", GETPOST('STRIPE_TEST_WEBHOOK_KEY', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_LIVE_PUBLISHABLE_KEY", GETPOST('STRIPE_LIVE_PUBLISHABLE_KEY', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_LIVE_SECRET_KEY", GETPOST('STRIPE_LIVE_SECRET_KEY', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_LIVE_WEBHOOK_KEY", GETPOST('STRIPE_LIVE_WEBHOOK_KEY', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + } + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_CREDITOR", GETPOST('ONLINE_PAYMENT_CREDITOR', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_BANK_ACCOUNT_FOR_PAYMENTS", GETPOST('STRIPE_BANK_ACCOUNT_FOR_PAYMENTS', 'int'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS", GETPOST('STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS', 'int'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_CSS_URL", GETPOST('ONLINE_PAYMENT_CSS_URL', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_MESSAGE_FORM", GETPOST('ONLINE_PAYMENT_MESSAGE_FORM', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_MESSAGE_OK", GETPOST('ONLINE_PAYMENT_MESSAGE_OK', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_MESSAGE_KO", GETPOST('ONLINE_PAYMENT_MESSAGE_KO', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_SENDEMAIL", GETPOST('ONLINE_PAYMENT_SENDEMAIL'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + // Stock decrement + $result = dolibarr_set_const($db, "ONLINE_PAYMENT_WAREHOUSE", (GETPOST('ONLINE_PAYMENT_WAREHOUSE', 'alpha') > 0 ? GETPOST('ONLINE_PAYMENT_WAREHOUSE', 'alpha') : ''), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; // Payment token for URL - $result=dolibarr_set_const($db, "PAYMENT_SECURITY_TOKEN",GETPOST('PAYMENT_SECURITY_TOKEN','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; - $result=dolibarr_set_const($db, "PAYMENT_SECURITY_TOKEN_UNIQUE",GETPOST('PAYMENT_SECURITY_TOKEN_UNIQUE','alpha'),'chaine',0,'',$conf->entity); - if (! $result > 0) $error++; + $result = dolibarr_set_const($db, "PAYMENT_SECURITY_TOKEN", GETPOST('PAYMENT_SECURITY_TOKEN', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; + $result = dolibarr_set_const($db, "PAYMENT_SECURITY_TOKEN_UNIQUE", GETPOST('PAYMENT_SECURITY_TOKEN_UNIQUE', 'alpha'), 'chaine', 0, '', $conf->entity); + if (! $result > 0) + $error ++; - if (! $error) - { - $db->commit(); - setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); - } - else - { - $db->rollback(); + if (! $error) { + $db->commit(); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + } else { + $db->rollback(); dol_print_error($db); - } + } } if ($action=="setlive") @@ -99,6 +128,7 @@ if ($action=="setlive") setEventMessages($langs->trans("Error"), null, 'errors'); } } +//TODO: import script for stripe account saving in alone or connect mode for stripe.class.php /* @@ -106,20 +136,11 @@ if ($action=="setlive") */ $form=new Form($db); - -//$SECRET_TEST_KEY="sk_test_xxxxxxxxxxxxxxxxxxxxxxxx"; // Stripe test secret key -//if (empty($conf->global->STRIPE_TEST_SECRET_KEY)) $conf->global->STRIPE_TEST_SECRET_KEY = $SECRET_TEST_KEY; -//$PUBLISHABLE_TEST_KEY="pk_test_xxxxxxxxxxxxxxxxxxxxxxxx"; // Stripe test publishable key -//if (empty($conf->global->STRIPE_TEST_PUBLISHABLE_KEY)) $conf->global->STRIPE_TEST_PUBLISHABLE_KEY = $PUBLISHABLE_TEST_KEY; - -//$SECRET_LIVE_KEY="sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"; // Stripe live secret key -//if (empty($conf->global->STRIPE_LIVE_SECRET_KEY)) $conf->global->STRIPE_LIVE_SECRET_KEY = $SECRET_LIVE_KEY; -//$PUBLISHABLE_LIVE_KEY="pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"; // Stripe live publishable key -//if (empty($conf->global->STRIPE_LIVE_PUBLISHABLE_KEY)) $conf->global->STRIPE_LIVE_PUBLISHABLE_KEY = $PUBLISHABLE_LIVE_KEY; +$formproduct=new FormProduct($db); llxHeader('',$langs->trans("StripeSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("ModuleSetup").' Stripe',$linkback); $head=stripeadmin_prepare_head(); @@ -147,45 +168,81 @@ if (!empty($conf->global->STRIPE_LIVE)) { print ''; print img_picto($langs->trans("Activated"),'switch_on'); + print ''; } else { print ''; print img_picto($langs->trans("Disabled"),'switch_off'); + print ''; } print ''; -print ''; -print ''.$langs->trans("STRIPE_TEST_PUBLISHABLE_KEY").''; -print ''; -print '   '.$langs->trans("Example").': pk_test_xxxxxxxxxxxxxxxxxxxxxxxx'; -print ''; +if (empty($conf->stripeconnect->enabled)) +{ + print ''; + print ''.$langs->trans("STRIPE_TEST_PUBLISHABLE_KEY").''; + print ''; + print '   '.$langs->trans("Example").': pk_test_xxxxxxxxxxxxxxxxxxxxxxxx'; + print ''; -print ''; -print ''.$langs->trans("STRIPE_TEST_SECRET_KEY").''; -print ''; -print '   '.$langs->trans("Example").': sk_test_xxxxxxxxxxxxxxxxxxxxxxxx'; -print ''; + print ''; + print ''.$langs->trans("STRIPE_TEST_SECRET_KEY").''; + print ''; + print '   '.$langs->trans("Example").': sk_test_xxxxxxxxxxxxxxxxxxxxxxxx'; + print ''; -print ''; -print ''.$langs->trans("STRIPE_LIVE_PUBLISHABLE_KEY").''; -print ''; -print '   '.$langs->trans("Example").': pk_live_xxxxxxxxxxxxxxxxxxxxxxxx'; -print ''; + print ''; + print ''.$langs->trans("STRIPE_TEST_WEBHOOK_KEY").''; + print ''; + print '   '.$langs->trans("Example").': whsec_xxxxxxxxxxxxxxxxxxxxxxxx'; + print ''; +} else { + print ''.$langs->trans("StripeConnect").''; + print ''.$langs->trans("StripeConnect_Mode").'
    '; + print $langs->trans("STRIPE_APPLICATION_FEE_PLATFORM").' '; + print price($conf->global->STRIPE_APPLICATION_FEE_PERCENT); + print '% + '; + print price($conf->global->STRIPE_APPLICATION_FEE); + print ' '.$langs->getCurrencySymbol($conf->currency).' '.$langs->trans("minimum").' '.price($conf->global->STRIPE_APPLICATION_FEE_MINIMAL).' '.$langs->getCurrencySymbol($conf->currency).' '; + print ''; +} + +if (empty($conf->stripeconnect->enabled)) +{ + print ''; + print ''.$langs->trans("STRIPE_LIVE_PUBLISHABLE_KEY").''; + print ''; + print '   '.$langs->trans("Example").': pk_live_xxxxxxxxxxxxxxxxxxxxxxxx'; + print ''; + + print ''; + print ''.$langs->trans("STRIPE_LIVE_SECRET_KEY").''; + print ''; + print '   '.$langs->trans("Example").': sk_live_xxxxxxxxxxxxxxxxxxxxxxxx'; + print ''; + + print ''; + print ''.$langs->trans("STRIPE_LIVE_WEBHOOK_KEY").''; + print ''; + print '   '.$langs->trans("Example").': whsec_xxxxxxxxxxxxxxxxxxxxxxxx'; + print ''; +} +else +{ + print ''.$langs->trans("StripeConnect").''; + print ''.$langs->trans("StripeConnect_Mode").''; +} -print ''; -print ''.$langs->trans("STRIPE_LIVE_SECRET_KEY").''; -print ''; -print '   '.$langs->trans("Example").': sk_live_xxxxxxxxxxxxxxxxxxxxxxxx'; -print ''; print ''; print '
    '; + print ''; print ''; -print ''; +print ''; print ''; print "\n"; @@ -195,6 +252,28 @@ print 'name; print ''; +print ''; + +if ($conf->global->MAIN_FEATURES_LEVEL >= 2) // What is this for ? +{ + print ''; +} + +if ($conf->global->MAIN_FEATURES_LEVEL >= 2) // What is this for ? +{ + // Stock for automatic decrement + print ''; +} + print '\n"; + } + $db->free($resql); + } + else + { + dol_print_error($db); + } + + + // Bouton Enregistrer + if ($action != 'add_paiement') + { + $checkboxlabel=$langs->trans("ClosePaidInvoicesAutomatically"); + if ($facture->type == 2) $checkboxlabel=$langs->trans("ClosePaidCreditNotesAutomatically"); + $buttontitle=$langs->trans('ToMakePayment'); + if ($facture->type == 2) $buttontitle=$langs->trans('ToMakePaymentBack'); + + print '
    '; + print ' '.$checkboxlabel; + /*if (! empty($conf->prelevement->enabled)) + { + $langs->load("withdrawals"); + if (! empty($conf->global->WITHDRAW_DISABLE_AUTOCREATE_ONPAYMENTS)) print '
    '.$langs->trans("IfInvoiceNeedOnWithdrawPaymentWontBeClosed"); + }*/ + print '


    '; + print '
    '; + } + + // Form to confirm payment + if ($action == 'add_paiement') + { + $preselectedchoice=$addwarning?'no':'yes'; + + print '
    '; + if (!empty($totalpayment)) $text=$langs->trans('ConfirmCustomerPayment',$totalpayment,$langs->trans("Currency".$conf->currency)); + if (!empty($multicurrency_totalpayment)) + { + $text.='
    '.$langs->trans('ConfirmCustomerPayment',$multicurrency_totalpayment,$langs->trans("paymentInInvoiceCurrency")); + } + if (GETPOST('closepaidinvoices')) + { + $text.='
    '.$langs->trans("AllCompletelyPayedInvoiceWillBeClosed"); + print ''; + } + print $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$facture->id.'&socid='.$facture->socid.'&type='.$facture->type,$langs->trans('ReceivedCustomersPayments'),$text,'confirm_paiement',$formquestion,$preselectedchoice); + } + + print "\n"; + } +} + + +/** + * Show list of payments + */ + +if (! GETPOST('action')) +{ + if ($page == -1) $page = 0 ; + $limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit; + $offset = $limit * $page ; + + if (! $sortorder) $sortorder='DESC'; + if (! $sortfield) $sortfield='p.datep'; + + $sql = 'SELECT p.datep as dp, p.amount, f.amount as fa_amount, f.facnumber'; + $sql.=', f.rowid as facid, c.libelle as paiement_type, p.num_paiement'; + $sql.= ' FROM '.MAIN_DB_PREFIX.'paiement as p, '.MAIN_DB_PREFIX.'facture as f, '.MAIN_DB_PREFIX.'c_paiement as c'; + $sql.= ' WHERE p.fk_facture = f.rowid AND p.fk_paiement = c.id'; + $sql.= ' AND f.entity = '.$conf->entity; + if ($socid) + { + $sql.= ' AND f.fk_soc = '.$socid; + } + + $sql.= ' ORDER BY '.$sortfield.' '.$sortorder; + $sql.= $db->plimit($limit+1, $offset); + $resql = $db->query($sql); + + if ($resql) + { + $num = $db->num_rows($resql); + $i = 0; + $var=true; + + print_barre_liste($langs->trans('Payments'), $page, $_SERVER["PHP_SELF"],'',$sortfield,$sortorder,'',$num); + print '
    '.$langs->trans("UsageParameter").''.$langs->trans("UsageParameter").''.$langs->trans("Value").'
    '; +print $langs->trans("BankAccount").''; +print $form->select_comptes($conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS, 'STRIPE_BANK_ACCOUNT_FOR_PAYMENTS', 0, '', 1); +print '
    '; + print $langs->trans("BankAccountForBankTransfer").''; + print $form->select_comptes($conf->global->STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS, 'STRIPE_BANK_ACCOUNT_FOR_BANKTRANSFERS', 0, '', 1); + print '
    '; + print $langs->trans("ONLINE_PAYMENT_WAREHOUSE").''; + print $formproduct->selectWarehouses($conf->global->ONLINE_PAYMENT_WAREHOUSE,'ONLINE_PAYMENT_WAREHOUSE','',1,$disabled); + print '
    '; print $langs->trans("CSSUrlForPaymentForm").''; print ''; @@ -253,6 +332,8 @@ $token=''; include DOL_DOCUMENT_ROOT.'/core/tpl/onlinepaymentlinks.tpl.php'; +print info_admin($langs->trans("ExampleOfTestCreditCard", '4242424242424242', '4000000000000101', '4000000000000069', '4000000000000341')); + if (! empty($conf->use_javascript_ajax)) { print "\n".''."\n"; + } + + print '
    '; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + dol_fiche_head(); + + print ''; + + // Invoice + /*if ($facture->id > 0) + { + print '\n"; + }*/ + + // Third party + print '\n"; + + // Bank account + if (! empty($conf->banque->enabled)) + { + //$form->select_comptes($accountid,'accountid',0,'',2); + print ''; + } + else + { + print ''; + } + + // Cheque number +// print ''; +// print ''; + + // Check transmitter +// print ''; +// print ''; + + // Bank name +// print ''; +// print ''; + + // Comments + print ''; + print ''; + + print '
    '.$langs->trans('Invoice').''.$facture->getNomUrl(4)."
    '.$langs->trans('Company').''.$facture->thirdparty->getNomUrl(4)."
    '.$langs->trans('Numero'); +// print ' ('.$langs->trans("ChequeOrTransferNumber").')'; +// print '
    '.$langs->trans('CheckTransmitter'); +// print ' ('.$langs->trans("ChequeMaker").')'; +// print '
    '.$langs->trans('Bank'); +// print ' ('.$langs->trans("ChequeBank").')'; +// print '
    '.$langs->trans('Comments').''; + print '
    '; + + dol_fiche_end(); + + + $customerstripe=$stripe->customerStripe($facture->thirdparty, $stripeacc, $servicestatus); + + print '
    '; + print_barre_liste($langs->trans('StripeSourceList').' '.$typeElementString.' '.$button, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder,'',$num, '', ''); + + print ''."\n"; + // Titles with sort buttons + print ''; + print ''; + print ''; + print ''; + print ''; + print "\n"; + foreach ($customerstripe->sources->data as $src) { + print ''; + + print ''; + +print ''; +print ''; + // Default + print ''; +print ''; +} +// TODO more dolibarize with new stripe function and stripeconnect +//if ($stripe->getStripeCustomerAccount($facture->socid)) { +//$account=\Stripe\Account::retrieve("".$stripe->getStripeCustomerAccount($facture->socid).""); +//} + + if (($account->type=='custom' or $account->type=='express') && $entity==1) { + print ''; + + print ''; + + print ''; + // Default + print ''; + print ''; + } + if (empty($input)&&!$stripe->getStripeCustomerAccount($facture->socid)) + { + print ''; + } + + print "
    '.$langs->trans('Type').''.$langs->trans('Informations').'
    id!=$source) or ($src->object=='source' && $src->card->three_d_secure=='required')) { print'class="opacitymedium"';} +print'>id!=$source) or ($src->object=='source' && $src->card->three_d_secure=='required')) { + print ' disabled'; + } elseif (($customerstripe->default_source==$src->id && $action != 'add_paiement') or ($source==$src->id && $action == 'add_paiement')) { + print ' checked'; + } + print '>id!=$source) or ($src->object=='source' && $src->card->three_d_secure=='required')) { print'class="opacitymedium"';} + +print' >'; +if ($src->object=='card'){ +if ($src->brand == 'Visa') {$brand='cc-visa';} +elseif ($src->brand == 'MasterCard') {$brand='cc-mastercard';} +elseif ($src->brand == 'American Express') {$brand='cc-amex';} +elseif ($src->brand == 'Discover') {$brand='cc-discover';} +elseif ($src->brand == 'JCB') {$brand='cc-jcb';} +elseif ($src->brand == 'Diners Club') {$brand='cc-diners-club';} +else {$brand='credit-card-alt';} +print ''; +} +elseif ($src->object=='source' && $src->type=='card'){ +if ($src->card->brand == 'Visa') {$brand='cc-visa';} +elseif ($src->card->brand == 'MasterCard') {$brand='cc-mastercard';} +elseif ($src->card->brand == 'American Express') {$brand='cc-amex';} +elseif ($src->card->brand == 'Discover') {$brand='cc-discover';} +elseif ($src->card->brand == 'JCB') {$brand='cc-jcb';} +elseif ($src->card->brand == 'Diners Club') {$brand='cc-diners-club';} +else {$brand='credit-card-alt';} + +print ''; +} +elseif ($src->object=='source' && $src->type=='sepa_debit'){ +print ''; +} +print 'id!=$source) or ($src->object=='source' && $src->card->three_d_secure=='required')) { print'class="opacitymedium"';} +print' >'; +if ($src->object=='card'){ +print '**** '.$src->last4.'
    Exp. '.$src->exp_month.'/'.$src->exp_year.''; +print '
    '; + if ($src->country) + { + $img=picto_from_langcode($src->country); + print $img?$img.' ':''; + print getCountry($src->country,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; +} +elseif ($src->object=='source' && $src->type=='card'){ + print $src->owner->name.'
    **** '.$src->card->last4.' - '.$src->card->exp_month.'/'.$src->card->exp_year.''; +print '
    '; + if ($src->card->country) + { + $img=picto_from_langcode($src->card->country); + print $img?$img.' ':''; + print getCountry($src->card->country,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; +} +elseif ($src->object=='source' && $src->type=='sepa_debit'){ +print 'info sepa'; +print ''; + if ($src->sepa_debit->country) + { + $img=picto_from_langcode($src->sepa_debit->country); + print $img?$img.' ':''; + print getCountry($src->sepa_debit->country,1); + } + else print img_warning().' '.$langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("CompanyCountry")).''; +} +print 'id!=$source) or ($src->object=='source' && $src->card->three_d_secure=='required')) { print'class="opacitymedium"';} +print'>'; + if (($customerstripe->default_source==$src->id)) { + print ""; + } + print '
    getStripeCustomerAccount($facture->socid)!=$source) { print'class="opacitymedium"';} + print'>global->STRIPE_EXTERNAL_ACCOUNT && $action == 'add_paiement')) { + print ' checked'; + } elseif ($action == 'add_paiement' && $conf->global->STRIPE_EXTERNAL_ACCOUNT!=$source) { + print ' disabled'; + } + print '>getStripeCustomerAccount($facture->socid)!=$source) { print'class="opacitymedium"';} + print '>getStripeCustomerAccount($facture->socid)!=$source) { print'class="opacitymedium"';} + print'>'.$langs->trans('sold'); + print'id!=$source) { print'class="opacitymedium"';} + print'>'; + + print 'id!=$source) { print'class="opacitymedium"';} + print'>'; + // if (($customer->default_source!=$src->id)) { + // print img_picto($langs->trans("Disabled"),'off'); + // } else { + // print img_picto($langs->trans("Default"),'on'); + // } + print '
    '.$langs->trans("None").'
    "; + + + /* + * List of unpaid invoices + */ + + $sql = 'SELECT f.rowid as facid, f.facnumber, f.total_ttc, f.multicurrency_code, f.multicurrency_total_ttc, f.type, '; + $sql.= ' f.datef as df, f.fk_soc as socid'; + $sql.= ' FROM '.MAIN_DB_PREFIX.'facture as f'; + + if(!empty($conf->global->FACTURE_PAYMENTS_ON_DIFFERENT_THIRDPARTIES_BILLS)) { + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON (f.fk_soc = s.rowid)'; + } + + $sql.= ' WHERE f.entity = '.$conf->entity; + $sql.= ' AND (f.fk_soc = '.$facture->socid; + + if(!empty($conf->global->FACTURE_PAYMENTS_ON_DIFFERENT_THIRDPARTIES_BILLS) && !empty($facture->thirdparty->parent)) { + $sql.= ' OR f.fk_soc IN (SELECT rowid FROM '.MAIN_DB_PREFIX.'societe WHERE parent = '.$facture->thirdparty->parent.')'; + } + + $sql.= ') AND f.paye = 0'; + $sql.= ' AND f.fk_statut = 1'; // Statut=0 => not validated, Statut=2 => canceled + if ($facture->type != 2) + { + $sql .= ' AND type IN (0,1,3,5)'; // Standard invoice, replacement, deposit, situation + } + else + { + $sql .= ' AND type = 2'; // If paying back a credit note, we show all credit notes + } + + // Sort invoices by date and serial number: the older one comes first + $sql.=' ORDER BY f.datef ASC, f.facnumber ASC'; + + $resql = $db->query($sql); + if ($resql) + { + $num = $db->num_rows($resql); + if ($num > 0) + { + $sign=1; + if ($facture->type == 2) $sign=-1; + + $arraytitle=$langs->trans('Invoice'); + if ($facture->type == 2) $arraytitle=$langs->trans("CreditNotes"); + $alreadypayedlabel=$langs->trans('Received'); + $multicurrencyalreadypayedlabel=$langs->trans('MulticurrencyReceived'); + if ($facture->type == 2) { $alreadypayedlabel=$langs->trans("PaidBack"); $multicurrencyalreadypayedlabel=$langs->trans("MulticurrencyPaidBack"); } + $remaindertopay=$langs->trans('RemainderToTake'); + $multicurrencyremaindertopay=$langs->trans('MulticurrencyRemainderToTake'); + if ($facture->type == 2) { $remaindertopay=$langs->trans("RemainderToPayBack"); $multicurrencyremaindertopay=$langs->trans("MulticurrencyRemainderToPayBack"); } + + $i = 0; + //print '
    '; + print '
    '; + print_barre_liste($langs->trans('StripeInvoiceList').' '.$typeElementString.' '.$button, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder,'',$num, '', ''); + print ''; + print ''; + print ''; + print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + print ''; + print ''; + print ''; + print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + print ''; + print "\n"; + + $var=true; + $total=0; + $totalrecu=0; + $totalrecucreditnote=0; + $totalrecudeposits=0; + + while ($i < $num) + { + $objp = $db->fetch_object($resql); + $var=!$var; + + $soc = new Societe($db); + $soc->fetch($objp->socid); + + $invoice=new Facture($db); + $invoice->fetch($objp->facid); + $paiement = $invoice->getSommePaiement(); + $creditnotes=$invoice->getSumCreditNotesUsed(); + $deposits=$invoice->getSumDepositsUsed(); + $alreadypayed=price2num($paiement + $creditnotes + $deposits,'MT'); + $remaintopay=price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits,'MT'); + + // Multicurrency Price + if (!empty($conf->multicurrency->enabled)) + { + $multicurrency_payment = $invoice->getSommePaiement(1); + $multicurrency_creditnotes=$invoice->getSumCreditNotesUsed(1); + $multicurrency_deposits=$invoice->getSumDepositsUsed(1); + $multicurrency_alreadypayed=price2num($multicurrency_payment + $multicurrency_creditnotes + $multicurrency_deposits,'MT'); + $multicurrency_remaintopay=price2num($invoice->multicurrency_total_ttc - $multicurrency_payment - $multicurrency_creditnotes - $multicurrency_deposits,'MT'); + } + + print ''; + + print '\n"; + + // Date + print '\n"; + + // Currency + if (!empty($conf->multicurrency->enabled)) print '\n"; + + // Multicurrency Price + if (!empty($conf->multicurrency->enabled)) + { + print ''; + + // Multicurrency Price + print ''; + + // Multicurrency Price + print ''; + } + + // Price + print ''; + + // Received or paid back + print ''; + + // Remain to take or to pay back + print ''; + //$test= price(price2num($objp->total_ttc - $paiement - $creditnotes - $deposits)); + + // Amount + print '"; + + // Multicurrency Price + if (! empty($conf->multicurrency->enabled)) + { + print '"; + } + + // Warning + print ''; + + $parameters=array(); + $reshook=$hookmanager->executeHooks('printObjectLine',$parameters,$objp,$action); // Note that $action and $object may have been modified by hook + + print "\n"; + + $total+=$objp->total; + $total_ttc+=$objp->total_ttc; + $totalrecu+=$paiement; + $totalrecucreditnote+=$creditnotes; + $totalrecudeposits+=$deposits; + $i++; + } + if ($i > 1) + { + $amount=round(price($sign * price2num($total_ttc - $totalrecu - $totalrecucreditnote - $totalrecudeposits,'MT'))*100); + // Print total + print ''; + print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + if (!empty($conf->multicurrency->enabled)) print ''; + print ''; + print ''; + print ''; + print ''; + if (!empty($conf->multicurrency->enabled)) {print '';} + print "\n"; + } + print "
    '.$arraytitle.''.$langs->trans('Date').''.$langs->trans('Currency').''.$langs->trans('MulticurrencyAmountTTC').''.$multicurrencyalreadypayedlabel.''.$multicurrencyremaindertopay.''.$langs->trans('AmountTTC').''.$alreadypayedlabel.''.$remaindertopay.''.$langs->trans('PaymentAmount').''.$langs->trans('MulticurrencyPaymentAmount').' 
    '; + print $invoice->getNomUrl(1,''); + if($objp->socid != $facture->thirdparty->id) print ' - '.$soc->getNomUrl(1).' '; + print "'.dol_print_date($db->jdate($objp->df),'day')."'.$objp->multicurrency_code."'; + if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) print price($sign * $objp->multicurrency_total_ttc); + print ''; + if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) + { + print price($sign * $multicurrency_payment); + if ($multicurrency_creditnotes) print '+'.price($multicurrency_creditnotes); + if ($multicurrency_deposits) print '+'.price($multicurrency_deposits); + } + print ''; + if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) print price($sign * $multicurrency_remaintopay); + print ''.price($sign * $objp->total_ttc).''.price($sign * $paiement); + if ($creditnotes) print '+'.price($creditnotes); + if ($deposits) print '+'.price($deposits); + print ''.price($sign * $remaintopay).''; + + // Add remind amount + $namef = 'amount_'.$objp->facid; + $nameRemain = 'remain_'.$objp->facid; + + if ($action != 'add_paiement') + { + if (!empty($conf->use_javascript_ajax)) + print img_picto("Auto fill",'rightarrow', "class='AutoFillAmout' data-rowname='".$namef."' data-value='".($sign * $remaintopay)."'"); + print ''; + print ''; + } + else + { + print ''; + print ''; + } + print "'; + + // Add remind multicurrency amount + $namef = 'multicurrency_amount_'.$objp->facid; + $nameRemain = 'multicurrency_remain_'.$objp->facid; + + if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) + { + if ($action != 'add_paiement') + { + if (!empty($conf->use_javascript_ajax)) + print img_picto("Auto fill",'rightarrow', "class='AutoFillAmout' data-rowname='".$namef."' data-value='".($sign * $multicurrency_remaintopay)."'"); + print ''; + print ''; + } + else + { + print ''; + print ''; + } + } + print "'; + //print "xx".$amounts[$invoice->id]."-".$amountsresttopay[$invoice->id]."
    "; + if ($amounts[$invoice->id] && (abs($amounts[$invoice->id]) > abs($amountsresttopay[$invoice->id])) + || $multicurrency_amounts[$invoice->id] && (abs($multicurrency_amounts[$invoice->id]) > abs($multicurrency_amountsresttopay[$invoice->id]))) + { + print ' '.img_warning($langs->trans("PaymentHigherThanReminderToPay")); + } + print '
    '.$langs->trans('TotalTTC').''.price($sign * $total_ttc).''.price($sign * $totalrecu); + if ($totalrecucreditnote) print '+'.price($totalrecucreditnote); + if ($totalrecudeposits) print '+'.price($totalrecudeposits); + print ''.price($sign * price2num($total_ttc - $totalrecu - $totalrecucreditnote - $totalrecudeposits,'MT')).'
    "; + //print "
    '; + print ''; + print_liste_field_titre('Invoice',$_SERVER["PHP_SELF"],'facnumber','','','',$sortfield,$sortorder); + print_liste_field_titre('Date',$_SERVER["PHP_SELF"],'dp','','','',$sortfield,$sortorder); + print_liste_field_titre('Type',$_SERVER["PHP_SELF"],'libelle','','','',$sortfield,$sortorder); + print_liste_field_titre('Amount',$_SERVER["PHP_SELF"],'fa_amount','','','align="right"',$sortfield,$sortorder); + print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch '); + print "\n"; + + while ($i < min($num,$limit)) + { + $objp = $db->fetch_object($resql); + $var=!$var; + print ''; + print '\n"; + print '\n"; + print '\n"; + print ''; + + $parameters=array(); + $reshook=$hookmanager->executeHooks('printObjectLine',$parameters,$objp,$action); // Note that $action and $object may have been modified by hook + + print ''; + $i++; + } + print '
    '.$objp->facnumber."'.dol_print_date($db->jdate($objp->dp))."'.$objp->paiement_type.' '.$objp->num_paiement."'.price($objp->amount).' 
    '; + } +} + +llxFooter(); + +$db->close(); diff --git a/htdocs/stripe/transaction.php b/htdocs/stripe/transaction.php new file mode 100644 index 00000000000..6628a3b46cd --- /dev/null +++ b/htdocs/stripe/transaction.php @@ -0,0 +1,210 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Put here all includes required by your class file + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; +require_once DOL_DOCUMENT_ROOT.'/stripe/class/stripe.class.php'; +//require_once DOL_DOCUMENT_ROOT.'/core/lib/stripe.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; +require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT . '/accountancy/class/accountingjournal.class.php'; + +$langs->load("compta"); +$langs->load("salaries"); +$langs->load("bills"); +$langs->load("hrm"); +$langs->load("stripe"); + +// Security check +$socid = GETPOST("socid","int"); +if ($user->societe_id) $socid=$user->societe_id; +//$result = restrictedArea($user, 'salaries', '', '', ''); + +$limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit; +$rowid = GETPOST("rowid",'alpha'); +$sortfield = GETPOST("sortfield",'alpha'); +$sortorder = GETPOST("sortorder",'alpha'); +$page = GETPOST("page",'int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +$offset = $conf->liste_limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; + + + +/* + * View + */ + +$form = new Form($db); +$societestatic = new Societe($db); +$memberstatic = new Adherent($db); +$acc = new Account($db); +$stripe = new Stripe($db); + +llxHeader('', $langs->trans("StripeTransactionList")); + +if (! empty($conf->stripe->enabled) && (empty($conf->global->STRIPE_LIVE) || GETPOST('forcesandbox','alpha'))) +{ + $service = 'StripeTest'; + dol_htmloutput_mesg($langs->trans('YouAreCurrentlyInSandboxMode', 'Stripe'), '', 'warning'); +} +else +{ + $service = 'StripeLive'; +} + +$stripeaccount = $stripe->getStripeAccount($service); +/*if (empty($stripeaccount)) +{ + print $langs->trans('ErrorStripeAccountNotDefined'); +}*/ + +if (! $rowid) { + + print '
    '; + if ($optioncss != '') + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + $title=$langs->trans("StripeTransactionList"); + $title.=($stripeaccount?' (Stripe connection with Stripe OAuth Connect account '.$stripeaccount.')':' (Stripe connection with keys from Stripe module setup)'); + + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $totalnboflines, 'title_accountancy.png', 0, '', '', $limit); + + print '
    '; + print '' . "\n"; + + print ''; + print_liste_field_titre("Ref", $_SERVER["PHP_SELF"], "", "", "", "", $sortfield, $sortorder); + //print_liste_field_titre("StripeCustomerId",$_SERVER["PHP_SELF"],"","","","",$sortfield,$sortorder); + //print_liste_field_titre("CustomerId", $_SERVER["PHP_SELF"], "", "", "", "", $sortfield, $sortorder); + print_liste_field_titre("Origin", $_SERVER["PHP_SELF"], "", "", "", "", $sortfield, $sortorder); + print_liste_field_titre("DatePayment", $_SERVER["PHP_SELF"], "", "", "", 'align="center"', $sortfield, $sortorder); + print_liste_field_titre("Type", $_SERVER["PHP_SELF"], "", "", "", 'align="left"', $sortfield, $sortorder); + print_liste_field_titre("Paid", $_SERVER["PHP_SELF"], "", "", "", 'align="right"', $sortfield, $sortorder); + print_liste_field_titre("Fee", $_SERVER["PHP_SELF"], "", "", "", 'align="right"', $sortfield, $sortorder); + print_liste_field_titre("Status", $_SERVER["PHP_SELF"], "", "", "", 'align="right"'); + print "\n"; + + print "\n"; + + if ($stripeaccount) + { + $txn = \Stripe\BalanceTransaction::all(array("limit" => $limit), array("stripe_account" => $stripeaccount)); + } + else + { + $txn = \Stripe\BalanceTransaction::all(array("limit" => $limit)); + } + + foreach ($txn->data as $txn) + { + //$charge = $txn; + //var_dump($txn); + + // The metadata FULLTAG is defined by the online payment page + /*$FULLTAG=$charge->metadata->FULLTAG; + + // Save into $tmparray all metadata + $tmparray = dolExplodeIntoArray($FULLTAG,'.','='); + // Load origin object according to metadata + if (! empty($tmparray['CUS'])) + { + $societestatic->fetch($tmparray['CUS']); + } + else + { + $societestatic->id = 0; + } + if (! empty($tmparray['MEM'])) + { + $memberstatic->fetch($tmparray['MEM']); + } + else + { + $memberstatic->id = 0; + }*/ + + $societestatic->fetch($charge->metadata->idcustomer); + $societestatic->id = $charge->metadata->idcustomer; + $societestatic->lastname = $obj->lastname; + $societestatic->firstname = $obj->firstname; + $societestatic->admin = $obj->admin; + $societestatic->login = $obj->login; + $societestatic->email = $obj->email; + $societestatic->societe_id = $obj->fk_soc; + + print ''; + + // Ref + print "\n"; + // Stripe customer + //print "\n"; + // Link + /*print "\n";*/ + // Origine + print "\n"; + // Date payment + print '\n"; + // Type + print ''; + // Amount + print ""; + print ""; + // Status + print "'; + print "\n"; + } + print "
    " . $txn->source . "".$charge->customer.""; + if ($societestatic->id > 0) + { + print $societestatic->getNomUrl(1); + } + if ($memberstatic->id > 0) + { + print $memberstatic->getNomUrl(1); + } + print ""; + print $FULLTAG; + if ($charge->metadata->source=="order"){ + $object = new Commande($db); + $object->fetch($charge->metadata->idsource); + print "".img_picto('', 'object_order')." ".$object->ref.""; + } elseif ($charge->metadata->source=="invoice"){ + $object = new Facture($db); + $object->fetch($charge->metadata->idsource); + print "".img_picto('', 'object_invoice')." ".$object->ref.""; + } + print "' . dol_print_date($txn->created, '%d/%m/%Y %H:%M') . "' . $txn->type . '" . price(($txn->amount) / 100) . "" . price(($txn->fee) / 100) . ""; + print $txn->status; + print '
    "; + print '
    '; + print '
    '; +} else {} + +llxFooter(); +$db->close(); diff --git a/htdocs/supplier_proposal/admin/supplier_proposal_extrafields.php b/htdocs/supplier_proposal/admin/supplier_proposal_extrafields.php index 0e84519e406..8d9429051f8 100644 --- a/htdocs/supplier_proposal/admin/supplier_proposal_extrafields.php +++ b/htdocs/supplier_proposal/admin/supplier_proposal_extrafields.php @@ -59,7 +59,7 @@ $textobject=$langs->transnoentitiesnoconv("CommRequests"); llxHeader('',$langs->trans("SupplierProposalSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SupplierProposalSetup"),$linkback,'title_setup'); diff --git a/htdocs/supplier_proposal/admin/supplier_proposaldet_extrafields.php b/htdocs/supplier_proposal/admin/supplier_proposaldet_extrafields.php index c8d6cf4cae1..75a0374a14c 100644 --- a/htdocs/supplier_proposal/admin/supplier_proposaldet_extrafields.php +++ b/htdocs/supplier_proposal/admin/supplier_proposaldet_extrafields.php @@ -65,7 +65,7 @@ $textobject=$langs->transnoentitiesnoconv("CommRequests"); llxHeader('',$langs->trans("SupplierProposalSetup")); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("SupplierProposalSetup"),$linkback,'title_setup'); $head = supplier_proposal_admin_prepare_head(); diff --git a/htdocs/supplier_proposal/card.php b/htdocs/supplier_proposal/card.php index c4f5c5c6ba9..ade114f11b3 100644 --- a/htdocs/supplier_proposal/card.php +++ b/htdocs/supplier_proposal/card.php @@ -643,8 +643,11 @@ if (empty($reshook)) $label, $array_options, $ref_supplier, - $fk_unit - ); + $fk_unit, + '', + 0, + $productsupplier->fourn_multicurrency_unitprice + ); //var_dump($tva_tx);var_dump($productsupplier->fourn_pu);var_dump($price_base_type);exit; } if ($idprod == -99 || $idprod == 0) @@ -927,9 +930,11 @@ if (empty($reshook)) } else if ($action == 'update_extras') { + $object->oldcopy = dol_clone($object); + // Fill array 'array_options' with data from update form $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute', 'none')); if ($ret < 0) $error++; if (! $error) @@ -993,9 +998,9 @@ if ($action == 'create') $projectid = (! empty($objectsrc->fk_project) ? $objectsrc->fk_project : ''); $soc = $objectsrc->thirdparty; - $cond_reglement_id = (! empty($objectsrc->cond_reglement_id)?$objectsrc->cond_reglement_id:(! empty($soc->cond_reglement_id)?$soc->cond_reglement_id:1)); + $cond_reglement_id = (! empty($objectsrc->cond_reglement_id)?$objectsrc->cond_reglement_id:(! empty($soc->cond_reglement_id)?$soc->cond_reglement_id:0)); // TODO maybe add default value option $mode_reglement_id = (! empty($objectsrc->mode_reglement_id)?$objectsrc->mode_reglement_id:(! empty($soc->mode_reglement_id)?$soc->mode_reglement_id:0)); - $remise_percent = (! empty($objectsrc->remise_percent)?$objectsrc->remise_percent:(! empty($soc->remise_percent)?$soc->remise_percent:0)); + $remise_percent = (! empty($objectsrc->remise_percent)?$objectsrc->remise_percent:(! empty($soc->remise_supplier_percent)?$soc->remise_supplier_percent:0)); $remise_absolue = (! empty($objectsrc->remise_absolue)?$objectsrc->remise_absolue:(! empty($soc->remise_absolue)?$soc->remise_absolue:0)); // Replicate extrafields @@ -1048,6 +1053,21 @@ if ($action == 'create') } print '' . "\n"; + if ($soc->id > 0) + { + // Discounts for third party + print '' . $langs->trans('Discounts') . ''; + + $absolute_discount = $soc->getAvailableDiscounts('', '', 0, 1); + + $thirdparty = $soc; + $discount_type = 1; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . GETPOST('origin') . '&originid=' . GETPOST('originid')); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + + print ''; + } + // Terms of payment print '' . $langs->trans('PaymentConditionsShort') . ''; $form->select_conditions_paiements(GETPOST('cond_reglement_id') > 0 ? GETPOST('cond_reglement_id') : $cond_reglement_id, 'cond_reglement_id', -1, 1); @@ -1387,6 +1407,29 @@ if ($action == 'create') print ''; + // Relative and absolute discounts + if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) { + $filterabsolutediscount = "fk_invoice_supplier_source IS NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + $filtercreditnote = "fk_invoice_supplier_source IS NOT NULL"; // If we want deposit to be substracted to payments only and not to total of final invoice + } else { + $filterabsolutediscount = "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')"; + $filtercreditnote = "fk_invoice_supplier_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS PAID)%')"; + } + + print ''; + // Payment term print ''; + print ''; } } diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 3fcdf411a24..6d992246eea 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -2109,7 +2109,7 @@ class User extends CommonObject * * @param int $withpictoimg Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto, -1=Include photo into link, -2=Only picto photo, -3=Only photo very small) * @param string $option On what the link point to ('leave', 'nolink', ) - * @param integer $infologin Add complete info tooltip + * @param integer $infologin 0=Add default info tooltip, 1=Add complete info tooltip, -1=No info tooltip * @param integer $notooltip 1=Disable tooltip on picto and name * @param int $maxlen Max length of visible user name * @param int $hidethirdpartylogo Hide logo of thirdparty if user is external user @@ -2136,6 +2136,7 @@ class User extends CommonObject $label.= '
    '; } + // Info Login $label.= '
    '; $label.= '' . $langs->trans("User") . '
    '; $label.= '' . $langs->trans('Name') . ': ' . $this->getFullName($langs,''); @@ -2144,20 +2145,18 @@ class User extends CommonObject $label.= '
    ' . $langs->trans("EMail").': '.$this->email; if (! empty($this->admin)) $label.= '
    ' . $langs->trans("Administrator").': '.yn($this->admin); - if (! empty($this->societe_id) ) // Add thirdparty for external users + if (! empty($this->socid) ) // Add thirdparty for external users { $thirdpartystatic = new Societe($db); - $thirdpartystatic->fetch($this->societe_id); + $thirdpartystatic->fetch($this->socid); if (empty($hidethirdpartylogo)) $companylink = ' '.$thirdpartystatic->getNomUrl(2, (($option == 'nolink')?'nolink':'')); // picto only of company $company=' ('.$langs->trans("Company").': '.$thirdpartystatic->name.')'; } - $type=($this->societe_id?$langs->trans("External").$company:$langs->trans("Internal")); + $type=($this->socid?$langs->trans("External").$company:$langs->trans("Internal")); $label.= '
    ' . $langs->trans("Type") . ': ' . $type; $label.= '
    ' . $langs->trans("Status").': '.$this->getLibStatut(0); $label.='
    '; - - // Info Login - if ($infologin) + if ($infologin > 0) { $label.= '
    '; $label.= '
    '.$langs->trans("Connection").''; @@ -2176,6 +2175,7 @@ class User extends CommonObject if (! empty($conf->browser->phone)) $label.= '
    '.$langs->trans("Phone").': '.$conf->browser->phone; if (! empty($_SESSION["disablemodules"])) $label.= '
    '.$langs->trans("DisabledModules").':
    '.join(', ',explode(',',$_SESSION["disablemodules"])); } + if ($infologin < 0) $label=''; $url = DOL_URL_ROOT.'/user/card.php?id='.$this->id; if ($option == 'leave') $url = DOL_URL_ROOT.'/holiday/list.php?id='.$this->id; @@ -2201,7 +2201,7 @@ class User extends CommonObject $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"'; $linkclose.= ' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; } - if (! is_object($hookmanager)) + /*if (! is_object($hookmanager)) { include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $hookmanager=new HookManager($this->db); @@ -2210,6 +2210,7 @@ class User extends CommonObject $parameters=array('id'=>$this->id); $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ $linkstart.=$linkclose.'>'; $linkend=''; @@ -2221,14 +2222,14 @@ class User extends CommonObject $paddafterimage=''; if (abs($withpictoimg) == 1) $paddafterimage='style="margin-right: 3px;"'; // Only picto - if ($withpictoimg > 0) $picto='
    '.img_object('', 'user', $paddafterimage.' '.($notooltip?'':'class="classfortooltip"'), 0, 0, $notooltip?0:1).'
    '; + if ($withpictoimg > 0) $picto='
    '.img_object('', 'user', $paddafterimage.' '.($notooltip?'':'class="classfortooltip"'), 0, 0, $notooltip?0:1).'
    '; // Picto must be a photo - else $picto='
    '.Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto'.($withpictoimg==-3?'small':''), 'mini', 0, 1).'
    '; + else $picto='
    '.Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto'.($withpictoimg==-3?'small':''), 'mini', 0, 1).'
    '; $result.=$picto; } if ($withpictoimg > -2 && $withpictoimg != 2) { - if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $result.='
    '; + if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $result.='
    '; if ($mode == 'login') $result.=dol_trunc($this->login, $maxlen); else $result.=$this->getFullName($langs,'',($mode == 'firstname' ? 2 : -1),$maxlen); if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $result.='
    '; @@ -2238,6 +2239,18 @@ class User extends CommonObject $result.=$companylink; + global $action; + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('userdao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + return $result; } @@ -2394,8 +2407,8 @@ class User extends CommonObject } if ($this->address && ! empty($conf->global->LDAP_FIELD_ADDRESS)) $info[$conf->global->LDAP_FIELD_ADDRESS] = $this->address; if ($this->zip && ! empty($conf->global->LDAP_FIELD_ZIP)) $info[$conf->global->LDAP_FIELD_ZIP] = $this->zip; - if ($this->town && ! empty($conf->global->LDAP_FIELD_TOWN)) $info[$conf->global->LDAP_FIELD_TOWN] = $this->town; - if ($this->note_public && ! empty($conf->global->LDAP_FIELD_DESCRIPTION)) $info[$conf->global->LDAP_FIELD_DESCRIPTION] = $this->note_public; + if ($this->town && ! empty($conf->global->LDAP_FIELD_TOWN)) $info[$conf->global->LDAP_FIELD_TOWN] = $this->town; + if ($this->note_public && ! empty($conf->global->LDAP_FIELD_DESCRIPTION)) $info[$conf->global->LDAP_FIELD_DESCRIPTION] = dol_string_nohtmltag($this->note_public, 2); if ($this->socid > 0) { $soc = new Societe($this->db); @@ -2731,7 +2744,11 @@ class User extends CommonObject */ function get_full_tree($deleteafterid=0, $filter='') { - global $conf,$user; + global $conf, $user; + global $hookmanager; + + // Actions hooked (by external module) + $hookmanager->initHooks(array('userdao')); $this->users = array(); @@ -2741,12 +2758,12 @@ class User extends CommonObject // Init $this->users array $sql = "SELECT DISTINCT u.rowid, u.firstname, u.lastname, u.fk_user, u.fk_soc, u.login, u.email, u.gender, u.admin, u.statut, u.photo, u.entity"; // Distinct reduce pb with old tables with duplicates $sql.= " FROM ".MAIN_DB_PREFIX."user as u"; - if(! empty($conf->multicompany->enabled) && $conf->entity == 1 && (! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) || (! empty($user->admin) && empty($user->entity)))) - { - $sql.= " WHERE u.entity IS NOT NULL"; - } - else - { + // Add fields from hooks + $parameters=array(); + $reshook=$hookmanager->executeHooks('printUserListWhere',$parameters); // Note that $action and $object may have been modified by hook + if ($reshook > 0) { + $sql.=$hookmanager->resPrint; + } else { $sql.= " WHERE u.entity IN (".getEntity('user').")"; } if ($filter) $sql.=" AND ".$filter; diff --git a/htdocs/user/class/usergroup.class.php b/htdocs/user/class/usergroup.class.php index 5104d813a05..859c5267d04 100644 --- a/htdocs/user/class/usergroup.class.php +++ b/htdocs/user/class/usergroup.class.php @@ -836,7 +836,7 @@ class UserGroup extends CommonObject $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"'; $linkclose.= ' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; } - if (! is_object($hookmanager)) + /*if (! is_object($hookmanager)) { include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $hookmanager=new HookManager($this->db); @@ -845,6 +845,7 @@ class UserGroup extends CommonObject $parameters=array('id'=>$this->id); $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks if ($reshook > 0) $linkclose = $hookmanager->resPrint; + */ $linkstart = ''; @@ -855,6 +856,18 @@ class UserGroup extends CommonObject if ($withpicto != 2) $result.= $this->name; $result .= $linkend; + global $action; + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('groupdao')); + $parameters=array('id'=>$this->id, 'getnomurl'=>$result); + $reshook=$hookmanager->executeHooks('getNomUrl',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) $result = $hookmanager->resPrint; + else $result .= $hookmanager->resPrint; + return $result; } @@ -895,7 +908,7 @@ class UserGroup extends CommonObject // Champs if ($this->name && ! empty($conf->global->LDAP_GROUP_FIELD_FULLNAME)) $info[$conf->global->LDAP_GROUP_FIELD_FULLNAME] = $this->name; //if ($this->name && ! empty($conf->global->LDAP_GROUP_FIELD_NAME)) $info[$conf->global->LDAP_GROUP_FIELD_NAME] = $this->name; - if ($this->note && ! empty($conf->global->LDAP_GROUP_FIELD_DESCRIPTION)) $info[$conf->global->LDAP_GROUP_FIELD_DESCRIPTION] = $this->note; + if ($this->note && ! empty($conf->global->LDAP_GROUP_FIELD_DESCRIPTION)) $info[$conf->global->LDAP_GROUP_FIELD_DESCRIPTION] = dol_string_nohtmltag($this->note, 2); if (! empty($conf->global->LDAP_GROUP_FIELD_GROUPMEMBERS)) { $valueofldapfield=array(); diff --git a/htdocs/user/clicktodial.php b/htdocs/user/clicktodial.php index 64919029536..51e4364e2ba 100644 --- a/htdocs/user/clicktodial.php +++ b/htdocs/user/clicktodial.php @@ -102,7 +102,7 @@ if ($id > 0) $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/document.php b/htdocs/user/document.php index ff7fe9b5ab9..5d89ef1b162 100644 --- a/htdocs/user/document.php +++ b/htdocs/user/document.php @@ -34,11 +34,11 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; $langs->load("users"); $langs->load('other'); - $action=GETPOST('action','aZ09'); $confirm=GETPOST('confirm'); $id=(GETPOST('userid','int') ? GETPOST('userid','int') : GETPOST('id','int')); $ref = GETPOST('ref', 'alpha'); +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userdoc'; // To manage different context of search // Define value to know what current user can do on users $canadduser=(! empty($user->admin) || $user->rights->user->user->creer); @@ -94,8 +94,7 @@ if ($id > 0 || ! empty($ref)) } // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('usercard','userdoc','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','userdoc','globalcard')); /* @@ -134,7 +133,7 @@ if ($object->id) $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/group/card.php b/htdocs/user/group/card.php index 8f99fa1bb81..3f350b7773e 100644 --- a/htdocs/user/group/card.php +++ b/htdocs/user/group/card.php @@ -45,10 +45,13 @@ if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS)) $langs->load("users"); $langs->load("other"); -$id=GETPOST('id', 'int'); -$action=GETPOST('action', 'alpha'); -$confirm=GETPOST('confirm', 'alpha'); -$userid=GETPOST('user', 'int'); +$id = GETPOST('id', 'int'); +$action = GETPOST('action', 'alpha'); +$cancel = GETPOST('cancel', 'aZ09'); +$confirm = GETPOST('confirm', 'alpha'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'groupcard'; // To manage different context of search + +$userid = GETPOST('user', 'int'); // Security check $result = restrictedArea($user, 'user', $id, 'usergroup&usergroup', 'user'); @@ -71,8 +74,9 @@ $extrafields = new ExtraFields($db); $extralabels=$extrafields->fetch_name_optionals_label($object->table_element); // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array -$contextpage=array('groupcard','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('groupcard','globalcard')); + + /** * Actions @@ -84,6 +88,21 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e if (empty($reshook)) { + if ($cancel) + { + if (! empty($backtopage)) + { + header("Location: ".$backtopage); + exit; + } + else + { + header("Location: ".DOL_URL_ROOT.'/user/group/list.php'); + exit; + } + $action=''; + } + // Action remove group if ($action == 'confirm_delete' && $confirm == "yes") { @@ -91,7 +110,7 @@ if (empty($reshook)) { { $object->fetch($id); $object->delete(); - header("Location: index.php"); + header("Location: index.php?restore_lastsearch_values=1"); exit; } else @@ -110,9 +129,9 @@ if (empty($reshook)) { setEventMessages($langs->trans("NameNotDefined"), null, 'errors'); $action="create"; // Go back to create page } else { - $object->nom = trim($_POST["nom"]); // For backward compatibility - $object->name = trim($_POST["nom"]); - $object->note = trim($_POST["note"]); + $object->name = trim(GETPOST("nom",'nohtml')); + $object->nom = $object->name; // For backward compatibility + $object->note = trim(GETPOST("note",'none')); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost($extralabels,$object); @@ -193,9 +212,9 @@ if (empty($reshook)) { $object->oldcopy = clone $object; - $object->name = trim($_POST["group"]); + $object->name = trim(GETPOST("group",'nohtml')); $object->nom = $object->name; // For backward compatibility - $object->note = dol_htmlcleanlastbr($_POST["note"]); + $object->note = dol_htmlcleanlastbr(GETPOST("note",'none')); // Fill array 'array_options' with data from add form $ret = $extrafields->setOptionalsFromPost($extralabels,$object); @@ -258,7 +277,7 @@ if ($action == 'create') print "
    "; print ''; - print ''; + print ''; // Multicompany if (! empty($conf->multicompany->enabled) && is_object($mc)) @@ -294,7 +313,11 @@ if ($action == 'create') dol_fiche_end(); - print '
    '; + print '
    '; + print ''; + print '   '; + print ''; + print '
    '; print ""; } @@ -309,8 +332,6 @@ else { if ($id) { - $object->fetch($id); - $head = group_prepare_head($object); $title = $langs->trans("Group"); @@ -330,7 +351,7 @@ else { dol_fiche_head($head, 'group', $title, -1, 'group'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); @@ -343,7 +364,7 @@ else if (! empty($conf->mutlicompany->enabled)) { print ''; - print '".''; - print '\n"; } @@ -460,7 +481,7 @@ else if (! empty($user->admin)) { print ''; - print img_delete($langs->trans("RemoveFromGroup")); + print img_picto($langs->trans("RemoveFromGroup"), 'unlink'); print ''; } else @@ -490,7 +511,7 @@ else $genallowed = $user->rights->user->user->creer; $delallowed = $user->rights->user->user->supprimer; - $somethingshown = $formfile->show_documents('usergroup', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', 0, '', $soc->default_lang); + $somethingshown = $formfile->showdocuments('usergroup', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->modelpdf, 1, 0, 0, 28, 0, '', 0, '', $soc->default_lang); // Show links to link elements $linktoelem = $form->showLinkToObjectBlock($object, null, null); @@ -520,7 +541,7 @@ else print '
    ' . $langs->trans('Discounts') . ''; + + $absolute_discount = $soc->getAvailableDiscounts('', $filterabsolutediscount, 0, 1); + $absolute_creditnote = $soc->getAvailableDiscounts('', $filtercreditnote, 0, 1); + $absolute_discount = price2num($absolute_discount, 'MT'); + $absolute_creditnote = price2num($absolute_creditnote, 'MT'); + + $thirdparty = $soc; + $discount_type = 1; + $backtopage = urlencode($_SERVER["PHP_SELF"] . '?id=' . $object->id); + include DOL_DOCUMENT_ROOT.'/core/tpl/object_discounts.tpl.php'; + + print '
    '; print ' - + dol_no_mouse_hover; //var_dump($user->conf->THEME_ELDY_RGB); $useboldtitle=(isset($conf->global->THEME_ELDY_USEBOLDTITLE)?$conf->global->THEME_ELDY_USEBOLDTITLE:0); -$borderwith=2; +$borderwidth=1; // Case of option always editable if (! isset($conf->global->THEME_ELDY_BACKBODY)) $conf->global->THEME_ELDY_BACKBODY=$colorbackbody; @@ -277,7 +277,11 @@ input.select2-input { .liste_titre input[name=month_date_when], .liste_titre input[name=monthvalid], .liste_titre input[name=search_ordermonth], .liste_titre input[name=search_deliverymonth], .liste_titre input[name=search_smonth], .liste_titre input[name=search_month], .liste_titre input[name=search_emonth], .liste_titre input[name=smonth], .liste_titre input[name=month], .liste_titre select[name=month], -.liste_titre input[name=month_lim], .liste_titre input[name=month_start], .liste_titre input[name=month_end], .liste_titre input[name=month_create] { +.liste_titre input[name=month_lim], .liste_titre input[name=month_start], .liste_titre input[name=month_end], .liste_titre input[name=month_create], +.liste_titre input[name=search_month_lim], .liste_titre input[name=search_month_start], .liste_titre input[name=search_month_end], .liste_titre input[name=search_month_create], +.liste_titre input[name=search_day_date_when], .liste_titre input[name=search_month_date_when], .liste_titre input[name=search_year_date_when], +.liste_titre input[name=search_month_create], .liste_titre input[name=search_month_start], .liste_titre input[name=search_month_end] +{ margin-right: 4px; } input[type=submit] { @@ -294,12 +298,12 @@ input, input.flat, textarea, textarea.flat, form.flat select, select, select.fla } input { - line-height: 17px; - padding: 4px; + line-height: 1.3em; + padding: 5px; padding-left: 5px; } select { - padding: 4px; + padding: 5px; padding-left: 2px; } input, select { @@ -355,23 +359,24 @@ input.buttonpayment { line-height: 24px; padding: 8px; background: none; - padding-left: 30px; + padding-left: 38px; text-align: ; border: 1px solid #ddd; background-color: #eee; white-space: normal; + box-shadow: 1px 1px 8px #bbb; } input.buttonpaymentcb { background-image: url(); background-size: 26px; background-repeat: no-repeat; - background-position: 2px 11px; + background-position: 5px 11px; } input.buttonpaymentcheque { background-image: url(); background-size: 24px; background-repeat: no-repeat; - background-position: 2px 8px; + background-position: 5px 8px; } input.buttonpaymentpaypal { background-image: url(); @@ -407,6 +412,12 @@ td.onholidayallday { td.leftborder, td.hide0 { border-left: 1px solid #ccc; } +td.leftborder, td.hide6 { + border-right: 1px solid #ccc; +} +td.rightborder { + border-right: 1px solid #ccc; +} td.actionbuttons a { padding-left: 6px; @@ -414,7 +425,6 @@ td.actionbuttons a { select.flat, form.flat select { font-weight: normal; font-size: unset; - height: 2em; } .optionblue { color: rgb(); @@ -464,9 +474,6 @@ input[name=price], input[name=weight], input[name=volume], input[name=surface], input[name=surface] { margin-right: 4px; } fieldset { border: 1px solid #AAAAAA !important; } .legendforfieldsetstep { padding-bottom: 10px; } -.trextrafieldseparator td { - border-bottom: 2px solid rgb(120,120,120) !important; -} input#onlinepaymenturl, input#directdownloadlink { opacity: 0.7; } @@ -594,7 +601,7 @@ textarea.centpercent { .liste_titre .nowrap { white-space: nowrap; } -.nowraponall { +.nowraponall { /* no wrap on all devices */ white-space: nowrap; } .wordwrap { @@ -624,6 +631,9 @@ textarea.centpercent { .paddingright2 { padding-: 2px; } +.cursordefault { + cursor: default; +} .cursorpointer { cursor: pointer; } @@ -767,13 +777,16 @@ select.flat.selectlimit { .amountpaymentcomplete { color: #008800; font-weight: bold; + font-size: 1.4em; } .amountremaintopay { color: #880000; font-weight: bold; + font-size: 1.4em; } .amountremaintopayback { font-weight: bold; + font-size: 1.4em; } .savingdocmask { margin-top: 6px; @@ -797,6 +810,9 @@ select.flat.selectlimit { .fa-file-text-o, .fa-file-code-o, .fa-file-powerpoint-o, .fa-file-excel-o, .fa-file-word-o, .fa-file-o, .fa-file-image-o, .fa-file-video-o, .fa-file-audio-o, .fa-file-archive-o, .fa-file-pdf-o { color: #055; } +.fa-trash, .fa-crop, .fa-pencil { + font-size: 1.4em; +} /* DOL_XXX for future usage (when left menu has been removed). If we do not use datatable */ /*.table-responsive { @@ -823,6 +839,9 @@ div.fiche>form>div.div-table-responsive { div.fiche>div.tabBar>form>div.div-table-responsive { min-height: 392px; } +div.fiche { + /* text-align: justify; */ +} .flexcontainer { browser->name, array('chrome','firefox'))) echo 'display: inline-flex;'."\n"; ?> @@ -830,19 +849,15 @@ div.fiche>div.tabBar>form>div.div-table-responsive { justify-content: flex-start; } .thumbstat { - flex: 1 1 116px; + min-width: 150px; } .thumbstat150 { - flex: 1 1 170px; + min-width: 170px; } .thumbstat, .thumbstat150 { - /* flex-grow: 1; */ - /* flex-shrink: 1; */ - /* flex-basis: 140px; */ - display: inline; - width: 100%; - justify-content: flex-start; - align-self: flex-start; + flex-grow: 1; + flex-shrink: 0; + min-width: 150px; } select.selectarrowonleft { @@ -863,7 +878,9 @@ select.selectarrowonleft option { /* rule for not too small screen only */ @media only screen and (min-width: px) { + .width25 { width: 25px; } .width50 { width: 50px; } + .width75 { width: 75px; } .width100 { width: 100px; } .width200 { width: 200px; } .minwidth100 { min-width: 100px; } @@ -879,8 +896,10 @@ select.selectarrowonleft option { .minwidth400imp { min-width: 400px !important; } .minwidth500imp { min-width: 500px !important; } } +.widthauto { width: auto; } .width25 { width: 25px; } .width50 { width: 50px; } +.width75 { width: 75px; } .width100 { width: 100px; } .width200 { width: 200px; } .maxwidth25 { max-width: 25px; } @@ -984,10 +1003,15 @@ select.selectarrowonleft option { width: 95%; } + select { + padding-top: 4px; + padding-bottom: 5px; + } input, input[type=text], input[type=password], select, textarea { min-width: 20px; - min-height: 1.4em; - line-height: 1.4em; + font-size: px; + /* min-height: 1.4em; */ + /* line-height: 1.4em; */ /* padding: .4em .1em; */ /* border-bottom: 1px solid #BBB; */ /* max-width: inherit; why this ? */ @@ -1048,6 +1072,13 @@ select.selectarrowonleft option { div.statusrefbis { padding-right: 3px !important; } + /* TODO + div.statusref { + padding-top: 0px !important; + padding-left: 0px !important; + border: none !important; + } + */ input.buttonpayment { min-width: 300px; @@ -1314,11 +1345,13 @@ div.nopadding { .pictowarning, .pictopreview { padding-: 3px; } -.pictoedit, .pictowarning, .pictodelete { +.pictowarning { vertical-align: text-bottom; } -.fiche img.pictoedit { - opacity: 0.7; +.fiche .arearef img.pictoedit, .fiche .arearef span.pictoedit, +.fiche .fichecenter img.pictoedit, .fiche .fichecenter span.pictoedit, +.tagtdnote span.pictoedit { + opacity: 0.4; } .colorthumb { padding-left: 1px !important; @@ -1340,7 +1373,7 @@ div.attachareaformuserfileecm { div.arearef { padding-top: 2px; margin-bottom: 10px; - padding-bottom: 7px; + padding-bottom: 10px; } div.arearefnobottom { padding-top: 2px; @@ -1355,6 +1388,12 @@ div.divphotoref { div.paginationref { padding-bottom: 10px; } +/* TODO +div.statusref { + padding: 10px; + border: 1px solid #bbb; + border-radius: 6px; +} */ div.statusref { float: right; padding-left: 12px; @@ -1400,8 +1439,14 @@ img.photorefnoborder { .underrefbanner { } .underbanner { - border-bottom: px solid rgb(); + border-bottom: px solid rgb(); + /* border-bottom: 2px solid rgb(); */ } +.trextrafieldseparator td { + /* border-bottom: 2px solid rgb() !important; */ + border-bottom: 2px solid rgb() !important; +} + .tdhrthin { margin: 0; padding-bottom: 0 !important; @@ -1652,6 +1697,10 @@ div.mainmenu.project { background-image: url(); } +div.mainmenu.ticketsup { + background-image: url(); +} + div.mainmenu.tools { background-image: url(); } @@ -1663,10 +1712,11 @@ div.mainmenu.website { 'name of class for div') -$moduletomainmenu=array('user'=>'','syslog'=>'','societe'=>'companies','projet'=>'project','propale'=>'commercial','commande'=>'commercial', +$moduletomainmenu=array( + 'user'=>'','syslog'=>'','societe'=>'companies','projet'=>'project','propale'=>'commercial','commande'=>'commercial', 'produit'=>'products','service'=>'products','stock'=>'products', 'don'=>'accountancy','tax'=>'accountancy','banque'=>'accountancy','facture'=>'accountancy','compta'=>'accountancy','accounting'=>'accountancy','adherent'=>'members','import'=>'tools','export'=>'tools','mailing'=>'tools', - 'contrat'=>'commercial','ficheinter'=>'commercial','deplacement'=>'commercial', + 'contrat'=>'commercial','ficheinter'=>'commercial','ticketsup'=>'ticketsup','deplacement'=>'commercial', 'fournisseur'=>'companies', 'barcode'=>'','fckeditor'=>'','categorie'=>'', ); @@ -1682,7 +1732,7 @@ $generic=1; // Put here list of menu entries when the div.mainmenu.menuentry was previously defined $divalreadydefined=array('home','companies','products','commercial','externalsite','accountancy','project','tools','members','agenda','ftp','holiday','hrm','bookmark','cashdesk','ecm','geoipmaxmind','gravatar','clicktodial','paypal','stripe','webservices','website'); // Put here list of menu entries we are sure we don't want -$divnotrequired=array('multicurrency','salaries','margin','opensurvey','paybox','expensereport','incoterm','prelevement','propal','workflow','notification','supplier_proposal','cron','product','productbatch','expedition'); +$divnotrequired=array('multicurrency','salaries','ticketsup','margin','opensurvey','paybox','expensereport','incoterm','prelevement','propal','workflow','notification','supplier_proposal','cron','product','productbatch','expedition'); foreach($mainmenuusedarray as $val) { if (empty($val) || in_array($val,$divalreadydefined)) continue; @@ -1893,6 +1943,16 @@ div.login_block_other { padding-top: 3px; text-align: right; } color: # !important; font-weight: normal !important; } +.login_block_getinfo { + text-align: center; +} +.login_block_getinfo div.login_block_user { + display: block; +} +.login_block_getinfo .atoplogin, .login_block_getinfo .atoplogin:hover { + color: #333 !important; + font-weight: normal !important; +} .alogin, .alogin:hover { font-weight: normal !important; font-size: px !important; @@ -1912,7 +1972,7 @@ img.login, img.printer, img.entity { color: white; font-weight: bold; } -.userimgatoplogin img.userphoto { /* size for user photo in login bar */ +.userimg.atoplogin img.userphoto, .userimgatoplogin img.userphoto { /* size for user photo in login bar */ width: 16px; height: 16px; border-radius: 8px; @@ -1984,7 +2044,7 @@ input.vmenusearchselectcombo[type=text] { .companylogo { } .searchform { padding-top: 10px; } -a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active { white-space: nowrap; font-size:px; font-family: ; text-align: ; font-weight: bold; } +a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active, span.vmenu { white-space: nowrap; font-size:px; font-family: ; text-align: ; font-weight: bold; } font.vmenudisabled { font-size:px; font-family: ; text-align: ; font-weight: bold; color: #aaa; margin-left: 4px; } a.vmenu:link, a.vmenu:visited { color: #; } @@ -2186,13 +2246,24 @@ div.tabBar div.titre { padding-top: 20px; } +/* tabBar used for creation/update/send forms */ div.tabBarWithBottom { padding-bottom: 18px; border-bottom: 1px solid #aaa; } +div.tabBarWithBottom tr { + background: unset !important; +} +div.tabBarWithBottom table.border>tbody>tr:last-of-type>td { + border-bottom: none !important; +} + div.tabBar table.tableforservicepart2:last-child { border-bottom: 1px solid #aaa; } +.tableforservicepart1 .tdhrthin { + height: unset; +} div.popuptabset { padding: 6px; @@ -2250,9 +2321,10 @@ a.tab:link, a.tab:visited, a.tab:hover, a.tab#active { background: rgb() !important; margin: 0 0.2em 0 0.2em !important; - border-right: 1px solid #AAA !important; - border-left: 1px solid #AAA !important; - border-top: 2px solid rgb() !important; + border-right: 1px solid #CCC !important; + border-left: 1px solid #CCC !important; + /* border-top: px solid rgb() !important; */ + border-top: px solid rgb() !important; } a.tab:hover { @@ -2423,16 +2495,20 @@ tr.nocellnopadd td.nobordernopadding, tr.nocellnopadd td.nocellnopadd } -table.border, table.dataTable, .table-border, .table-border-col, .table-key-border-col, .table-val-border-col, div.border { +table.border, table.bordernooddeven, table.dataTable, .table-border, .table-border-col, .table-key-border-col, .table-val-border-col, div.border { border-collapse: collapse !important; padding: 1px 2px 1px 3px; /* t r b l */ } table.borderplus { border: 1px solid #BBB; } -.border tbody tr, .border tbody tr td, div.tabBar table.border tr, div.tabBar table.border tr td, div.tabBar div.border .table-border-row, div.tabBar div.border .table-key-border-col, div.tabBar div.border .table-val-border-col { +.border tbody tr, .bordernooddeven tbody tr, .border tbody tr td, .bordernooddeven tbody tr td, div.tabBar table.border tr, div.tabBar table.border tr td, div.tabBar div.border .table-border-row, div.tabBar div.border .table-key-border-col, div.tabBar div.border .table-val-border-col { height: 22px; } +tr.liste_titre.box_titre td table td, .bordernooddeven tr td { + height: 22px; +} + div.tabBar div.border .table-border-row, div.tabBar div.border .table-key-border-col, div.tabBar .table-val-border-col { vertical-align: middle; } @@ -2443,7 +2519,7 @@ div .tdtop { padding-bottom: 0px; } -table.border td, div.border div div.tagtd { +table.border td, table.bordernooddeven td, div.border div div.tagtd { padding: 5px 2px 5px 2px; border-collapse: collapse; } @@ -2469,30 +2545,49 @@ td.border, div.tagtable div div.border { /* Main boxes */ -.noborderbottom { +.nobordertop, .nobordertop tr:first-of-type td { + border-top: none !important; +} +.noborderbottom, .noborderbottom tr:last-of-type td { border-bottom: none !important; } -.ficheaddleft table.noborder { - margin: 0px 0px 0px 0px; +.bordertop { + border-top: 1px solid rgb(); } +.borderbottom { + border-bottom: 1px solid rgb(); +} + + +/*.ficheaddleft table.noborder { + margin: 0px 0px 0px 0px; +}*/ table.liste, table.noborder, table.formdoc, div.noborder { width: 100%; border-collapse: separate !important; border-spacing: 0px; - border-top-width: px; + border-top-width: px; border-top-color: rgb(); border-top-style: solid; + /* border-top-width: 2px; + border-top-color: rgb(); + border-top-style: solid; */ border-bottom-width: 1px; - border-bottom-color: #BBB; + border-bottom-color: rgb(); border-bottom-style: solid; margin: 0px 0px 5px 0px; } div.tabBar div.ficheaddleft table.noborder:last-of-type { - border-bottom: 1px solid #aaa; + border-bottom: 1px solid rgb(); +} +div.tabBar table.border>tbody>tr:last-of-type>td { + border-bottom-width: 1px; + border-bottom-color: rgb(); + border-bottom-style: solid; } div.tabBar div.ficheaddleft table.noborder { border-bottom: none; @@ -2506,17 +2601,23 @@ table.paddingtopbottomonly tr td { background: rgb() !important; } tr.liste_titre_filter td.liste_titre { - border-bottom: 1px solid #ddd; +/* border-bottom: 1px solid #ddd; */ } .liste_titre_create td, .liste_titre_create th, .liste_titre_create .tagtd { - /*border-top-width: 1px; + border-top-width: 1px; border-top-color: rgb(); - border-top-style: solid;*/ + border-top-style: solid; +} +/*.liste_titre_create td.nobottom, tr#trlinefordates td { + background-color: rgb() !important; +}*/ +tr#trlinefordates td { + border-bottom: 0px !important; } .liste_titre_add td, .liste_titre_add th, .liste_titre_add .tagtd { - border-top-width: 2px; + border-top-width: 1px; border-top-color: rgb(); border-top-style: solid; } @@ -2527,9 +2628,10 @@ table.liste tr, table.noborder tr, div.noborder form { table.liste th, table.noborder th, table.noborder tr.liste_titre td, table.noborder tr.box_titre td { padding: 7px 8px 7px 8px; /* t r b l */ } -table.liste td, table.noborder td, div.noborder form div { +table.liste td, table.noborder td, div.noborder form div, table.tableforservicepart1 td, table.tableforservicepart2 td { padding: 7px 8px 7px 8px; /* t r b l */ line-height: 1.2em; + height: 22px; } div.liste_titre_bydiv .divsearchfield { padding: 2px 1px 2px 7px; /* t r b l */ @@ -2765,7 +2867,7 @@ td.oddeven, table.nohover tr.impair, table.nohover tr.pair, table.nohover tr.imp background-color: # !important; background: # !important; } -td.evenodd, tr.nohoverpair td { +td.evenodd, tr.nohoverpair td, #trlinefordates td { background-color: # !important; background: # !important; } @@ -2802,7 +2904,7 @@ div.liste_titre { padding-bottom: 2px; } div.liste_titre_bydiv { - border-top-width: px; + border-top-width: px; border-top-color: rgb(); border-top-style: solid; @@ -2827,7 +2929,7 @@ div.liste_titre_bydiv, .liste_titre div.tagtr, tr.liste_titre, tr.liste_titre_se { background: rgb(); font-weight: ; - border-bottom: 1px solid #ddd; +/* border-bottom: 1px solid #ddd; */ color: rgb(); font-family: ; @@ -2835,10 +2937,11 @@ div.liste_titre_bydiv, .liste_titre div.tagtr, tr.liste_titre, tr.liste_titre_se } tr.liste_titre th, tr.liste_titre td, th.liste_titre { - border-bottom: 1px solid #888; + border-bottom: 1px solid rgb(); } tr.liste_titre:first-child th, tr:first-child th.liste_titre { - border-bottom: 1px solid #ddd ! important; +/* border-bottom: 1px solid #ddd ! important; */ + border-bottom: unset; } tr.liste_titre th, th.liste_titre, tr.liste_titre td, td.liste_titre, form.liste_titre div { @@ -2851,7 +2954,7 @@ tr.liste_titre th a, th.liste_titre a, tr.liste_titre td a, td.liste_titre a, fo text-shadow: none !important; } tr.liste_titre_topborder td { - border-top-width: px; + border-top-width: px; border-top-color: rgb(); border-top-style: solid; } @@ -2866,7 +2969,8 @@ tr.liste_titre_topborder td { background: transparent; } tr.liste_titre:last-child th.liste_titre, tr.liste_titre:last-child th.liste_titre_sel, tr.liste_titre td.liste_titre, tr.liste_titre td.liste_titre_sel, form.liste_titre div.tagtd { /* For last line of table headers only */ - border-bottom: 1px solid #ddd; + /* border-bottom: 1px solid #ddd; */ + border-bottom: unset; } @@ -2900,24 +3004,34 @@ form.liste_total div { tr.liste_sub_total, tr.liste_sub_total td { border-bottom: 1px solid #aaa; } - +/* to avoid too much border on contract card */ .tableforservicepart1 .impair, .tableforservicepart1 .pair, .tableforservicepart2 .impair, .tableforservicepart2 .pair { background: #FFF; } .tableforservicepart1 tbody tr td, .tableforservicepart2 tbody tr td { border-bottom: none; } +table.tableforservicepart1:first-of-type tr:first-of-type td { + border-top: 1px solid #888; +} +table.tableforservicepart1 tr td { + border-top: 0px; +} .paymenttable, .margintable { - border-top-width: px !important; + /*border-top-width: px !important; border-top-color: rgb() !important; - border-top-style: solid !important; + border-top-style: solid !important;*/ + border-top: none !important; margin: 0px 0px 0px 0px !important; } .paymenttable tr td:first-child, .margintable tr td:first-child { padding-left: 2px; } +.paymenttable, .margintable tr td { + height: 22px; +} /* Disable shadows */ .noshadow { @@ -2931,13 +3045,16 @@ div.tabBar .noborder { } #tablelines tr.liste_titre td, .paymenttable tr.liste_titre td, .margintable tr.liste_titre td, .tableforservicepart1 tr.liste_titre td { - border-bottom: 1px solid #AAA !important; + border-bottom: 1px solid rgb() !important; +} +#tablelines tr td { + height: unset; } - /* Prepare to remove class pair - impair */ -.noborder > tbody > tr:nth-child(even):not(.liste_titre), .liste > tbody > tr:nth-child(even):not(.liste_titre) { +.noborder > tbody > tr:nth-child(even):not(.liste_titre), .liste > tbody > tr:nth-child(even):not(.liste_titre), +.border > tbody > tr:nth-of-type(even):not(.liste_titre), .liste > tbody > tr:nth-of-type(even):not(.liste_titre) { background: linear-gradient(bottom, rgb() 85%, rgb() 100%); background: -o-linear-gradient(bottom, rgb() 85%, rgb() 100%); background: -moz-linear-gradient(bottom, rgb() 85%, rgb() 100%); @@ -2948,7 +3065,9 @@ div.tabBar .noborder { border-bottom: 1px solid #ddd; } -.noborder > tbody > tr:nth-child(odd):not(.liste_titre), .liste > tbody > tr:nth-child(odd):not(.liste_titre) { +.noborder > tbody > tr:nth-child(odd):not(.liste_titre), .liste > tbody > tr:nth-child(odd):not(.liste_titre), +.border > tbody > tr:nth-of-type(odd):not(.liste_titre), .liste > tbody > tr:nth-of-type(odd):not(.liste_titre) +{ background: linear-gradient(bottom, rgb() 85%, rgb() 100%); background: -o-linear-gradient(bottom, rgb() 85%, rgb() 100%); background: -moz-linear-gradient(bottom, rgb() 85%, rgb() 100%); @@ -2961,10 +3080,6 @@ div.tabBar .noborder { ul.noborder li:nth-child(even):not(.liste_titre) { background-color: rgb() !important; - background-color: rgb() !important; - background-color: rgb() !important; - background-color: rgb() !important; - background-color: rgb() !important; } @@ -2984,32 +3099,58 @@ ul.noborder li:nth-child(even):not(.liste_titre) { } .boxstats, .boxstats130 { display: inline-block; - margin: 3px; - /* border: 1px solid #CCC; */ + margin: 8px; + margin-top: 5px; + margin-bottom: 5px; text-align: center; - border-radius: 2px; - background: #eee; + + background: #f8f8f8; + border: 1px solid #eee; + box-shadow: 1px 1px 6px #bbb; + border-radius: 0px; } .boxstats, .boxstats130, .boxstatscontent { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.boxstats { - padding: 3px; - width: 104px; -} .boxstats130 { width: 158px; height: 48px; padding: 3px } +.boxstats { + padding: 3px; + width: 121px; +} .boxstatscontent { padding: 3px; } +.boxstatsempty { + width: 121px; + padding-left: 3px; + padding-right: 3px; + margin-left: 8px; + margin-right: 8px; +} +.boxstats150empty { + width: 158px; + padding-left: 3px; + padding-right: 3px; + margin-left: 8px; + margin-right: 8px; +} @media only screen and (max-width: 767px) { + .boxstats, .boxstats130 { + margin: 3px; + /*border: 1px solid #ccc; + box-shadow: none; */ + } + .boxstats130 { + text-align: + } .thumbstat { flex: 1 1 110px; } @@ -3024,8 +3165,12 @@ ul.noborder li:nth-child(even):not(.liste_titre) { width: 148px; } .boxstats { - width: 100px; + width: 111px; } + .boxstatsempty { + width: 111px; + } + } .boxstats:hover { @@ -3086,6 +3231,15 @@ span.dashboardlineko { .boxtable { margin-bottom: 8px !important; border-bottom-width: 1px; + + border-top: px solid rgb(); + /* border-top: 2px solid rgb() !important; */ +} +table.noborder.boxtable tr td { + height: unset; +} +.boxtablenotop { + border-top-width: 0 !important; } .boxtablenobottom { border-bottom-width: 0 !important; @@ -3107,7 +3261,7 @@ a.valignmiddle.dashboardlineindicator { .box { padding-right: 0px; padding-left: 0px; - padding-bottom: 12px; + padding-bottom: 25px; } tr.box_titre { @@ -3133,9 +3287,6 @@ img.boxhandle, img.boxclose { padding-left: 5px; } -.noborderbottom { - border-bottom: none !important; -} .formboxfilter { vertical-align: middle; margin-bottom: 6px; @@ -3447,6 +3598,13 @@ div.ui-tooltip { /* Calendar */ /* ============================================================================== */ +.ui-datepicker-calendar .ui-state-default, .ui-datepicker-calendar .ui-widget-content .ui-state-default, +.ui-datepicker-calendar .ui-widget-header .ui-state-default, .ui-datepicker-calendar .ui-button, +html .ui-datepicker-calendar .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active +{ + border: unset; +} + img.datecallink { padding-left: 2px !important; padding-right: 2px !important; } .ui-datepicker-trigger { @@ -3648,13 +3806,16 @@ table.cal_event { border: none; border-collapse: collapse; margin-bottom: 1px table.cal_event td { border: none; padding-: 2px; padding-: 2px; padding-top: 0px; padding-bottom: 0px; } table.cal_event td.cal_event { padding: 4px 4px !important; } table.cal_event td.cal_event_right { padding: 4px 4px !important; } -.cal_event a:link { color: #111111; font-size: 11px; font-weight: normal !important; } -.cal_event a:visited { color: #111111; font-size: 11px; font-weight: normal !important; } -.cal_event a:active { color: #111111; font-size: 11px; font-weight: normal !important; } -.cal_event_busy a:hover { color: #111111; font-size: 11px; font-weight: normal !important; color:rgba(255,255,255,.75); } +.cal_event { font-size: 1em; } +.cal_event a:link { color: #111111; font-weight: normal !important; } +.cal_event a:visited { color: #111111; font-weight: normal !important; } +.cal_event a:active { color: #111111; font-weight: normal !important; } +.cal_event_busy a:hover { color: #111111; font-weight: normal !important; color:rgba(255,255,255,.75); } .cal_event_busy { } .cal_peruserviewname { max-width: 140px; height: 22px; } +.calendarviewcontainertr { height: 100px; } + /* ============================================================================== */ /* Ajax - Liste deroulante de l'autocompletion */ @@ -4981,6 +5142,275 @@ div.tabsElem a.tab { +/* ============================================================================== */ +/* Ticket module */ +/* ============================================================================== */ + +#cd-timeline { + position: relative; + padding: 2em 0; + margin-bottom: 2em; +} +#cd-timeline::before { + /* this is the vertical line */ + content: ''; + position: absolute; + top: 0; + left: 18px; + height: 100%; + width: 4px; + background: #d7e4ed; +} +@media only screen and (min-width: 1170px) { + #cd-timeline { + margin-bottom: 3em; + } + #cd-timeline::before { + left: 50%; + margin-left: -2px; + } +} + +.cd-timeline-block { + position: relative; + margin: 2em 0; +} +.cd-timeline-block:after { + content: ""; + display: table; + clear: both; +} +.cd-timeline-block:first-child { + margin-top: 0; +} +.cd-timeline-block:last-child { + margin-bottom: 0; +} +@media only screen and (min-width: 1170px) { + .cd-timeline-block { + margin: 4em 0; + } + .cd-timeline-block:first-child { + margin-top: 0; + } + .cd-timeline-block:last-child { + margin-bottom: 0; + } +} + +.cd-timeline-img { + position: absolute; + top: 0; + left: 0; + width: 40px; + height: 40px; + border-radius: 50%; + box-shadow: 0 0 0 4px white, inset 0 2px 0 rgba(0, 0, 0, 0.08), 0 3px 0 4px rgba(0, 0, 0, 0.05); + background: #d7e4ed; +} +.cd-timeline-img img { + display: block; + width: 24px; + height: 24px; + position: relative; + left: 50%; + top: 50%; + margin-left: -12px; + margin-top: -12px; +} +.cd-timeline-img.cd-picture { + background: #75ce66; +} +.cd-timeline-img.cd-movie { + background: #c03b44; +} +.cd-timeline-img.cd-location { + background: #f0ca45; +} +@media only screen and (min-width: 1170px) { + .cd-timeline-img { + width: 60px; + height: 60px; + left: 50%; + margin-left: -30px; + /* Force Hardware Acceleration in WebKit */ + -webkit-transform: translateZ(0); + -webkit-backface-visibility: hidden; + } + .cssanimations .cd-timeline-img.is-hidden { + visibility: hidden; + } + .cssanimations .cd-timeline-img.bounce-in { + visibility: visible; + -webkit-animation: cd-bounce-1 0.6s; + -moz-animation: cd-bounce-1 0.6s; + animation: cd-bounce-1 0.6s; + } +} + +@-webkit-keyframes cd-bounce-1 { + 0% { + opacity: 0; + -webkit-transform: scale(0.5); + } + + 60% { + opacity: 1; + -webkit-transform: scale(1.2); + } + + 100% { + -webkit-transform: scale(1); + } +} +@-moz-keyframes cd-bounce-1 { + 0% { + opacity: 0; + -moz-transform: scale(0.5); + } + + 60% { + opacity: 1; + -moz-transform: scale(1.2); + } + + 100% { + -moz-transform: scale(1); + } +} +@keyframes cd-bounce-1 { + 0% { + opacity: 0; + -webkit-transform: scale(0.5); + -moz-transform: scale(0.5); + -ms-transform: scale(0.5); + -o-transform: scale(0.5); + transform: scale(0.5); + } + + 60% { + opacity: 1; + -webkit-transform: scale(1.2); + -moz-transform: scale(1.2); + -ms-transform: scale(1.2); + -o-transform: scale(1.2); + transform: scale(1.2); + } + + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); + } +} +.cd-timeline-content { + position: relative; + margin-left: 60px; + background: white; + border-radius: 0.25em; + padding: 1em; + background-image: -o-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: -moz-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: -ms-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); +} +.cd-timeline-content:after { + content: ""; + display: table; + clear: both; +} +.cd-timeline-content h2 { + color: #303e49; +} +.cd-timeline-content .cd-date { + font-size: 13px; + font-size: 0.8125rem; +} +.cd-timeline-content .cd-date { + display: inline-block; +} +.cd-timeline-content p { + margin: 1em 0; + line-height: 1.6; +} + +.cd-timeline-content .cd-date { + float: left; + padding: .2em 0; + opacity: .7; +} +.cd-timeline-content::before { + content: ''; + position: absolute; + top: 16px; + right: 100%; + height: 0; + width: 0; + border: 7px solid transparent; + border-right: 7px solid white; +} +@media only screen and (min-width: 768px) { + .cd-timeline-content h2 { + font-size: 20px; + font-size: 1.25rem; + } + .cd-timeline-content { + font-size: 16px; + font-size: 1rem; + } + .cd-timeline-content .cd-read-more, .cd-timeline-content .cd-date { + font-size: 14px; + font-size: 0.875rem; + } +} +@media only screen and (min-width: 1170px) { + .cd-timeline-content { + margin-left: 0; + padding: 1.6em; + width: 43%; + } + .cd-timeline-content::before { + top: 24px; + left: 100%; + border-color: transparent; + border-left-color: white; + } + .cd-timeline-content .cd-read-more { + float: left; + } + .cd-timeline-content .cd-date { + position: absolute; + width: 55%; + left: 115%; + top: 6px; + font-size: 16px; + font-size: 1rem; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content { + float: right; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content::before { + top: 24px; + left: auto; + right: 100%; + border-color: transparent; + border-right-color: white; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content .cd-read-more { + float: right; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content .cd-date { + left: auto; + right: 115%; + text-align: right; + } + +} + + /* ============================================================================== */ /* CSS style used for small screen */ diff --git a/htdocs/theme/md/img/menus/ticketsup.png b/htdocs/theme/md/img/menus/ticketsup.png new file mode 100644 index 00000000000..98beeaf593a Binary files /dev/null and b/htdocs/theme/md/img/menus/ticketsup.png differ diff --git a/htdocs/theme/md/img/object_ticketsup.png b/htdocs/theme/md/img/object_ticketsup.png new file mode 100644 index 00000000000..8ece94fbefc Binary files /dev/null and b/htdocs/theme/md/img/object_ticketsup.png differ diff --git a/htdocs/theme/md/img/ticketsup.png b/htdocs/theme/md/img/ticketsup.png new file mode 100644 index 00000000000..3ee928e07b3 Binary files /dev/null and b/htdocs/theme/md/img/ticketsup.png differ diff --git a/htdocs/theme/md/img/title_ticketsup.png b/htdocs/theme/md/img/title_ticketsup.png new file mode 100644 index 00000000000..3ec332075e6 Binary files /dev/null and b/htdocs/theme/md/img/title_ticketsup.png differ diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index 07c02ba74fa..2d7389dae0e 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -103,7 +103,7 @@ $dol_no_mouse_hover=$conf->dol_no_mouse_hover; //var_dump($user->conf->THEME_ELDY_RGB); $useboldtitle=(isset($conf->global->THEME_ELDY_USEBOLDTITLE)?$conf->global->THEME_ELDY_USEBOLDTITLE:1); -$borderwith=2; +$borderwidth=2; // Case of option always editable if (! isset($conf->global->THEME_ELDY_BACKBODY)) $conf->global->THEME_ELDY_BACKBODY=$colorbackbody; @@ -153,7 +153,7 @@ if (! empty($user->conf->THEME_ELDY_ENABLE_PERSONALIZED)) $colorbacklinepairhover=((! isset($user->conf->THEME_ELDY_USE_HOVER) || $user->conf->THEME_ELDY_USE_HOVER === '0')?'':($user->conf->THEME_ELDY_USE_HOVER === '1'?'edf4fb':$user->conf->THEME_ELDY_USE_HOVER)); } -//if (empty($colortopbordertitle1)) $colortopbordertitle1=$colorbackhmenu1; +if (empty($colortopbordertitle1)) $colortopbordertitle1=$colorbackhmenu1; // Set text color to black or white @@ -290,7 +290,11 @@ textarea.cke_source:focus .liste_titre input[name=month_date_when], .liste_titre input[name=monthvalid], .liste_titre input[name=search_ordermonth], .liste_titre input[name=search_deliverymonth], .liste_titre input[name=search_smonth], .liste_titre input[name=search_month], .liste_titre input[name=search_emonth], .liste_titre input[name=smonth], .liste_titre input[name=month], -.liste_titre input[name=month_lim], .liste_titre input[name=month_start], .liste_titre input[name=month_end], .liste_titre input[name=month_create] { +.liste_titre input[name=month_lim], .liste_titre input[name=month_start], .liste_titre input[name=month_end], .liste_titre input[name=month_create], +.liste_titre input[name=search_month_lim], .liste_titre input[name=search_month_start], .liste_titre input[name=search_month_end], .liste_titre input[name=search_month_create], +.liste_titre input[name=search_day_date_when], .liste_titre input[name=search_month_date_when], .liste_titre input[name=search_year_date_when], +.liste_titre input[name=search_month_create], .liste_titre input[name=search_month_start], .liste_titre input[name=search_month_end] +{ margin-right: 4px; } input, input.flat, textarea, textarea.flat, form.flat select, select, select.flat, .dataTables_length label select { @@ -411,6 +415,12 @@ td.actionbuttons a { td.leftborder, td.hide0 { border-left: 1px solid #ccc; } +td.leftborder, td.hide6 { + border-right: 1px solid #ccc; +} +td.rightborder { + border-right: 1px solid #ccc; +} select.flat, form.flat select { font-weight: normal; @@ -592,7 +602,7 @@ textarea.centpercent { .liste_titre .nowrap { white-space: nowrap; } -.nowraponall { +.nowraponall { /* no wrap on all devices */ white-space: nowrap; } .wordwrap { @@ -622,6 +632,9 @@ textarea.centpercent { .paddingright2 { padding-: 2px; } +.cursordefault { + cursor: default; +} .cursorpointer { cursor: pointer; } @@ -797,6 +810,9 @@ select.flat.selectlimit { .fa-file-text-o, .fa-file-code-o, .fa-file-powerpoint-o, .fa-file-excel-o, .fa-file-word-o, .fa-file-o, .fa-file-image-o, .fa-file-video-o, .fa-file-audio-o, .fa-file-archive-o, .fa-file-pdf-o { color: #505; } +.fa-trash, .fa-crop, .fa-pencil { + font-size: 1.4em; +} /* DOL_XXX for future usage (when left menu has been removed). If we do not use datatable */ /*.table-responsive { @@ -860,7 +876,9 @@ select.selectarrowonleft option { /* rule for not too small screen only */ @media only screen and (min-width: px) { + .width25 { width: 25px; } .width50 { width: 50px; } + .width75 { width: 75px; } .width100 { width: 100px; } .width200 { width: 200px; } .minwidth100 { min-width: 100px; } @@ -876,8 +894,11 @@ select.selectarrowonleft option { .minwidth400imp { min-width: 400px !important; } .minwidth500imp { min-width: 500px !important; } } +.widthauto { width: auto; } .width25 { width: 25px; } +.width75 { width: 75px; } .width50 { width: 50px; } +.width75 { width: 75px; } .width100 { width: 100px; } .width200 { width: 200px; } .maxwidth25 { max-width: 25px; } @@ -971,10 +992,15 @@ select.selectarrowonleft option { width: 95%; } + select { + padding-top: 4px; + padding-bottom: 5px; + } input, input[type=text], input[type=password], select, textarea { min-width: 20px; min-height: 1.4em; line-height: 1.4em; + font-size: px; /* padding: .4em .1em; */ /* border-bottom: 1px solid #BBB; */ /* max-width: inherit; why this */ @@ -1179,6 +1205,16 @@ div.login_block { /* position: initial !important;*/ display: none; } +.login_block_getinfo { + text-align: center; +} +.login_block_getinfo div.login_block_user { + display: block; +} +.login_block_getinfo .atoplogin, .login_block_getinfo .atoplogin:hover { + color: #333 !important; + font-weight: normal !important; +} #id-right { padding-left: 0 ! important; } @@ -1200,7 +1236,7 @@ div.fiche { div.fiche { margin-: dol_optimize_smallscreen)?'24':'6')); ?>px; - margin-: dol_optimize_smallscreen)?'16':'6')); ?>px; + margin-: dol_optimize_smallscreen)?'22':'6')); ?>px; dol_hide_leftmenu) && ! empty($conf->dol_hide_topmenu)) print 'margin-top: 4px;'; ?> margin-bottom: 15px; } @@ -1324,9 +1360,14 @@ table.noborder tr.liste_titre td { .pictowarning, .pictopreview { padding-: 3px; } -.pictoedit, .pictowarning, .pictodelete { +.pictowarning { vertical-align: text-bottom; } +.fiche .arearef img.pictoedit, .fiche .arearef span.pictoedit, +.fiche .fichecenter img.pictoedit, .fiche .fichecenter span.pictoedit, +.tagtdnote span.pictoedit { + opacity: 0.9; +} img.hideonsmartphone.pictoactionview { vertical-align: bottom; } @@ -1409,7 +1450,7 @@ img.photorefnoborder { .underrefbanner { } .underbanner { - border-bottom: px solid rgb(); + border-bottom: px solid rgb(); } .tdhrthin { margin: 0; @@ -1673,6 +1714,10 @@ div.mainmenu.project { background-image: url(); } +div.mainmenu.ticketsup { + background-image: url(); +} + div.mainmenu.tools { background-image: url(); } @@ -1687,7 +1732,7 @@ div.mainmenu.website { $moduletomainmenu=array('user'=>'','syslog'=>'','societe'=>'companies','projet'=>'project','propale'=>'commercial','commande'=>'commercial', 'produit'=>'products','service'=>'products','stock'=>'products', 'don'=>'accountancy','tax'=>'accountancy','banque'=>'accountancy','facture'=>'accountancy','compta'=>'accountancy','accounting'=>'accountancy','adherent'=>'members','import'=>'tools','export'=>'tools','mailing'=>'tools', - 'contrat'=>'commercial','ficheinter'=>'commercial','deplacement'=>'commercial', + 'contrat'=>'commercial','ficheinter'=>'commercial','ticketsup'=>'ticketsup','deplacement'=>'commercial', 'fournisseur'=>'companies', 'barcode'=>'','fckeditor'=>'','categorie'=>'', ); @@ -1703,7 +1748,7 @@ $generic=1; // Put here list of menu entries when the div.mainmenu.menuentry was previously defined $divalreadydefined=array('home','companies','products','commercial','externalsite','accountancy','project','tools','members','agenda','ftp','holiday','hrm','bookmark','cashdesk','ecm','geoipmaxmind','gravatar','clicktodial','paypal','stripe','webservices','website'); // Put here list of menu entries we are sure we don't want -$divnotrequired=array('multicurrency','salaries','margin','opensurvey','paybox','expensereport','incoterm','prelevement','propal','workflow','notification','supplier_proposal','cron','product','productbatch','expedition'); +$divnotrequired=array('multicurrency','salaries','ticketsup','margin','opensurvey','paybox','expensereport','incoterm','prelevement','propal','workflow','notification','supplier_proposal','cron','product','productbatch','expedition'); foreach($mainmenuusedarray as $val) { if (empty($val) || in_array($val,$divalreadydefined)) continue; @@ -1930,7 +1975,7 @@ img.login, img.printer, img.entity { color: white; font-weight: bold; } -.userimgatoplogin img.userphoto { /* size for user photo in login bar */ +.userimg.atoplogin img.userphoto, .userimgatoplogin img.userphoto { /* size for user photo in login bar */ border-radius: 8px; width: 16px; height: 16px; @@ -1996,7 +2041,7 @@ div.vmenu, td.vmenu { .companylogo { padding-top: 4px; } .searchform { padding-top: 10px; } -a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active { white-space: nowrap; font-size:px; font-family: ; text-align: ; font-weight: bold; } +a.vmenu:link, a.vmenu:visited, a.vmenu:hover, a.vmenu:active, span.vmenu { white-space: nowrap; font-size:px; font-family: ; text-align: ; font-weight: bold; } font.vmenudisabled { font-size:px; font-family: ; text-align: ; font-weight: bold; color: #aaa; margin-left: 4px; } a.vmenu:link, a.vmenu:visited { color: #; } @@ -2190,6 +2235,9 @@ div.tabBarWithBottom { div.tabBar table.tableforservicepart2:last-child { border-bottom: 1px solid #aaa; } +.tableforservicepart1 .tdhrthin { + height: unset; +} /* ============================================================================== */ /* Boutons actions */ @@ -2462,7 +2510,7 @@ tr.nocellnopadd td.nobordernopadding, tr.nocellnopadd td.nocellnopadd } -table.border, table.dataTable, .table-border, .table-border-col, .table-key-border-col, .table-val-border-col, div.border { +table.border, table.bordernooddeven, table.dataTable, .table-border, .table-border-col, .table-key-border-col, .table-val-border-col, div.border { border: 1px solid #f4f4f4; border-collapse: collapse !important; padding: 1px 2px 1px 3px; /* t r b l */ @@ -2470,10 +2518,12 @@ table.border, table.dataTable, .table-border, .table-border-col, .table-key-bord table.borderplus { border: 1px solid #BBB; } - -.border tbody tr, .border tbody tr td, div.tabBar table.border tr { +.border tbody tr, .border tbody tr td, div.tabBar table.border tr, div.tabBar table.border tr td, div.tabBar div.border .table-border-row, div.tabBar div.border .table-key-border-col, div.tabBar div.border .table-val-border-col { height: 22px; } +tr.liste_titre.box_titre td table td, .bordernooddeven tr td { + height: 22px; +} table.border td, div.border div div.tagtd { padding: 2px 2px 2px 2px; @@ -2498,9 +2548,19 @@ td.border, div.tagtable div div.border { /* Main boxes */ -.noborderbottom { +.nobordertop, .nobordertop tr:first-of-type td { + border-top: none !important; +} +.noborderbottom, .noborderbottom tr:last-of-type td { border-bottom: none !important; } +.bordertop { + border-top: 1px solid rgb(); +} +.borderbottom { + border-bottom: 1px solid rgb(); +} + .ficheaddleft table.noborder { margin: 0px 0px 0px 0px; } @@ -2520,7 +2580,7 @@ table.liste, table.noborder, table.formdoc, div.noborder { border-collapse: separate !important; border-spacing: 0px; - border-top-width: px; + border-top-width: px; border-top-color: rgb(); border-top-style: solid; @@ -2581,7 +2641,7 @@ tr.liste_titre_filter td.liste_titre { table.liste th, table.noborder th, table.noborder tr.liste_titre td { padding: 8px 6px 8px 6px; /* t r b l */ } -table.noborder td, div.noborder form, div.noborder form div { +table.noborder td, div.noborder form, div.noborder form div, table.tableforservicepart1 td, table.tableforservicepart2 td { padding: 4px 6px 4px 6px; /* t r b l */ } @@ -2716,7 +2776,7 @@ div.pagination li span:focus { color: #000; background-color: #eee; border-color: #ddd; - padding-top: 8px; + /* padding-top: 8px; */ } div.pagination li .active a, div.pagination li .active span, @@ -2880,7 +2940,7 @@ div.liste_titre { border-top-style: solid; } div.liste_titre_bydiv { - border-top-width: px; + border-top-width: px; border-top-color: rgb(); border-top-style: solid; @@ -2923,7 +2983,7 @@ tr.liste_titre th a, th.liste_titre a, tr.liste_titre td a, td.liste_titre a, fo text-shadow: none !important; } tr.liste_titre_topborder td { - border-top-width: px; + border-top-width: px; border-top-color: rgb(); border-top-style: solid; } @@ -2983,7 +3043,7 @@ tr.liste_sub_total, tr.liste_sub_total td { } .paymenttable, .margintable { - border-top-width: px !important; + border-top-width: px !important; border-top-color: rgb() !important; border-top-style: solid !important; margin: 0px 0px 0px 0px !important; @@ -2992,6 +3052,9 @@ tr.liste_sub_total, tr.liste_sub_total td { { padding-left: 2px; } +.paymenttable, .margintable tr td { + height: 22px; +} /* Disable shadows */ .noshadow { @@ -3012,6 +3075,9 @@ div .tdtop { #tablelines tr.liste_titre td, .paymenttable tr.liste_titre td, .margintable tr.liste_titre td, .tableforservicepart1 tr.liste_titre td { border-bottom: 1px solid #AAA !important; } +#tablelines tr td { + height: unset; +} /* Prepare to remove class pair - impair */ @@ -3055,7 +3121,7 @@ div .tdtop { } .boxstats, .boxstats130 { display: inline-block; - margin: 3px; + margin: 8px; /* border: 1px solid #CCC; */ text-align: center; border-radius: 2px; @@ -3078,6 +3144,12 @@ div .tdtop { } @media only screen and (max-width: 767px) { + .boxstats, .boxstats130 { + margin: 3px; + border: 1px solid #ddd; + box-shadow: none; + background: #ddd; + } .thumbstat { flex: 1 1 110px; } @@ -3139,6 +3211,9 @@ span.dashboardlineko { margin-bottom: 8px !important; border-bottom-width: 1px; } +.boxtablenotop { + /* border-top-width: 0 !important; */ +} .boxtablenobottom { /* border-bottom-width: 0 !important; */ } @@ -3149,8 +3224,8 @@ span.dashboardlineko { text-align: center; } .boxworkingboard .tdboxstats { - padding-left: 0px !important; - padding-right: 0px !important; + padding-left: 1px !important; + padding-right: 1px !important; } a.valignmiddle.dashboardlineindicator { line-height: 30px; @@ -3187,10 +3262,6 @@ img.boxhandle, img.boxclose { padding-left: 5px; } -.noborderbottom { - border-bottom: none !important; -} - .formboxfilter { vertical-align: middle; margin-bottom: 6px; @@ -3493,6 +3564,13 @@ div.ui-tooltip { /* Calendar */ /* ============================================================================== */ +.ui-datepicker-calendar .ui-state-default, .ui-datepicker-calendar .ui-widget-content .ui-state-default, +.ui-datepicker-calendar .ui-widget-header .ui-state-default, .ui-datepicker-calendar .ui-button, +html .ui-datepicker-calendar .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active +{ + border: unset; +} + img.datecallink { padding-left: 2px !important; padding-right: 2px !important; } .ui-datepicker-trigger { @@ -3696,10 +3774,11 @@ table.cal_event { border: none; border-collapse: collapse; margin-bottom: 1px table.cal_event td { border: none; padding-: 2px; padding-: 2px; padding-top: 0px; padding-bottom: 0px; } table.cal_event td.cal_event { padding: 4px 4px !important; } table.cal_event td.cal_event_right { padding: 4px 4px !important; } -.cal_event a:link { color: #111111; font-size: 11px; font-weight: normal !important; } -.cal_event a:visited { color: #111111; font-size: 11px; font-weight: normal !important; } -.cal_event a:active { color: #111111; font-size: 11px; font-weight: normal !important; } -.cal_event_busy a:hover { color: #111111; font-size: 11px; font-weight: normal !important; color:rgba(255,255,255,.75); } +.cal_event { font-size: 1em; } +.cal_event a:link { color: #111111; font-weight: normal !important; } +.cal_event a:visited { color: #111111; font-weight: normal !important; } +.cal_event a:active { color: #111111; font-weight: normal !important; } +.cal_event_busy a:hover { color: #111111; font-weight: normal !important; color:rgba(255,255,255,.75); } .cal_event_busy { } .cal_peruserviewname { max-width: 140px; height: 22px; } @@ -5021,6 +5100,275 @@ border-top-right-radius: 6px; +/* ============================================================================== */ +/* Ticket module */ +/* ============================================================================== */ + +#cd-timeline { + position: relative; + padding: 2em 0; + margin-bottom: 2em; +} +#cd-timeline::before { + /* this is the vertical line */ + content: ''; + position: absolute; + top: 0; + left: 18px; + height: 100%; + width: 4px; + background: #d7e4ed; +} +@media only screen and (min-width: 1170px) { + #cd-timeline { + margin-bottom: 3em; + } + #cd-timeline::before { + left: 50%; + margin-left: -2px; + } +} + +.cd-timeline-block { + position: relative; + margin: 2em 0; +} +.cd-timeline-block:after { + content: ""; + display: table; + clear: both; +} +.cd-timeline-block:first-child { + margin-top: 0; +} +.cd-timeline-block:last-child { + margin-bottom: 0; +} +@media only screen and (min-width: 1170px) { + .cd-timeline-block { + margin: 4em 0; + } + .cd-timeline-block:first-child { + margin-top: 0; + } + .cd-timeline-block:last-child { + margin-bottom: 0; + } +} + +.cd-timeline-img { + position: absolute; + top: 0; + left: 0; + width: 40px; + height: 40px; + border-radius: 50%; + box-shadow: 0 0 0 4px white, inset 0 2px 0 rgba(0, 0, 0, 0.08), 0 3px 0 4px rgba(0, 0, 0, 0.05); + background: #d7e4ed; +} +.cd-timeline-img img { + display: block; + width: 24px; + height: 24px; + position: relative; + left: 50%; + top: 50%; + margin-left: -12px; + margin-top: -12px; +} +.cd-timeline-img.cd-picture { + background: #75ce66; +} +.cd-timeline-img.cd-movie { + background: #c03b44; +} +.cd-timeline-img.cd-location { + background: #f0ca45; +} +@media only screen and (min-width: 1170px) { + .cd-timeline-img { + width: 60px; + height: 60px; + left: 50%; + margin-left: -30px; + /* Force Hardware Acceleration in WebKit */ + -webkit-transform: translateZ(0); + -webkit-backface-visibility: hidden; + } + .cssanimations .cd-timeline-img.is-hidden { + visibility: hidden; + } + .cssanimations .cd-timeline-img.bounce-in { + visibility: visible; + -webkit-animation: cd-bounce-1 0.6s; + -moz-animation: cd-bounce-1 0.6s; + animation: cd-bounce-1 0.6s; + } +} + +@-webkit-keyframes cd-bounce-1 { + 0% { + opacity: 0; + -webkit-transform: scale(0.5); + } + + 60% { + opacity: 1; + -webkit-transform: scale(1.2); + } + + 100% { + -webkit-transform: scale(1); + } +} +@-moz-keyframes cd-bounce-1 { + 0% { + opacity: 0; + -moz-transform: scale(0.5); + } + + 60% { + opacity: 1; + -moz-transform: scale(1.2); + } + + 100% { + -moz-transform: scale(1); + } +} +@keyframes cd-bounce-1 { + 0% { + opacity: 0; + -webkit-transform: scale(0.5); + -moz-transform: scale(0.5); + -ms-transform: scale(0.5); + -o-transform: scale(0.5); + transform: scale(0.5); + } + + 60% { + opacity: 1; + -webkit-transform: scale(1.2); + -moz-transform: scale(1.2); + -ms-transform: scale(1.2); + -o-transform: scale(1.2); + transform: scale(1.2); + } + + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); + } +} +.cd-timeline-content { + position: relative; + margin-left: 60px; + background: white; + border-radius: 0.25em; + padding: 1em; + background-image: -o-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: -moz-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: -ms-linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); + background-image: linear-gradient(bottom, rgba(0,0,0,0.1) 0%, rgba(230,230,230,0.4) 100%); +} +.cd-timeline-content:after { + content: ""; + display: table; + clear: both; +} +.cd-timeline-content h2 { + color: #303e49; +} +.cd-timeline-content .cd-date { + font-size: 13px; + font-size: 0.8125rem; +} +.cd-timeline-content .cd-date { + display: inline-block; +} +.cd-timeline-content p { + margin: 1em 0; + line-height: 1.6; +} + +.cd-timeline-content .cd-date { + float: left; + padding: .2em 0; + opacity: .7; +} +.cd-timeline-content::before { + content: ''; + position: absolute; + top: 16px; + right: 100%; + height: 0; + width: 0; + border: 7px solid transparent; + border-right: 7px solid white; +} +@media only screen and (min-width: 768px) { + .cd-timeline-content h2 { + font-size: 20px; + font-size: 1.25rem; + } + .cd-timeline-content { + font-size: 16px; + font-size: 1rem; + } + .cd-timeline-content .cd-read-more, .cd-timeline-content .cd-date { + font-size: 14px; + font-size: 0.875rem; + } +} +@media only screen and (min-width: 1170px) { + .cd-timeline-content { + margin-left: 0; + padding: 1.6em; + width: 43%; + } + .cd-timeline-content::before { + top: 24px; + left: 100%; + border-color: transparent; + border-left-color: white; + } + .cd-timeline-content .cd-read-more { + float: left; + } + .cd-timeline-content .cd-date { + position: absolute; + width: 55%; + left: 115%; + top: 6px; + font-size: 16px; + font-size: 1rem; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content { + float: right; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content::before { + top: 24px; + left: auto; + right: 100%; + border-color: transparent; + border-right-color: white; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content .cd-read-more { + float: right; + } + .cd-timeline-block:nth-child(even) .cd-timeline-content .cd-date { + left: auto; + right: 115%; + text-align: right; + } + +} + + /* ============================================================================== */ /* CSS style used for small screen */ diff --git a/htdocs/ticketsup/.tx/config b/htdocs/ticketsup/.tx/config new file mode 100644 index 00000000000..9af4e566424 --- /dev/null +++ b/htdocs/ticketsup/.tx/config @@ -0,0 +1,10 @@ +[main] +host = https://www.transifex.com + +[dolibarr_tickets.ticketsuplang] +file_filter = langs//ticketsup.lang +source_file = langs/en_US/ticketsup.lang +source_lang = en_US +type = MOZILLAPROPERTIES + + diff --git a/htdocs/ticketsup/card.php b/htdocs/ticketsup/card.php new file mode 100644 index 00000000000..bb6b5804578 --- /dev/null +++ b/htdocs/ticketsup/card.php @@ -0,0 +1,814 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ticketsup/card.php + * \ingroup ticketsup + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticketsup.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php'; +require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; +if (!empty($conf->projet->enabled)) { + include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php'; + include_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php'; +} +if (!empty($conf->contrat->enabled)) { + include_once DOL_DOCUMENT_ROOT . '/core/lib/contract.lib.php'; + include_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formcontract.class.php'; +} + +// Load traductions files requiredby by page +$langs->loadLangs(array("companies","other","ticketsup")); + +// Get parameters +$id = GETPOST('id', 'int'); +$track_id = GETPOST('track_id', 'alpha', 3); +$ref = GETPOST('ref', 'alpha'); +$projectid = GETPOST('projectid', 'int'); +$action = GETPOST('action', 'alpha', 3); + +// Initialize technical object to manage hooks of ticketsup. Note that conf->hooks_modules contains array array +$hookmanager->initHooks(array('ticketsupcard','globalcard')); + +$extrafields = new ExtraFields($db); +$extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + +$object = new Ticketsup($db); + +if (!$action) { + $action = 'view'; +} +//Select mail models is same action as add_message +if (GETPOST('modelselected')) { + $action = 'add_message'; +} + +// Store current page url +$url_page_current = DOL_URL_ROOT.'/ticketsup/card.php'; + +if ($id || $track_id || $ref) { + $res = $object->fetch($id, $ref, $track_id); +} + +// Security check +$result = restrictedArea($user, 'ticketsup', $object->id); + + + +/* + * Actions + */ + +$actionobject = new ActionsTicketsup($db); +$actionobject->doActions($action, $object); + +$permissiondellink = $user->rights->ticketsup->write; +include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be include, not include_once + + + + +/* + * View + */ + +$userstat = new User($db); +$form = new Form($db); +$formticket = new FormTicketsup($db); +$formproject = new FormProjets($db); + +if ($action == 'view' || $action == 'add_message' || $action == 'close' || $action == 'delete' || $action == 'editcustomer' || $action == 'progression' || $action == 'reopen' || $action == 'editsubject' || $action == 'edit_extrafields' || $action == 'set_extrafields' || $action == 'classify' || $action == 'sel_contract' || $action == 'edit_message_init' || $action == 'set_status' || $action == 'dellink') { + + if ($res > 0) { + // or for unauthorized internals users + if (!$user->societe_id && ($conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY && $object->fk_user_assign != $user->id) && !$user->rights->ticketsup->manage) { + accessforbidden('', 0); + } + + $help_url = 'FR:DocumentationModuleTicket'; + $page_title = $actionobject->getTitle($action); + + llxHeader('', $page_title, $help_url); + + // Confirmation close + if ($action == 'close') { + print $form->formconfirm($url_page_current . "?track_id=" . $object->track_id, $langs->trans("CloseATicket"), $langs->trans("ConfirmCloseAticket"), "confirm_close", '', '', 1); + if ($ret == 'html') { + print '
    '; + } + } + // Confirmation delete + if ($action == 'delete') { + print $form->formconfirm($url_page_current . "?track_id=" . $object->track_id, $langs->trans("Delete"), $langs->trans("ConfirmDeleteTicket"), "confirm_delete_ticket", '', '', 1); + } + // Confirm reopen + if ($action == 'reopen') { + print $form->formconfirm($url_page_current . '?track_id=' . $object->track_id, $langs->trans('ReOpen'), $langs->trans('ConfirmReOpenTicket'), 'confirm_reopen', '', '', 1); + } + // Confirmation status change + if ($action == 'set_status') { + $new_status = GETPOST('new_status'); + //var_dump($url_page_current . "?track_id=" . $object->track_id); + print $form->formconfirm($url_page_current . "?track_id=" . $object->track_id . "&new_status=" . GETPOST('new_status'), $langs->trans("TicketChangeStatus"), $langs->trans("TicketConfirmChangeStatus", $langs->transnoentities($object->statuts_short[$new_status])), "confirm_set_status", '', '', 1); + } + + // project info + if ($projectid) { + $projectstat = new Project($db); + if ($projectstat->fetch($projectid) > 0) { + $projectstat->fetch_thirdparty(); + + // To verify role of users + //$userAccess = $object->restrictedProjectArea($user,'read'); + $userWrite = $projectstat->restrictedProjectArea($user, 'write'); + //$userDelete = $object->restrictedProjectArea($user,'delete'); + //print "userAccess=".$userAccess." userWrite=".$userWrite." userDelete=".$userDelete; + + $head = project_prepare_head($projectstat); + dol_fiche_head($head, 'ticketsup', $langs->trans("Project"), 0, ($projectstat->public ? 'projectpub' : 'project')); + + /* + * Projet synthese pour rappel + */ + print '
    '; @@ -1720,9 +1763,9 @@ if ($action == 'create') // Send if ($object->statut == SupplierProposal::STATUS_VALIDATED || $object->statut == SupplierProposal::STATUS_SIGNED) { if (empty($conf->global->MAIN_USE_ADVANCED_PERMS) || $user->rights->supplier_proposal->send_advance) { - print ''; + print ''; } else - print ''; + print ''; } // Create an order diff --git a/htdocs/supplier_proposal/class/supplier_proposal.class.php b/htdocs/supplier_proposal/class/supplier_proposal.class.php index 91eff1b5a8a..ce70fe996d3 100644 --- a/htdocs/supplier_proposal/class/supplier_proposal.class.php +++ b/htdocs/supplier_proposal/class/supplier_proposal.class.php @@ -367,13 +367,14 @@ class SupplierProposal extends CommonObject * @param int $fk_unit Id of the unit to use. * @param string $origin 'order', 'supplier_proposal', ... * @param int $origin_id Id of origin line + * @param double $pu_ht_devise Amount in currency * @return int >0 if OK, <0 if KO * * @see add_product */ - function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='',$array_option=0, $ref_fourn='', $fk_unit='', $origin='', $origin_id=0) + function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1=0, $txlocaltax2=0, $fk_product=0, $remise_percent=0, $price_base_type='HT', $pu_ttc=0, $info_bits=0, $type=0, $rang=-1, $special_code=0, $fk_parent_line=0, $fk_fournprice=0, $pa_ht=0, $label='',$array_option=0, $ref_fourn='', $fk_unit='', $origin='', $origin_id=0, $pu_ht_devise=0) { - global $mysoc; + global $mysoc, $conf; dol_syslog(get_class($this)."::addline supplier_proposalid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type"); include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php'; @@ -384,6 +385,7 @@ class SupplierProposal extends CommonObject if (empty($info_bits)) $info_bits=0; if (empty($rang)) $rang=0; if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0; + if (empty($pu_ht)) $pu_ht=0; $remise_percent=price2num($remise_percent); $qty=price2num($qty); @@ -417,17 +419,23 @@ class SupplierProposal extends CommonObject $localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc); $txtva = preg_replace('/\s*\(.*\)/','',$txtva); // Remove code into vatrate. - $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx); + if ($conf->multicurrency->enabled && $pu_ht_devise > 0) { + $pu = 0; + } + + $tabprice=calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise); $total_ht = $tabprice[0]; $total_tva = $tabprice[1]; $total_ttc = $tabprice[2]; $total_localtax1 = $tabprice[9]; $total_localtax2 = $tabprice[10]; + $pu = $pu_ht = $tabprice[3]; // MultiCurrency $multicurrency_total_ht = $tabprice[16]; $multicurrency_total_tva = $tabprice[17]; $multicurrency_total_ttc = $tabprice[18]; + $pu_ht_devise = $tabprice[19]; // Rang to use $rangtouse = $rang; @@ -492,7 +500,7 @@ class SupplierProposal extends CommonObject // Multicurrency $this->line->fk_multicurrency = $this->fk_multicurrency; $this->line->multicurrency_code = $this->multicurrency_code; - $this->line->multicurrency_subprice = price2num($pu_ht * $this->multicurrency_tx); + $this->line->multicurrency_subprice = $pu_ht_devise; $this->line->multicurrency_total_ht = $multicurrency_total_ht; $this->line->multicurrency_total_tva = $multicurrency_total_tva; $this->line->multicurrency_total_ttc = $multicurrency_total_ttc; @@ -1127,7 +1135,7 @@ class SupplierProposal extends CommonObject { global $conf; - $sql = "SELECT p.rowid, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc"; + $sql = "SELECT p.rowid, p.entity, p.ref, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc"; $sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht"; $sql.= ", p.datec"; $sql.= ", p.date_valid as datev"; @@ -1145,8 +1153,8 @@ class SupplierProposal extends CommonObject $sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc"; $sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement"; $sql.= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."supplier_proposal as p"; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')'; - $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid AND cr.entity IN ('.getEntity('c_payment_term').')'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id'; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid'; $sql.= " WHERE p.fk_statut = c.id"; $sql.= " AND p.entity IN (".getEntity('supplier_proposal').")"; if ($ref) $sql.= " AND p.ref='".$ref."'"; @@ -1161,6 +1169,7 @@ class SupplierProposal extends CommonObject $obj = $this->db->fetch_object($resql); $this->id = $obj->rowid; + $this->entity = $obj->entity; $this->ref = $obj->ref; $this->remise = $obj->remise; diff --git a/htdocs/supplier_proposal/contact.php b/htdocs/supplier_proposal/contact.php new file mode 100644 index 00000000000..3773aab6f0c --- /dev/null +++ b/htdocs/supplier_proposal/contact.php @@ -0,0 +1,206 @@ + + * Copyright (C) 2005-2018 Destailleur Laurent + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2017 Ferran Marcet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/supplier_proposal/contact.php + * \ingroup supplier_proposal + * \brief Tab to manage contact of a supplier proposal + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/supplier_proposal/class/supplier_proposal.class.php'; +require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/supplier_proposal.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; +require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; + +$langs->loadLangs(array("propal","facture","orders","sendings","companies")); + +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'alpha'); + +// Security check +if ($user->societe_id) $socid=$user->societe_id; +$result = restrictedArea($user, 'supplier_proposal', $id, 'supplier_proposal', ''); + +$object = new SupplierProposal($db); + +$permissiontoedit = $user->rights->supplier_proposal->creer; + + +/* + * Add a new contact + */ + +if ($action == 'addcontact' && $permissiontoedit) +{ + $result = $object->fetch($id); + + if ($result > 0 && $id > 0) + { + $contactid = (GETPOST('userid') ? GETPOST('userid') : GETPOST('contactid')); + $result = $object->add_contact($contactid, $_POST["type"], $_POST["source"]); + } + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else + { + if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') + { + $langs->load("errors"); + setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors'); + } + else + { + setEventMessages($object->error, $object->errors, 'errors'); + } + } +} + +// Toggle the status of a contact +else if ($action == 'swapstatut' && $permissiontoedit) +{ + if ($object->fetch($id)) + { + $result=$object->swapContactStatus(GETPOST('ligne')); + } + else + { + dol_print_error($db); + } +} + +// Deleting a contact +else if ($action == 'deletecontact' && $permissiontoedit) +{ + $object->fetch($id); + $result = $object->delete_contact(GETPOST("lineid",'int')); + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else { + dol_print_error($db); + } +} + + + +/* + * View + */ + +$help_url=''; +llxHeader('',$langs->trans("SupplierProposals"),$help_url); + +$form = new Form($db); +$formcompany = new FormCompany($db); +$contactstatic=new Contact($db); +$userstatic=new User($db); + + +/* *************************************************************************** */ +/* */ +/* Mode vue et edition */ +/* */ +/* *************************************************************************** */ + +if ($id > 0 || ! empty($ref)) +{ + $langs->trans("SupplierProposal"); + + if ($object->fetch($id, $ref) > 0) + { + $object->fetch_thirdparty(); + + $head = supplier_proposal_prepare_head($object); + dol_fiche_head($head, 'contact', $langs->trans("CommRequest"), -1, 'supplier_proposal'); + + // Supplier order card + + $linkback = ''.$langs->trans("BackToList").''; + + $morehtmlref='
    '; + // Ref supplier + $morehtmlref.=$form->editfieldkey("RefSupplier", 'ref_supplier', $object->ref_supplier, $object, 0, 'string', '', 0, 1); + $morehtmlref.=$form->editfieldval("RefSupplier", 'ref_supplier', $object->ref_supplier, $object, 0, 'string', '', null, null, '', 1); + // Thirdparty + $morehtmlref.='
    '.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1); + // Project + if (! empty($conf->projet->enabled)) + { + $langs->load("projects"); + $morehtmlref.='
    '.$langs->trans('Project') . ' '; + if ($permissiontoedit) + { + if ($action != 'classify') + //$morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ' : '; + $morehtmlref.=' : '; + if ($action == 'classify') { + //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); + $morehtmlref.='
    '; + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); + $morehtmlref.=''; + $morehtmlref.='
    '; + } else { + $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); + } + } else { + if (! empty($object->fk_project)) { + $proj = new Project($db); + $proj->fetch($object->fk_project); + $morehtmlref.=''; + $morehtmlref.=$proj->ref; + $morehtmlref.=''; + } else { + $morehtmlref.=''; + } + } + } + $morehtmlref.='
    '; + + + dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref, '', 0, '', '', 1); + + dol_fiche_end(); + + // Contacts lines + include DOL_DOCUMENT_ROOT.'/core/tpl/contacts.tpl.php'; + + } + else + { + // Contact not found + print "ErrorRecordNotFound"; + } +} + + +llxFooter(); +$db->close(); diff --git a/htdocs/supplier_proposal/list.php b/htdocs/supplier_proposal/list.php index 1a7d248f83f..09cbd063e64 100644 --- a/htdocs/supplier_proposal/list.php +++ b/htdocs/supplier_proposal/list.php @@ -57,6 +57,7 @@ $massaction=GETPOST('massaction','alpha'); $show_files=GETPOST('show_files','int'); $confirm=GETPOST('confirm','alpha'); $toselect = GETPOST('toselect', 'array'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'supplierproposallist'; $search_user=GETPOST('search_user','int'); $search_sale=GETPOST('search_sale','int'); @@ -97,9 +98,6 @@ if ($object_statut != '') $search_status=$object_statut; // Nombre de ligne pour choix de produit/service predefinis $NBLINES=4; -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage='supplierproposallist'; - // Security check $module='supplier_proposal'; $dbtable=''; @@ -116,6 +114,7 @@ $result = restrictedArea($user, $module, $objectid, $dbtable); $diroutputmassaction=$conf->supplier_proposal->dir_output . '/temp/massgeneration/'.$user->id; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$object = new SupplierProposal($db); $hookmanager->initHooks(array('supplier_proposallist')); $extrafields = new ExtraFields($db); @@ -387,6 +386,12 @@ if ($resql) if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); $massactionbutton=$form->selectMassAction('', $arrayofmassactions); + $newcardbutton=''; + if($user->rights->supplier_proposal->creer) + { + $newcardbutton=''.$langs->trans('NewAskPrice').''; + } + // Lignes des champs de filtre print '
    '; if ($optioncss != '') print ''; @@ -397,7 +402,7 @@ if ($resql) print ''; print ''; - print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, '', '', $limit); + print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_commercial.png', 0, $newcardbutton, '', $limit); $topicmail="SendSupplierProposalRef"; $modelmail="supplier_proposal_send"; @@ -599,7 +604,7 @@ if ($resql) // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields - $parameters=array('arrayfields'=>$arrayfields); + $parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['sp.datec']['checked'])) print_liste_field_titre($arrayfields['sp.datec']['label'],$_SERVER["PHP_SELF"],"sp.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); diff --git a/htdocs/supplier_proposal/tpl/linkedobjectblock.tpl.php b/htdocs/supplier_proposal/tpl/linkedobjectblock.tpl.php index 8c49e42f557..ca604e61fcc 100644 --- a/htdocs/supplier_proposal/tpl/linkedobjectblock.tpl.php +++ b/htdocs/supplier_proposal/tpl/linkedobjectblock.tpl.php @@ -55,7 +55,7 @@ foreach($linkedObjectBlock as $key => $objectlink) echo price($objectlink->total_ht); } ?>
    getLibStatut(3); ?>">transnoentitiesnoconv("RemoveLink")); ?>">transnoentitiesnoconv("RemoveLink"), 'unlink'); ?>
    '; + + $linkback = '' . $langs->trans("BackToList") . ''; + + // Ref + print ''; + + // Label + print ''; + + // Customer + print ""; + print ''; + + // Visibility + print ''; + + // Statut + print ''; + + print "
    ' . $langs->trans('Ref') . ''; + // Define a complementary filter for search of next/prev ref. + if (!$user->rights->projet->all->lire) { + $objectsListId = $projectstat->getProjectsAuthorizedForUser($user, $mine, 0); + $projectstat->next_prev_filter = " rowid in (" . (count($objectsListId) ? join(',', array_keys($objectsListId)) : '0') . ")"; + } + print $form->showrefnav($projectstat, 'ref', $linkback, 1, 'ref', 'ref', ''); + print '
    ' . $langs->trans("Label") . '' . $projectstat->title . '
    " . $langs->trans("ThirdParty") . "'; + if ($projectstat->thirdparty->id > 0) { + print $projectstat->thirdparty->getNomUrl(1); + } else { + print ' '; + } + + print '
    ' . $langs->trans("Visibility") . ''; + if ($projectstat->public) { + print $langs->trans('SharedProject'); + } else { + print $langs->trans('PrivateProject'); + } + + print '
    ' . $langs->trans("Status") . '' . $projectstat->getLibStatut(4) . '
    "; + + print ''; + } else { + print "ErrorRecordNotFound"; + } + } elseif ($socid > 0) { + $object->fetch_thirdparty(); + $head = societe_prepare_head($object->thirdparty); + + dol_fiche_head($head, 'ticketsup', $langs->trans("ThirdParty"), 0, 'company'); + dol_banner_tab($object->thirdparty, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); + dol_fiche_end(); + } + + if (!$user->societe_id && $conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY) { + $object->next_prev_filter = "te.fk_user_assign = '" . $user->id . "'"; + } elseif ($user->societe_id > 0) { + $object->next_prev_filter = "te.fk_soc = '" . $user->societe_id . "'"; + } + + $head = ticketsup_prepare_head($object); + + dol_fiche_head($head, 'tabTicketsup', $langs->trans("Ticket"), -1, 'ticketsup'); + + $morehtmlref ='
    '; + $morehtmlref.= $object->subject; + // Author + if ($object->fk_user_create > 0) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + + $langs->load("users"); + $fuser = new User($db); + $fuser->fetch($object->fk_user_create); + $morehtmlref .= $fuser->getNomUrl(0); + } + if (!empty($object->origin_email)) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + $morehtmlref .= $object->origin_email . ' (' . $langs->trans("TicketEmailOriginIssuer") . ')'; + } + + // Project + if (! empty($conf->projet->enabled)) + { + $langs->load("projects"); + $morehtmlref.='
    '.$langs->trans('Project') . ' '; + if ($user->rights->ticketsup->write) + { + if ($action != 'classify') + $morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ' : '; + if ($action == 'classify') { + //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=''; + $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1); + $morehtmlref.=''; + $morehtmlref.=''; + } else { + $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); + } + } else { + if (! empty($object->fk_project)) { + $proj = new Project($db); + $proj->fetch($object->fk_project); + $morehtmlref.=''; + $morehtmlref.=$proj->ref; + $morehtmlref.=''; + } else { + $morehtmlref.=''; + } + } + } + + $morehtmlref.='
    '; + + $linkback = '' . $langs->trans("BackToList") . ' '; + + dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref); + + print '
    '; + print '
    '; + print ''; + + // Track ID + print ''; + + // Subject + print ''; + + // Creation date + print ''; + + // Read date + if (!empty($object->date_read)) { + print ''; + + print ''; + } + + // Close date + if (!empty($object->date_close)) { + print ''; + } + + print ''; + + // Thirdparty + print ''; + + // User assigned + print ''; + + // Progression + print ''; + print ''; + + // Timing (Duration sum of linked fichinter + $object->fetchObjectLinked(); + $num = count($object->linkedObjects); + $timing = 0; + if ($num) { + foreach ($object->linkedObjects as $objecttype => $objects) { + if ($objecttype = "fichinter") { + foreach ($objects as $fichinter) { + $timing += $fichinter->duration; + } + } + } + } + print ''; + + // Other attributes + $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if (empty($reshook) && !empty($extrafields->attribute_label)) { + if ($action == "edit_extrafields") { + print ''; + print ''; + print ''; + print ''; + + print $object->showOptionals($extrafields, 'edit'); + print ''; + print ''; + } else { + print $object->showOptionals($extrafields); + if ($user->rights->ticketsup->write) { + print ''; + } + } + } + print '
    ' . $langs->trans("TicketTrackId") . ''; + if (!empty($object->track_id)) { + if (empty($object->ref)) { + $object->ref = $object->id; + print $form->showrefnav($object, 'id', $linkback, 1, 'rowid', 'track_id'); + } else { + print $object->track_id; + } + } else { + print $langs->trans('None'); + } + print '
    '; + print $form->editfieldkey("Subject", 'subject', $object->subject, $object, $user->rights->ticketsup->write && !$user->societe_id, 'string'); + print ''; + print $form->editfieldval("Subject", 'subject', $object->subject, $object, $user->rights->ticketsup->write && !$user->societe_id, 'string'); + print '
    ' . $langs->trans("DateCreation") . ''; + print dol_print_date($object->datec, 'dayhour'); + print '
    ' . $langs->trans("TicketReadOn") . ''; + print dol_print_date($object->date_read, 'dayhour'); + print '
    ' . $langs->trans("TicketTimeToRead") . ''; + print '' . convertSecondToTime($object->date_read - $object->datec) . ''; + print '
    ' . $langs->trans("TicketCloseOn") . ''; + print dol_print_date($object->date_close, 'dayhour'); + print '
    '; + print ''; + if ($action != 'editcustomer' && $object->fk_statut < 8 && !$user->societe_id && $user->rights->ticketsup->write) { + print ''; + } + print '
    '; + print $langs->trans('ThirdParty'); + print '' . img_edit($langs->transnoentitiesnoconv('Edit'), 1) . '
    '; + print '
    '; + + if ($action == 'editcustomer') { + $form->form_thirdparty($url_page_current . '?track_id=' . $object->track_id, $object->fk_soc, 'editcustomer', ($object->fk_soc ? 's.rowid <> ' . $object->fk_soc : ''), 1); + } else { + $form->form_thirdparty($url_page_current . '?track_id=' . $object->track_id, $object->fk_soc, 'none', 's.rowid <> ' . $object->fk_soc, 1); + } + print '
    ' . $langs->trans("AssignedTo") . ''; + if ($object->fk_user_assign > 0) { + $userstat->fetch($object->fk_user_assign); + print $userstat->getNomUrl(1); + } else { + print $langs->trans('None'); + } + + // Show user list to assignate one if status is "read" + if (GETPOST('set','alpha') == "assign_ticket" && $object->fk_statut < 8 && !$user->societe_id && $user->rights->ticketsup->write) { + print '
    '; + print ''; + print ''; + print ''; + print ' '; + print $form->select_dolusers($user->id, 'fk_user_assign', 1); + print ' '; + print '
    '; + } + if ($object->fk_statut < 8 && GETPOST('set','alpha') != "assign_ticket" && $user->rights->ticketsup->manage) { + print '' . img_picto('', 'edit') . ' ' . $langs->trans('Modify') . ''; + } + print '
    '; + print ''; + if ($action != 'progression' && $object->fk_statut < 8 && !$user->societe_id) { + print ''; + } + print '
    '; + print $langs->trans('Progression') . ''; + print '' . img_edit($langs->trans('Modify')) . '
    '; + print '
    '; + if ($user->rights->ticketsup->write && $action == 'progression') { + print '
    '; + print ''; + print ''; + print ''; + print ''; + print ' '; + print '
    '; + } else { + print($object->progress > 0 ? $object->progress : '0') . '%'; + } + print '
    '; + + print $form->textwithpicto($langs->trans("TicketDurationAuto"), $langs->trans("TicketDurationAutoInfos"), 1); + print ''; + print convertSecondToTime($timing, 'all', $conf->global->MAIN_DURATION_OF_WORKDAY); + print '
    '; + print ' '; + print ' '; + print '
    '; + print '' . img_picto('', 'edit') . ' ' . $langs->trans('Edit') . ''; + print '
    '; + + + // Fin colonne gauche et début colonne droite + print '
    '; + + + // View Original message + $actionobject->viewTicketOriginalMessage($user, $action, $object); + + + /*************************************************** + * + * Classification and actions on ticket + * + ***************************************************/ + /* + * Ticket properties + */ + print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table + print ''; + print ''; + print ''; + print ''; + if (GETPOST('set') == 'properties' && $user->rights->ticketsup->write) { + /* + * Form to change ticket properties + */ + $j = 0; + $ticketprop[$j] = array( + 'dict' => 'type', + 'list_function' => 'selectTypesTickets', + 'label' => 'TicketChangeType', + ); + $j++; + $ticketprop[$j] = array( + 'dict' => 'category', + 'list_function' => 'selectCategoriesTickets', + 'label' => 'TicketChangeCategory', + ); + $j++; + $ticketprop[$j] = array( + 'dict' => 'severity', + 'list_function' => 'selectSeveritiesTickets', + 'label' => 'TicketChangeSeverity', + ); + foreach ($ticketprop as $property) { + print ''; + print ''; + print ''; + } + } else { + // Type + print ''; + + // Category + print ''; + + // Severity + print ''; + } + print '
    '; + print $langs->trans('Properties'); + print '
    '; + + print '
    '; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
    '; + print ' '; + print ''; + print $formticket->{$property['list_function']}($object->type_code, 'update_value', '', 0); + print ''; + print ' '; + print '
    '; + print '
    '; + + print '
    ' . $langs->trans("Type") . ''; + print $object->type_label; + /*if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + }*/ + + print '
    ' . $langs->trans("Category") . ''; + print $object->category_label; + /*if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + }*/ + + print '
    ' . $langs->trans("TicketSeverity") . ''; + print $object->severity_label; + /*if ($user->admin && !$noadmininfo) { + print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + }*/ + + print '
    '; // End table actions + print '
    '; + + // Display navbar with links to change ticket status + print ''; + if (!$user->societe_id && $user->rights->ticketsup->write && $object->fk_status < 8 && GETPOST('set') !== 'properties') { + $actionobject->viewStatusActions($object); + } + + + if (! empty($conf->global->MAIN_DISABLE_CONTACTS_TAB)) + { + print load_fiche_titre($langs->trans('Contacts'), '', 'title_companies.png'); + + print '
    '; + print '
    '; + + print '
    '; + print '
    ' . $langs->trans("Source") . '
    +
    ' . $langs->trans("Company") . '
    +
    ' . $langs->trans("Contacts") . '
    +
    ' . $langs->trans("ContactType") . '
    +
    ' . $langs->trans("Phone") . '
    +
    ' . $langs->trans("Status") . '
    '; + print '
    '; + + // Contact list + $companystatic = new Societe($db); + $contactstatic = new Contact($db); + $userstatic = new User($db); + foreach (array('internal', 'external') as $source) { + $tmpobject = $object; + $tab = $tmpobject->listeContact(-1, $source); + $num = count($tab); + $i = 0; + while ($i < $num) { + $var = !$var; + print '
    '; + + print '
    '; + if ($tab[$i]['source'] == 'internal') { + echo $langs->trans("User"); + } + + if ($tab[$i]['source'] == 'external') { + echo $langs->trans("ThirdPartyContact"); + } + + print '
    '; + print '
    '; + + if ($tab[$i]['socid'] > 0) { + $companystatic->fetch($tab[$i]['socid']); + echo $companystatic->getNomUrl(1); + } + if ($tab[$i]['socid'] < 0) { + echo $conf->global->MAIN_INFO_SOCIETE_NOM; + } + if (!$tab[$i]['socid']) { + echo ' '; + } + print '
    '; + + print '
    '; + if ($tab[$i]['source'] == 'internal') { + if ($userstatic->fetch($tab[$i]['id'])) { + print $userstatic->getNomUrl(1); + } + } + if ($tab[$i]['source'] == 'external') { + if ($contactstatic->fetch($tab[$i]['id'])) { + print $contactstatic->getNomUrl(1); + } + } + print '
    +
    ' . $tab[$i]['libelle'] . '
    '; + + print '
    '; + + print dol_print_phone($tab[$i]['phone'], '', '', '', AC_TEL).'
    '; + + if (! empty($tab[$i]['phone_perso'])) { + //print img_picto($langs->trans('PhonePerso'),'object_phoning.png','',0,0,0).' '; + print '
    '.dol_print_phone($tab[$i]['phone_perso'], '', '', '', AC_TEL).'
    '; + } + if (! empty($tab[$i]['phone_mobile'])) { + //print img_picto($langs->trans('PhoneMobile'),'object_phoning.png','',0,0,0).' '; + print dol_print_phone($tab[$i]['phone_mobile'], '', '', '', AC_TEL).'
    '; + } + print '
    '; + + print ''; + + print '
    '; + + $i++; + } + } + + print '
    '; + print '
    '; + } + + // Contract + if ($action == 'sel_contract') { + if (!empty($conf->contrat->enabled)) { + $langs->load('contrats'); + print load_fiche_titre($langs->trans('LinkToAContract'), '', 'title_commercial.png'); + + $form_contract = new FormContract($db); + $form_contract->formSelectContract( + $url_page_current.'?track_id='.$object->track_id, + $object->fk_soc, + GETPOST('contractid'), + 'contractid' + ); + } + } + + print '
    '; + print '
    '; + + print dol_fiche_end(); + + + /* ActionBar */ + print '
    '; + + // Show link to add a message (if read and not closed) + if ($object->fk_statut < 8 && $action != "add_message") { + print ''; + } + + // Link to create an intervention + // socid is needed otherwise fichinter ask it and forgot origin after form submit :\ + if (!$object->fk_soc && $user->rights->ficheinter->creer) { + print ''; + } + if ($object->fk_soc > 0 && $object->fk_statut < 8 && $user->rights->ficheinter->creer) { + print ''; + } + + // Button to edit Properties + if ($object->fk_statut < 5 && $user->rights->ticketsup->write) { + print ''; + } + + // Button to link to a contract + if ($user->rights->ticketsup->write && $object->fk_statut < 5 && $user->rights->contrat->creer) { + print ''; + } + + // Close ticket if statut is read + if ($object->fk_statut > 0 && $object->fk_statut < 8 && $user->rights->ticketsup->write) { + print ''; + } + + // Re-open ticket + if (!$user->socid && $object->fk_statut == 8 && !$user->societe_id) { + print ''; + } + + // Delete ticket + if ($user->rights->ticketsup->delete && !$user->societe_id) { + print ''; + } + print '
    '; + + if ($action == 'view' || $action == 'edit_message_init') { + print '
    '; + + //print '
    '; + // Message list + print load_fiche_titre($langs->trans('TicketMessagesList'), '', 'messages@ticketsup'); + $show_private_message = ($user->societe_id ? 0 : 1); + $actionobject->viewTicketTimelineMessages($show_private_message, true, $object); + + print '
    '; + print '
    '; + print '
    '; + } elseif ($action == 'add_message') { + $action='new_message'; + $modelmail='ticketsup_send'; + + print '
    '; + print load_fiche_titre($langs->trans('TicketAddMessage'), '', 'messages@ticketsup'); + + // Define output language + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && ! empty($_REQUEST['lang_id'])) { + $newlang = $_REQUEST['lang_id']; + } + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) { + $newlang = $object->default_lang; + } + + $formticket = new FormTicketsup($db); + + $formticket->action = $action; + $formticket->track_id = $object->track_id; + $formticket->id = $object->id; + + $formticket->withfile = 2; + $formticket->param = array('fk_user_create' => $user->id); + $formticket->param['langsmodels']=(empty($newlang)?$langs->defaultlang:$newlang); + + // Tableau des parametres complementaires du post + $formticket->param['models']=$modelmail; + $formticket->param['models_id']=GETPOST('modelmailselected', 'int'); + //$formticket->param['socid']=$object->fk_soc; + $formticket->param['returnurl']=$_SERVER["PHP_SELF"].'?track_id='.$object->track_id; + + + $formticket->withsubstit = 1; + + if ($object->fk_soc > 0) { + $object->fetch_thirdparty(); + $formticket->substit['__THIRDPARTY_NAME__'] = $object->thirdparty->name; + } + $formticket->substit['__SIGNATURE__'] = $user->signature; + $formticket->substit['__TICKETSUP_TRACKID__'] = $object->track_id; + $formticket->substit['__TICKETSUP_REF__'] = $object->ref; + $formticket->substit['__TICKETSUP_SUBJECT__'] = $object->subject; + $formticket->substit['__TICKETSUP_TYPE__'] = $object->type_code; + $formticket->substit['__TICKETSUP_CATEGORY__'] = $object->category_code; + $formticket->substit['__TICKETSUP_SEVERITY__'] = $object->severity_code; + $formticket->substit['__TICKETSUP_MESSAGE__'] = $object->message; + $formticket->substit['__TICKETSUP_PROGRESSION__'] = $object->progress; + if ($object->fk_user_assign > 0) { + $userstat->fetch($object->fk_user_assign); + $formticket->substit['__TICKETSUP_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname); + } + + if ($object->fk_user_create > 0) { + $userstat->fetch($object->fk_user_create); + $formticket->substit['__TICKETSUP_USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname); + } + + + $formticket->showMessageForm('100%'); + print '
    '; + } + } +} // End action view + +/*************************************************** + * LINKED OBJECT BLOCK + * + * Put here code to view linked object + ****************************************************/ +$somethingshown = $form->showLinkedObjectBlock($object); + +// End of page +llxFooter(''); +$db->close(); diff --git a/htdocs/ticketsup/class/actions_ticketsup.class.php b/htdocs/ticketsup/class/actions_ticketsup.class.php new file mode 100644 index 00000000000..035b43ea1c6 --- /dev/null +++ b/htdocs/ticketsup/class/actions_ticketsup.class.php @@ -0,0 +1,1558 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/** + * \file ticketsup/class/actions_ticketsup.class.php + * \ingroup ticketsup + * \brief File Class ticketsup + */ + +require_once "ticketsup.class.php"; +require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php'; +require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php'; +require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php'; + + +/** + * Class Actions of the module ticketsup + */ +class ActionsTicketsup +{ + public $db; + public $dao; + + public $mesg; + public $error; + public $errors = array(); + //! Numero de l'erreur + public $errno = 0; + + public $template_dir; + public $template; + + public $label; + public $description; + + public $fk_statut; + public $fk_soc; + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + } + + /** + * Instantiation of DAO class + * + * @return void + */ + public function getInstanceDao() + { + if (!is_object($this->dao)) { + $this->dao = new Ticketsup($this->db); + } + } + + /** + * doActions + * + * @param string $action Action type + * @param Ticketsup $object Object Ticketsup + * @return int 0 + */ + public function doActions(&$action = '', Ticketsup $object=null) + { + global $conf, $user, $langs, $mysoc; + + /* + * Add file in email form + */ + if (GETPOST('addfile')) { + // altairis : allow files from public interface + if (GETPOST('track_id')) { + $res = $object->fetch('', '', GETPOST('track_id','alpha')); + } + + ////if($res > 0) + ////{ + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + + // Set tmp directory TODO Use a dedicated directory for temp mails files + $vardir = $conf->ticketsup->dir_output . (!empty($object->track_id) ? '/' . dol_sanitizeFileName($object->track_id) : ''); + $upload_dir_tmp = $vardir . '/temp'; + if (!dol_is_dir($upload_dir_tmp)) { + dol_mkdir($upload_dir_tmp); + } + dol_add_file_process($upload_dir_tmp, 0, 0, 'addedfile', dol_print_date(dol_now(), '%Y%m%d%H%M%S') . '-__file__'); + $action = !empty($object->track_id) ? 'add_message' : 'create_ticket'; + ////} + } + + /* + * Remove file in email form + */ + if (GETPOST('removedfile')) { + // altairis : allow files from public interface + if (GETPOST('track_id')) { + $res = $object->fetch('', '', GETPOST('track_id','alpha')); + } + + ////if($res > 0) + ////{ + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + + // Set tmp directory + $vardir = $conf->ticketsup->dir_output . (!empty($object->track_id) ? '/' . dol_sanitizeFileName($object->track_id) : ''); + $upload_dir_tmp = $vardir . '/temp'; + + // TODO Delete only files that was uploaded from email form + dol_remove_file_process($_POST['removedfile'], 0); + $action = !empty($object->track_id) ? 'add_message' : 'create_ticket'; + ////} + } + + if (GETPOST('add_ticket') && $user->rights->ticketsup->write) { + $error = 0; + + if (!GETPOST("subject")) { + $error++; + $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")); + $action = 'create_ticket'; + } elseif (!GETPOST("message")) { + $error++; + $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")); + $action = 'create_ticket'; + } + + if (!$error) { + $this->db->begin(); + + $object->track_id = generate_random_id(16); + + $object->ref = GETPOST("ref", 'alpha'); + $object->fk_soc = GETPOST("socid", 'int') > 0 ? GETPOST("socid", 'int') : 0; + $object->subject = GETPOST("subject", 'alpha'); + $object->message = GETPOST("message"); + + $object->type_code = GETPOST("type_code", 'alpha'); + $object->category_code = GETPOST("category_code", 'alpha'); + $object->severity_code = GETPOST("severity_code", 'alpha'); + $notifyTiers = GETPOST("notify_tiers_at_create", 'alpha'); + $object->notify_tiers_at_create = empty($notifyTiers) ? 0 : 1; + + $extrafields = new ExtraFields($this->db); + $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object); + + $id = $object->create($user); + if ($id <= 0) { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + $action = 'create_ticket'; + } + + if (!$error && $id > 0) + { + $this->db->commit(); + + // File transfer + $this->copyFilesForTicket(); + + // Add contact + $contactid = GETPOST('contactid', 'int'); + $type_contact = GETPOST("type", 'alpha'); + + if ($contactid > 0 && $type_contact) { + $result = $object->add_contact($contactid, GETPOST("type"), 'external'); + } + + // altairis: link ticket to project + if (GETPOST('projectid') > 0) { + $object->setProject(GETPOST('projectid')); + } + + // Auto assign user + if ($conf->global->TICKETS_AUTO_ASSIGN_USER_CREATE) { + $result = $object->assignUser($user, $user->id, 1); + $object->add_contact($user->id, "SUPPORTTEC", 'internal'); + } + + // Auto assign contrat + $contractid = 0; + if ($conf->global->TICKETS_AUTO_ASSIGN_CONTRACT_CREATE) { + $contrat = new Contrat($this->db); + $contrat->socid = $object->fk_soc; + $list = $contrat->getListOfContracts(); + + if (is_array($list) && !empty($list)) { + if (count($list) == 1) { + $contractid = $list[0]->id; + $object->setContract($contractid); + } else { + } + } + } + + // Auto create fiche intervention + if ($conf->global->TICKETS_AUTO_CREATE_FICHINTER_CREATE) + { + $fichinter = new Fichinter($this->db); + $fichinter->socid = $object->fk_soc; + $fichinter->fk_project = GETPOST('projectid', 'int'); + $fichinter->fk_contrat = $contractid; + $fichinter->author = $user->id; + $fichinter->modelpdf = 'soleil'; + $fichinter->origin = $object->element; + $fichinter->origin_id = $object->id; + + // Extrafields + $extrafields = new ExtraFields($this->db); + $extralabels = $extrafields->fetch_name_optionals_label($fichinter->table_element); + $array_options = $extrafields->getOptionalsFromPost($extralabels); + $fichinter->array_options = $array_options; + + $id = $fichinter->create($user); + if ($id <= 0) { + setEventMessages($fichinter->error, null, 'errors'); + } + } + + if (!empty($backtopage)) { + $url = $backtopage; + } else { + $url = 'card.php?track_id=' . $object->track_id; + } + + header("Location: " . $url); + exit; + } else { + $this->db->rollback(); + setEventMessages($this->error, $this->errors, 'errors'); + } + } else { + setEventMessages($this->error, $this->errors, 'errors'); + } + } + + if ($action == 'edit' && $user->rights->ticketsup->write) { + $error = 0; + + if ($object->fetch(GETPOST('id')) < 0) { + $error++; + array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); + $_GET["action"] = $_POST["action"] = ''; + } + } + + if (GETPOST('update') && GETPOST('id') && $user->rights->ticketsup->write) { + $error = 0; + + $ret = $object->fetch(GETPOST('id')); + if ($ret < 0) { + $error++; + array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); + $action = ''; + } elseif (!GETPOST("label")) { + $error++; + array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Label"))); + $action = 'edit'; + } elseif (!GETPOST("subject")) { + $error++; + array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject"))); + $action = 'edit'; + } + + if (!$error) { + $this->db->begin(); + + $object->label = GETPOST("label"); + $object->description = GETPOST("description"); + + //... + $ret = $object->update(GETPOST('id'), $user); + if ($ret <= 0) { + $error++; + $this->errors = $object->error; + $this->errors = $object->errors; + $action = 'edit'; + } + + if (!$error && $ret > 0) { + $this->db->commit(); + } else { + $this->db->rollback(); + } + } + } + + if ($action == "mark_ticket_read" && $user->rights->ticketsup->write) { + $object->fetch('', '', GETPOST("track_id",'alpha')); + + if ($object->markAsRead($user) > 0) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogMesgReadBy', $user->getFullName($langs)); + $ret = $object->createTicketLog($user, $log_action); + if ($ret > 0) { + setEventMessages($langs->trans('TicketMarkedAsRead'), null, 'mesgs'); + } else { + setEventMessages($langs->trans('TicketMarkedAsReadButLogActionNotSaved'), null, 'errors'); + } + header("Location: card.php?track_id=" . $object->track_id . "&action=view"); + exit; + } else { + array_push($this->errors, $object->error); + } + $action = 'view'; + } + + if ($action == "assign_user" && GETPOST('btn_assign_user','aplha') && $user->rights->ticketsup->write) { + $object->fetch('', '', GETPOST("track_id",'alpha')); + $useroriginassign = $object->fk_user_assign; + $usertoassign = GETPOST('fk_user_assign','int'); + + /*if (! ($usertoassign > 0)) { + $error++; + array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("AssignedTo"))); + $action = 'view'; + }*/ + + if (!$error) + { + $ret = $object->assignUser($user, $usertoassign); + if ($ret < 0) $error++; + } + + if (! $error) // Update list of contacts + { + // Si déjà un user assigné on le supprime des contacts + if ($useroriginassign > 0) { + $internal_contacts = $object->listeContact(-1, 'internal'); + + foreach ($internal_contacts as $key => $contact) { + if ($contact['code'] == "SUPPORTTEC" && $contact['id'] == $useroriginassign) { + } + { + //print "user à effacer : ".$useroriginassign; + $object->delete_contact($contact['rowid']); + } + } + } + + if ($usertoassign > 0) $object->add_contact($usertoassign, "SUPPORTTEC", 'internal', $notrigger = 0); + } + + if (! $error) + { + // Log action in ticket logs table + $object->fetch_user($usertoassign); + $log_action = $langs->trans('TicketLogAssignedTo', $object->user->getFullName($langs)); + $ret = $object->createTicketLog($user, $log_action); + if ($ret > 0) { + setEventMessages($langs->trans('TicketAssigned'), null, 'mesgs'); + } else { + setEventMessages($langs->trans('TicketAssignedButLogActionNotSaved'), null, 'errors'); + } + header("Location: card.php?track_id=" . $object->track_id . "&action=view"); + exit; + } else { + array_push($this->errors, $object->error); + } + $action = 'view'; + } + + if ($action == "change_property" && GETPOST('btn_update_ticket_prop') && $user->rights->ticketsup->write) { + $this->fetch('', '', GETPOST('track_id','alpha')); + + $fieldtomodify = GETPOST('property') . '_code'; + $fieldtomodify_label = GETPOST('property') . '_label'; + + $oldvalue_code = $object->$fieldtomodify; + $newvalue_code = $object->getValueFrom('c_ticketsup_' . GETPOST('property'), GETPOST('update_value'), 'code'); + + $oldvalue_label = $object->$fieldtomodify_label; + $newvalue_label = $object->getValueFrom('c_ticketsup_' . GETPOST('property'), GETPOST('update_value'), 'label'); + + $object->$fieldtomodify = $newvalue_code; + + $ret = $object->update($user); + if ($ret > 0) { + $log_action = $langs->trans('TicketLogPropertyChanged', $oldvalue_label, $newvalue_label); + $ret = $object->createTicketLog($user, $log_action); + if ($ret > 0) { + setEventMessages($langs->trans('TicketUpdated'), null, 'mesgs'); + } + } + $action = 'view'; + } + + if ($action == "new_message" && GETPOST('btn_add_message') && $user->rights->ticketsup->read) { + $ret = $this->newMessage($user, $action); + if ($ret) { + if (!empty($backtopage)) { + $url = $backtopage; + } else { + $url = 'card.php?action=view&track_id=' . $object->track_id; + } + + header("Location: " . $url); + exit; + } else { + setEventMessages($object->error, null, 'errors'); + $action = 'add_message'; + } + } + + if ($action == "new_public_message" && GETPOST('btn_add_message')) { + $this->newMessagePublic($user, $action); + } + + if ($action == "confirm_close" && GETPOST('confirm', 'alpha') == 'yes' && $user->rights->ticketsup->write) { + $this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')); + if ($object->close()) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogClosedBy', $user->getFullName($langs)); + $ret = $object->createTicketLog($user, $log_action); + if ($ret > 0) { + setEventMessages('
    ' . $langs->trans('TicketMarkedAsClosed') . '
    '); + } else { + setEventMessages($langs->trans('TicketMarkedAsClosedButLogActionNotSaved'), null, 'warnings'); + } + $url = 'card.php?action=view&track_id=' . GETPOST('track_id', 'alpha'); + header("Location: " . $url); + } else { + $action = ''; + setEventMessages($this->error, $this->errors, 'errors'); + } + } + + if ($action == "confirm_public_close" && GETPOST('confirm', 'alpha') == 'yes') { + $this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')); + if (($_SESSION['email_customer'] == $object->origin_email || $_SESSION['email_customer'] == $object->thirdparty->email) && $object->close()) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogClosedBy', $_SESSION['email_customer']); + $ret = $object->createTicketLog($user, $log_action); + if ($ret > 0) { + setEventMessages('
    ' . $langs->trans('TicketMarkedAsClosed') . '
    ', null, 'mesgs'); + } else { + setEventMessages($langs->trans('TicketMarkedAsClosedButLogActionNotSaved'), null, 'warnings'); + } + $url = 'view.php?action=view_ticket&track_id=' . GETPOST('track_id', 'alpha'); + header("Location: " . $url); + } else { + setEventMessages($this->error, $this->errors, 'errors'); + $action = ''; + } + } + + if ($action == 'confirm_delete_ticket' && GETPOST('confirm', 'alpha') == "yes" && $user->rights->ticketsup->delete) { + if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + if ($object->delete($user) > 0) { + setEventMessages('
    ' . $langs->trans('TicketDeletedSuccess') . '
    ', null, 'mesgs'); + Header("Location: ".DOL_URL_ROOT."/ticketsup/list.php"); + exit; + } else { + $langs->load("errors"); + $mesg = '
    ' . $langs->trans($this->error) . '
    '; + $action = ''; + } + } + } + + // Set parent company + if ($action == 'set_thirdparty' && $user->rights->societe->creer) { + if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $result = $object->setCustomer(GETPOST('editcustomer', 'int')); + $url = 'card.php?action=view&track_id=' . GETPOST('track_id', 'alpha'); + header("Location: " . $url); + exit(); + } + } + + if ($action == 'set_progression' && $user->rights->ticketsup->write) { + if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $result = $object->setProgression(GETPOST('progress')); + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogProgressSetTo', GETPOST('progress')); + $ret = $object->createTicketLog($user, $log_action); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } + + if ($action == 'setsubject') { + if ($this->fetch(GETPOST('id', 'int'))) { + if ($action == 'setsubject') { + $object->subject = trim(GETPOST('subject', 'alpha')); + } + + if ($action == 'setsubject' && empty($object->subject)) { + $mesg .= ($mesg ? '
    ' : '') . $langs->trans("ErrorFieldRequired", $langs->transnoentities("Subject")); + } + + if (!$mesg) { + if ($object->update($user) >= 0) { + header("Location: " . $_SERVER['PHP_SELF'] . "?track_id=" . $object->track_id); + exit; + } + $mesg = $object->error; + } + } + } + + if ($action == "set_extrafields" && GETPOST('btn_edit_extrafields') && $user->rights->ticketsup->write && !GETPOST('cancel')) { + $res = $this->fetch('', '', GETPOST('track_id','alpha')); + + $extrafields = new ExtraFields($this->db); + $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object); + + $ret = $object->update($user); + if ($ret > 0) { + setEventMessages($langs->trans('TicketUpdated'), null, 'mesgs'); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + + $action = 'view'; + } // Reopen ticket + elseif ($action == 'confirm_reopen' && $user->rights->ticketsup->manage && !GETPOST('cancel')) { + if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + // prevent browser refresh from reopening ticket several times + if ($object->fk_statut == 8) { + $res = $object->setStatut(4); + if ($res) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogReopen'); + $ret = $object->createTicketLog($user, $log_action); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } + } + } // Categorisation dans projet + elseif ($action == 'classin' && $user->rights->ticketsup->write) { + if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $object->setProject(GETPOST('projectid')); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } // Categorisation dans contrat + elseif ($action == 'setcontract' && $user->rights->ticketsup->write) { + if ($this->fetch(GETPOST('id', 'int'), '', GETPOST('track_id', 'alpha')) >= 0) { + $object->setContract(GETPOST('contractid')); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } elseif ($action == "set_message" && $user->rights->ticketsup->manage) { + // altairis: manage cancel button + if (!GETPOST('cancel')) { + $this->fetch('', '', GETPOST('track_id','alpha')); + $oldvalue_message = $object->message; + $fieldtomodify = GETPOST('message_initial'); + + $object->message = $fieldtomodify; + $ret = $object->update($user); + if ($ret > 0) { + $log_action = $langs->trans('TicketInitialMessageModified') . " \n"; + // include the Diff class + dol_include_once('/ticketsup/class/utils_diff.class.php'); + // output the result of comparing two files as plain text + $log_action .= Diff::toString(Diff::compare(strip_tags($oldvalue_message), strip_tags($object->message))); + + $ret = $object->createTicketLog($user, $log_action); + if ($ret > 0) { + setEventMessages($langs->trans('TicketMessageSuccesfullyUpdated'), null, 'mesgs'); + } + } + } + + $action = 'view'; + } // Reopen ticket + elseif ($action == 'confirm_set_status' && $user->rights->ticketsup->write && !GETPOST('cancel')) { + if ($this->fetch(GETPOST('id', 'int'), GETPOST('track_id', 'alpha')) >= 0) { + $new_status = GETPOST('new_status', 'int'); + $old_status = $object->fk_statut; + $res = $object->setStatut($new_status); + if ($res) { + // Log action in ticket logs table + $log_action = $langs->trans('TicketLogStatusChanged', $langs->transnoentities($object->statuts_short[$old_status]), $langs->transnoentities($object->statuts_short[$new_status])); + $ret = $object->createTicketLog($user, $log_action); + $url = 'card.php?action=view&track_id=' . $object->track_id; + header("Location: " . $url); + exit(); + } + } + } + + return 0; + } + + /** + * Add new message on a ticket (private area) + * + * @param User $user User for action + * @param string $action Action string + */ + private function newMessage($user, &$action) + { + global $mysoc, $conf, $langs; + + if (!class_exists('Contact')) { + include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; + } + + $contactstatic = new Contact($this->db); + + $error = 0; + + $object = new Ticketsup($this->db); + $ret = $object->fetch('', '', GETPOST('track_id','alpha')); + $object->socid = $object->fk_soc; + $object->fetch_thirdparty(); + if ($ret < 0) { + $error++; + array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); + $action = ''; + } + + if (!GETPOST("message")) { + $error++; + array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message"))); + $action = 'add_message'; + } + + if (!$error) { + $object->message = GETPOST("message"); + $object->private = GETPOST("private_message"); + $send_email = GETPOST('send_email', 'int'); + + $id = $object->createTicketMessage($user); + if ($id <= 0) { + $error++; + $this->errors = $object->error; + $this->errors = $object->errors; + $action = 'add_message'; + } + + if (!$error && $id > 0) { + setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs'); + + /* + * Send email to linked contacts + */ + if ($send_email > 0) { + // Retrieve internal contact datas + $internal_contacts = $object->getInfosTicketInternalContact(); + $sendto = array(); + if (is_array($internal_contacts) && count($internal_contacts) > 0) { + // altairis: set default subject + $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; + $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); + + $message_intro = $langs->trans('TicketNotificationEmailBody', "#" . $object->id); + $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE; + + $message = $langs->trans('TicketMessageMailIntroText'); + $message .= "\n\n"; + $message .= GETPOST('message'); + + // Coordonnées client + $message .= "\n\n"; + $message .= "==============================================\n"; + $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty') . " : " . $object->thirdparty->name : ''; + $message .= !empty($object->thirdparty->town) ? "\n" . $langs->trans('Town') . " : " . $object->thirdparty->town : ''; + $message .= !empty($object->thirdparty->phone) ? "\n" . $langs->trans('Phone') . " : " . $object->thirdparty->phone : ''; + + // Build array to display recipient list + foreach ($internal_contacts as $key => $info_sendto) { + // altairis: avoid duplicate notifications + if ($info_sendto['id'] == $user->id) { + continue; + } + + if ($info_sendto['email'] != '') { + if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; + + //Contact type + $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); + } + } + $message .= "\n"; + // URL ticket + $url_internal_ticket = dol_buildpath('/ticketsup/card.php', 2) . '?track_id=' . $object->track_id; + + // altairis: make html link on url + $message .= "\n" . $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . '' . $object->track_id . '' . "\n"; + + // Add global email address recipient + // altairis: use new TICKETS_NOTIFICATION_EMAIL_TO configuration variable + if ($conf->global->TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKETS_NOTIFICATION_EMAIL_TO, $sendto)) { + if(!empty($conf->global->TICKETS_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKETS_NOTIFICATION_EMAIL_TO; + } + + // altairis: dont try to send email if no recipient + if (!empty($sendto)) { + $this->sendTicketMessageByEmail($subject, $message, '', $sendto); + } + } + + /* + * Email for externals users if not private + */ + if (empty($object->private)) { + // Retrieve email of all contacts (external) + $external_contacts = $object->getInfosTicketExternalContact(); + + // If no contact, get email from thirdparty + if (is_array($external_contacts) && count($external_contacts) === 0) { + if (!empty($object->fk_soc)) { + $object->fetch_thirdparty($object->fk_soc); + $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id)); + $external_contacts = array_merge($external_contacts, $array_company); + } elseif (empty($object->fk_soc) && !empty($object->origin_email)) { + $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id)); + $external_contacts = array_merge($external_contacts, $array_external); + } + } + + $sendto = array(); + if (is_array($external_contacts) && count($external_contacts) > 0) { + // altairis: get default subject for email to external contacts + $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE; + $subject = GETPOST('subject') ? GETPOST('subject') : '[' . $label_title . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); + + $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKETS_MESSAGE_MAIL_INTRO; + $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE; + + // We put intro after + $message = GETPOST('message'); + $message .= "\n\n"; + + foreach ($external_contacts as $key => $info_sendto) { + // altairis: avoid duplicate emails to external contacts + if ($info_sendto['id'] == $user->contactid) { + continue; + } + + if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) { + if(!empty($info_sendto['email'])) $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; + + $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); + } + } + + // If public interface is not enable, use link to internal page into mail + $url_public_ticket = (!empty($conf->global->TICKETS_ENABLE_PUBLIC_INTERFACE) ? + (!empty($conf->global->TICKETS_URL_PUBLIC_INTERFACE) ? + $conf->global->TICKETS_URL_PUBLIC_INTERFACE . '/view.php' : + dol_buildpath('/ticketsup/public/view.php', 2) + ) : + dol_buildpath('/ticketsup/card.php', 2) + ) . '?track_id=' . $object->track_id; + $message .= "\n" . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . '' . $object->track_id . '' . "\n"; + + // Build final message + $message = $message_intro . $message; + + // Add signature + $message .= '
    ' . $message_signature; + + if (!empty($object->origin_email)) { + $sendto[] = $object->origin_email; + } + + if ($object->fk_soc > 0 && ! in_array($object->origin_email, $sendto)) { + $object->socid = $object->fk_soc; + $object->fetch_thirdparty(); + if(!empty($object->thirdparty->email)) $sendto[] = $object->thirdparty->email; + } + + // altairis: Add global email address reciepient + if ($conf->global->TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKETS_NOTIFICATION_EMAIL_TO, $sendto)) { + if(!empty($conf->global->TICKETS_NOTIFICATION_EMAIL_TO)) $sendto[] = $conf->global->TICKETS_NOTIFICATION_EMAIL_TO; + } + + // altairis: dont try to send email when no recipient + if (!empty($sendto)) { + $this->sendTicketMessageByEmail($subject, $message, '', $sendto); + } + } + } + } + + $this->copyFilesForTicket(); + + // Set status to "answered" if not set yet, only for internal users + if ($object->fk_statut < 3 && !$user->societe_id) { + $object->setStatut(3); + } + + return 1; + } else { + setEventMessages($object->error, $object->errors, 'errors'); + return -1; + } + } else { + setEventMessages($this->error, $this->errors, 'errors'); + return -1; + } + } + + /** + * Add new message on a ticket (public area) + * + * @param User $user User for action + * @param string $action Action string + */ + private function newMessagePublic($user, &$action) + { + + global $mysoc, $conf, $langs; + + $error = 0; + $ret = $object->fetch('', '', GETPOST('track_id','alpha')); + $object->socid = $object->fk_soc; + $object->fetch_thirdparty(); + if ($ret < 0) { + $error++; + array_push($this->errors, $langs->trans("ErrorTicketIsNotValid")); + $action = ''; + } + + if (!GETPOST("message")) { + $error++; + array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message"))); + $action = 'add_message'; + } + + if (!$error) { + $object->message = GETPOST("message"); + $id = $object->createTicketMessage($user); + if ($id <= 0) { + $error++; + $this->errors = $object->error; + $this->errors = $object->errors; + $action = 'add_message'; + } + + if (!$error && $id > 0) { + setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs'); + + // Retrieve internal contact datas + $internal_contacts = $object->getInfosTicketInternalContact(); + $sendto = array(); + if (is_array($internal_contacts) && count($internal_contacts) > 0) { + $subject = '[' . $mysoc->name . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); + + $message = $langs->trans('TicketMessageMailIntroAutoNewPublicMessage', $object->subject); + $message .= "\n"; + $message .= GETPOST('message'); + $message .= "\n"; + + // Coordonnées client + if ($object->thirdparty->id > 0) { + $message .= "\n\n"; + $message .= "==============================================\n"; + $message .= $langs->trans('Thirparty') . " : " . $object->thirdparty->name; + $message .= !empty($object->thirdparty->town) ? $langs->trans('Town') . " : " . $object->thirdparty->town : ''; + $message .= "\n"; + $message .= !empty($object->thirdparty->phone) ? $langs->trans('Phone') . " : " . $object->thirdparty->phone : ''; + $message .= "\n"; + } + + // Build array to display recipient list + foreach ($internal_contacts as $key => $info_sendto) { + if ($info_sendto['email'] != '') { + $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; + } + + // Contact type + $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); + $message .= "\n"; + } + + // URL ticket + $url_internal_ticket = dol_buildpath('/ticketsup/card.php', 2) . '?track_id=' . $object->track_id; + $message .= "\n" . $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . $url_internal_ticket . "\n"; + + $message .= "\n\n"; + + $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE; + + // Add global email address reciepient + if ($conf->global->TICKETS_NOTIFICATION_ALSO_MAIN_ADDRESS && !in_array($conf->global->TICKETS_NOTIFICATION_EMAIL_FROM, $sendto)) { + $sendto[] = $conf->global->TICKETS_NOTIFICATION_EMAIL_FROM; + } + + $this->sendTicketMessageByEmail($subject, $message, '', $sendto); + } + + /* + * Email for externals users if not private + */ + + // Retrieve email of all contacts external + $external_contacts = $object->getInfosTicketExternalContact(); + $sendto = array(); + if (is_array($external_contacts) && count($external_contacts) > 0) { + $subject = '[' . $mysoc->name . '- ticket #' . $object->track_id . '] ' . $langs->trans('TicketNewMessage'); + + $message = $langs->trans('TicketMessageMailIntroAutoNewPublicMessage', $object->subject); + $message .= "\n"; + + $message .= GETPOST('message'); + $message .= "\n\n"; + + $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKETS_MESSAGE_MAIL_SIGNATURE; + foreach ($external_contacts as $key => $info_sendto) { + if ($info_sendto['email'] != '') { + $sendto[] = trim($info_sendto['firstname'] . " " . $info_sendto['lastname']) . " <" . $info_sendto['email'] . ">"; + } + $recipient = ''; + $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1') . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient') . ' : ' . $recipient . "\n" : ''); + } + + $url_public_ticket = ($conf->global->TICKETS_URL_PUBLIC_INTERFACE ? $conf->global->TICKETS_URL_PUBLIC_INTERFACE . '/view.php' : dol_buildpath('/ticketsup/public/view.php', 2)) . '?track_id=' . $object->track_id; + $message .= "\n\n" . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . $url_public_ticket . "\n"; + + // Add signature + $message .= '\n\n' . $message_signature; + + if (!empty($object->origin_email) && !in_array($object->origin_email, $sendto)) { + $sendto[] = $object->origin_email; + } + if ($object->fk_soc > 0 && !in_array($object->origin_email, $sendto)) { + $sendto[] = $object->thirdparty->email; + } + $this->sendTicketMessageByEmail($subject, $message, '', $sendto); + } + + $this->copyFilesForTicket(); + + $url = 'view.php?action=view_ticket&track_id=' . $object->track_id; + header("Location: " . $url); + exit; + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } else { + setEventMessages($this->error, $this->errors, 'errors'); + } + } + + /** + * Fetch object + * + * @param int $id ID of ticket + * @param string $ref Reference of ticket + * @param string $track_id Track ID of ticket (for public area) + * @return void + */ + public function fetch($id = 0, $ref = '', $track_id = '') + { + $this->getInstanceDao(); + return $this->dao->fetch($id, $ref, $track_id); + } + + /** + * Print statut + * + * @param int $mode Display mode + * @return void + */ + public function getLibStatut($mode = 0) + { + $this->getInstanceDao(); + $this->dao->fk_statut = $this->fk_statut; + return $this->dao->getLibStatut($mode); + } + + /** + * Get ticket info + * + * @param int $id Object id + */ + public function getInfo($id) + { + $this->getInstanceDao(); + $this->dao->fetch($id, '', $track_id); + + $this->label = $this->dao->label; + $this->description = $this->dao->description; + } + + /** + * Get action title + * + * @param string $action Type of action + */ + public function getTitle($action = '') + { + global $langs; + + if ($action == 'create_ticket') { + return $langs->trans("CreateTicket"); + } elseif ($action == 'edit') { + return $langs->trans("EditTicket"); + } elseif ($action == 'view') { + return $langs->trans("TicketCard"); + } elseif ($action == 'add_message') { + return $langs->trans("AddMessage"); + } else { + return $langs->trans("TicketsManagement"); + } + } + + /** + * View html list of logs + * + * @param boolean $show_user Show user who make action + */ + public function viewTicketLogs($show_user = true) + { + global $conf, $langs, $bc; + + // Load logs in cache + $ret = $this->dao->loadCacheLogsTicket(); + + if (is_array($this->dao->cache_logs_ticket) && count($this->dao->cache_logs_ticket) > 0) { + print ''; + + print ''; + + print ''; + + if ($show_user) { + print ''; + } + + $var = true; + + foreach ($this->dao->cache_logs_ticket as $id => $arraylogs) { + $var = !$var; + print ""; + print ''; + + if ($show_user) { + print ''; + } + print ''; + print ""; + print ''; + print ''; + } + + print '
    '; + print $langs->trans('DateCreation'); + print ''; + print $langs->trans('User'); + print '
    '; + print dol_print_date($arraylogs['datec'], 'dayhour'); + print ''; + if ($arraylogs['fk_user_create'] > 0) { + $userstat = new User($this->db); + $res = $userstat->fetch($arraylogs['fk_user_create']); + if ($res) { + print $userstat->getNomUrl(1); + } + } + print '
    '; + print dol_nl2br($arraylogs['message']); + + print '
    '; + } else { + print '
    ' . $langs->trans('NoLogForThisTicket') . '
    '; + } + } + + /** + * View list of logs with timeline view + * + * @param boolean $show_user Show user who make action + * @param Ticketsup $object Object + */ + public function viewTimelineTicketLogs($show_user = true, $object = true) + { + global $conf, $langs, $bc; + + // Load logs in cache + $ret = $object->loadCacheLogsTicket(); + + if (is_array($object->cache_logs_ticket) && count($object->cache_logs_ticket) > 0) { + print '
    '; + + foreach ($object->cache_logs_ticket as $id => $arraylogs) { + print '
    '; + print '
    '; + //print ''; + print '
    '; + + print '
    '; + print dol_nl2br($arraylogs['message']); + + print ''; + print dol_print_date($arraylogs['datec'], 'dayhour'); + + if ($show_user) { + if ($arraylogs['fk_user_create'] > 0) { + $userstat = new User($this->db); + $res = $userstat->fetch($arraylogs['fk_user_create']); + if ($res) { + print '
    '.$userstat->getNomUrl(1).''; + } + } + } + print '
    '; + print '
    '; + print '
    '; + } + print '
    '; + } else { + print '
    ' . $langs->trans('NoLogForThisTicket') . '
    '; + } + } + + /** + * Show ticket original message + * + * @param User $user User wich display + * @param string $action Action mode + * @param TicketSup $object Object ticket + * @return void + */ + public function viewTicketOriginalMessage($user, $action, $object) + { + global $langs; + if (!empty($user->rights->ticketsup->manage) && $action == 'edit_message_init') { + // MESSAGE + + print '
    '; + print ''; + print ''; + print ''; + } + + // Initial message + print '
    '; + print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table + print ''; + print ''; + + print ''; + + print ''; + print ''; + print '
    '; + print '' . $langs->trans("InitialMessage") . ' '; + if ($user->rights->ticketsup->manage) { + print '' . img_edit($langs->trans('Modify')) . ' ' . $langs->trans('Modify') . ''; + } + print '
    '; + if (!empty($user->rights->ticketsup->manage) && $action == 'edit_message_init') { + // MESSAGE + $msg = GETPOST('message_initial', 'alpha') ? GETPOST('message_initial', 'alpha') : $object->message; + include_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + $uselocalbrowser = true; + $doleditor = new DolEditor('message_initial', $msg, '100%', 250, 'dolibarr_details', 'In', true, $uselocalbrowser); + $doleditor->Create(); + } else { + // Deal with format differences (text / HTML) + if (dol_textishtml($object->message)) { + print $object->message; + } else { + print dol_nl2br($object->message); + } + + //print '
    ' . $object->message . '
    '; + } + print '
    '; + if ($user->rights->ticketsup->manage && $action == 'edit_message_init') { + print ' '; + print ' '; + print ''; + } + } + /** + * View html list of message for ticket + * + * @param boolean $show_private Show private messages + * @param boolean $show_user Show user who make action + */ + public function viewTicketMessages($show_private, $show_user = true) + { + global $conf, $langs, $user, $bc; + global $object; + + // Load logs in cache + $ret = $object->loadCacheMsgsTicket(); + $action = GETPOST('action'); + + $this->viewTicketOriginalMessage($user, $action); + + if (is_array($object->cache_msgs_ticket) && count($object->cache_msgs_ticket) > 0) { + print_titre($langs->trans('TicketMailExchanges')); + + print ''; + + print ''; + + print ''; + + if ($show_user) { + print ''; + } + + foreach ($object->cache_msgs_ticket as $id => $arraymsgs) { + if (!$arraymsgs['private'] + || ($arraymsgs['private'] == "1" && $show_private) + ) { + //print ''; + $var = !$var; + print ""; + print ''; + if ($show_user) { + print ''; + } + print ''; + print ""; + print ''; + print ''; + } + } + + print '
    '; + print $langs->trans('DateCreation'); + print ''; + print $langs->trans('User'); + print '
    '; + print dol_print_date($arraymsgs['datec'], 'dayhour'); + print ''; + if ($arraymsgs['fk_user_action'] > 0) { + $userstat = new User($this->db); + $res = $userstat->fetch($arraymsgs['fk_user_action']); + if ($res) { + print $userstat->getNomUrl(0); + } + } else { + print $langs->trans('Customer'); + } + print '
    '; + print $arraymsgs['message']; + print '
    '; + } else { + print '
    ' . $langs->trans('NoMsgForThisTicket') . '
    '; + } + } + + /** + * View list of message for ticket with timeline display + * + * @param boolean $show_private Show private messages + * @param boolean $show_user Show user who make action + * @param Ticketsup $object Object ticketsup + */ + public function viewTicketTimelineMessages($show_private, $show_user, Ticketsup $object) + { + global $conf, $langs, $user, $bc; + + // Load logs in cache + $ret = $object->loadCacheMsgsTicket(); + $action = GETPOST('action'); + + if (is_array($object->cache_msgs_ticket) && count($object->cache_msgs_ticket) > 0) { + print '
    '; + + foreach ($object->cache_msgs_ticket as $id => $arraymsgs) { + if (!$arraymsgs['private'] + || ($arraymsgs['private'] == "1" && $show_private) + ) { + print '
    '; + print '
    '; + print ''; + print '
    '; + + print '
    '; + print $arraymsgs['message']; + + print ''; + print dol_print_date($arraymsgs['datec'], 'dayhour'); + + if ($show_user) { + if ($arraymsgs['fk_user_action'] > 0) { + $userstat = new User($this->db); + $res = $userstat->fetch($arraymsgs['fk_user_action']); + if ($res) { + print '
    '; + print $userstat->getNomUrl(1); + } + } else { + print '
    '; + print $langs->trans('Customer'); + } + } + print '
    '; + print '
    '; + print '
    '; + } + } + print '
    '; + } else { + print '
    ' . $langs->trans('NoMsgForThisTicket') . '
    '; + } + } + + /** + * load_previous_next_ref + * + * @param string $filter Filter + * @param int $fieldid Id + * @return int 0 + */ + function load_previous_next_ref($filter, $fieldid) + { + $this->getInstanceDao(); + return $object->load_previous_next_ref($filter, $fieldid); + } + + /** + * Send ticket by email to linked contacts + * + * @param string $subject Email subject + * @param string $message Email message + * @param int $send_internal_cc Receive a copy on internal email ($conf->global->TICKETS_NOTIFICATION_EMAIL_FROM) + * @param array $array_receiver Array of receiver. exemple array('name' => 'John Doe', 'email' => 'john@doe.com', etc...) + */ + public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array()) + { + global $conf, $langs; + + if ($conf->global->TICKETS_DISABLE_ALL_MAILS) { + dol_syslog(get_class($this) . '::sendTicketMessageByEmail: Emails are disable into ticketsup setup by option TICKETSUP_DISABLE_ALL_MAILS', LOG_WARNING); + return ''; + } + + $langs->load("mails"); + + if (!class_exists('Contact')) { + include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; + } + + $contactstatic = new Contact($this->db); + + // If no receiver defined, load all ticket linked contacts + if (!is_array($array_receiver) || !count($array_receiver) > 0) { + $array_receiver = $object->getInfosTicketInternalContact(); + $array_receiver = array_merge($array_receiver, $object->getInfosTicketExternalContact()); + } + + if ($send_internal_cc) { + $sendtocc = $conf->global->TICKETS_NOTIFICATION_EMAIL_FROM; + } + + $from = $conf->global->TICKETS_NOTIFICATION_EMAIL_FROM; + if (is_array($array_receiver) && count($array_receiver) > 0) { + foreach ($array_receiver as $key => $receiver) { + // Create form object + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($this->db); + + $attachedfiles = $formmail->get_attached_files(); + $filepath = $attachedfiles['paths']; + $filename = $attachedfiles['names']; + $mimetype = $attachedfiles['mimes']; + + $message_to_send = dol_nl2br($message); + + // Envoi du mail + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $receiver, $from, $message_to_send, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1); + if ($mailfile->error) { + setEventMessages($mailfile->error, null, 'errors'); + } else { + $result = $mailfile->sendfile(); + if ($result) { + setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs'); + } else { + $langs->load("other"); + if ($mailfile->error) { + setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors'); + dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver) . ' : ' . $mailfile->error); + } else { + setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors'); + } + } + } + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + } else { + $langs->load("other"); + setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings'); + } + } + + /** + * Copy files into ticket directory + * Used for files linked into messages + * + * @return void + */ + public function copyFilesForTicket() + { + global $conf, $object; + + // Create form object + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; + + $maxwidthsmall = 270; + $maxheightsmall = 150; + $maxwidthmini = 128; + $maxheightmini = 72; + + $formmail = new FormMail($this->db); + + $attachedfiles = $formmail->get_attached_files(); + $filepath = $attachedfiles['paths']; + $filename = $attachedfiles['names']; + $mimetype = $attachedfiles['mimes']; + + // Copy files into ticket directory + $destdir = $conf->ticketsup->dir_output . '/' . $object->track_id; + + if (!dol_is_dir($destdir)) { + dol_mkdir($destdir); + } + foreach ($filename as $i => $val) { + $res = dol_move($filepath[$i], $destdir . '/' . $filename[$i]); + if (image_format_supported($destdir . '/' . $filename[$i]) == 1) { + // Create small thumbs for image (Ratio is near 16/9) + // Used on logon for example + $imgThumbSmall = vignette($destdir . '/' . $filename[$i], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs"); + // Create mini thumbs for image (Ratio is near 16/9) + // Used on menu or for setup page for example + $imgThumbMini = vignette($destdir . '/' . $filename[$i], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs"); + } + $formmail->remove_attached_files($i); + } + } + + /** + * Print html navbar with link to set ticket status + * + * @param Ticketsup $object Ticket sup + * @return void + */ + public function viewStatusActions(Ticketsup $object) + { + global $langs; + + print '
    '; + print '
    '; + print '
    '; + print '
    '; + print '' . $langs->trans('TicketChangeStatus') . ''; + print '
    '; + // Exclude status which requires specific method + $exclude_status = array(Ticketsup::STATUS_CLOSED, Ticketsup::STATUS_CANCELED); + // Exclude actual status + $exclude_status = array_merge($exclude_status, array(intval($object->fk_statut))); + + // Sort results to be similar to status object list + //sort($exclude_status); + + //print '
    '; + foreach ($object->statuts_short as $status => $statut_label) { + if (!in_array($status, $exclude_status)) { + print '
    '; + + if ($status == 1) + $urlforbutton = $_SERVER['PHP_SELF'] . '?track_id=' . $object->track_id . '&action=mark_ticket_read'; // To set as read, we use a dedicated action + else + $urlforbutton = $_SERVER['PHP_SELF'] . '?track_id=' . $object->track_id . '&action=set_status&new_status=' . $status; + + print ''; + print img_picto($langs->trans($object->statuts_short[$status]), 'statut' . $status . '.png@ticketsup') . ' ' . $langs->trans($object->statuts_short[$status]); + print ''; + print '
    '; + } + } + print '

    '; + } + + + /** + * deleteObjectLinked + * + * @return number + */ + public function deleteObjectLinked() + { + return $this->dao->deleteObjectLinked(); + } + + /** + * Hook to add email element template + * + * @param array $parameters Parameters + * @param Ticketsup $object Object for action + * @param string $action Action string + * @param HookManager $hookmanager Hookmanager object + * @return int + */ + public function emailElementlist($parameters, &$object, &$action, $hookmanager) + { + global $langs; + + $error = 0; + + if (in_array('admin', explode(':', $parameters['context']))) { + $this->results = array('ticketsup_send' => $langs->trans('MailToSendTicketsupMessage')); + } + + if (! $error) { + return 0; // or return 1 to replace standard code + } else { + $this->errors[] = 'Error message'; + return -1; + } + } +} diff --git a/htdocs/ticketsup/class/api_dictionaryticketsupcategories.class.php b/htdocs/ticketsup/class/api_dictionaryticketsupcategories.class.php new file mode 100644 index 00000000000..d33f96db881 --- /dev/null +++ b/htdocs/ticketsup/class/api_dictionaryticketsupcategories.class.php @@ -0,0 +1,98 @@ + + * Copyright (C) 2016 Laurent Destailleur + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use Luracast\Restler\RestException; + +require_once DOL_DOCUMENT_ROOT.'/main.inc.php'; + +/** + * API class for ticketsup types + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + */ +class DictionaryTicketsupCategories extends DolibarrApi +{ + /** + * Constructor + */ + function __construct() + { + global $db; + $this->db = $db; + } + + /** + * Get the list of ticketsup types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return List of events types + * + * @throws RestException + */ + function index($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, pos, label, use_default, description"; + $sql.= " FROM ".MAIN_DB_PREFIX."c_ticketsup_category as t"; + $sql.= " WHERE t.active = 1"; + // Add sql filters + if ($sqlfilters) + { + if (! DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql.= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of ticketsup categories : '.$this->db->lasterror()); + } + + return $list; + } + + +} diff --git a/htdocs/ticketsup/class/api_dictionaryticketsupseverities.class.php b/htdocs/ticketsup/class/api_dictionaryticketsupseverities.class.php new file mode 100644 index 00000000000..892563ad426 --- /dev/null +++ b/htdocs/ticketsup/class/api_dictionaryticketsupseverities.class.php @@ -0,0 +1,98 @@ + + * Copyright (C) 2016 Laurent Destailleur + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use Luracast\Restler\RestException; + +require_once DOL_DOCUMENT_ROOT.'/main.inc.php'; + +/** + * API class for ticketsup severities + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + */ +class DictionaryTicketsupSeverities extends DolibarrApi +{ + /** + * Constructor + */ + function __construct() + { + global $db; + $this->db = $db; + } + + /** + * Get the list of ticketsup types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return List of events types + * + * @throws RestException + */ + function index($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, pos, label, use_default, color, description"; + $sql.= " FROM ".MAIN_DB_PREFIX."c_ticketsup_severity as t"; + $sql.= " WHERE t.active = 1"; + // Add sql filters + if ($sqlfilters) + { + if (! DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql.= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of ticketsup severities : '.$this->db->lasterror()); + } + + return $list; + } + + +} diff --git a/htdocs/ticketsup/class/api_dictionaryticketsuptypes.class.php b/htdocs/ticketsup/class/api_dictionaryticketsuptypes.class.php new file mode 100644 index 00000000000..90b8d4f6bbc --- /dev/null +++ b/htdocs/ticketsup/class/api_dictionaryticketsuptypes.class.php @@ -0,0 +1,100 @@ + + * Copyright (C) 2016 Laurent Destailleur + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +use Luracast\Restler\RestException; + +require_once DOL_DOCUMENT_ROOT.'/main.inc.php'; + +/** + * API class for ticketsup types + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + */ +class DictionaryTicketsupTypes extends DolibarrApi +{ + /** + * Constructor + */ + function __construct() + { + global $db; + $this->db = $db; + } + + /** + * Get the list of ticketsup types. + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Number of items per page + * @param int $page Page number (starting from zero) + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)" + * @return List of events types + * + * @throws RestException + */ + function index($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '') + { + $list = array(); + + $sql = "SELECT rowid, code, pos, label, use_default, description"; + $sql.= " FROM ".MAIN_DB_PREFIX."c_ticketsup_type as t"; + $sql.= " WHERE t.active = 1"; + if ($type) $sql.=" AND t.type LIKE '%" . $this->db->escape($type) . "%'"; + if ($module) $sql.=" AND t.module LIKE '%" . $this->db->escape($module) . "%'"; + // Add sql filters + if ($sqlfilters) + { + if (! DolibarrApi::_checkFilters($sqlfilters)) + { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + + $sql.= $this->db->order($sortfield, $sortorder); + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit, $offset); + } + + $result = $this->db->query($sql); + + if ($result) { + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + for ($i = 0; $i < $min; $i++) { + $list[] = $this->db->fetch_object($result); + } + } else { + throw new RestException(503, 'Error when retrieving list of ticketsup types : '.$this->db->lasterror()); + } + + return $list; + } + + +} diff --git a/htdocs/ticketsup/class/api_ticketsups.class.php b/htdocs/ticketsup/class/api_ticketsups.class.php new file mode 100644 index 00000000000..ee696b34505 --- /dev/null +++ b/htdocs/ticketsup/class/api_ticketsups.class.php @@ -0,0 +1,543 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + use Luracast\Restler\RestException; + +require 'ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticketsup.lib.php'; + +/** + * API class for ticketsup object + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + * + * + */ +class Ticketsups extends DolibarrApi +{ + /** + * @var array $FIELDS Mandatory fields, checked when create and update object + */ + public static $FIELDS = array( + 'subject', + 'message' + ); + + /** + * @var array $FIELDS_MESSAGES Mandatory fields, checked when create and update object + */ + public static $FIELDS_MESSAGES = array( + 'track_id', + 'message' + ); + + /** + * @var Ticketsup $ticketsup {@type Ticketsup} + */ + public $ticketsup; + + /** + * Constructor + * + * @url GET ticketsup/ + * + */ + public function __construct() + { + global $db; + $this->db = $db; + $this->ticketsup = new Ticketsup($this->db); + } + + /** + * Get properties of a ticketsup object + * + * Return an array with ticketsup informations + * + * @param int $id ID of ticketsup + * @param string $track_id Tracking ID of ticket + * @param string $ref Reference for ticket + * @return array|mixed data without useless information + * + * @url GET track_id/{track_id} + * @url GET ref/{ref} + * @url GET {id} + * @throws RestException + */ + public function get($id = 0, $track_id = '', $ref = '') + { + if (! DolibarrApiAccess::$user->rights->ticketsup->read) { + throw new RestException(401); + } + + // Check parameters + if (!$id && !$track_id && !$ref) { + throw new RestException(401, 'Wrong parameters'); + } + + $result = $this->ticketsup->fetch($id, $ref, $track_id); + if (! $result) { + throw new RestException(404, 'Ticketsup not found'); + } + + // String for user assigned + if ($this->ticketsup->fk_user_assign > 0) { + $userStatic = new User($this->db); + $userStatic->fetch($this->ticketsup->fk_user_assign); + $this->ticketsup->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname; + } + + // Messages of ticket + $messages = array(); + $this->ticketsup->loadCacheMsgsTicket(); + if (is_array($this->ticketsup->cache_msgs_ticket) && count($this->ticketsup->cache_msgs_ticket) > 0) { + $num = count($this->ticketsup->cache_msgs_ticket); + $i = 0; + while ($i < $num) { + if ($this->ticketsup->cache_msgs_ticket[$i]['fk_user_action'] > 0) { + $user_action = new User($this->db); + $user_action->fetch($this->ticketsup->cache_msgs_ticket[$i]['fk_user_action']); + } + + // Now define messages + $messages[] = array( + 'id' => $this->ticketsup->cache_msgs_ticket[$i]['id'], + 'fk_user_action' => $this->ticketsup->cache_msgs_ticket[$i]['fk_user_action'], + 'fk_user_action_socid' => $user_action->socid, + 'fk_user_action_string' => dolGetFirstLastname($user_action->firstname, $user_action->lastname), + 'message' => $this->ticketsup->cache_msgs_ticket[$i]['message'], + 'datec' => $this->ticketsup->cache_msgs_ticket[$i]['datec'], + 'private' => $this->ticketsup->cache_msgs_ticket[$i]['private'] + ); + $i++; + } + $this->ticketsup->messages = $messages; + } + + // History + $history = array(); + $this->ticketsup->loadCacheLogsTicket(); + if (is_array($this->ticketsup->cache_logs_ticket) && count($this->ticketsup->cache_logs_ticket) > 0) { + $num = count($this->ticketsup->cache_logs_ticket); + $i = 0; + while ($i < $num) { + if ($this->ticketsup->cache_logs_ticket[$i]['fk_user_create'] > 0) { + $user_action = new User($this->db); + $user_action->fetch($this->ticketsup->cache_logs_ticket[$i]['fk_user_create']); + } + + // Now define messages + $history[] = array( + 'id' => $this->ticketsup->cache_logs_ticket[$i]['id'], + 'fk_user_action' => $this->ticketsup->cache_logs_ticket[$i]['fk_user_create'], + 'fk_user_action_string' => dolGetFirstLastname($user_action->firstname, $user_action->lastname), + 'message' => $this->ticketsup->cache_logs_ticket[$i]['message'], + 'datec' => $this->ticketsup->cache_logs_ticket[$i]['datec'], + ); + $i++; + } + $this->ticketsup->history = $history; + } + + + if (! DolibarrApi::_checkAccessToResource('ticketsup', $this->ticketsup->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + return $this->_cleanObjectDatas($this->ticketsup); + } + + /** + * List ticketsups + * + * Get a list of ticketsups + * + * @param int $socid Filter list with thirdparty ID + * @param string $mode Use this param to filter list + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Limit for list + * @param int $page Page number + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')" + * + * @return array Array of ticketsup objects + * + */ + public function index($socid = 0, $mode = "", $sortfield = "s.rowid", $sortorder = "ASC", $limit = 0, $page = 0, $sqlfilters = '') + { + global $db, $conf; + + $obj_ret = array(); + + if (!$socid && DolibarrApiAccess::$user->societe_id) { + $socid = DolibarrApiAccess::$user->societe_id; + } + + // If the internal user must only see his customers, force searching by him + if (! DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) { + $search_sale = DolibarrApiAccess::$user->id; + } + + $sql = "SELECT s.rowid"; + if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) { + $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects) + } + $sql.= " FROM ".MAIN_DB_PREFIX."ticketsup as s"; + + if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) { + $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale + } + + $sql.= ' WHERE s.entity IN ('.getEntity('ticketsup', 1).')'; + if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) { + $sql.= " AND s.fk_soc = sc.fk_soc"; + } + if ($socid > 0) { + $sql.= " AND s.fk_soc = ".$socid; + } + if ($search_sale > 0) { + $sql.= " AND s.rowid = sc.fk_soc"; // Join for the needed table to filter by sale + } + + // Example of use $mode + if ($mode == 'new') { + $sql.= " AND s.fk_statut IN (0)"; + } + if ($mode == 'read') { + $sql.= " AND s.fk_statut IN (1)"; + } + if ($mode == 'answered') { + $sql.= " AND s.fk_statut IN (3)"; + } + if ($mode == 'assign') { + $sql.= " AND s.fk_statut IN (4)"; + } + if ($mode == 'inprogress') { + $sql.= " AND s.fk_statut IN (5)"; + } + if ($mode == 'waiting') { + $sql.= " AND s.fk_statut IN (6)"; + } + if ($mode == 'closed') { + $sql.= " AND s.fk_statut IN (8)"; + } + if ($mode == 'deleted') { + $sql.= " AND s.fk_statut IN (9)"; + } + + // Insert sale filter + if ($search_sale > 0) { + $sql .= " AND sc.fk_user = ".$search_sale; + } + // Add sql filters + if ($sqlfilters) { + if (! DolibarrApi::_checkFilters($sqlfilters)) { + throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters); + } + $regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)'; + $sql.=" AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")"; + } + + $sql.= $db->order($sortfield, $sortorder); + + $nbtotalofrecords = 0; + if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { + $result = $db->query($sql); + $nbtotalofrecords = $db->num_rows($result); + } + + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql.= $db->plimit($limit + 1, $offset); + } + $result = $db->query($sql); + if ($result) { + $num = $db->num_rows($result); + while ($i < $num) { + $obj = $db->fetch_object($result); + $ticketsup_static = new Ticketsup($db); + if ($ticketsup_static->fetch($obj->rowid)) { + if ($ticketsup_static->fk_user_assign > 0) { + $userStatic = new User($this->db); + $userStatic->fetch($ticketsup_static->fk_user_assign); + $ticketsup_static->fk_user_assign_string = $userStatic->firstname.' '.$userStatic->lastname; + } + $obj_ret[] = $this->_cleanObjectDatas($ticketsup_static); + } + $i++; + } + } else { + throw new RestException(503, 'Error when retrieve ticketsup list'); + } + if (! count($obj_ret)) { + throw new RestException(404, 'No ticketsup found'); + } + return $obj_ret; + } + + /** + * Create ticketsup object + * + * @param array $request_data Request datas + * @return int ID of ticketsup + * + */ + public function post($request_data = null) + { + $ticketstatic = new Ticketsup($this->db); + if (! DolibarrApiAccess::$user->rights->ticketsup->write) { + throw new RestException(401); + } + // Check mandatory fields + $result = $this->_validate($request_data); + + foreach ($request_data as $field => $value) { + $this->ticketsup->$field = $value; + } + if (empty($this->ticketsup->ref)) { + $this->ticketsup->ref = $ticketstatic->getDefaultRef(); + } + if (empty($this->ticketsup->track_id)) { + $this->ticketsup->track_id = generate_random_id(16); + } + if (! $this->ticketsup->create(DolibarrApiAccess::$user)) { + throw new RestException(500); + } + return $this->ticketsup->id; + } + + /** + * Create ticketsup object + * + * @param array $request_data Request datas + * @return int ID of ticketsup + * + */ + public function postNewMessage($request_data = null) + { + $ticketstatic = new Ticketsup($this->db); + if (! DolibarrApiAccess::$user->rights->ticketsup->write) { + throw new RestException(401); + } + // Check mandatory fields + $result = $this->_validateMessage($request_data); + + foreach ($request_data as $field => $value) { + $this->ticketsup->$field = $value; + } + $ticketMessageText = $this->ticketsup->message; + $result = $this->ticketsup->fetch('', '', $this->ticketsup->track_id); + if (! $result) { + throw new RestException(404, 'Ticketsup not found'); + } + $this->ticketsup->message = $ticketMessageText; + if (! $this->ticketsup->createTicketMessage(DolibarrApiAccess::$user)) { + throw new RestException(500); + } + return $this->ticketsup->id; + } + + /** + * Update ticketsup + * + * @param int $id Id of ticketsup to update + * @param array $request_data Datas + * @return int + * + */ + public function put($id, $request_data = null) + { + if (! DolibarrApiAccess::$user->rights->ticketsup->write) { + throw new RestException(401); + } + + $result = $this->ticketsup->fetch($id); + if (! $result) { + throw new RestException(404, 'Ticketsup not found'); + } + + if (! DolibarrApi::_checkAccessToResource('ticketsup', $this->ticketsup->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + foreach ($request_data as $field => $value) { + $this->ticketsup->$field = $value; + } + + if ($this->ticketsup->update($id, DolibarrApiAccess::$user)) { + return $this->get($id); + } + + return false; + } + + /** + * Delete ticketsup + * + * @param int $id Ticketsup ID + * @return array + * + */ + public function delete($id) + { + if (! DolibarrApiAccess::$user->rights->ticketsup->delete) { + throw new RestException(401); + } + $result = $this->ticketsup->fetch($id); + if (! $result) { + throw new RestException(404, 'Ticketsup not found'); + } + + if (! DolibarrApi::_checkAccessToResource('ticketsup', $this->ticketsup->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + if (!$this->ticketsup->delete($id)) { + throw new RestException(500); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'Ticketsup deleted' + ) + ); + } + + /** + * Validate fields before create or update object + * + * @param array $data Data to validate + * @return array + * + * @throws RestException + */ + private function _validate($data) + { + $ticketsup = array(); + foreach (Ticketsups::$FIELDS as $field) { + if (!isset($data[$field])) { + throw new RestException(400, "$field field missing"); + } + $ticketsup[$field] = $data[$field]; + } + return $ticketsup; + } + + /** + * Validate fields before create or update object message + * + * @param array $data Data to validate + * @return array + * + * @throws RestException + */ + private function _validateMessage($data) + { + $ticketsup = array(); + foreach (Ticketsups::$FIELDS_MESSAGES as $field) { + if (!isset($data[$field])) { + throw new RestException(400, "$field field missing"); + } + $ticketsup[$field] = $data[$field]; + } + return $ticketsup; + } + + + /** + * Clean sensible object datas + * + * @param object $object Object to clean + * @return array Array of cleaned object properties + * + * @todo use an array for properties to clean + * + */ + function _cleanObjectDatas($object) + { + + // Remove $db object property for object + unset($object->db); + + $attr2clean = array( + "contact", + "contact_id", + "ref_previous", + "ref_next", + "ref_ext", + "table_element_line", + "statut", + "country", + "country_id", + "country_code", + "barcode_type", + "barcode_type_code", + "barcode_type_label", + "barcode_type_coder", + "mode_reglement_id", + "cond_reglement_id", + "cond_reglement", + "fk_delivery_address", + "shipping_method_id", + "modelpdf", + "fk_account", + "note_public", + "note_private", + "note", + "total_ht", + "total_tva", + "total_localtax1", + "total_localtax2", + "total_ttc", + "fk_incoterms", + "libelle_incoterms", + "location_incoterms", + "name", + "lastname", + "firstname", + "civility_id", + "cache_msgs_ticket", + "cache_logs_ticket" + ); + foreach ($attr2clean as $toclean) { + unset($object->$toclean); + } + + // If object has lines, remove $db property + if (isset($object->lines) && count($object->lines) > 0) { + $nboflines = count($object->lines); + for ($i=0; $i < $nboflines; $i++) { + $this->_cleanObjectDatas($object->lines[$i]); + } + } + + // If object has linked objects, remove $db property + if (isset($object->linkedObjects) && count($object->linkedObjects) > 0) { + foreach ($object->linkedObjects as $type_object => $linked_object) { + foreach ($linked_object as $object2clean) { + $this->_cleanObjectDatas($object2clean); + } + } + } + return $object; + } +} diff --git a/htdocs/ticketsup/class/ticketsup.class.php b/htdocs/ticketsup/class/ticketsup.class.php new file mode 100644 index 00000000000..ed27003846b --- /dev/null +++ b/htdocs/ticketsup/class/ticketsup.class.php @@ -0,0 +1,2596 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/class/ticketsup.class.php + * \ingroup ticketsup + * \brief Class file for object ticketsup + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT . "/core/class/commonobject.class.php"; +require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php'; +//require_once(DOL_DOCUMENT_ROOT."/societe/class/societe.class.php"); +//require_once(DOL_DOCUMENT_ROOT."/product/class/product.class.php"); + + +/** + * Class to manage ticket + */ +class Ticketsup extends CommonObject +{ + /** + * @var string ID to identify managed object + */ + public $element = 'ticketsup'; + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'ticketsup'; + /** + * @var string Name of field for link to tickets + */ + public $fk_element='fk_ticket'; + /** + * @var int Does ticketsupcore support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + */ + public $ismultientitymanaged = 0; + /** + * @var int Does ticketsupcore support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 1; + /** + * @var string String with name of icon for ticketsupcore. Must be the part after the 'object_' into object_ticketsupcore.png + */ + public $picto = 'ticketsup'; + + + /** + * @var string Hash to identify ticket + */ + public $track_id; + + /** + * @var int Thirdparty ID + */ + public $fk_soc; + + /** + * @var int Project ID + */ + public $fk_project; + + /** + * @var string Person email who have create ticket + */ + public $origin_email; + + /** + * @var int User id who have create ticket + */ + public $fk_user_create; + + /** + * @var int User id who have ticket assigned + */ + public $fk_user_assign; + + /** + * var string Ticket subject + */ + public $subject; + + /** + * @var string Ticket message + */ + public $message; + + /** + * @var int Ticket statut + */ + public $fk_statut; + + /** + * @var string State resolution + */ + public $resolution; + + /** + * @var int Progress in percent + */ + public $progress; + + /** + * @var int Duration for ticket + */ + public $timing; + + /** + * @var string Type code + */ + public $type_code; + + /** + * @var string Category code + */ + public $category_code; + + /** + * @var string Severity code + */ + public $severity_code; + + /** + * @var int Création date + */ + public $datec = ''; + + /** + * @var int Read date + */ + public $date_read = ''; + + /** + * @var int Close ticket date + */ + public $date_close = ''; + + /** + * @var array cache_types_tickets + */ + public $cache_types_tickets; + + /** + * @var array tickets categories + */ + public $cache_category_tickets; + + /** + * @var int Notify tiers at create + */ + public $notify_tiers_at_create; + + public $lines; + + /** + * @var string Regex pour les images + */ + public $regeximgext = '\.jpg|\.jpeg|\.bmp|\.gif|\.png|\.tiff'; + + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'position'=>1, 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"), + 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1), + 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object"), + 'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-2, 'enabled'=>0, 'position'=>20, 'notnull'=>1, 'index'=>1), + 'track_id' => array('type'=>'varchar(255)', 'label'=>'TrackID', 'visible'=>0, 'enabled'=>1, 'position'=>30, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"), + 'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>1, 'enabled'=>1, 'position'=>49, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object"), + 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToThirparty"), + 'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>1, 'enabled'=>1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"LinkToProject"), + 'fk_user_create' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Author', 'visible'=>1, 'enabled'=>1, 'position'=>510, 'notnull'=>1), + 'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>510, 'notnull'=>1), + 'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>12, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'message' => array('type'=>'text', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>60, 'notnull'=>-1,), + 'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-2, 'enabled'=>1, 'position'=>40, 'notnull'=>1), + 'progress' => array('type'=>'varchar(100)', 'label'=>'Progression', 'visible'=>1, 'enabled'=>1, 'position'=>41, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'category_code' => array('type'=>'varchar(32)', 'label'=>'Category', 'visible'=>1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'searchall'=>1, 'help'=>""), + 'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>-2, 'enabled'=>1, 'position'=>500, 'notnull'=>1), + 'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>-2, 'enabled'=>1, 'position'=>500, 'notnull'=>1), + 'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-2, 'enabled'=>1, 'position'=>500, 'notnull'=>1), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-2, 'enabled'=>1, 'position'=>501, 'notnull'=>1), + 'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted')) + ); + + /** + * Status + */ + const STATUS_NOT_READ = 0; + const STATUS_READ = 1; + const STATUS_ANSWERED = 3; + const STATUS_ASSIGNED = 4; + const STATUS_IN_PROGRESS = 5; + const STATUS_WAITING = 6; + const STATUS_CLOSED = 8; + const STATUS_CANCELED = 9; + + + + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + + $this->statuts_short = array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted'); + $this->statuts = array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'Closed', 9 => 'Deleted'); + } + + /** + * Check properties of ticket are ok (like ref, track_id, ...). + * All properties must be already loaded on object (this->ref, this->track_id, ...). + * + * @return int 0 if OK, <0 if KO + */ + private function verify() + { + $this->errors = array(); + + $result = 0; + + // Clean parameters + if (isset($this->ref)) { + $this->ref = trim($this->ref); + } + + if (isset($this->track_id)) { + $this->track_id = trim($this->track_id); + } + + if (isset($this->fk_soc)) { + $this->fk_soc = trim($this->fk_soc); + } + + if (isset($this->fk_project)) { + $this->fk_project = trim($this->fk_project); + } + + if (isset($this->origin_email)) { + $this->origin_email = trim($this->origin_email); + } + + if (isset($this->fk_user_create)) { + $this->fk_user_create = trim($this->fk_user_create); + } + + if (isset($this->fk_user_assign)) { + $this->fk_user_assign = trim($this->fk_user_assign); + } + + if (isset($this->subject)) { + $this->subject = trim($this->subject); + } + + if (isset($this->message)) { + $this->message = trim($this->message); + } + + if (isset($this->fk_statut)) { + $this->fk_statut = trim($this->fk_statut); + } + + if (isset($this->resolution)) { + $this->resolution = trim($this->resolution); + } + + if (isset($this->progress)) { + $this->progress = trim($this->progress); + } + + if (isset($this->timing)) { + $this->timing = trim($this->timing); + } + + if (isset($this->type_code)) { + $this->type_code = trim($this->type_code); + } + + if (isset($this->category_code)) { + $this->category_code = trim($this->category_code); + } + + if (isset($this->severity_code)) { + $this->severity_code = trim($this->severity_code); + } + + if (empty($this->ref)) { + $this->errors[] = 'ErrorBadRef'; + dol_syslog(get_class($this) . "::create error -1 ref null", LOG_ERR); + $result = -1; + } + + return $result; + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create($user, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + $this->datec = dol_now(); + + // Check more parameters + // If error, this->errors[] is filled + $result = $this->verify(); + if ($result >= 0) { + // Insert request + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticketsup("; + $sql .= "ref,"; + $sql .= "track_id,"; + $sql .= "fk_soc,"; + $sql .= "fk_project,"; + $sql .= "origin_email,"; + $sql .= "fk_user_create,"; + $sql .= "fk_user_assign,"; + $sql .= "subject,"; + $sql .= "message,"; + $sql .= "fk_statut,"; + $sql .= "resolution,"; + $sql .= "progress,"; + $sql .= "timing,"; + $sql .= "type_code,"; + $sql .= "category_code,"; + $sql .= "severity_code,"; + $sql .= "datec,"; + $sql .= "date_read,"; + $sql .= "date_close,"; + $sql .= "entity,"; + $sql .= "notify_tiers_at_create"; + $sql .= ") VALUES ("; + $sql .= " " . (!isset($this->ref) ? '' : "'" . $this->db->escape($this->ref) . "'") . ","; + $sql .= " " . (!isset($this->track_id) ? 'NULL' : "'" . $this->db->escape($this->track_id) . "'") . ","; + $sql .= " " . ($this->fk_soc > 0 ? $this->db->escape($this->fk_soc) : "null") . ","; + $sql .= " " . ($this->fk_project > 0 ? $this->db->escape($this->fk_project) : "null") . ","; + $sql .= " " . (!isset($this->origin_email) ? 'NULL' : "'" . $this->db->escape($this->origin_email) . "'") . ","; + $sql .= " " . ($this->fk_user_create > 0 ? $this->fk_user_create : ($user->id > 0 ? $user->id : 'NULL')) . ","; + $sql .= " " . ($this->fk_user_assign > 0 ? $this->fk_user_assign : 'NULL') . ","; + $sql .= " " . (!isset($this->subject) ? 'NULL' : "'" . $this->db->escape($this->subject) . "'") . ","; + $sql .= " " . (!isset($this->message) ? 'NULL' : "'" . $this->db->escape($this->message) . "'") . ","; + $sql .= " " . (!isset($this->fk_statut) ? '0' : "'" . $this->db->escape($this->fk_statut) . "'") . ","; + $sql .= " " . (!isset($this->resolution) ? 'NULL' : "'" . $this->db->escape($this->resolution) . "'") . ","; + $sql .= " " . (!isset($this->progress) ? '0' : "'" . $this->db->escape($this->progress) . "'") . ","; + $sql .= " " . (!isset($this->timing) ? 'NULL' : "'" . $this->db->escape($this->timing) . "'") . ","; + $sql .= " " . (!isset($this->type_code) ? 'NULL' : "'" . $this->db->escape($this->type_code) . "'") . ","; + $sql .= " " . (!isset($this->category_code) ? 'NULL' : "'" . $this->db->escape($this->category_code) . "'") . ","; + $sql .= " " . (!isset($this->severity_code) ? 'NULL' : "'" . $this->db->escape($this->severity_code) . "'") . ","; + $sql .= " " . (!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'" . $this->db->idate($this->datec) . "'") . ","; + $sql .= " " . (!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_read) . "'") . ","; + $sql .= " " . (!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_close) . "'") . ""; + $sql .= ", " . $conf->entity; + $sql .= ", " . (!isset($this->notify_tiers_at_create) ? '1' : "'" . $this->db->escape($this->notify_tiers_at_create) . "'"); + $sql .= ")"; + + $this->db->begin(); + + dol_syslog(get_class($this) . "::create sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + + if (!$error) { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "ticketsup"); + + if (!$notrigger) { + // Call trigger + $result=$this->call_trigger('TICKET_CREATE', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + } + + //Update extrafield + if (!$error) { + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) { // For avoid conflicts if trigger used + $result = $this->insertExtraFields(); + if ($result < 0) { + $error++; + } + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return $this->id; + } + } else { + $this->db->rollback(); + dol_syslog(get_class($this) . "::Create fails verify " . join(',', $this->errors), LOG_WARNING); + return -3; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @param string $ref Ref + * @param string $track_id Track id, a hash like ref + * @return int <0 if KO, >0 if OK + */ + public function fetch($id = '', $ref = '', $track_id = '') + { + global $langs; + + // Check parameters + if (! $id && ! $track_id && ! $ref) { + $this->error = 'ErrorWrongParameters'; + dol_print_error(get_class($this) . "::fetch " . $this->error); + return -1; + } + + $sql = "SELECT"; + $sql .= " t.rowid,"; + $sql .= " t.ref,"; + $sql .= " t.track_id,"; + $sql .= " t.fk_soc,"; + $sql .= " t.fk_project,"; + $sql .= " t.origin_email,"; + $sql .= " t.fk_user_create,"; + $sql .= " t.fk_user_assign,"; + $sql .= " t.subject,"; + $sql .= " t.message,"; + $sql .= " t.fk_statut,"; + $sql .= " t.resolution,"; + $sql .= " t.progress,"; + $sql .= " t.timing,"; + $sql .= " t.type_code,"; + $sql .= " t.category_code,"; + $sql .= " t.severity_code,"; + $sql .= " t.datec,"; + $sql .= " t.date_read,"; + $sql .= " t.date_close,"; + $sql .= " t.tms"; + $sql .= ", type.code as type_code, type.label as type_label, category.code as category_code, category.label as category_label, severity.code as severity_code, severity.label as severity_label"; + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup as t"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_type as type ON type.code=t.type_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_category as category ON category.code=t.category_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_severity as severity ON severity.code=t.severity_code"; + + if ($id) { + $sql .= " WHERE t.rowid = " . $this->db->escape($id); + } else { + $sql .= " WHERE t.entity IN (" . getEntity($this->element, 1) . ")"; + if ($track_id) { + $sql .= " AND t.track_id = '" . $this->db->escape($track_id) . "'"; + } elseif ($ref) { + $sql .= " AND t.ref = '" . $this->db->escape($ref) . "'"; + } + } + + dol_syslog(get_class($this) . "::fetch sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + if ($this->db->num_rows($resql)) { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + $this->ref = $obj->ref; + $this->track_id = $obj->track_id; + $this->fk_soc = $obj->fk_soc; + $this->socid = $obj->fk_soc; // for fetch_thirdparty() method + $this->fk_project = $obj->fk_project; + $this->origin_email = $obj->origin_email; + $this->fk_user_create = $obj->fk_user_create; + $this->fk_user_assign = $obj->fk_user_assign; + $this->subject = $obj->subject; + $this->message = $obj->message; + $this->fk_statut = $obj->fk_statut; + $this->resolution = $obj->resolution; + $this->progress = $obj->progress; + $this->timing = $obj->timing; + + $this->type_code = $obj->type_code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label_type = ($langs->trans("TicketTypeShort" . $obj->type_code) != ("TicketTypeShort" . $obj->type_code) ? $langs->trans("TicketTypeShort" . $obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : '')); + $this->type_label = $label_type; + + $this->category_code = $obj->category_code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label_category = ($langs->trans("TicketCategoryShort" . $obj->category_code) != ("TicketCategoryShort" . $obj->category_code) ? $langs->trans("TicketCategoryShort" . $obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : '')); + $this->category_label = $label_category; + + $this->severity_code = $obj->severity_code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label_severity = ($langs->trans("TicketSeverityShort" . $obj->severity_code) != ("TicketSeverityShort" . $obj->severity_code) ? $langs->trans("TicketSeverityShort" . $obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : '')); + $this->severity_label = $label_severity; + + $this->datec = $this->db->jdate($obj->datec); + $this->date_read = $this->db->jdate($obj->date_read); + $this->date_close = $this->db->jdate($obj->date_close); + $this->tms = $this->db->jdate($obj->tms); + + $this->fetch_optionals(); + } + $this->db->free($resql); + + return 1; + } else { + $this->error = "Error " . $this->db->lasterror(); + dol_syslog(get_class($this) . "::fetch " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Load all objects in memory from database + * + * @param User $user User for action + * @param string $sortorder Sort order + * @param string $sortfield Sort field + * @param int $limit page number + * @param int $offset Offset for query + * @param int $arch archive or not (not used) + * @param array $filter Filter for query + * output + * @return int <0 if KO, >0 if OK + */ + public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = '', $offset = 0, $arch = '', $filter = '') + { + global $langs; + + $extrafields = new ExtraFields($this->db); + + // fetch optionals attributes and labels + $extralabels = $extrafields->fetch_name_optionals_label($this->element); + + $sql = "SELECT"; + $sql .= " t.rowid,"; + $sql .= " t.ref,"; + $sql .= " t.track_id,"; + $sql .= " t.fk_soc,"; + $sql .= " t.fk_project,"; + $sql .= " t.origin_email,"; + $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,"; + $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,"; + $sql .= " t.subject,"; + $sql .= " t.message,"; + $sql .= " t.fk_statut,"; + $sql .= " t.resolution,"; + $sql .= " t.progress,"; + $sql .= " t.timing,"; + $sql .= " t.type_code,"; + $sql .= " t.category_code,"; + $sql .= " t.severity_code,"; + $sql .= " t.datec,"; + $sql .= " t.date_read,"; + $sql .= " t.date_close,"; + $sql .= " t.tms"; + $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label"; + // Add fields for extrafields + foreach ($extrafields->attribute_list as $key => $val) { + $sql .= ($extrafields->attribute_type[$key] != 'separate' ? ",ef." . $key . ' as options_' . $key : ''); + } + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup as t"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_type as type ON type.code=t.type_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_category as category ON category.code=t.category_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_severity as severity ON severity.code=t.severity_code"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON s.rowid=t.fk_soc"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as uc ON uc.rowid=t.fk_user_create"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as ua ON ua.rowid=t.fk_user_assign"; + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "ticketsup_extrafields as ef on (t.rowid = ef.fk_object)"; + } + if (!$user->rights->societe->client->voir && !$user->socid) { + $sql .= ", " . MAIN_DB_PREFIX . "societe_commerciaux as sc"; + } + + $sql .= " WHERE t.entity IN (" . getEntity('ticketsup') . ")"; + + // Manage filter + if (!empty($filter)) { + foreach ($filter as $key => $value) { + if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year + $sql .= ' AND ' . $key . ' = \'' . $value . '\''; + } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) { + $sql .= " AND " . $key . " = '" . $this->db->escape($value) ."'"; + } elseif ($key == 't.fk_statut') { + if (is_array($value) && count($value) > 0) { + $sql .= 'AND ' . $key . ' IN (' . implode(',', $value) . ')'; + } else { + $sql .= ' AND ' . $key . ' = ' . $this->db->escape($value); + } + } else { + $sql .= ' AND ' . $key . ' LIKE \'%' . $value . '%\''; + } + } + } + if (!$user->rights->societe->client->voir && !$user->socid) { + $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = " . $user->id; + } elseif ($user->socid) { + $sql .= " AND t.fk_soc = " . $user->socid; + } + + $sql .= " ORDER BY " . $sortfield . ' ' . $sortorder; + if (!empty($limit)) { + $sql .= ' ' . $this->db->plimit($limit + 1, $offset); + } + + dol_syslog(get_class($this) . "::fetch_all sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + + if ($resql) { + $this->lines = array(); + + $num = $this->db->num_rows($resql); + $i = 0; + + if ($num) { + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + + $line = new TicketsLine(); + + $line->rowid = $obj->rowid; + $line->ref = $obj->ref; + $line->track_id = $obj->track_id; + $line->fk_soc = $obj->fk_soc; + $line->fk_project = $obj->fk_project; + $line->origin_email = $obj->origin_email; + + $line->fk_user_create = $obj->fk_user_create; + $line->user_create_lastname = $obj->user_create_lastname; + $line->user_create_firstname = $obj->user_create_firstname; + + $line->fk_user_assign = $obj->fk_user_assign; + $line->user_assign_lastname = $obj->user_assign_lastname; + $line->user_assign_firstname = $obj->user_assign_firstname; + + $line->subject = $obj->subject; + $line->message = $obj->message; + $line->fk_statut = $obj->fk_statut; + $line->resolution = $obj->resolution; + $line->progress = $obj->progress; + $line->timing = $obj->timing; + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label_type = ($langs->trans("TicketTypeShort" . $obj->type_code) != ("TicketTypeShort" . $obj->type_code) ? $langs->trans("TicketTypeShort" . $obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : '')); + $line->type_label = $label_type; + + $this->category_code = $obj->category_code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label_category = ($langs->trans("TicketCategoryShort" . $obj->category_code) != ("TicketCategoryShort" . $obj->category_code) ? $langs->trans("TicketCategoryShort" . $obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : '')); + $line->category_label = $label_category; + + $this->severity_code = $obj->severity_code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label_severity = ($langs->trans("TicketSeverityShort" . $obj->severity_code) != ("TicketSeverityShort" . $obj->severity_code) ? $langs->trans("TicketSeverityShort" . $obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : '')); + $line->severity_label = $label_severity; + + $line->datec = $this->db->jdate($obj->datec); + $line->date_read = $this->db->jdate($obj->date_read); + $line->date_close = $this->db->jdate($obj->date_close); + + // Extra fields + if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) { + foreach ($extrafields->attribute_label as $key => $val) { + $tmpkey = 'options_' . $key; + $line->{$tmpkey} = $obj->$tmpkey; + } + } + + $this->lines[$i] = $line; + $i++; + } + } + $this->db->free($resql); + return $num; + } else { + $this->error = "Error " . $this->db->lasterror(); + dol_syslog(get_class($this) . "::fetch_all " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update($user = 0, $notrigger = 0) + { + global $conf, $langs, $hookmanager; + $error = 0; + + // Clean parameters + if (isset($this->ref)) { + $this->ref = trim($this->ref); + } + + if (isset($this->track_id)) { + $this->track_id = trim($this->track_id); + } + + if (isset($this->fk_soc)) { + $this->fk_soc = trim($this->fk_soc); + } + + if (isset($this->fk_project)) { + $this->fk_project = trim($this->fk_project); + } + + if (isset($this->origin_email)) { + $this->origin_email = trim($this->origin_email); + } + + if (isset($this->fk_user_create)) { + $this->fk_user_create = trim($this->fk_user_create); + } + + if (isset($this->fk_user_assign)) { + $this->fk_user_assign = trim($this->fk_user_assign); + } + + if (isset($this->subject)) { + $this->subject = trim($this->subject); + } + + if (isset($this->message)) { + $this->message = trim($this->message); + } + + if (isset($this->fk_statut)) { + $this->fk_statut = trim($this->fk_statut); + } + + if (isset($this->resolution)) { + $this->resolution = trim($this->resolution); + } + + if (isset($this->progress)) { + $this->progress = trim($this->progress); + } + + if (isset($this->timing)) { + $this->timing = trim($this->timing); + } + + if (isset($this->type_code)) { + $this->timing = trim($this->type_code); + } + + if (isset($this->category_code)) { + $this->timing = trim($this->category_code); + } + + if (isset($this->severity_code)) { + $this->timing = trim($this->severity_code); + } + + // Check parameters + // Put here code to add a control on parameters values + // Update request + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup SET"; + $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "") . ","; + $sql .= " track_id=" . (isset($this->track_id) ? "'" . $this->db->escape($this->track_id) . "'" : "null") . ","; + $sql .= " fk_soc=" . (isset($this->fk_soc) ? "'" . $this->db->escape($this->fk_soc) . "'" : "null") . ","; + $sql .= " fk_project=" . (isset($this->fk_project) ? "'" . $this->db->escape($this->fk_project) . "'" : "null") . ","; + $sql .= " origin_email=" . (isset($this->origin_email) ? "'" . $this->db->escape($this->origin_email) . "'" : "null") . ","; + $sql .= " fk_user_create=" . (isset($this->fk_user_create) ? $this->fk_user_create : "null") . ","; + $sql .= " fk_user_assign=" . (isset($this->fk_user_assign) ? $this->fk_user_assign : "null") . ","; + $sql .= " subject=" . (isset($this->subject) ? "'" . $this->db->escape($this->subject) . "'" : "null") . ","; + $sql .= " message=" . (isset($this->message) ? "'" . $this->db->escape($this->message) . "'" : "null") . ","; + $sql .= " fk_statut=" . (isset($this->fk_statut) ? $this->fk_statut : "null") . ","; + $sql .= " resolution=" . (isset($this->resolution) ? $this->resolution : "null") . ","; + $sql .= " progress=" . (isset($this->progress) ? "'" . $this->db->escape($this->progress) . "'" : "null") . ","; + $sql .= " timing=" . (isset($this->timing) ? "'" . $this->db->escape($this->timing) . "'" : "null") . ","; + $sql .= " type_code=" . (isset($this->type_code) ? "'" . $this->db->escape($this->type_code) . "'" : "null") . ","; + $sql .= " category_code=" . (isset($this->category_code) ? "'" . $this->db->escape($this->category_code) . "'" : "null") . ","; + $sql .= " severity_code=" . (isset($this->severity_code) ? "'" . $this->db->escape($this->severity_code) . "'" : "null") . ","; + $sql .= " datec=" . (dol_strlen($this->datec) != 0 ? "'" . $this->db->idate($this->datec) . "'" : 'null') . ","; + $sql .= " date_read=" . (dol_strlen($this->date_read) != 0 ? "'" . $this->db->idate($this->date_read) . "'" : 'null') . ","; + $sql .= " date_close=" . (dol_strlen($this->date_close) != 0 ? "'" . $this->db->idate($this->date_close) . "'" : 'null') . ""; + + $sql .= " WHERE rowid=" . $this->id; + + $this->db->begin(); + + dol_syslog(get_class($this) . "::update sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + + if (!$error) { + // FIXME le hook fait double emploi avec le trigger !! + $hookmanager->initHooks(array('TicketSupDao')); + $parameters = array('ticketsupid' => $this->id); + $reshook = $hookmanager->executeHooks('insertExtraFields', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if (empty($reshook)) { + if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) { // For avoid conflicts if trigger used + $result = $this->insertExtraFields(); + if ($result < 0) { + $error++; + } + } + } elseif ($reshook < 0) { + $error++; + } + + if (!$notrigger) { + // Call trigger + $result=$this->call_trigger('TICKET_MODIFY', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return 1; + } + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete($user, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + $this->db->begin(); + + if (!$error) { + if (!$notrigger) { + // Call trigger + $result = $this->call_trigger('TICKET_DELETE', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + } + + if (!$error) { + // Delete linked contacts + $res = $this->delete_linked_contact(); + if ($res < 0) { + dol_syslog(get_class($this) . "::delete error", LOG_ERR); + $error++; + } + } + + if (!$error) { + // Delete linked object + $res = $this->deleteObjectLinked(); + if ($res < 0) $error++; + } + + if (!$error) { + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "ticketsup_logs"; + $sql .= " WHERE fk_track_id = '" . $this->db->escape($this->track_id) . "'"; + $resql = $this->db->query($sql); + } + if (!$error) { + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "ticketsup_msg"; + $sql .= " WHERE fk_track_id = '" . $this->db->escape($this->track_id) . "'"; + $resql = $this->db->query($sql); + } + + // Removed extrafields + if (!$error) { + $result = $this->deleteExtraFields(); + if ($result < 0) { + $error++; + dol_syslog(get_class($this) . "::delete error -3 " . $this->error, LOG_ERR); + } + } + + if (!$error) { + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "ticketsup"; + $sql .= " WHERE rowid=" . $this->id; + + dol_syslog(get_class($this) . "::delete sql=" . $sql); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return 1; + } + } + + /** + * Load an object from its id and create a new one in database + * + * @param int $fromid Id of object to clone + * @return int New id of clone + */ + public function createFromClone($fromid) + { + global $user, $langs; + + $error = 0; + + $object = new Ticketsup($this->db); + + $this->db->begin(); + + // Load source object + $object->fetch($fromid); + $object->id = 0; + $object->statut = 0; + + // Clear fields + // ... + // Create clone + $result = $object->create($user); + + // Other options + if ($result < 0) { + $this->error = $object->error; + $error++; + } + + if (!$error) { + } + + // End + if (!$error) { + $this->db->commit(); + return $object->id; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->id = 0; + + $this->ref = 'TI0501-001'; + $this->track_id = 'XXXXaaaa'; + $this->origin_email = 'email@email.com'; + $this->fk_project = '1'; + $this->fk_user_create = '1'; + $this->fk_user_assign = '1'; + $this->subject = 'Subject of ticket'; + $this->message = 'Message of ticket'; + $this->fk_statut = '0'; + $this->resolution = '1'; + $this->progress = '10'; + $this->timing = '30'; + $this->type_code = 'TYPECODE'; + $this->category_code = 'CATEGORYCODE'; + $this->severity_code = 'SEVERITYCODE'; + $this->datec = ''; + $this->date_read = ''; + $this->date_close = ''; + $this->tms = ''; + } + + + public function printSelectStatus($selected = "") + { + print Form::selectarray('search_fk_statut', $this->statuts_short, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = ''); + } + /** + * Charge dans cache la liste des types de tickets (paramétrable dans dictionnaire) + * + * @return int Nb lignes chargees, 0 si deja chargees, <0 si ko + */ + public function loadCacheTypesTickets() + { + global $langs; + + if (! empty($this->cache_types_tickets) && count($this->cache_types_tickets)) { + return 0; + } + // Cache deja charge + + $sql = "SELECT rowid, code, label, use_default, pos, description"; + $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticketsup_type"; + $sql .= " WHERE active > 0"; + $sql .= " ORDER BY pos"; + dol_syslog(get_class($this) . "::load_cache_type_tickets sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->trans("TicketTypeShort" . $obj->code) != ("TicketTypeShort" . $obj->code) ? $langs->trans("TicketTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code; + $this->cache_types_tickets[$obj->rowid]['label'] = $label; + $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default; + $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos; + $i++; + } + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Charge dans cache la liste des catégories de tickets (paramétrable dans dictionnaire) + * + * @return int Nb lignes chargees, 0 si deja chargees, <0 si ko + */ + public function loadCacheCategoriesTickets() + { + global $langs; + + if (! empty($this->cache_category_ticket) && count($this->cache_category_tickets)) { + return 0; + } + // Cache deja charge + + $sql = "SELECT rowid, code, label, use_default, pos, description"; + $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticketsup_category"; + $sql .= " WHERE active > 0"; + $sql .= " ORDER BY pos"; + dol_syslog(get_class($this) . "::load_cache_categories_tickets sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->trans("TicketCategoryShort" . $obj->code) != ("TicketCategoryShort" . $obj->code) ? $langs->trans("TicketCategoryShort" . $obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_category_tickets[$obj->rowid]['label'] = $label; + $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default; + $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos; + $i++; + } + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Charge dans cache la liste des sévérité de tickets (paramétrable dans dictionnaire) + * + * @return int Nb lignes chargees, 0 si deja chargees, <0 si ko + */ + public function loadCacheSeveritiesTickets() + { + global $langs; + + if (! empty($this->cache_severity_tickets) && count($this->cache_severity_tickets)) { + return 0; + } + // Cache deja charge + + $sql = "SELECT rowid, code, label, use_default, pos, description"; + $sql .= " FROM " . MAIN_DB_PREFIX . "c_ticketsup_severity"; + $sql .= " WHERE active > 0"; + $sql .= " ORDER BY pos"; + dol_syslog(get_class($this) . "::loadCacheSeveritiesTickets sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + + $this->cache_severity_tickets[$obj->rowid]['code'] = $obj->code; + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->trans("TicketSeverityShort" . $obj->code) != ("TicketSeverityShort" . $obj->code) ? $langs->trans("TicketSeverityShort" . $obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_severity_tickets[$obj->rowid]['label'] = $label; + $this->cache_severity_tickets[$obj->rowid]['use_default'] = $obj->use_default; + $this->cache_severity_tickets[$obj->rowid]['pos'] = $obj->pos; + $i++; + } + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + + /** + * Return status label of object + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto + * @return string Label + */ + public function getLibStatut($mode = 0) + { + return $this->libStatut($this->fk_statut, $mode); + } + + + /** + * Return status label of object + * + * @param string $statut id statut + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto + * @return string Label + */ + function LibStatut($statut, $mode = 0) + { + global $langs; + + if ($mode == 0) { + return $langs->trans($this->statuts[$statut]); + } + if ($mode == 1) { + return $langs->trans($this->statuts_short[$statut]); + } + if ($mode == 2) { + if ($statut == 0) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 1) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 3) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 4) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 5) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 6) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 8) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 9) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + } + if ($mode == 3) { + if ($statut == 0) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticketsup'); + } + + if ($statut == 1) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticketsup'); + } + + if ($statut == 3) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticketsup'); + } + + if ($statut == 4) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticketsup'); + } + + if ($statut == 5) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticketsup'); + } + + if ($statut == 6) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticketsup'); + } + + if ($statut == 8) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticketsup'); + } + + if ($statut == 9) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticketsup'); + } + } + if ($mode == 4) { + if ($statut == 0) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 1) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 3) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 4) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 5) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 6) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 8) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + + if ($statut == 9) { + return img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticketsup') . ' ' . $langs->trans($this->statuts_short[$statut]); + } + } + if ($mode == 5) { + if ($statut == 0) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut0.png@ticketsup'); + } + + if ($statut == 1) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut1.png@ticketsup'); + } + + if ($statut == 3) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut3.png@ticketsup'); + } + + if ($statut == 4) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut4.png@ticketsup'); + } + + if ($statut == 5) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut5.png@ticketsup'); + } + + if ($statut == 6) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut6.png@ticketsup'); + } + + if ($statut == 8) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut8.png@ticketsup'); + } + + if ($statut == 9) { + return $langs->trans($this->statuts_short[$statut]) . ' ' . img_picto($langs->trans($this->statuts_short[$statut]), 'statut9.png@ticketsup'); + } + } + } + + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + function getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1) + { + global $db, $conf, $langs; + global $dolibarr_main_authentication, $dolibarr_main_demo; + global $menumanager; + + if (! empty($conf->dol_no_mouse_hover)) $notooltip=1; // Force disable tooltips + + $result = ''; + $companylink = ''; + + $label = '' . $langs->trans("ShowTicket") . ''; + $label.= '
    '; + $label.= '' . $langs->trans('Ref') . ': ' . $this->ref.'
    '; + $label.= '' . $langs->trans('TicketTrackId') . ': ' . $this->track_id.'
    '; + $label.= '' . $langs->trans('Subject') . ': ' . $this->subject; + + $url = dol_buildpath('/ticketsup/card.php',1).'?id='.$this->id; + + if ($option != 'nolink') + { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1; + if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1'; + } + + $linkclose=''; + if (empty($notooltip)) + { + if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) + { + $label=$langs->trans("ShowTicket"); + $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose.=' title="'.dol_escape_htmltag($label, 1).'"'; + $linkclose.=' class="classfortooltip'.($morecss?' '.$morecss:'').'"'; + } + else $linkclose = ($morecss?' class="'.$morecss.'"':''); + + $linkstart = ''; + $linkend=''; + + $result .= $linkstart; + if ($withpicto) $result.=img_object(($notooltip?'':$label), ($this->picto?$this->picto:'generic'), ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1); + if ($withpicto != 2) $result.= $this->ref; + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + return $result; + } + + + /** + * Mark a message as read + * + * @param User $user Object user + * @param int $notrigger No trigger + * @return int <0 if KO, >0 if OK + */ + public function markAsRead($user, $notrigger = 0) + { + global $conf, $langs; + + if ($this->statut != 9) { // no closed + $this->db->begin(); + + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup"; + $sql .= " SET fk_statut = 1, date_read='" . $this->db->idate(dol_now()) . "'"; + $sql .= " WHERE rowid = " . $this->id; + + dol_syslog(get_class($this) . "::markAsRead sql=" . $sql); + $resql = $this->db->query($sql); + if ($resql) { + if (!$error && !$notrigger) { + // Call trigger + $result=$this->call_trigger('TICKET_MARK_READ', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + + + if (!$error) { + $this->db->commit(); + return 1; + } else { + $this->db->rollback(); + $this->error = join(',', $this->errors); + dol_syslog(get_class($this) . "::markAsRead " . $this->error, LOG_ERR); + return -1; + } + } else { + $this->db->rollback(); + $this->error = $this->db->lasterror(); + dol_syslog(get_class($this) . "::markAsRead " . $this->error, LOG_ERR); + return -1; + } + } + } + + /** + * Mark a message as read + * + * @param User $user Object user + * @param int $id_assign_user ID of user assigned + * @param int $notrigger Disable trigger + * @return int <0 if KO, 0=Nothing done, >0 if OK + */ + public function assignUser($user, $id_assign_user, $notrigger = 0) + { + global $conf, $langs; + + $this->db->begin(); + + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup"; + if ($id_assign_user > 0) + { + $sql .= " SET fk_user_assign=".$id_assign_user.", fk_statut=4"; + } + else + { + $sql .= " SET fk_user_assign=null, fk_statut=1"; + } + $sql .= " WHERE rowid = " . $this->id; + + dol_syslog(get_class($this) . "::assignUser sql=" . $sql); + $resql = $this->db->query($sql); + if ($resql) { + $this->fk_user_assign = $id_assign_user; // May be used by trigger + + if (! $notrigger) { + // Call trigger + $result = $this->call_trigger('TICKET_ASSIGNED', $user); + if ($result < 0) { + $error ++; + } + // End call triggers + } + + if (! $error) { + $this->db->commit(); + return 1; + } else { + $this->db->rollback(); + $this->error = join(',', $this->errors); + dol_syslog(get_class($this) . "::assignUser " . $this->error, LOG_ERR); + return - 1; + } + } else { + $this->db->rollback(); + $this->error = $this->db->lasterror(); + dol_syslog(get_class($this) . "::assignUser " . $this->error, LOG_ERR); + return - 1; + } + + return 0; + } + + /** + * Create log for the ticket + * 1- create entry into database for message storage + * 2- if trigger, send an email to ticket contacts + * + * @param User $user User that create + * @param string $message Log message + * @param int $noemail 0=send email after, 1=disable emails + * @return int <0 if KO, >0 if OK + */ + public function createTicketLog(User $user, $message, $noemail = 0) + { + global $conf, $langs; + + $this->db->begin(); + + // Clean parameters + $this->message = trim($this->message); + + // Check parameters + if (!$message) { + $this->error = 'ErrorBadValueForParameter'; + return -1; + } + + // Insert request + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticketsup_logs("; + $sql .= "entity,"; + $sql .= "datec,"; + $sql .= "fk_track_id,"; + $sql .= "fk_user_create,"; + $sql .= "message"; + $sql .= ") VALUES ("; + $sql .= " " . $conf->entity . ","; + $sql .= " '" . $this->db->idate(dol_now()) . "',"; + $sql .= " '" . $this->db->escape($this->track_id) . "',"; + $sql .= " " . ($user->id > 0 ? $user->id : 'NULL') . ","; + $sql .= " '" . $this->db->escape($message) . "'"; + $sql .= ")"; + + dol_syslog(get_class($this) . "::create_ticket_log sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + if ($conf->global->TICKETS_ACTIVATE_LOG_BY_EMAIL && !$noemail) { + $this->sendLogByEmail($user, $message); + } + + if (!$error) { + $this->db->commit(); + return 1; + } + } else { + $this->db->rollback(); + $this->error = "Error " . $this->db->lasterror(); + dol_syslog(get_class($this) . "::create_ticket_log " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Send notification of changes by email + * + * @param User $user User that create + * @param string $message Log message + * @return int <0 if KO, >0 if OK (number of emails sent) + */ + private function sendLogByEmail($user, $message) + { + global $conf, $langs; + + $nb_sent = 0; + + $langs->load('ticketsup'); + + // Retrieve email of all contacts (internal and external) + $contacts = $this->listeContact(-1, 'internal'); + $contacts = array_merge($contacts, $this->listeContact(-1, 'external')); + + /* If origin_email and no socid, we add email to the list * */ + if (!empty($this->origin_email) && empty($this->fk_soc)) { + $array_ext = array(array('firstname' => '', 'lastname' => '', 'email' => $this->origin_email, 'libelle' => $langs->transnoentities('TicketEmailOriginIssuer'), 'socid' => "-1")); + $contacts = array_merge($contacts, $array_ext); + } + + if (!empty($this->fk_soc)) { + $this->fetch_thirdparty($this->fk_soc); + $array_company = array(array('firstname' => '', 'lastname' => $this->client->name, 'email' => $this->client->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $this->client->id)); + $contacts = array_merge($contacts, $array_company); + } + + // foreach contact send email with notification message + if (count($contacts) > 0) { + foreach ($contacts as $key => $info_sendto) { + $message = ''; + $subject = '[' . $conf->global->MAIN_INFO_SOCIETE_NOM . '] ' . $langs->transnoentities('TicketNotificationEmailSubject', $this->track_id); + $message .= $langs->transnoentities('TicketNotificationEmailBody', $this->track_id) . "\n\n"; + $message .= $langs->transnoentities('Title') . ' : ' . $this->subject . "\n"; + + $recipient_name = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1'); + $recipient = (!empty($recipient_name) ? $recipient_name : $info_sendto['email']) . ' (' . strtolower($info_sendto['libelle']) . ')'; + $message .= $langs->transnoentities('TicketNotificationRecipient') . ' : ' . $recipient . "\n"; + $message .= "\n"; + $message .= '* ' . $langs->transnoentities('TicketNotificationLogMessage') . ' *' . "\n"; + $message .= dol_html_entity_decode($log_message, ENT_QUOTES) . "\n"; + + if ($info_sendto['source'] == 'internal') { + $url_internal_ticket = dol_buildpath('/ticketsup/card.php', 2) . '?track_id=' . $this->track_id; + $message .= "\n" . $langs->transnoentities('TicketNotificationEmailBodyInfosTrackUrlinternal') . ' : ' . '' . $this->track_id . '' . "\n"; + } else { + $url_public_ticket = ($conf->global->TICKETS_URL_PUBLIC_INTERFACE ? $conf->global->TICKETS_URL_PUBLIC_INTERFACE . '/' : dol_buildpath('/ticketsup/public/view.php', 2)) . '?track_id=' . $this->track_id; + $message .= "\n" . $langs->transnoentities('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : ' . '' . $this->track_id . '' . "\n"; + } + + $message .= "\n"; + $message .= $langs->transnoentities('TicketEmailPleaseDoNotReplyToThisEmail') . "\n"; + + $from = $conf->global->MAIN_INFO_SOCIETE_NOM . '<' . $conf->global->TICKETS_NOTIFICATION_EMAIL_FROM . '>'; + $replyto = $from; + + // Init to avoid errors + $filepath = array(); + $filename = array(); + $mimetype = array(); + + $message = dol_nl2br($message); + + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $info_sendto['email'], $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, 0); + if ($mailfile->error) { + setEventMessage($mailfile->error, 'errors'); + } else { + $result = $mailfile->sendfile(); + if ($result > 0) { + $nb_sent++; + } + } + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + + setEventMessage($langs->trans('TicketNotificationNumberEmailSent', $nb_sent)); + } + + return $nb_sent; + } + + /** + * Charge la liste des actions sur le ticket + * + * @return int Nb lignes chargees, 0 si deja chargees, <0 si ko + */ + public function loadCacheLogsTicket() + { + global $langs; + + if (count($this->cache_logs_ticket)) { + return 0; + } + // Cache deja charge + + $sql = "SELECT rowid, fk_user_create, datec, message"; + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup_logs"; + $sql .= " WHERE fk_track_id ='" . $this->db->escape($this->track_id) . "'"; + $sql .= " ORDER BY datec DESC"; + + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + $this->cache_logs_ticket[$i]['id'] = $obj->rowid; + $this->cache_logs_ticket[$i]['fk_user_create'] = $obj->fk_user_create; + $this->cache_logs_ticket[$i]['datec'] = $this->db->jdate($obj->datec); + $this->cache_logs_ticket[$i]['message'] = $obj->message; + $i++; + } + return $num; + } else { + $this->error = "Error " . $this->db->lasterror(); + dol_syslog(get_class($this) . "::loadCacheLogsTicket " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Add message into database + * + * @param User $user User that creates + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function createTicketMessage($user, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + // Clean parameters + if (isset($this->fk_track_id)) { + $this->fk_track_id = trim($this->fk_track_id); + } + + if (isset($this->message)) { + $this->message = trim($this->message); + } + + // Insert request + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticketsup_msg("; + + $sql .= "fk_track_id,"; + $sql .= "fk_user_action,"; + $sql .= "datec,"; + $sql .= "message,"; + $sql .= "private"; + $sql .= ") VALUES ("; + $sql .= " " . (!isset($this->fk_track_id) ? "'" . $this->db->escape($this->track_id) . "'" : "'" . $this->db->escape($this->fk_track_id) . "'") . ","; + $sql .= " " . ($this->fk_user_action > 0 ? $this->fk_user_action : $user->id) . ","; + $sql .= " '" . $this->db->idate(dol_now()) . "',"; + $sql .= " " . (!isset($this->message) ? 'NULL' : "'" . $this->db->escape($this->message) . "'") . ","; + $sql .= " " . (empty($this->private) ? '0' : "'" . $this->db->escape($this->private) . "'") . ""; + $sql .= ")"; + + $this->db->begin(); + + dol_syslog(get_class($this) . "::create_ticket_message sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + + if (!$error) { + if (!$notrigger) { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_CREATE',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::create_ticket_message " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return 1; + } + } + + /** + * Charge la liste des messages sur le ticket + * + * @return int Nb lignes chargees, 0 si deja chargees, <0 si ko + */ + public function loadCacheMsgsTicket() + { + global $langs; + + if (count($this->cache_msgs_ticket)) { + return 0; + } + // Cache deja charge + + $sql = "SELECT rowid, fk_user_action, datec, message, private"; + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup_msg"; + $sql .= " WHERE fk_track_id ='" . $this->db->escape($this->track_id) . "'"; + $sql .= " ORDER BY datec DESC"; + dol_syslog(get_class($this) . "::load_cache_actions_ticket sql=" . $sql, LOG_DEBUG); + + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + $this->cache_msgs_ticket[$i]['id'] = $obj->rowid; + $this->cache_msgs_ticket[$i]['fk_user_action'] = $obj->fk_user_action; + $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec); + $this->cache_msgs_ticket[$i]['message'] = $obj->message; + $this->cache_msgs_ticket[$i]['private'] = $obj->private; + $i++; + } + return $num; + } else { + $this->error = "Error " . $this->db->lasterror(); + dol_syslog(get_class($this) . "::load_cache_actions_ticket " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Close a ticket + * + * @return int <0 if KO, >0 if OK + */ + public function close() + { + global $conf, $user, $langs; + + if ($this->fk_statut != 9) { // not closed + $this->db->begin(); + + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup"; + $sql .= " SET fk_statut=8, progress=100, date_close='" . $this->db->idate(dol_now()) . "'"; + $sql .= " WHERE rowid = " . $this->id; + + dol_syslog(get_class($this) . "::close sql=" . $sql); + $resql = $this->db->query($sql); + if ($resql) { + $error = 0; + + // Valid and close fichinter linked + $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter'); + if ($this->linkedObjectsIds) + { + foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) { + $fichinter = new Fichinter($this->db); + $fichinter->fetch($fichinter_id); + if($fichinter->statut == 0) { + $result = $fichinter->setValid($user); + if (!$result) { + $this->errors[] = $fichinter->error; + $error++; + } + } + if ($fichinter->statut < 3) { + $result = $fichinter->setStatut(3); + if (!$result) { + $this->errors[] = $fichinter->error; + $error++; + } + } + } + } + + // Call trigger + $result=$this->call_trigger('TICKET_CLOSE', $user); + if ($result < 0) { + $error++; + } + // End call triggers + + if (!$error) { + $this->db->commit(); + return 1; + } else { + $this->db->rollback(); + $this->error = join(',', $this->errors); + dol_syslog(get_class($this) . "::close " . $this->error, LOG_ERR); + return -1; + } + } else { + $this->db->rollback(); + $this->error = $this->db->lasterror(); + dol_syslog(get_class($this) . "::close " . $this->error, LOG_ERR); + return -1; + } + } + } + + /** + * Search and fetch thirparties by email + * + * @param string $email Email + * @param int $type Type of thirdparties (0=any, 1=customer, 2=prospect, 3=supplier) + * @param array $filters Array of couple field name/value to filter the companies with the same name + * @param string $clause Clause for filters + * @return array Array of thirdparties object + */ + public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND') + { + $thirdparties = array(); + + // Generation requete recherche + $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "societe"; + $sql .= " WHERE entity IN (" . getEntity('ticketsup', 1) . ")"; + if (!empty($type)) { + if ($type == 1 || $type == 2) { + $sql .= " AND client = " . $type; + } elseif ($type == 3) { + $sql .= " AND fournisseur = 1"; + } + } + if (!empty($email)) { + if (!$exact) { + if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) { + $email = str_replace('*', '%', $email); + } else { + $email = '%' . $email . '%'; + } + } + $sql .= " AND "; + if (is_array($filters) && !empty($filters)) { + $sql .= "("; + } + + if (!$case) { + $sql .= "email LIKE '" . $this->db->escape($email) . "'"; + } else { + $sql .= "email LIKE BINARY '" . $this->db->escape($email) . "'"; + } + } + if (is_array($filters) && !empty($filters)) { + foreach ($filters as $field => $value) { + $sql .= " " . $clause . " " . $field . " LIKE BINARY '" . $this->db->escape($value) . "'"; + } + if (!empty($email)) { + $sql .= ")"; + } + } + + $res = $this->db->query($sql); + if ($res) { + while ($rec = $this->db->fetch_array($res)) { + $soc = new Societe($this->db); + $soc->fetch($rec['rowid']); + $thirdparties[] = $soc; + } + + return $thirdparties; + } else { + $this->error = $this->db->error() . ' sql=' . $sql; + dol_syslog(get_class($this) . "::searchSocidByEmail " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Search and fetch contacts by email + * + * @param string $email Email + * @param array $socid Limit to a thirdparty + * @param string $case Respect case + * @return array Array of contacts object + */ + public function searchContactByEmail($email, $socid = '', $case = '') + { + $contacts = array(); + + // Generation requete recherche + $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "socpeople"; + $sql .= " WHERE entity IN (" . getEntity('ticketsup', 1) . ")"; + if (!empty($socid)) { + $sql .= " AND fk_soc='" . $this->db->escape($socid) . "'"; + } + + if (!empty($email)) { + $sql .= " AND "; + + if (!$case) { + $sql .= "email LIKE '" . $this->db->escape($email) . "'"; + } else { + $sql .= "email LIKE BINARY '" . $this->db->escape($email) . "'"; + } + } + + $res = $this->db->query($sql); + if ($res) { + while ($rec = $this->db->fetch_array($res)) { + include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; + $contactstatic = new Contact($this->db); + $contactstatic->fetch($rec['rowid']); + $contacts[] = $contactstatic; + } + + return $contacts; + } else { + $this->error = $this->db->error() . ' sql=' . $sql; + dol_syslog(get_class($this) . "::searchContactByEmail " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Define parent commany of current ticket + * + * @param int $id Id of thirdparty to set or '' to remove + * @return int <0 if KO, >0 if OK + */ + public function setCustomer($id) + { + if ($this->id) { + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup"; + $sql .= " SET fk_soc = " . ($id > 0 ? $id : "null"); + $sql .= " WHERE rowid = " . $this->id; + dol_syslog(get_class($this) . '::setCustomer sql=' . $sql); + $resql = $this->db->query($sql); + if ($resql) { + return 1; + } else { + return -1; + } + } else { + return -1; + } + } + + /** + * Define progression of current ticket + * + * @param int $percent Progression percent + * @return int <0 if KO, >0 if OK + */ + public function setProgression($percent) + { + if ($this->id) { + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup"; + $sql .= " SET progress = " . ($percent > 0 ? $percent : "null"); + $sql .= " WHERE rowid = " . $this->id; + dol_syslog(get_class($this) . '::set_progression sql=' . $sql); + $resql = $this->db->query($sql); + if ($resql) { + return 1; + } else { + return -1; + } + } else { + return -1; + } + } + + /** + * Link element with a project + * Override core function because of key name 'fk_project' used for this module + * + * @param int $projectid Project id to link element to + * @return int <0 if KO, >0 if OK + */ + public function setProject($projectid) + { + if (!$this->table_element) { + dol_syslog(get_class($this) . "::setProject was called on objet with property table_element not defined", LOG_ERR); + return -1; + } + + $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element; + if ($projectid) { + $sql .= ' SET fk_project = ' . $projectid; + } else { + $sql .= ' SET fk_project = NULL'; + } + + $sql .= ' WHERE rowid = ' . $this->id; + + dol_syslog(get_class($this) . "::setProject sql=" . $sql); + if ($this->db->query($sql)) { + $this->fk_project = $projectid; + return 1; + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Link element with a contract + * + * @param int $contractid Contract id to link element to + * @return int <0 if KO, >0 if OK + */ + public function setContract($contractid) + { + if (!$this->table_element) { + dol_syslog(get_class($this) . "::setContract was called on objet with property table_element not defined", LOG_ERR); + return -1; + } + + $result = $this->add_object_linked('contrat', $contractid); + if ($result) { + $this->fk_contract = $contractid; + return 1; + } else { + dol_print_error($this->db); + return -1; + } + } + + /* gestion des contacts d'un ticket */ + + /** + * Return id des contacts interne de suivi + * + * @return array Liste des id contacts suivi ticket + */ + public function getIdTicketInternalContact() + { + return $this->getIdContact('internal', 'SUPPORTTEC'); + } + + /** + * Retrieve informations about internal contacts + * + * @return array Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status + */ + public function getInfosTicketInternalContact() + { + return $this->listeContact(-1, 'internal'); + } + + /** + * Return id des contacts clients pour le suivi ticket + * + * @return array Liste des id contacts suivi ticket + */ + public function getIdTicketCustomerContact() + { + return $this->getIdContact('external', 'SUPPORTCLI'); + } + + /** + * Retrieve informations about external contacts + * + * @return array Array with datas : firstname, lastname, socid (-1 for internal users), email, code, libelle, status + */ + public function getInfosTicketExternalContact() + { + return $this->listeContact(-1, 'external'); + } + + /** + * Return id des contacts clients des intervenants + * + * @return array Liste des id contacts intervenants + */ + public function getIdTicketInternalInvolvedContact() + { + return $this->getIdContact('internal', 'CONTRIBUTOR'); + } + + /** + * Return id des contacts clients des intervenants + * + * @return array Liste des id contacts intervenants + */ + public function getIdTicketCustomerInvolvedContact() + { + return $this->getIdContact('external', 'CONTRIBUTOR'); + } + + /** + * Return id of all contacts for ticket + * + * @return array Array of contacts for tickets + */ + public function getTicketAllContacts() + { + $array_contact = array(); + + $array_contact = $this->getIdTicketInternalContact($exclude_self); + + $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact($exclude_self)); + + $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact($exclude_self)); + $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact($exclude_self)); + + return $array_contact; + } + + /** + * Return id of all contacts for ticket + * + * @return array Array of contacts + */ + public function getTicketAllCustomerContacts() + { + $array_contact = array(); + + $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact($exclude_self)); + $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact($exclude_self)); + + return $array_contact; + } + + /** + * Send message + * + * @param string $subject Subject + * @param string $texte Message to send + * @return int <0 if KO, or number of changes if OK + */ + public function messageSend($subject, $texte) + { + global $conf, $langs, $mysoc, $dolibarr_main_url_root; + + $langs->load("other"); + + dol_syslog(get_class($this) . "::message_send action=$action, socid=$socid, texte=$texte, objet_type=$objet_type, objet_id=$objet_id, file=$file"); + + $internal_contacts = $this->getIdContact('internal', 'SUPPORTTEC'); + $external_contacts = $this->getIdContact('external', 'SUPPORTTEC'); + + if ($result) { + $num = $this->db->num_rows($result); + $i = 0; + while ($i < $num) { // For each notification couple defined (third party/actioncode) + $obj = $this->db->fetch_object($result); + + $sendto = $obj->firstname . " " . $obj->lastname . " <" . $obj->email . ">"; + $actiondefid = $obj->adid; + + if (dol_strlen($sendto)) + { + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + $application = ($conf->global->MAIN_APPLICATION_TITLE ? $conf->global->MAIN_APPLICATION_TITLE : 'Dolibarr ERP/CRM'); + + $subject = '[' . $application . '] ' . $langs->transnoentitiesnoconv("DolibarrNotification"); + + $message = $langs->transnoentities("YouReceiveMailBecauseOfNotification", $application, $mysoc->name) . "\n"; + $message .= $langs->transnoentities("YouReceiveMailBecauseOfNotification2", $application, $mysoc->name) . "\n"; + $message .= "\n"; + $message .= $texte; + // Add link + $link = ''; + switch ($objet_type) { + case 'ficheinter': + $link = '/fichinter/card.php?id=' . $objet_id; + break; + case 'propal': + $link = '/comm/propal.php?id=' . $objet_id; + break; + case 'facture': + $link = '/compta/facture/card.php?facid=' . $objet_id; + break; + case 'order': + $link = '/commande/card.php?facid=' . $objet_id; + break; + case 'order_supplier': + $link = '/fourn/commande/card.php?facid=' . $objet_id; + break; + } + // Define $urlwithroot + $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root)); + $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file + //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current + if ($link) { + $message .= "\n" . $urlwithroot . $link; + } + + $filename = basename($file); + + $mimefile = dol_mimetype($file); + + $msgishtml = 0; + + $replyto = $conf->notification->email_from; + + $message = dol_nl2br($message); + + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + $mailfile = new CMailFile( + $subject, + $sendto, + $replyto, + $message, + array($file), + array($mimefile), + array($filename[count($filename) - 1]), + '', + '', + 0, + $msgishtml + ); + + if ($mailfile->sendfile()) { + $now = dol_now(); + $sendto = htmlentities($sendto); + + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "notify (daten, fk_action, fk_contact, objet_type, objet_id, email)"; + $sql .= " VALUES ('" . $this->db->idate($now) . "', " . $actiondefid . ", " . $obj->cid . ", '" . $this->db->escape($objet_type) . "', " . $objet_id . ", '" . $this->db->escape($obj->email) . "')"; + dol_syslog("Notify::send sql=" . $sql); + if (!$this->db->query($sql)) { + dol_print_error($this->db); + } + } else { + $this->error = $mailfile->error; + //dol_syslog("Notify::send ".$this->error, LOG_ERR); + } + if (!empty($conf->global->TICKETS_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + $i++; + } + return $i; + } else { + $this->error = $this->db->error(); + return -1; + } + } + + /** + * Get array of all contacts for a ticket + * Override method of file commonobject.class.php to add phone number + * + * @param int $statut Status of lines to get (-1=all) + * @param string $source Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user) + * @param int $list 0:Return array contains all properties, 1:Return array contains just id + * @param string $code Filter on this code of contact type ('SHIPPING', 'BILLING', ...) + * @return array Array of contacts + */ + function listeContact($statut = -1, $source = 'external', $list = 0, $code = '') + { + global $langs; + + $tab = array(); + + $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user + if ($source == 'internal') { + $sql .= ", '-1' as socid, t.statut as statuscontact"; + } + + if ($source == 'external' || $source == 'thirdparty') { + $sql .= ", t.fk_soc as socid, t.statut as statuscontact"; + } + + $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email"; + if ($source == 'internal') { + $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile"; + } + + if ($source == 'external') { + $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso"; + } + + $sql .= ", tc.source, tc.element, tc.code, tc.libelle"; + $sql .= " FROM " . MAIN_DB_PREFIX . "c_type_contact tc"; + $sql .= ", " . MAIN_DB_PREFIX . "element_contact ec"; + if ($source == 'internal') { + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user t on ec.fk_socpeople = t.rowid"; + } + + if ($source == 'external' || $source == 'thirdparty') { + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople t on ec.fk_socpeople = t.rowid"; + } + + $sql .= " WHERE ec.element_id =" . $this->id; + $sql .= " AND ec.fk_c_type_contact=tc.rowid"; + $sql .= " AND tc.element='" . $this->db->escape($this->element) . "'"; + if ($source == 'internal') { + $sql .= " AND tc.source = 'internal'"; + } + + if ($source == 'external' || $source == 'thirdparty') { + $sql .= " AND tc.source = 'external'"; + } + + $sql .= " AND tc.active=1"; + if ($statut >= 0) { + $sql .= " AND ec.statut = '" . $statut . "'"; + } + + $sql .= " ORDER BY t.lastname ASC"; + + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + + if (!$list) { + $transkey = "TypeContact_" . $obj->element . "_" . $obj->source . "_" . $obj->code; + $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle); + $tab[$i] = array( + 'source' => $obj->source, + 'socid' => $obj->socid, + 'id' => $obj->id, + 'nom' => $obj->lastname, // For backward compatibility + 'civility' => $obj->civility, + 'lastname' => $obj->lastname, + 'firstname' => $obj->firstname, + 'email' => $obj->email, + 'rowid' => $obj->rowid, + 'code' => $obj->code, + 'libelle' => $libelle_type, + 'status' => $obj->statuslink, + 'statuscontact'=>$obj->statuscontact, + 'fk_c_type_contact' => $obj->fk_c_type_contact, + 'phone' => $obj->phone, + 'phone_mobile' => $obj->phone_mobile); + } else { + $tab[$i] = $obj->id; + } + + $i++; + } + + return $tab; + } else { + $this->error = $this->db->error(); + dol_print_error($this->db); + return -1; + } + } + + /** + * Get a default reference. + * + * @param Societe $thirdparty Thirdparty + * @return string Reference + */ + public function getDefaultRef($thirdparty = '') + { + global $conf; + + $defaultref = ''; + $modele = empty($conf->global->TICKETSUP_ADDON) ? 'mod_ticketsup_simple' : $conf->global->TICKETSUP_ADDON; + + // Search template files + $file = ''; + $classname = ''; + $filefound = 0; + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + foreach ($dirmodels as $reldir) { + $file = dol_buildpath($reldir . "core/modules/ticketsup/" . $modele . '.php', 0); + if (file_exists($file)) { + $filefound = 1; + $classname = $modele; + break; + } + } + + if ($filefound) { + $result = dol_include_once($reldir . "core/modules/ticketsup/" . $modele . '.php'); + $modTicketsup = new $classname; + + $defaultref = $modTicketsup->getNextValue($thirdparty, $this); + } + + if (is_numeric($defaultref) && $defaultref <= 0) { + $defaultref = ''; + } + + return $defaultref; + } + + + /** + * Return if at least one photo is available + * + * @param string $sdir Directory to scan + * @return boolean True if at least one photo is available, False if not + */ + function is_photo_available($sdir) + { + include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + + global $conf; + + $dir = $sdir . '/'; + $nbphoto = 0; + + $dir_osencoded = dol_osencode($dir); + if (file_exists($dir_osencoded)) { + $handle = opendir($dir_osencoded); + if (is_resource($handle)) { + while (($file = readdir($handle)) != false) { + if (!utf8_check($file)) { + $file = utf8_encode($file); + } + // To be sure data is stored in UTF8 in memory + if (dol_is_file($dir . $file)) { + return true; + } + } + } + } + return false; + } + +} + + +/** + * Ticket line Class + */ +class TicketsLine +{ + public $id; + + /** + * @var string $ref Ticket reference + */ + public $ref; + + /** + * Hash to identify ticket + */ + public $track_id; + + /** + * Thirdparty ID + */ + public $fk_soc; + + /** + * Project ID + */ + public $fk_project; + + /** + * Person email who have create ticket + */ + public $origin_email; + + /** + * User id who have create ticket + */ + public $fk_user_create; + + /** + * User id who have ticket assigned + */ + public $fk_user_assign; + + /** + * Ticket subject + */ + public $subject; + + /** + * Ticket message + */ + public $message; + + /** + * Ticket statut + */ + public $fk_statut; + + /** + * State resolution + */ + public $resolution; + + /** + * Progress in percent + */ + public $progress; + + /** + * Duration for ticket + */ + public $timing; + + /** + * Type code + */ + public $type_code; + + /** + * Category code + */ + public $category_code; + + /** + * Severity code + */ + public $severity_code; + + /** + * Type label + */ + public $type_label; + + /** + * Category label + */ + public $category_label; + + /** + * Severity label + */ + public $severity_label; + + /** + * Creation date + */ + public $datec = ''; + + /** + * Read date + */ + public $date_read = ''; + + /** + * Close ticket date + */ + public $date_close = ''; + +} diff --git a/htdocs/ticketsup/class/ticketsuplogs.class.php b/htdocs/ticketsup/class/ticketsuplogs.class.php new file mode 100644 index 00000000000..3a73a35fd3e --- /dev/null +++ b/htdocs/ticketsup/class/ticketsuplogs.class.php @@ -0,0 +1,332 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file ticketsup/class/ticketsuplogs.class.php + * \ingroup ticketsup + * \brief This file CRUD class file (Create/Read/Update/Delete) for ticket logs + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT . "/core/class/commonobject.class.php"; +//require_once(DOL_DOCUMENT_ROOT."/societe/class/societe.class.php"); +//require_once(DOL_DOCUMENT_ROOT."/product/class/product.class.php"); + + +/** + * Class of log for ticketsup + */ +class Ticketsuplogs// extends CommonObject +{ + public $db; //!< To store db handler + public $error; //!< To return error code (or message) + public $errors = array(); //!< To return several error codes (or messages) + public $element = 'ticketsuplogs'; //!< Id that identify managed objects + public $table_element = 'ticketsuplogs'; //!< Name of table without prefix where object is stored + + public $id; + + public $fk_track_id; + public $fk_user_create; + public $datec = ''; + public $message; + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + return 1; + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + public function create($user, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + // Clean parameters + + if (isset($this->fk_track_id)) { + $this->fk_track_id = trim($this->fk_track_id); + } + + if (isset($this->fk_user_create)) { + $this->fk_user_create = trim($this->fk_user_create); + } + + if (isset($this->message)) { + $this->message = trim($this->message); + } + + // Check parameters + // Put here code to add control on parameters values + + // Insert request + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "ticketsup_logs("; + + $sql .= "fk_track_id,"; + $sql .= "fk_user_create,"; + $sql .= "datec,"; + $sql .= "message"; + + $sql .= ") VALUES ("; + + $sql .= " " . (!isset($this->fk_track_id) ? 'NULL' : "'" . $this->db->escape($this->fk_track_id) . "'") . ","; + $sql .= " " . (!isset($this->fk_user_create) ? 'NULL' : "'" . $this->db->escape($this->fk_user_create) . "'") . ","; + $sql .= " " . (!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'" . $this->db->idate($this->datec). "'") . ","; + $sql .= " " . (!isset($this->message) ? 'NULL' : "'" . $this->db->escape($this->message) . "'") . ""; + + $sql .= ")"; + + $this->db->begin(); + + dol_syslog(get_class($this) . "::create sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + + if (!$error) { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "ticketsup_logs"); + + if (!$notrigger) { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_CREATE',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return $this->id; + } + } + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @return int <0 if KO, >0 if OK + */ + public function fetch($id) + { + global $langs; + $sql = "SELECT"; + $sql .= " t.rowid,"; + + $sql .= " t.fk_track_id,"; + $sql .= " t.fk_user_create,"; + $sql .= " t.datec,"; + $sql .= " t.message"; + + $sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup_logs as t"; + $sql .= " WHERE t.rowid = " . $id; + + dol_syslog(get_class($this) . "::fetch sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + if ($this->db->num_rows($resql)) { + $obj = $this->db->fetch_object($resql); + + $this->id = $obj->rowid; + + $this->fk_track_id = $obj->fk_track_id; + $this->fk_user_create = $obj->fk_user_create; + $this->datec = $this->db->jdate($obj->datec); + $this->message = $obj->message; + } + $this->db->free($resql); + + return 1; + } else { + $this->error = "Error " . $this->db->lasterror(); + dol_syslog(get_class($this) . "::fetch " . $this->error, LOG_ERR); + return -1; + } + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function update($user = 0, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + // Clean parameters + + if (isset($this->fk_track_id)) { + $this->fk_track_id = trim($this->fk_track_id); + } + + if (isset($this->fk_user_create)) { + $this->fk_user_create = trim($this->fk_user_create); + } + + if (isset($this->message)) { + $this->message = trim($this->message); + } + + // Check parameters + // Put here code to add a control on parameters values + + // Update request + $sql = "UPDATE " . MAIN_DB_PREFIX . "ticketsup_logs SET"; + + $sql .= " fk_track_id=" . (isset($this->fk_track_id) ? "'" . $this->db->escape($this->fk_track_id) . "'" : "null") . ","; + $sql .= " fk_user_create=" . ($this->fk_user_create > 0 ? $this->fk_user_create : "null") . ","; + $sql .= " datec=" . (dol_strlen($this->datec) != 0 ? "'" . $this->db->idate($this->datec) . "'" : 'null') . ","; + $sql .= " message=" . (isset($this->message) ? "'" . $this->db->escape($this->message) . "'" : "null") . ""; + + $sql .= " WHERE rowid=" . $this->id; + + $this->db->begin(); + + dol_syslog(get_class($this) . "::update sql=" . $sql, LOG_DEBUG); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + + if (!$error) { + if (!$notrigger) { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_MODIFY',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::update " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return 1; + } + } + + /** + * Delete object in database + * + * @param User $user User that deletes + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + public function delete($user, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + $this->db->begin(); + + if (!$error) { + if (!$notrigger) { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_DELETE',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + } + } + + if (!$error) { + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "ticketsup_logs"; + $sql .= " WHERE rowid=" . $this->id; + + dol_syslog(get_class($this) . "::delete sql=" . $sql); + $resql = $this->db->query($sql); + if (!$resql) { + $error++; + $this->errors[] = "Error " . $this->db->lasterror(); + } + } + + // Commit or rollback + if ($error) { + foreach ($this->errors as $errmsg) { + dol_syslog(get_class($this) . "::delete " . $errmsg, LOG_ERR); + $this->error .= ($this->error ? ', ' . $errmsg : $errmsg); + } + $this->db->rollback(); + return -1 * $error; + } else { + $this->db->commit(); + return 1; + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->id = 0; + + $this->fk_track_id = ''; + $this->fk_user_create = ''; + $this->datec = ''; + $this->message = ''; + } +} diff --git a/htdocs/ticketsup/class/ticketsupstats.class.php b/htdocs/ticketsup/class/ticketsupstats.class.php new file mode 100644 index 00000000000..3c299b911af --- /dev/null +++ b/htdocs/ticketsup/class/ticketsupstats.class.php @@ -0,0 +1,161 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file /ticketsup/class/ticketsupstats.class.php + * \ingroup ticketsup + * \brief Fichier de la classe de gestion des stats des tickets + */ +require_once DOL_DOCUMENT_ROOT . '/core/class/stats.class.php'; +require_once 'ticketsup.class.php'; + + +/** + * Classe permettant la gestion des stats des deplacements et notes de frais + */ +class TicketsupStats extends Stats +{ + public $table_element; + + public $socid; + public $userid; + + public $from; + public $field; + public $where; + + /** + * Constructor + * + * @param DoliDB $db Database handler + * @param int $socid Id third party + * @param mixed $userid Id user for filter or array of user ids + * @return void + */ + public function __construct($db, $socid = 0, $userid = 0) + { + global $conf; + + $this->db = $db; + $this->socid = $socid; + $this->userid = $userid; + + $object = new Ticketsup($this->db); + $this->from = MAIN_DB_PREFIX . $object->table_element; + $this->field = 'km'; + + $this->where = " fk_statut > 0"; + $this->where .= " AND entity = " . $conf->entity; + if ($this->socid) { + $this->where .= " AND fk_soc = " . $this->socid; + } + if (is_array($this->userid) && count($this->userid) > 0) { + $this->where .= ' AND fk_user IN (' . join(',', $this->userid) . ')'; + } elseif ($this->userid > 0) { + $this->where .= ' AND fk_user = ' . $this->userid; + } + } + + /** + * Renvoie le nombre de tickets par annee + * + * @return array Array of values + */ + public function getNbByYear() + { + $sql = "SELECT YEAR(datec) as dm, count(*)"; + $sql .= " FROM " . $this->from; + $sql .= " GROUP BY dm DESC"; + $sql .= " WHERE " . $this->where; + + return $this->_getNbByYear($sql); + } + + /** + * Renvoie le nombre de facture par mois pour une annee donnee + * + * @param string $year Year to scan + * @return array Array of values + */ + public function getNbByMonth($year) + { + $sql = "SELECT MONTH(datec) as dm, count(*)"; + $sql .= " FROM " . $this->from; + $sql .= " WHERE YEAR(datec) = " . $year; + $sql .= " AND " . $this->where; + $sql .= " GROUP BY dm"; + $sql .= $this->db->order('dm', 'DESC'); + + $res = $this->_getNbByMonth($year, $sql); + //var_dump($res);print '
    '; + return $res; + } + + /** + * Renvoie le montant de facture par mois pour une annee donnee + * + * @param int $year Year to scan + * @return array Array of values + */ + public function getAmountByMonth($year) + { + $sql = "SELECT date_format(datec,'%m') as dm, sum(" . $this->field . ")"; + $sql .= " FROM " . $this->from; + $sql .= " WHERE date_format(datec,'%Y') = '" . $year . "'"; + $sql .= " AND " . $this->where; + $sql .= " GROUP BY dm"; + $sql .= $this->db->order('dm', 'DESC'); + + $res = $this->_getAmountByMonth($year, $sql); + //var_dump($res);print '
    '; + return $res; + } + + /** + * Return average amount + * + * @param int $year Year to scan + * @return array Array of values + */ + public function getAverageByMonth($year) + { + $sql = "SELECT date_format(datec,'%m') as dm, avg(" . $this->field . ")"; + $sql .= " FROM " . $this->from; + $sql .= " WHERE date_format(datec,'%Y') = '" . $year . "'"; + $sql .= " AND " . $this->where; + $sql .= " GROUP BY dm"; + $sql .= $this->db->order('dm', 'DESC'); + + return $this->_getAverageByMonth($year, $sql); + } + + /** + * Return nb, total and average + * + * @return array Array of values + */ + public function getAllByYear() + { + $sql = "SELECT date_format(datec,'%Y') as year, count(*) as nb, sum(" . $this->field . ") as total, avg(" . $this->field . ") as avg"; + $sql .= " FROM " . $this->from; + $sql .= " WHERE " . $this->where; + $sql .= " GROUP BY year"; + $sql .= $this->db->order('year', 'DESC'); + + return $this->_getAllByYear($sql); + } +} diff --git a/htdocs/ticketsup/class/utils_diff.class.php b/htdocs/ticketsup/class/utils_diff.class.php new file mode 100644 index 00000000000..bd9e0e2d5ca --- /dev/null +++ b/htdocs/ticketsup/class/utils_diff.class.php @@ -0,0 +1,403 @@ + + * + * A class containing a diff implementation + * + * Created by Stephen Morley - http://stephenmorley.org/ - and released under the + * terms of the CC0 1.0 Universal legal code: + * + * http://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + + +/** + * A class containing functions for computing diffs and formatting the output. + */ +class Diff +{ + // define the constants + const UNMODIFIED = 0; + const DELETED = 1; + const INSERTED = 2; + + /* Returns the diff for two strings. The return value is an array, each of + * whose values is an array containing two values: a line (or character, if + * $compareCharacters is true), and one of the constants DIFF::UNMODIFIED (the + * line or character is in both strings), DIFF::DELETED (the line or character + * is only in the first string), and DIFF::INSERTED (the line or character is + * only in the second string). The parameters are: + * + * $string1 - the first string + * $string2 - the second string + * $compareCharacters - true to compare characters, and false to compare + * lines; this optional parameter defaults to false + */ + public static function compare( + $string1, + $string2, + $compareCharacters = false + ) { + + // initialise the sequences and comparison start and end positions + $start = 0; + if ($compareCharacters) { + $sequence1 = $string1; + $sequence2 = $string2; + $end1 = strlen($string1) - 1; + $end2 = strlen($string2) - 1; + } else { + $sequence1 = preg_split('/\R/', $string1); + $sequence2 = preg_split('/\R/', $string2); + $end1 = count($sequence1) - 1; + $end2 = count($sequence2) - 1; + } + + // skip any common prefix + while ($start <= $end1 && $start <= $end2 + && $sequence1[$start] == $sequence2[$start]) { + $start++; + } + + // skip any common suffix + while ($end1 >= $start && $end2 >= $start + && $sequence1[$end1] == $sequence2[$end2]) { + $end1--; + $end2--; + } + + // compute the table of longest common subsequence lengths + $table = self::computeTable($sequence1, $sequence2, $start, $end1, $end2); + + // generate the partial diff + $partialDiff = self::generatePartialDiff($table, $sequence1, $sequence2, $start); + + // generate the full diff + $diff = array(); + for ($index = 0; $index < $start; $index++) { + $diff[] = array($sequence1[$index], self::UNMODIFIED); + } + while (count($partialDiff) > 0) { + $diff[] = array_pop($partialDiff); + } + + for ($index = $end1 + 1; + $index < ($compareCharacters ? strlen($sequence1) : count($sequence1)); + $index++) { + $diff[] = array($sequence1[$index], self::UNMODIFIED); + } + + // return the diff + return $diff; + } + + /* Returns the diff for two files. The parameters are: + * + * $file1 - the path to the first file + * $file2 - the path to the second file + * $compareCharacters - true to compare characters, and false to compare + * lines; this optional parameter defaults to false + */ + public static function compareFiles( + $file1, + $file2, + $compareCharacters = false + ) { + + // return the diff of the files + return self::compare( + file_get_contents($file1), + file_get_contents($file2), + $compareCharacters + ); + } + + /* Returns the table of longest common subsequence lengths for the specified + * sequences. The parameters are: + * + * $sequence1 - the first sequence + * $sequence2 - the second sequence + * $start - the starting index + * $end1 - the ending index for the first sequence + * $end2 - the ending index for the second sequence + */ + private static function computeTable( + $sequence1, + $sequence2, + $start, + $end1, + $end2 + ) { + + // determine the lengths to be compared + $length1 = $end1 - $start + 1; + $length2 = $end2 - $start + 1; + + // initialise the table + $table = array(array_fill(0, $length2 + 1, 0)); + + // loop over the rows + for ($index1 = 1; $index1 <= $length1; $index1++) { + // create the new row + $table[$index1] = array(0); + + // loop over the columns + for ($index2 = 1; $index2 <= $length2; $index2++) { + // store the longest common subsequence length + if ($sequence1[$index1 + $start - 1]== $sequence2[$index2 + $start - 1] + ) { + $table[$index1][$index2] = $table[$index1 - 1][$index2 - 1] + 1; + } else { + $table[$index1][$index2] = max($table[$index1 - 1][$index2], $table[$index1][$index2 - 1]); + } + } + } + + // return the table + return $table; + } + + /* Returns the partial diff for the specificed sequences, in reverse order. + * The parameters are: + * + * $table - the table returned by the computeTable function + * $sequence1 - the first sequence + * $sequence2 - the second sequence + * $start - the starting index + */ + private static function generatePartialDiff( + $table, + $sequence1, + $sequence2, + $start + ) { + + // initialise the diff + $diff = array(); + + // initialise the indices + $index1 = count($table) - 1; + $index2 = count($table[0]) - 1; + + // loop until there are no items remaining in either sequence + while ($index1 > 0 || $index2 > 0) { + // check what has happened to the items at these indices + if ($index1 > 0 && $index2 > 0 + && $sequence1[$index1 + $start - 1]== $sequence2[$index2 + $start - 1] + ) { + // update the diff and the indices + $diff[] = array($sequence1[$index1 + $start - 1], self::UNMODIFIED); + $index1--; + $index2--; + } elseif ($index2 > 0 + && $table[$index1][$index2] == $table[$index1][$index2 - 1] + ) { + // update the diff and the indices + $diff[] = array($sequence2[$index2 + $start - 1], self::INSERTED); + $index2--; + } else { + // update the diff and the indices + $diff[] = array($sequence1[$index1 + $start - 1], self::DELETED); + $index1--; + } + } + + // return the diff + return $diff; + } + + /* Returns a diff as a string, where unmodified lines are prefixed by ' ', + * deletions are prefixed by '- ', and insertions are prefixed by '+ '. The + * parameters are: + * + * $diff - the diff array + * $separator - the separator between lines; this optional parameter defaults + * to "\n" + */ + public static function toString($diff, $separator = "\n") + { + + // initialise the string + $string = ''; + + // loop over the lines in the diff + foreach ($diff as $line) { + // extend the string with the line + switch ($line[1]) { + case self::UNMODIFIED: + $string .= ' ' . $line[0]; + break; + case self::DELETED: + $string .= '- ' . $line[0]; + break; + case self::INSERTED: + $string .= '+ ' . $line[0]; + break; + } + + // extend the string with the separator + $string .= $separator; + } + + // return the string + return $string; + } + + /* Returns a diff as an HTML string, where unmodified lines are contained + * within 'span' elements, deletions are contained within 'del' elements, and + * insertions are contained within 'ins' elements. The parameters are: + * + * $diff - the diff array + * $separator - the separator between lines; this optional parameter defaults + * to '
    ' + */ + public static function toHTML($diff, $separator = '
    ') + { + + // initialise the HTML + $html = ''; + + // loop over the lines in the diff + foreach ($diff as $line) { + // extend the HTML with the line + switch ($line[1]) { + case self::UNMODIFIED: + $element = 'span'; + break; + case self::DELETED: + $element = 'del'; + break; + case self::INSERTED: + $element = 'ins'; + break; + } + $html .= + '<' . $element . '>' + . htmlspecialchars($line[0]) + . ''; + + // extend the HTML with the separator + $html .= $separator; + } + + // return the HTML + return $html; + } + + /* Returns a diff as an HTML table. The parameters are: + * + * $diff - the diff array + * $indentation - indentation to add to every line of the generated HTML; this + * optional parameter defaults to '' + * $separator - the separator between lines; this optional parameter + * defaults to '
    ' + */ + public static function toTable($diff, $indentation = '', $separator = '
    ') + { + + // initialise the HTML + $html = $indentation . "\n"; + + // loop over the lines in the diff + $index = 0; + while ($index < count($diff)) { + // determine the line type + switch ($diff[$index][1]) { + // display the content on the left and right + case self::UNMODIFIED: + $leftCell = self::getCellContent( + $diff, + $indentation, + $separator, + $index, + self::UNMODIFIED + ); + $rightCell = $leftCell; + break; + + // display the deleted on the left and inserted content on the right + case self::DELETED: + $leftCell = self::getCellContent( + $diff, + $indentation, + $separator, + $index, + self::DELETED + ); + $rightCell = self::getCellContent( + $diff, + $indentation, + $separator, + $index, + self::INSERTED + ); + break; + + // display the inserted content on the right + case self::INSERTED: + $leftCell = ''; + $rightCell = self::getCellContent( + $diff, + $indentation, + $separator, + $index, + self::INSERTED + ); + break; + } + + // extend the HTML with the new row + $html .= + $indentation + . " \n" + . $indentation + . ' \n" + . $indentation + . ' \n" + . $indentation + . " \n"; + } + + // return the HTML + return $html . $indentation . "
    ' + . $leftCell + . "' + . $rightCell + . "
    \n"; + } + + /* Returns the content of the cell, for use in the toTable function. The + * parameters are: + * + * $diff - the diff array + * $indentation - indentation to add to every line of the generated HTML + * $separator - the separator between lines + * $index - the current index, passes by reference + * $type - the type of line + */ + private static function getCellContent($diff, $indentation, $separator, &$index, $type) + { + // initialise the HTML + $html = ''; + + // loop over the matching lines, adding them to the HTML + while ($index < count($diff) && $diff[$index][1] == $type) { + $html .= + '' + . htmlspecialchars($diff[$index][0]) + . '' + . $separator; + $index++; + } + + // return the HTML + return $html; + } +} diff --git a/htdocs/ticketsup/contact.php b/htdocs/ticketsup/contact.php new file mode 100644 index 00000000000..7202cba8dd2 --- /dev/null +++ b/htdocs/ticketsup/contact.php @@ -0,0 +1,194 @@ + + * Copyright (C) 2011 Regis Houssin + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/** + * \file htdocs/ticketsup/contact.php + * \ingroup ticketsup + * \brief Contacts of tickets + */ + +require '../main.inc.php'; + +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticketsup.lib.php'; + +require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT . "/core/lib/company.lib.php"; +require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formcompany.class.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("ticketsup"); + +// Get parameters +$socid = GETPOST("socid", 'int'); +$action = GETPOST("action", 'alpha'); +$track_id = GETPOST("track_id", 'alpha'); +$id = GETPOST("id", 'int'); +$ref = GETPOST('ref', 'alpha'); + +$type = GETPOST('type', 'alpha'); +$source = GETPOST('source', 'alpha'); + +$ligne = GETPOST('ligne', 'int'); +$lineid = GETPOST('lineid', 'int'); + + + + +// Protection if external user +if ($user->societe_id > 0) { + $socid = $user->societe_id; + accessforbidden(); +} + +// Store current page url +$url_page_current = dol_buildpath('/ticketsup/contact.php', 1); + +$object = new Ticketsup($db); + +/* + * Ajout d'un nouveau contact + */ + +if ($action == 'addcontact' && $user->rights->ticketsup->write) { + $result = $object->fetch($id, '', $track_id); + + if ($result > 0 && ($id > 0 || (!empty($track_id)))) { + $contactid = (GETPOST('userid', 'int') ? GETPOST('userid', 'int') : GETPOST('contactid', 'int')); + $result = $object->add_contact($contactid, $type, $source); + } + + if ($result >= 0) { + Header("Location: " . $url_page_current . "?id=" . $object->id); + exit; + } else { + if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') { + $langs->load("errors"); + setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors'); + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } +} + +// bascule du statut d'un contact +if ($action == 'swapstatut' && $user->rights->ticketsup->write) { + if ($object->fetch($id, '', $track_id)) { + $result = $object->swapContactStatus($ligne); + } else { + dol_print_error($db, $object->error); + } +} + +// Efface un contact +if ($action == 'deletecontact' && $user->rights->ticketsup->write) { + if ($object->fetch($id, '', $track_id)) { + $result = $object->delete_contact($lineid); + + if ($result >= 0) { + Header("Location: " . $url_page_current . "?id=" . $object->id); + exit; + } + } +} + +/* + * View + */ +$help_url = 'FR:DocumentationModuleTicket'; +llxHeader('', $langs->trans("TicketContacts"), $help_url); + +$form = new Form($db); +$formcompany = new FormCompany($db); +$contactstatic = new Contact($db); +$userstatic = new User($db); + +/* *************************************************************************** */ +/* */ +/* Mode vue et edition */ +/* */ +/* *************************************************************************** */ + +if ($id > 0 || !empty($track_id) || !empty($ref)) { + if ($object->fetch($id, $ref, $track_id) > 0) + { + if ($socid > 0) { + $object->fetch_thirdparty(); + $head = societe_prepare_head($object->thirdparty); + dol_fiche_head($head, 'ticketsup', $langs->trans("ThirdParty"), 0, 'company'); + dol_banner_tab($object->thirdparty, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); + dol_fiche_end(); + } + + if (!$user->societe_id && $conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY) { + $object->next_prev_filter = "te.fk_user_assign = '" . $user->id . "'"; + } elseif ($user->societe_id > 0) { + $object->next_prev_filter = "te.fk_soc = '" . $user->societe_id . "'"; + } + + $head = ticketsup_prepare_head($object); + + dol_fiche_head($head, 'contact', $langs->trans("Ticket"), -1, 'ticketsup'); + + $morehtmlref ='
    '; + $morehtmlref.= $object->subject; + // Author + if ($object->fk_user_create > 0) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + + $langs->load("users"); + $fuser = new User($db); + $fuser->fetch($object->fk_user_create); + $morehtmlref .= $fuser->getNomUrl(0); + } + if (!empty($object->origin_email)) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + $morehtmlref .= $object->origin_email . ' (' . $langs->trans("TicketEmailOriginIssuer") . ')'; + } + $morehtmlref.='
    '; + + $linkback = '' . $langs->trans("BackToList") . ' '; + + dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref); + + dol_fiche_end(); + + //print '
    '; + + $permission = $user->rights->ticketsup->write; + + // Contacts lines (modules that overwrite templates must declare this into descriptor) + $dirtpls=array_merge($conf->modules_parts['tpl'], array('/core/tpl')); + foreach ($dirtpls as $reldir) { + $res=@include dol_buildpath($reldir.'/contacts.tpl.php'); + if ($res) { + break; + } + } + } else { + print "ErrorRecordNotFound"; + } +} + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/ticketsup/css/bg.css.php b/htdocs/ticketsup/css/bg.css.php new file mode 100755 index 00000000000..d54233860f7 --- /dev/null +++ b/htdocs/ticketsup/css/bg.css.php @@ -0,0 +1,57 @@ +conf loaded (not done into main because of NOLOGIN constant defined) +if (empty($user->id) && ! empty($_SESSION['dol_login'])) { + $user->fetch('', $_SESSION['dol_login']); +} + + +// Define css type +header('Content-type: text/css'); +// Important: Following code is to avoid page request by browser and PHP CPU at +// each Dolibarr page access. +if (empty($dolibarr_nocache)) { + header('Cache-Control: max-age=3600, public, must-revalidate'); +} else { + header('Cache-Control: no-cache'); +} + +// On the fly GZIP compression for all pages (if browser support it). Must set the bit 3 of constant to 1. +if (isset($conf->global->MAIN_OPTIMIZE_SPEED) && ($conf->global->MAIN_OPTIMIZE_SPEED & 0x04)) { + ob_start("ob_gzhandler"); +} + + +print 'html {'; +if (! empty($conf->global->TICKETS_SHOW_MODULE_LOGO)) { + print 'background: url("../public/img/bg_ticket.png") no-repeat 95% 90%;'; +} +print '}'; diff --git a/htdocs/ticketsup/css/styles.css b/htdocs/ticketsup/css/styles.css new file mode 100644 index 00000000000..694e1c27816 --- /dev/null +++ b/htdocs/ticketsup/css/styles.css @@ -0,0 +1,115 @@ +html { + min-height: 100%; height: 100%; +} + +body { + font-size: 0.88em; + background: none; + min-height: 600px; + /*padding-bottom:150px;*/ +} + +div.corps { + font-family: arial; + position: static; + padding: 2em 1em; + overflow-x: auto; + border: 2px solid rgb(153, 153, 153); + background-color: rgb(255, 255, 255); + box-shadow: 2px 2px 2px rgb(245, 245, 245); + border-radius: 10px 10px 10px 10px; + margin: 1.5em; + background : #ffffff; + + +} + +.index_create, .index_display { + float: left; + width: 33%; + text-align: center; +} + +.orange { + color: #fef4e9; + border: solid 1px #da7c0c; + background: #f78d1d; + background: -webkit-gradient(linear, left top, left bottom, from(#faa51a), to(#f47a20)); + background: -moz-linear-gradient(top, #faa51a, #f47a20); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#faa51a', endColorstr='#f47a20'); +} +.orange:active { + color: #fcd3a5; + background: -webkit-gradient(linear, left top, left bottom, from(#f47a20), to(#faa51a)); + background: -moz-linear-gradient(top, #f47a20, #faa51a); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f47a20', endColorstr='#faa51a'); +} + +.orange:hover { + background: #f47c20; + background: -webkit-gradient(linear, left top, left bottom, from(#f88e11), to(#f06015)); + background: -moz-linear-gradient(top, #f88e11, #f06015); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f88e11', endColorstr='#f06015'); +} + + +.blue { + color: #d9eef7; + border: solid 1px #0076a3; + background: #0095cd; + background: -webkit-gradient(linear, left top, left bottom, from(#00adee), to(#0078a5)); + background: -moz-linear-gradient(top, #00adee, #0078a5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00adee', endColorstr='#0078a5'); +} +.blue:active { + color: #80bed6; + background: -webkit-gradient(linear, left top, left bottom, from(#0078a5), to(#00adee)); + background: -moz-linear-gradient(top, #0078a5, #00adee); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0078a5', endColorstr='#00adee'); +} +.blue:hover { + background: #007ead; + background: -webkit-gradient(linear, left top, left bottom, from(#0095cc), to(#00678e)); + background: -moz-linear-gradient(top, #0095cc, #00678e); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0095cc', endColorstr='#00678e'); +} + +#form_create_ticket, +#form_view_ticket { + + margin-left: 10px; + margin-right: 10px; + padding-left:1em; + padding-right:1em; + padding-top:1.5em; + padding-bottom:12px; + + border: 1px solid #C0C0C0; + background-color: #E0E0E0; + + -moz-box-shadow: 4px 4px 4px #DDD; + -webkit-box-shadow: 4px 4px 4px #DDD; + box-shadow: 4px 4px 4px #DDD; + + border-radius: 8px; + border:solid 1px rgba(168,168,168,.4); + border-top:solid 1px f8f8f8; + background-color: #f8f8f8; + background-image: -o-linear-gradient(top, rgba(250,250,250,.6) 0%, rgba(192,192,192,.3) 100%); + background-image: -moz-linear-gradient(top, rgba(250,250,250,.6) 0%, rgba(192,192,192,.3) 100%); + background-image: -webkit-linear-gradient(top, rgba(250,250,250,.6) 0%, rgba(192,192,192,.3) 100%); + background-image: -ms-linear-gradient(top, rgba(250,250,250,.6) 0%, rgba(192,192,192,.3) 100%); + background-image: linear-gradient(top, rgba(250,250,250,.6) 0%, rgba(192,192,192,.3) 100%); +} +#form_create_ticket input.text, +#form_create_ticket textarea { width:450px;} + div.info { + background: none repeat scroll 0% 0% rgb(252, 245, 184); + padding: 2px 4px 2px 6px; + margin: 1.5em 1em; + border: 1px solid rgb(188, 169, 54); + font-weight: normal; + +} + +div.warning { color: #333333;} diff --git a/htdocs/ticketsup/document.php b/htdocs/ticketsup/document.php new file mode 100644 index 00000000000..3ec98fe1635 --- /dev/null +++ b/htdocs/ticketsup/document.php @@ -0,0 +1,161 @@ + + * Copyright (C) 2004-2010 Laurent Destailleur + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2010 Juanjo Menent + * Copyright (C) 2013-2016 Jean-François Ferry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ticketsup/document.php + * \ingroup ticketsup + * \brief files linked to a ticket + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticketsup.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; +require_once DOL_DOCUMENT_ROOT . "/core/lib/company.lib.php"; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formfile.class.php'; + +$langs->loadLangs(array("companies","other","ticketsup","mails")); + +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$track_id = GETPOST('track_id', 'alpha'); +$action = GETPOST('action','alpha'); +$confirm = GETPOST('confirm','alpha'); + +// Security check +if (!$user->rights->ticketsup->read) { + accessforbidden(); +} + +// Get parameters +$sortfield = GETPOST("sortfield", 'alpha'); +$sortorder = GETPOST("sortorder", 'alpha'); +$page = GETPOST("page", 'int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +$offset = $conf->liste_limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; +if (! $sortorder) $sortorder="ASC"; +if (! $sortfield) $sortfield="position_name"; + +$object = new Ticketsup($db); +$result = $object->fetch($id, $ref, $track_id); + +// to match document rules and compatibility +$old_ref = $object->ref; +$object->ref = $object->track_id; + + +if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); +} else { + $upload_dir = $conf->ticketsup->dir_output . "/" . dol_sanitizeFileName($object->track_id); +} + + +/* + * Actions + */ + +include_once DOL_DOCUMENT_ROOT . '/core/actions_linkedfiles.inc.php'; + +$object->ref = $old_ref; + + + +/* + * View + */ + +$form = new Form($db); + +$help_url = ''; +llxHeader('', $langs->trans("TicketDocumentsLinked") . ' - ' . $langs->trans("Files"), $help_url); + +if ($object->id) +{ + /* + * Show tabs + */ + if ($socid > 0) { + $object->fetch_thirdparty(); + $head = societe_prepare_head($object->thirdparty); + dol_fiche_head($head, 'ticketsup', $langs->trans("ThirdParty"), 0, 'company'); + dol_banner_tab($object->thirdparty, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); + dol_fiche_end(); + } + + if (!$user->societe_id && $conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY) { + $object->next_prev_filter = "te.fk_user_assign = '" . $user->id . "'"; + } elseif ($user->societe_id > 0) { + $object->next_prev_filter = "te.fk_soc = '" . $user->societe_id . "'"; + } + + $head = ticketsup_prepare_head($object); + + dol_fiche_head($head, 'tabTicketDocument', $langs->trans("Ticket"), 0, 'ticketsup'); + + $morehtmlref ='
    '; + $morehtmlref.= $object->subject; + // Author + if ($object->fk_user_create > 0) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + + $langs->load("users"); + $fuser = new User($db); + $fuser->fetch($object->fk_user_create); + $morehtmlref .= $fuser->getNomUrl(0); + } + if (!empty($object->origin_email)) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + $morehtmlref .= $object->origin_email . ' (' . $langs->trans("TicketEmailOriginIssuer") . ')'; + } + $morehtmlref.='
    '; + + $linkback = '' . $langs->trans("BackToList") . ' '; + + dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref); + + dol_fiche_end(); + + // Construit liste des fichiers + $filearray = dol_dir_list($upload_dir, "files", 0, '', '\.meta$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1); + $totalsize = 0; + foreach ($filearray as $key => $file) { + $totalsize += $file['size']; + } + + $object->ref = $object->track_id; // For compatibility we use track ID for directory + $modulepart = 'ticketsup'; + $permission = $user->rights->ticketsup->write; + $permtoedit = $user->rights->ticketsup->write; + $param = '&id=' . $object->id; + + include_once DOL_DOCUMENT_ROOT . '/core/tpl/document_actions_post_headers.tpl.php'; +} +else +{ + accessforbidden('', 0, 0); +} + +llxFooter(); +$db->close(); diff --git a/htdocs/ticketsup/history.php b/htdocs/ticketsup/history.php new file mode 100644 index 00000000000..c6de1d7c3c6 --- /dev/null +++ b/htdocs/ticketsup/history.php @@ -0,0 +1,150 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ticketsup/history.php + * \ingroup ticketsup + * \brief History of ticket + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticketsup.lib.php'; +require_once DOL_DOCUMENT_ROOT . "/core/lib/company.lib.php"; +require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php'; + +if (!class_exists('Contact')) { + include DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; +} + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("ticketsup"); + +// Get parameters +$id = GETPOST('id', 'int'); +$track_id = GETPOST('track_id', 'alpha', 3); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'alpha', 3); + +// Security check +if (!$user->rights->ticketsup->read) { + accessforbidden(); +} + +$extrafields = new ExtraFields($db); +$extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + +if (!$action) { + $action = 'view'; +} + +$object = new Ticketsup($db); +$object->fetch($id, $ref, $track_id); + + +/* + * Actions + */ + +$actionobject = new ActionsTicketsup($db); + +$actionobject->doActions($action, $object); + + + +/* + * View + */ + +$help_url = 'FR:DocumentationModuleTicket'; +$page_title = $actionobject->getTitle($action); +llxHeader('', $page_title, $help_url); + +$userstat = new User($db); +$form = new Form($db); +$formticket = new FormTicketsup($db); + +if ($action == 'view') { + $res = $object->fetch($id, $ref, $track_id); + + if ($res > 0) { + // restrict access for externals users + if ($user->societe_id > 0 && ($object->fk_soc != $user->societe_id) + ) { + accessforbidden('', 0); + } + // or for unauthorized internals users + if (!$user->societe_id && ($conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY && $object->fk_user_assign != $user->id) && !$user->rights->ticketsup->manage) { + accessforbidden('', 0); + } + + if ($socid > 0) { + $object->fetch_thirdparty(); + $head = societe_prepare_head($object->thirdparty); + dol_fiche_head($head, 'ticketsup', $langs->trans("ThirdParty"), 0, 'company'); + dol_banner_tab($object->thirdparty, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); + dol_fiche_end(); + } + + if (!$user->societe_id && $conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY) { + $object->next_prev_filter = "te.fk_user_assign = '" . $user->id . "'"; + } elseif ($user->societe_id > 0) { + $object->next_prev_filter = "te.fk_soc = '" . $user->societe_id . "'"; + } + $head = ticketsup_prepare_head($object); + + dol_fiche_head($head, 'tabTicketLogs', $langs->trans("Ticket"), 0, 'ticketsup'); + + $morehtmlref ='
    '; + $morehtmlref.= $object->subject; + // Author + if ($object->fk_user_create > 0) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + + $langs->load("users"); + $fuser = new User($db); + $fuser->fetch($object->fk_user_create); + $morehtmlref .= $fuser->getNomUrl(0); + } + if (!empty($object->origin_email)) { + $morehtmlref .= '
    ' . $langs->trans("CreatedBy") . ' '; + $morehtmlref .= $object->origin_email . ' (' . $langs->trans("TicketEmailOriginIssuer") . ')'; + } + $morehtmlref.='
    '; + + $linkback = '' . $langs->trans("BackToList") . ' '; + + dol_banner_tab($object, 'ref', $linkback, ($user->societe_id ? 0 : 1), 'ref', 'ref', $morehtmlref); + + dol_fiche_end(); + + print '
    '; + // Logs list + print load_fiche_titre($langs->trans('TicketHistory'), '', 'history@ticketsup'); + $actionobject->viewTimelineTicketLogs(true, $object); + print '
    '; + print '
    '; + } +} // End action view + +// End of page +llxFooter(''); +$db->close(); diff --git a/htdocs/ticketsup/img/gplv3.png b/htdocs/ticketsup/img/gplv3.png new file mode 100644 index 00000000000..ba78d4c4941 Binary files /dev/null and b/htdocs/ticketsup/img/gplv3.png differ diff --git a/htdocs/ticketsup/img/history.png b/htdocs/ticketsup/img/history.png new file mode 100644 index 00000000000..59f9ac7d94c Binary files /dev/null and b/htdocs/ticketsup/img/history.png differ diff --git a/htdocs/ticketsup/img/mark-read.png b/htdocs/ticketsup/img/mark-read.png new file mode 100644 index 00000000000..450a80cb3fa Binary files /dev/null and b/htdocs/ticketsup/img/mark-read.png differ diff --git a/htdocs/ticketsup/img/messages.png b/htdocs/ticketsup/img/messages.png new file mode 100644 index 00000000000..f2747b49283 Binary files /dev/null and b/htdocs/ticketsup/img/messages.png differ diff --git a/htdocs/ticketsup/img/statut0.png b/htdocs/ticketsup/img/statut0.png new file mode 100644 index 00000000000..6e631dc0086 Binary files /dev/null and b/htdocs/ticketsup/img/statut0.png differ diff --git a/htdocs/ticketsup/img/statut1.png b/htdocs/ticketsup/img/statut1.png new file mode 100644 index 00000000000..5bff8090beb Binary files /dev/null and b/htdocs/ticketsup/img/statut1.png differ diff --git a/htdocs/ticketsup/img/statut3.png b/htdocs/ticketsup/img/statut3.png new file mode 100644 index 00000000000..4e72abd23cc Binary files /dev/null and b/htdocs/ticketsup/img/statut3.png differ diff --git a/htdocs/ticketsup/img/statut4.png b/htdocs/ticketsup/img/statut4.png new file mode 100644 index 00000000000..e3fde9cc55b Binary files /dev/null and b/htdocs/ticketsup/img/statut4.png differ diff --git a/htdocs/ticketsup/img/statut5.png b/htdocs/ticketsup/img/statut5.png new file mode 100644 index 00000000000..6ea7f62a9ff Binary files /dev/null and b/htdocs/ticketsup/img/statut5.png differ diff --git a/htdocs/ticketsup/img/statut6.png b/htdocs/ticketsup/img/statut6.png new file mode 100644 index 00000000000..af7f6d433dc Binary files /dev/null and b/htdocs/ticketsup/img/statut6.png differ diff --git a/htdocs/ticketsup/img/statut8.png b/htdocs/ticketsup/img/statut8.png new file mode 100644 index 00000000000..84f3c5e7fc5 Binary files /dev/null and b/htdocs/ticketsup/img/statut8.png differ diff --git a/htdocs/ticketsup/img/ticketsup.png b/htdocs/ticketsup/img/ticketsup.png new file mode 100644 index 00000000000..ce4b71ca624 Binary files /dev/null and b/htdocs/ticketsup/img/ticketsup.png differ diff --git a/htdocs/ticketsup/index.php b/htdocs/ticketsup/index.php new file mode 100644 index 00000000000..16c4fc15bf7 --- /dev/null +++ b/htdocs/ticketsup/index.php @@ -0,0 +1,386 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ticketsup/history.php + * \ingroup ticketsup + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/ticketsupstats.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/dolgraph.class.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("ticketsup"); + +$WIDTH = DolGraph::getDefaultGraphSizeForStats('width'); +$HEIGHT = DolGraph::getDefaultGraphSizeForStats('height'); + +// Get parameters +$id = GETPOST('id', 'int'); +$msg_id = GETPOST('msg_id', 'int'); + +$action = GETPOST('action', 'alpha', 3); + +if ($user->societe_id) { + $socid = $user->societe_id; +} + +// Security check +$result = restrictedArea($user, 'ticketsup', 0, '', '', '', ''); + +$nowyear = strftime("%Y", dol_now()); +$year = GETPOST('year') > 0 ? GETPOST('year') : $nowyear; +//$startyear=$year-2; +$startyear = $year - 1; +$endyear = $year; + +$object = new Ticketsup($db); + + +/* + * Actions + */ + +// None + + +/* + * View + */ + +$form = new Form($db); +$tickesupstatic = new Ticketsup($db); + +llxHeader('', $langs->trans('TicketsIndex'), ''); + +$linkback=''; +print load_fiche_titre($langs->trans('TicketsIndex'),$linkback,'title_ticketsup.png'); + + +$dir = ''; +$filenamenb = $dir . "/" . $prefix . "ticketsupinyear-" . $endyear . ".png"; +$fileurlnb = DOL_URL_ROOT . '/viewimage.php?modulepart=ticketsup&file=ticketsupinyear-' . $endyear . '.png'; + +$stats = new TicketsupStats($db, $socid, $userid); +$param_year = 'DOLUSERCOOKIE_ticketsup_by_status_year'; +$param_shownb = 'DOLUSERCOOKIE_ticketsup_by_status_shownb'; +$param_showtot = 'DOLUSERCOOKIE_ticketsup_by_status_showtot'; +$autosetarray = preg_split("/[,;:]+/", GETPOST('DOL_AUTOSET_COOKIE')); +if (in_array('DOLUSERCOOKIE_ticketsup_by_status', $autosetarray)) { + $endyear = GETPOST($param_year, 'int'); + $shownb = GETPOST($param_shownb, 'alpha'); + $showtot = GETPOST($param_showtot, 'alpha'); +} else { + $tmparray = json_decode($_COOKIE['DOLUSERCOOKIE_ticketsup_by_status'], true); + $endyear = $tmparray['year']; + $shownb = $tmparray['shownb']; + $showtot = $tmparray['showtot']; +} +if (empty($shownb) && empty($showtot)) { + $showtot = 1; +} + +$nowarray = dol_getdate(dol_now(), true); +if (empty($endyear)) { + $endyear = $nowarray['year']; +} + +$startyear = $endyear - 1; +$WIDTH = (($shownb && $showtot) || !empty($conf->dol_optimize_smallscreen)) ? '256' : '320'; +$HEIGHT = '192'; + +print '
    '; + +/* + * Statistics area + */ +$tick = array( + 'unread' => 0, + 'read' => 0, + 'answered' => 0, + 'assigned' => 0, + 'inprogress' => 0, + 'waiting' => 0, + 'closed' => 0, + 'deleted' => 0, +); +$total = 0; +$sql = "SELECT t.fk_statut, COUNT(t.fk_statut) as nb"; +$sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup as t"; +if (!$user->rights->societe->client->voir && !$socid) { + $sql .= ", " . MAIN_DB_PREFIX . "societe_commerciaux as sc"; +} + +$sql .= ' WHERE t.entity IN (' . getEntity('ticketsup', 1) . ')'; +$sql .= " AND t.fk_statut IS NOT NULL"; +$sql .= " AND date_format(datec,'%Y') = '" . $endyear . "'"; +if (!$user->rights->societe->client->voir && !$socid) { + $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = " . $user->id; +} + +// External users restriction +if ($user->societe_id > 0) { + $sql .= " AND t.fk_soc='" . $user->societe_id . "'"; +} else { + // For internals users, + if (!empty($conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY) && !$user->rights->ticketsup->manage) { + $sql .= " AND t.fk_user_assign=" . $user->id; + } +} +$sql .= " GROUP BY t.fk_statut"; + +$result = $db->query($sql); +if ($result) { + while ($objp = $db->fetch_object($result)) { + $found = 0; + if ($objp->fk_statut == 0) { + $tick['unread'] = $objp->nb; + } + if ($objp->fk_statut == 1) { + $tick['read'] = $objp->nb; + } + if ($objp->fk_statut == 3) { + $tick['answered'] = $objp->nb; + } + if ($objp->fk_statut == 4) { + $tick['assigned'] = $objp->nb; + } + if ($objp->fk_statut == 5) { + $tick['inprogress'] = $objp->nb; + } + if ($objp->fk_statut == 6) { + $tick['waiting'] = $objp->nb; + } + if ($objp->fk_statut == 8) { + $tick['closed'] = $objp->nb; + } + if ($objp->fk_statut == 9) { + $tick['deleted'] = $objp->nb; + } + } + + if ((round($tick['unread']) ? 1 : 0) +(round($tick['read']) ? 1 : 0) +(round($tick['answered']) ? 1 : 0) +(round($tick['assigned']) ? 1 : 0) +(round($tick['inprogress']) ? 1 : 0) +(round($tick['waiting']) ? 1 : 0) +(round($tick['closed']) ? 1 : 0) +(round($tick['deleted']) ? 1 : 0) >= 2 + ) { + $dataseries = array(); + $dataseries[] = array('label' => $langs->trans("Unread"), 'data' => round($tick['unread'])); + $dataseries[] = array('label' => $langs->trans("Read"), 'data' => round($tick['read'])); + $dataseries[] = array('label' => $langs->trans("Answered"), 'data' => round($tick['answered'])); + $dataseries[] = array('label' => $langs->trans("Assigned"), 'data' => round($tick['assigned'])); + $dataseries[] = array('label' => $langs->trans("InProgress"), 'data' => round($tick['inprogress'])); + $dataseries[] = array('label' => $langs->trans("Waiting"), 'data' => round($tick['waiting'])); + $dataseries[] = array('label' => $langs->trans("Closed"), 'data' => round($tick['Closed'])); + $dataseries[] = array('label' => $langs->trans("Deleted"), 'data' => round($tick['Deleted'])); + } +} else { + dol_print_error($db); +} + +$stringtoshow = ''; +$stringtoshow .= ''; +$stringtoshow .= '
    '; // hideobject is to start hidden +$stringtoshow .= '
    '; +$stringtoshow .= ''; +$stringtoshow .= ''; +$stringtoshow .= $langs->trans("Year") . ' '; +$stringtoshow .= ''; +$stringtoshow .= '
    '; +$stringtoshow .= '
    '; + +print ''; +print ''; + +print ''; + +print '
    ' . $langs->trans("Statistics") . ' ' . img_picto('', 'filter.png', 'id="idsubimgDOLUSERCOOKIE_ticketsup_by_status" class="linkobject"') . '
    '; + +// don't display graph if no series +if (! empty($dataseries) && count($dataseries) > 1) { + $data = array(); + foreach ($dataseries as $key => $value) { + $data[] = array($value['label'], $value['data']); + } + $px1 = new DolGraph(); + $mesg = $px1->isGraphKo(); + if (!$mesg) { + $px1->SetData($data); + unset($data1); + $px1->SetPrecisionY(0); + $i = $startyear; + $legend = array(); + while ($i <= $endyear) { + $legend[] = $i; + $i++; + } + $px1->SetType(array('pie')); + $px1->SetLegend($legend); + $px1->SetMaxValue($px1->GetCeilMaxValue()); + $px1->SetWidth($WIDTH); + $px1->SetHeight($HEIGHT); + $px1->SetYLabel($langs->trans("TicketStatByStatus")); + $px1->SetShading(3); + $px1->SetHorizTickIncrement(1); + $px1->SetPrecisionY(0); + $px1->SetCssPrefix("cssboxes"); + $px1->mode = 'depth'; + //$px1->SetTitle($langs->trans("TicketStatByStatus")); + + $px1->draw($filenamenb, $fileurlnb); + print $px1->show(); + + print $stringtoshow; + } +} +print '
    '; + +// Build graphic number of object +$data = $stats->getNbByMonth($endyear, $startyear); + +print '
    '; + +/* + * Last tickets + */ +$max = 15; +$sql = "SELECT t.rowid, t.ref, t.track_id, t.datec, t.subject, t.type_code, t.category_code, t.severity_code, t.fk_statut, t.progress,"; +$sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label"; +$sql .= " FROM " . MAIN_DB_PREFIX . "ticketsup as t"; +$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_type as type ON type.code=t.type_code"; +$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_category as category ON category.code=t.category_code"; +$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_ticketsup_severity as severity ON severity.code=t.severity_code"; +if (!$user->rights->societe->client->voir && !$socid) { + $sql .= ", " . MAIN_DB_PREFIX . "societe_commerciaux as sc"; +} + +$sql .= ' WHERE t.entity IN (' . getEntity('ticketsup', 1) . ')'; +$sql .= " AND t.fk_statut=0"; +if (!$user->rights->societe->client->voir && !$socid) { + $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = " . $user->id; +} + +if ($user->societe_id > 0) { + $sql .= " AND t.fk_soc='" . $user->societe_id . "'"; +} else { + // Restricted to assigned user only + if ($conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY && !$user->rights->ticketsup->manage) { + $sql .= " AND t.fk_user_assign=" . $user->id; + } +} +$sql .= $db->order("t.datec", "DESC"); +$sql .= $db->plimit($max, 0); + +//print $sql; +$result = $db->query($sql); +if ($result) { + $num = $db->num_rows($result); + + $i = 0; + + $transRecordedType = $langs->trans("LatestNewTickets", $max); + + print '
    '; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + if ($num > 0) { + + while ($i < $num) { + $objp = $db->fetch_object($result); + + $tickesupstatic->id = $objp->rowid; + $tickesupstatic->ref = $objp->ref; + $tickesupstatic->track_id = $objp->track_id; + $tickesupstatic->fk_statut = $objp->fk_statut; + $tickesupstatic->progress = $objp->progress; + $tickesupstatic->subject = $objp->subject; + + print ''; + // Creation date + print '"; + + // Ref + print '\n"; + + // Subject + print '\n"; + + // Type + print ''; + + // Category + print '"; + + // Severity + print '"; + + print '"; + + print "\n"; + $i++; + } + + $db->free(); + } else { + print ''; + } + + print "
    ' . $transRecordedType . '' . $langs->trans('Ref') . '' . $langs->trans('Subject') . '' . $langs->trans('Type') . '' . $langs->trans('Category') . '' . $langs->trans('Severity') . '
    '; + print dol_print_date($db->jdate($objp->datec), 'dayhour'); + print "'; + print $tickesupstatic->getNomUrl(1); + print "'; + print '' . dol_trunc($objp->subject, 30) . ''; + print "'; + print $objp->type_label; + print ''; + print $objp->category_label; + print "'; + print $objp->severity_label; + print "'; + print $tickesupstatic->getLibStatut(3); + print "
    ' . $langs->trans('NoTicketsFound') . '
    "; + print '
    '; +} else { + dol_print_error($db); +} + +print '
    '; +print '
    '; + +print ''; + +// End of page +llxFooter(''); +$db->close(); diff --git a/htdocs/ticketsup/list.php b/htdocs/ticketsup/list.php new file mode 100644 index 00000000000..364eab864e2 --- /dev/null +++ b/htdocs/ticketsup/list.php @@ -0,0 +1,697 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ticketsup/list.php + * \ingroup ticketsup + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php'; +include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; +include_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php'; +include_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php'; + +// Load traductions files requiredby by page +$langs->loadLangs(array("ticketsup","companies","other","projects")); + + +// Get parameters +$action = GETPOST('action','alpha')?GETPOST('action','alpha'):'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... +$massaction = GETPOST('massaction','alpha'); // The bulk action (combo box choice into lists) +$show_files = GETPOST('show_files','int'); // Show files area generated by bulk actions ? +$confirm = GETPOST('confirm','alpha'); // Result of a confirmation +$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button +$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'ticketsuplist'; // To manage different context of search +$backtopage = GETPOST('backtopage','alpha'); // Go back to a dedicated page +$optioncss = GETPOST('optioncss','aZ'); // Option for the css output (always '' except when 'print') + +$id = GETPOST('id','int'); +$msg_id = GETPOST('msg_id', 'int'); +$socid = GETPOST('socid', 'int'); +$projectid = GETPOST('projectid', 'int'); +$search_fk_soc=GETPOST('$search_fk_soc','int')?GETPOST('$search_fk_soc','int'):GETPOST('socid','int'); +$search_fk_project=GETPOST('search_fk_project','int')?GETPOST('search_fk_project','int'):GETPOST('projectid','int'); +$search_fk_status = GETPOST('search_fk_status', 'alpha'); +$mode = GETPOST('mode', 'alpha'); + +// Load variable for pagination +$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit; +$sortfield = GETPOST('sortfield','alpha'); +$sortorder = GETPOST('sortorder','alpha'); +$page = GETPOST('page','int'); +if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1 +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; + +// Initialize technical objects +$object=new Ticketsup($db); +$extrafields = new ExtraFields($db); +$diroutputmassaction=$conf->ticketsup->dir_output . '/temp/massgeneration/'.$user->id; +if ($socid > 0) $hookmanager->initHooks(array('thirdpartyticket')); +elseif ($project > 0) $hookmanager->initHooks(array('projectticket')); +else $hookmanager->initHooks(array('ticketsuplist')); + +// Fetch optionals attributes and labels +$extralabels = $extrafields->fetch_name_optionals_label('ticketsup'); +$search_array_options=$extrafields->getOptionalsFromPost($extralabels,'','search_'); + +// Default sort order (if not yet defined by previous GETPOST) +if (! $sortfield) $sortfield="t.".key($object->fields); // Set here default search field. By default 1st field in definition. +if (! $sortorder) $sortorder="ASC"; + +// Initialize array of search criterias +$search_all=trim(GETPOST("search_all",'alpha')); +$search=array(); +foreach($object->fields as $key => $val) +{ + if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha'); +} + + +// List of fields to search into when doing a "search in all" +$fieldstosearchall = array(); +foreach($object->fields as $key => $val) +{ + if ($val['searchall']) $fieldstosearchall['t.'.$key]=$val['label']; +} + +// Definition of fields for list +$arrayfields=array(); +foreach($object->fields as $key => $val) +{ + // If $val['visible']==0, then we never show the field + if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']); +} +// Extra fields +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) +{ + foreach($extrafields->attribute_label as $key => $val) + { + if (! empty($extrafields->attribute_list[$key])) $arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>(($extrafields->attribute_list[$key]<0)?0:1), 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>(abs($extrafields->attribute_list[$key])!=3 && $extrafields->attribute_perms[$key])); + } +} +$object->fields = dol_sort_array($object->fields, 'position'); +$arrayfields = dol_sort_array($arrayfields, 'position'); +//if ($socid > 0) $arrayfields['t.fk_soc']['enabled']=0; +//if ($projectid > 0) $arrayfields['t.fk_project']['enabled']=0; + + +// Security check +if (!$user->rights->ticketsup->read) { + accessforbidden(); +} + +// Store current page url +$url_page_current = dol_buildpath('/ticketsup/list.php', 1); + + + +/* + * Actions + */ + +if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; } +if (! GETPOST('confirmmassaction','alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; } + +$parameters=array(); +$reshook=$hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +if (empty($reshook)) +{ + // Selection of new fields + include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php'; + + // Purge search criteria + if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers + { + foreach($object->fields as $key => $val) + { + $search[$key]=''; + } + $toselect=''; + $search_array_options=array(); + } + if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha') + || GETPOST('button_search_x','alpha') || GETPOST('button_search.x','alpha') || GETPOST('button_search','alpha')) + { + $massaction=''; // Protection to avoid mass action if we force a new search during a mass action confirmation + } + + // Mass actions + $objectclass='Ticketsup'; + $objectlabel='Ticketsup'; + $permtoread = $user->rights->ticketsup->read; + $permtodelete = $user->rights->ticketsup->delete; + $uploaddir = $conf->ticketsup->dir_output; + include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php'; +} + + + +/* + * View + */ + +$help_url = 'FR:DocumentationModuleTicket'; +llxHeader('', $langs->trans('TicketList'), $help_url); + +$form = new Form($db); +$formTicket = new FormTicketsup($db); + +$user_assign = new User($db); +$user_create = new User($db); +$socstatic = new Societe($db); + + +// Build and execute select +// -------------------------------------------------------------------- +$sql = 'SELECT '; +foreach($object->fields as $key => $val) +{ + $sql.='t.'.$key.', '; +} +// Add fields from extrafields +foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ", ef.".$key.' as options_'.$key : ''); +// Add fields from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; +$sql=preg_replace('/, $/','', $sql); +$sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; +if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."ticketsup_extrafields as ef on (t.rowid = ef.fk_object)"; +if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity('ticketsup').")"; +else $sql.=" WHERE 1 = 1"; +foreach($search as $key => $val) +{ + if ($key == 'fk_statut' && $search[$key] == -1) continue; + $mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0); + if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'fk_statut')?2:$mode_search)); +} +if ($search_all) $sql.= natural_search(array_keys($fieldstosearchall), $search_all); +if ($search_fk_soc) $sql.= natural_search('fk_soc', $search_fk_soc); +if ($search_fk_project) $sql.= natural_search('fk_project', $search_fk_project); +if (!$user->societe_id && ($mode == "my_assign" || (!$user->admin && $conf->global->TICKETS_LIMIT_VIEW_ASSIGNED_ONLY))) { + $sql.= " AND t.fk_user_assign=".$user->id; +} + +if (isset($search_fk_status) && $search_fk_status == 'non_closed') { + //$search['fk_statut'] = '0,1'; // + $sql.= " AND t.fk_statut IN (0, 1, 3, 4, 5, 6)"; +} + +// Add where from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; +// Add where from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql.=$hookmanager->resPrint; + +/* If a group by is required + $sql.= " GROUP BY " + foreach($object->fields as $key => $val) + { + $sql.='t.'.$key.', '; + } + // Add fields from extrafields + foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key : ''); + // Add where from hooks + $parameters=array(); + $reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters); // Note that $action and $object may have been modified by hook + $sql.=$hookmanager->resPrint; + */ + +$sql.=$db->order($sortfield,$sortorder); + +// Count total nb of records +$nbtotalofrecords = ''; +if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) +{ + $result = $db->query($sql); + $nbtotalofrecords = $db->num_rows($result); +} +// if total resultset is smaller then paging size (filtering), goto and load page 0 +if (($page * $limit) > $nbtotalofrecords) +{ + $page = 0; + $offset = 0; +} +// if total resultset is smaller the limit, no need to do paging. +if (is_numeric($nbtotalofrecords) && $limit > $nbtotalofrecords) +{ + $resql = $result; + $num = $nbtotalofrecords; +} +else +{ + $sql.= $db->plimit($limit+1, $offset); + + $resql=$db->query($sql); + if (! $resql) + { + dol_print_error($db); + exit; + } + + $num = $db->num_rows($resql); +} + +// Direct jump if only one record found +if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all) +{ + $obj = $db->fetch_object($resql); + $id = $obj->rowid; + header("Location: ".DOL_URL_ROOT.'/ticketsup/card.php?id='.$id); + exit; +} + + +// Output page +// -------------------------------------------------------------------- + +if ($socid && !$projectid && $user->rights->societe->lire) { + $socstat = new Societe($db); + $res = $socstat->fetch($socid); + if ($res > 0) { + + $tmpobject = $object; + $object = $socstat; // $object must be of type Societe when calling societe_prepare_head + $head = societe_prepare_head($socstat); + $object = $tmpobject; + + dol_fiche_head($head, 'ticketsup', $langs->trans("ThirdParty"), -1, 'company'); + + dol_banner_tab($socstat, 'socid', '', ($user->societe_id ? 0 : 1), 'rowid', 'nom'); + + print '
    '; + + print '
    '; + print ''; + + // Customer code + if ($socstat->client && !empty($socstat->code_client)) { + print ''; + print $htmllogobar; + $htmllogobar = ''; + print ''; + } + print '
    '; + print $langs->trans('CustomerCode') . ''; + print $socstat->code_client; + if ($socstat->check_codeclient() != 0) { + print ' (' . $langs->trans("WrongCustomerCode") . ')'; + } + + print '
    '; + print '
    '; + dol_fiche_end(); + } +} + +if ($projectid > 0) { + $projectstat = new Project($db); + if ($projectstat->fetch($projectid) > 0) { + $projectstat->fetch_thirdparty(); + + $savobject = $object; + $object = $projectstat; + + // To verify role of users + //$userAccess = $object->restrictedProjectArea($user,'read'); + $userWrite = $projectstat->restrictedProjectArea($user, 'write'); + //$userDelete = $object->restrictedProjectArea($user,'delete'); + //print "userAccess=".$userAccess." userWrite=".$userWrite." userDelete=".$userDelete; + + $head = project_prepare_head($projectstat); + dol_fiche_head($head, 'ticketsup', $langs->trans("Project"), -1, ($projectstat->public ? 'projectpub' : 'project')); + + // Project card + + $linkback = ''.$langs->trans("BackToList").''; + + $morehtmlref='
    '; + // Title + $morehtmlref.=$object->title; + // Thirdparty + if ($object->thirdparty->id > 0) + { + $morehtmlref.='
    '.$langs->trans('ThirdParty') . ' : ' . $object->thirdparty->getNomUrl(1, 'project'); + } + $morehtmlref.='
    '; + + // Define a complementary filter for search of next/prev ref. + if (! $user->rights->projet->all->lire) + { + $objectsListId = $object->getProjectsAuthorizedForUser($user,0,0); + $object->next_prev_filter=" rowid in (".(count($objectsListId)?join(',',array_keys($objectsListId)):'0').")"; + } + + dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); + + print '
    '; + print '
    '; + + print ''; + + // Visibility + print ''; + + print "
    ' . $langs->trans("Visibility") . ''; + if ($projectstat->public) { + print $langs->trans('SharedProject'); + } else { + print $langs->trans('PrivateProject'); + } + print '
    "; + + print '
    '; + dol_fiche_end(); + + $object = $savobject; + + } else { + print "ErrorRecordNotFound"; + } +} + +$arrayofselected=is_array($toselect)?$toselect:array(); + +$param=''; +if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage); +if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit); +foreach($search as $key => $val) +{ + $param.= '&search_'.$key.'='.urlencode($search[$key]); +} +if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss); +// Add $param from extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; +if ($socid) $param.='&socid='.urlencode($socid); +if ($projectid) $param.='&projectid='.urlencode($projectid); + +// List of mass actions available +$arrayofmassactions = array( + //'presend'=>$langs->trans("SendByMail"), + //'builddoc'=>$langs->trans("PDFMerge"), +); +if ($user->rights->ticketsup->delete) $arrayofmassactions['predelete']=$langs->trans("Delete"); +if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array(); +$massactionbutton=$form->selectMassAction('', $arrayofmassactions); + + +print '
    '; +if ($optioncss != '') print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +if ($socid) print ''; +if ($projectid) print ''; + +$buttontocreate = '' . $langs->trans('NewTicket') . ''; + +print_barre_liste($langs->trans('TicketList'), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'title_ticketsup', 0, $buttontocreate, '', $limit); + +if ($mode == 'my_assign') { + print '
    ' . $langs->trans('TicketAssignedToMeInfos') . '

    '; +} +// Add code for pre mass action (confirmation or email presend form) +$topicmail="SendTicketsupRef"; +$modelmail="ticketsup"; +$objecttmp=new Ticketsup($db); +$trackid='xxxx'.$object->id; +include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; + +if ($sall) +{ + foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val); + print $langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall); +} + +if ($search_fk_status == 'non_closed') { + print ''; + $param .= '&search_fk_status=non_closed'; +} else { + print ''; + $param .= '&search_fk_status=-1'; +} + + + +$moreforfilter = ''; +/*$moreforfilter.='
    '; + $moreforfilter.= $langs->trans('MyFilter') . ': '; + $moreforfilter.= '
    ';*/ + +$parameters=array(); +$reshook=$hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint; +else $moreforfilter = $hookmanager->resPrint; + +if (! empty($moreforfilter)) +{ + print '
    '; + print $moreforfilter; + print '
    '; +} + +$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage; +$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields +$selectedfields.=(count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); + +print '
    '; // You can use div-table-responsive-no-min if you dont need reserved height for your table +print ''."\n"; + + +// Fields title search +// -------------------------------------------------------------------- +print ''; + +foreach($object->fields as $key => $val) +{ + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) { + if ($key == 'type_code') { + print ''; + } elseif ($key == 'category_code') { + print ''; + } elseif ($key == 'severity_code') { + print ''; + } elseif ($key == 'fk_statut') { + print ''; + } + else { + print ''; + } + } +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; + +// Fields from hook +$parameters=array('arrayfields'=>$arrayfields); +$reshook=$hookmanager->executeHooks('printFieldListOption', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Action column +print ''; + +print ''."\n"; + + +// Fields title label +// -------------------------------------------------------------------- +print ''; +foreach($object->fields as $key => $val) +{ + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($align?'class="'.$align.'"':''), $sortfield, $sortorder, $align.' ')."\n"; +} +// Extra fields +include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; +// Hook fields +$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); +$reshook=$hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],'','','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n"; +print ''."\n"; + + +// Detect if we need a fetch on each output line +$needToFetchEachLine=0; +foreach ($extrafields->attribute_computed as $key => $val) +{ + if (preg_match('/\$object/',$val)) $needToFetchEachLine++; // There is at least one compute field that use $object +} + + + +// Loop on record +// -------------------------------------------------------------------- +$i=0; +$totalarray=array(); +while ($i < min($num, $limit)) +{ + $obj = $db->fetch_object($resql); + if (empty($obj)) break; // Should not happen + + // Store properties in $object + $object->id = $obj->rowid; + foreach($object->fields as $key => $val) + { + if (isset($obj->$key)) $object->$key = $obj->$key; + } + + // Show here line of result + print ''; + foreach($object->fields as $key => $val) + { + $align=''; + if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center'; + if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap'; + if ($key == 'status') $align.=($align?' ':'').'center'; + if (! empty($arrayfields['t.'.$key]['checked'])) + { + print ''; + print $object->showOutputField($val, $key, $obj->$key, ''); + print ''; + if (! $i) $totalarray['nbfield']++; + if (! empty($val['isameasure'])) + { + if (! $i) $totalarray['pos'][$totalarray['nbfield']]='t.'.$key; + $totalarray['val']['t.'.$key] += $obj->$key; + } + } + } + // Extra fields + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; + // Fields from hook + $parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj); + $reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $object); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Action column + print ''; + if (! $i) $totalarray['nbfield']++; + + print ''; + + $i++; +} + +// Show total line +if (isset($totalarray['pos'])) +{ + print ''; + $i=0; + while ($i < $totalarray['nbfield']) + { + $i++; + if (! empty($totalarray['pos'][$i])) print ''; + else + { + if ($i == 1) + { + if ($num < $limit) print ''; + else print ''; + } + else print ''; + } + } + print ''; +} + +// If no record found +if ($num == 0) +{ + $colspan=1; + foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; } + print ''; +} + + +$db->free($resql); + +$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql); +$reshook=$hookmanager->executeHooks('printFieldListFooter', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +print '
    '; + $formTicket->selectTypesTickets(dol_escape_htmltag($search[$key]), 'search_'.$key.'', '', 0, 1, 1, 0, 'maxwidth200'); + print ''; + $formTicket->selectCategoriesTickets(dol_escape_htmltag($search[$key]), 'search_'.$key.'', '', 0, 1, 1, 0, 'maxwidth200'); + print ''; + $formTicket->selectSeveritiesTickets(dol_escape_htmltag($search[$key]), 'search_'.$key.'', '', 0, 1, 1, 0, 'maxwidth200'); + print ''; + $object->printSelectStatus(dol_escape_htmltag($search[$key])); + print ''; +$searchpicto=$form->showFilterButtons(); +print $searchpicto; +print '
    '; + if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + { + $selected=0; + if (in_array($obj->rowid, $arrayofselected)) $selected=1; + print ''; + } + print '
    '.price($totalarray['val'][$totalarray['pos'][$i]]).''.$langs->trans("Total").''.$langs->trans("Totalforthispage").'
    '.$langs->trans("NoRecordFound").'
    '."\n"; +print '
    '."\n"; + +print '
    '."\n"; + + + +if (in_array('builddoc',$arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords)) +{ + $hidegeneratedfilelistifempty=1; + if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) $hidegeneratedfilelistifempty=0; + + require_once(DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'); + $formfile = new FormFile($db); + + // Show list of available documents + $urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder; + $urlsource.=str_replace('&','&',$param); + + $filedir=$diroutputmassaction; + $genallowed=$user->rights->ticketsup->read; + $delallowed=$user->rights->ticketsup->write; + + print $formfile->showdocuments('massfilesarea_ticketsup','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'','','',null,$hidegeneratedfilelistifempty); +} + +// End of page +llxFooter(''); +$db->close(); diff --git a/htdocs/ticketsup/new.php b/htdocs/ticketsup/new.php new file mode 100644 index 00000000000..0428f5286c2 --- /dev/null +++ b/htdocs/ticketsup/new.php @@ -0,0 +1,98 @@ + + * Copyright (C) 2016 Christophe Battarel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/ticketsup/new.php + * \ingroup ticketsup + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT . '/ticketsup/class/actions_ticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/html.formticketsup.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticketsup.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php'; + +// Load traductions files requiredby by page +$langs->load("companies"); +$langs->load("other"); +$langs->load("ticketsup"); + +// Get parameters +$id = GETPOST('id', 'int'); +$socid = GETPOST('socid', 'int'); +$contactid = GETPOST('contactid', 'int'); +$msg_id = GETPOST('msg_id', 'int'); +$notifyTiers = GETPOST("notify_tiers_at_create", 'alpha'); + +$action = GETPOST('action', 'alpha', 3); + +// Protection if external user +if (!$user->rights->ticketsup->read || !$user->rights->ticketsup->write) { + accessforbidden(); +} + +$object = new Ticketsup($db); +$actionobject = new ActionsTicketsup($db); + + +/* + * Actions + */ + +$actionobject->doActions($action, $object); + + + +/* + * View + */ + +$form = new Form($db); + +$help_url = 'FR:DocumentationModuleTicket'; +$page_title = $actionobject->getTitle($action); +llxHeader('', $page_title, $help_url); + + +if ($action == 'create_ticket') { + $formticket = new FormTicketsup($db); + + print load_fiche_titre($langs->trans('NewTicket'), '', 'title_ticketsup'); + + $formticket->withfromsocid = $socid ? $socid : $user->societe_id; + $formticket->withfromcontactid = $contactid ? $contactid : ''; + $formticket->withtitletopic = 1; + $formticket->withnotifytiersatcreate = ($notifyTiers?1:0); + $formticket->withusercreate = 1; + $formticket->withref = 1; + $formticket->fk_user_create = $user->id; + $formticket->withfile = 2; + $formticket->withextrafields = 1; + $formticket->param = array('origin' => GETPOST('origin'), 'originid' => GETPOST('originid')); + if (empty($defaultref)) { + $defaultref = ''; + } + + $formticket->showForm(); +} + +//$somethingshown=$object->showLinkedObjectBlock(); + +// End of page +llxFooter(''); +$db->close(); diff --git a/htdocs/ticketsup/tpl/index.html b/htdocs/ticketsup/tpl/index.html new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/htdocs/ticketsup/tpl/index.html @@ -0,0 +1 @@ + diff --git a/htdocs/ticketsup/tpl/linkedobjectblock.tpl.php b/htdocs/ticketsup/tpl/linkedobjectblock.tpl.php new file mode 100644 index 00000000000..824f392d638 --- /dev/null +++ b/htdocs/ticketsup/tpl/linkedobjectblock.tpl.php @@ -0,0 +1,64 @@ + + * Copyright (C) 2013 Jean-François FERRY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +// Protection to avoid direct call of template +if (empty($conf) || ! is_object($conf)) +{ + print "Error, template page can't be called as URL"; + exit; +} +?> + + + +load('ticketsup'); +$linkedObjectBlock = $GLOBALS['linkedObjectBlock']; +echo '
    '; +print_titre($langs->trans('RelatedTickets')); +?> + + + + + + + + + + + + socid = $object->fk_soc; + $object->fetch_thirdparty(); + ?> + + + + +
    trans("Subject"); ?>trans("DateCreation"); ?>trans("Customer"); ?>trans("Status"); ?>
    + subject) ? ' '.$object->subject : ''); ?> + + datec, 'day'); ?>thirdparty->getNomUrl(1); ?>getLibstatut(2); ?>
    + + diff --git a/htdocs/user/admin/group_extrafields.php b/htdocs/user/admin/group_extrafields.php index 0f11c3cd141..7277fa9f03b 100644 --- a/htdocs/user/admin/group_extrafields.php +++ b/htdocs/user/admin/group_extrafields.php @@ -65,7 +65,7 @@ $help_url='EN:Module_Users|FR:Module_Utilisateurs|ES:Módulo_Usuarios'; llxHeader('',$langs->trans("UsersSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("UsersSetup"),$linkback,'title_setup'); diff --git a/htdocs/user/admin/user_extrafields.php b/htdocs/user/admin/user_extrafields.php index b38f64c4600..c6b55d760f3 100644 --- a/htdocs/user/admin/user_extrafields.php +++ b/htdocs/user/admin/user_extrafields.php @@ -64,7 +64,7 @@ $help_url='EN:Module_Users|FR:Module_Utilisateurs|ES:Módulo_Usuarios'; llxHeader('',$langs->trans("UsersSetup"),$help_url); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($langs->trans("UsersSetup"),$linkback,'title_setup'); diff --git a/htdocs/user/agenda_extsites.php b/htdocs/user/agenda_extsites.php index 5b9e62c21df..ced0e759f31 100644 --- a/htdocs/user/agenda_extsites.php +++ b/htdocs/user/agenda_extsites.php @@ -40,6 +40,7 @@ $langs->load("other"); $def = array(); $actiontest=GETPOST('test','alpha'); $actionsave=GETPOST('save','alpha'); +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'useragenda'; // To manage different context of search if (empty($conf->global->AGENDA_EXT_NB)) $conf->global->AGENDA_EXT_NB=5; $MAXAGENDA=$conf->global->AGENDA_EXT_NB; @@ -68,8 +69,7 @@ if (($object->id != $user->id) && (! $user->rights->user->user->lire)) accessforbidden(); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('usercard','useragenda','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','useragenda','globalcard')); /* * Actions @@ -158,7 +158,7 @@ dol_fiche_head($head, 'extsites', $langs->trans("User"), -1, 'user'); $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/bank.php b/htdocs/user/bank.php index 0f26169adf3..9356828f884 100644 --- a/htdocs/user/bank.php +++ b/htdocs/user/bank.php @@ -200,7 +200,7 @@ if ($action != 'edit' && $action != 'create') // If not bank account yet, $acco $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); @@ -486,7 +486,7 @@ if ($id && ($action == 'edit' || $action == 'create' ) && $user->rights->user->u $title = $langs->trans("User"); dol_fiche_head($head, 'bank', $title, 0, 'user'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object, 'id', $linkback, $user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/card.php b/htdocs/user/card.php index aa169253fb3..d711fd8b4b4 100644 --- a/htdocs/user/card.php +++ b/htdocs/user/card.php @@ -3,7 +3,7 @@ * Copyright (C) 2002-2003 Jean-Louis Bergamo * Copyright (C) 2004-2015 Laurent Destailleur * Copyright (C) 2004 Eric Seigne - * Copyright (C) 2005-2017 Regis Houssin + * Copyright (C) 2005-2018 Regis Houssin * Copyright (C) 2005 Lionel Cousteix * Copyright (C) 2011 Herve Prot * Copyright (C) 2012 Juanjo Menent @@ -50,10 +50,11 @@ if (! empty($conf->categorie->enabled)) require_once DOL_DOCUMENT_ROOT.'/categor $id = GETPOST('id','int'); $action = GETPOST('action','aZ09'); $mode = GETPOST('mode','alpha'); -$confirm = GETPOST('confirm','alpha'); +$confirm = GETPOST('confirm','alpha'); $subaction = GETPOST('subaction','alpha'); $group = GETPOST("group","int",3); $cancel = GETPOST('cancel','alpha'); +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'useracard'; // To manage different context of search // Define value to know what current user can do on users $canadduser=(! empty($user->admin) || $user->rights->user->user->creer); @@ -67,6 +68,7 @@ if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS)) $canreadgroup=(! empty($user->admin) || $user->rights->user->group_advance->read); $caneditgroup=(! empty($user->admin) || $user->rights->user->group_advance->write); } + // Define value to know what current user can do on properties of edited user if ($id) { @@ -100,8 +102,7 @@ $extrafields = new ExtraFields($db); $extralabels=$extrafields->fetch_name_optionals_label($object->table_element); // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array -$contextpage=array('usercard','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','globalcard')); @@ -154,7 +155,7 @@ if (empty($reshook)) { $langs->load("errors"); setEventMessages($langs->trans("ErrorUserCannotBeDelete"), null, 'errors'); } else { - header("Location: index.php"); + header("Location: index.php?restore_lastsearch_values=1"); exit; } } @@ -202,7 +203,7 @@ if (empty($reshook)) { $object->office_fax = GETPOST("office_fax", 'alpha'); $object->user_mobile = GETPOST("user_mobile"); $object->skype = GETPOST("skype", 'alpha'); - $object->email = GETPOST("email", 'alpha'); + $object->email = preg_replace('/\s+/', '', GETPOST("email", 'alpha')); $object->job = GETPOST("job", 'alpha'); $object->signature = GETPOST("signature"); $object->accountancy_code = GETPOST("accountancy_code"); @@ -346,7 +347,7 @@ if (empty($reshook)) { $object->office_fax = GETPOST("office_fax", 'alpha'); $object->user_mobile = GETPOST("user_mobile"); $object->skype = GETPOST("skype", 'alpha'); - $object->email = GETPOST("email", 'alpha'); + $object->email = preg_replace('/\s+/', '', GETPOST("email", 'alpha')); $object->job = GETPOST("job", 'alpha'); $object->signature = GETPOST("signature"); $object->accountancy_code = GETPOST("accountancy_code"); @@ -1239,7 +1240,7 @@ else $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } } @@ -1795,7 +1796,7 @@ else } else { - print '
    '.$langs->trans("None").'
    '.$langs->trans("None").'
    '.$langs->trans("Name").'
    '.$langs->trans("Name").''.$object->name; + print ''.dol_escape_htmltag($object->name); if (empty($object->entity)) { print img_picto($langs->trans("GlobalGroup"),'redstar'); @@ -356,7 +377,7 @@ else { $mc->getInfo($object->entity); print "
    '.$langs->trans("Entity").''.$mc->label; + print ''.dol_escape_htmltag($mc->label); print "
    '; print ''; - print '\n"; // Multicompany diff --git a/htdocs/user/group/ldap.php b/htdocs/user/group/ldap.php index baf30a02399..2deab4b56a2 100644 --- a/htdocs/user/group/ldap.php +++ b/htdocs/user/group/ldap.php @@ -105,7 +105,7 @@ $head = group_prepare_head($object); dol_fiche_head($head, 'ldap', $langs->trans("Group"), -1, 'group'); -$linkback = ''.$langs->trans("BackToList").''; +$linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object,'id',$linback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/group/index.php b/htdocs/user/group/list.php similarity index 90% rename from htdocs/user/group/index.php rename to htdocs/user/group/list.php index 284a7fb289a..29bd19a25ae 100644 --- a/htdocs/user/group/index.php +++ b/htdocs/user/group/list.php @@ -19,7 +19,7 @@ */ /** - * \file htdocs/user/group/index.php + * \file htdocs/user/group/list.php * \ingroup core * \brief Page of user groups */ @@ -99,9 +99,10 @@ if (empty($reshook)) llxHeader(); -$sql = "SELECT g.rowid, g.nom as name, g.note, g.entity, g.datec, COUNT(DISTINCT ugu.fk_user) as nb"; +$sql = "SELECT g.rowid, g.nom as name, g.note, g.entity, g.datec, COUNT(DISTINCT ugu.fk_user) as nb, COUNT(DISTINCT ugr.fk_id) as nbpermissions"; $sql.= " FROM ".MAIN_DB_PREFIX."usergroup as g"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ugu ON ugu.fk_usergroup = g.rowid"; +$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_rights as ugr ON ugr.fk_usergroup = g.rowid"; if (! empty($conf->multicompany->enabled) && $conf->entity == 1 && ($conf->global->MULTICOMPANY_TRANSVERSE_MODE || ($user->admin && ! $user->entity))) { $sql.= " WHERE g.entity IS NOT NULL"; @@ -110,11 +111,8 @@ else { $sql.= " WHERE g.entity IN (0,".$conf->entity.")"; } -if (! empty($search_group)) -{ - $sql .= " AND (g.nom LIKE '%".$db->escape($search_group)."%' OR g.note LIKE '%".$db->escape($search_group)."%')"; -} -if ($sall) $sql.= " AND (g.nom LIKE '%".$db->escape($sall)."%' OR g.note LIKE '%".$db->escape($sall)."%')"; +if (! empty($search_group)) natural_search(array("g.nom", "g.note"), $search_group); +if ($sall) $sql.= natural_search(array("g.nom", "g.note"), $sall); $sql.= " GROUP BY g.rowid, g.nom, g.note, g.entity, g.datec"; $sql.= $db->order($sortfield,$sortorder); @@ -132,6 +130,12 @@ if ($resql) $text = $langs->trans("ListOfGroups"); + $newcardbutton=''; + if ($user->rights->propal->creer) + { + $newcardbutton=''.$langs->trans('NewGroup').''; + } + print ''."\n"; if ($optioncss != '') print ''; print ''; @@ -142,7 +146,7 @@ if ($resql) print ''; print ''; - print_barre_liste($text, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, "", $num, $nbtotalofrecords, 'title_generic', 0, '', '', $limit); + print_barre_liste($text, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, "", $num, $nbtotalofrecords, 'title_generic', 0, $newcardbutton, '', $limit); if ($sall) { @@ -172,6 +176,7 @@ if ($resql) print_liste_field_titre("Entity",$_SERVER["PHP_SELF"],"g.entity",$param,"",'align="center"',$sortfield,$sortorder); } print_liste_field_titre("NbOfUsers",$_SERVER["PHP_SELF"],"nb",$param,"",'align="center"',$sortfield,$sortorder); + print_liste_field_titre("NbOfPermissions",$_SERVER["PHP_SELF"],"nbpermissions",$param,"",'align="center"',$sortfield,$sortorder); print_liste_field_titre("DateCreationShort",$_SERVER["PHP_SELF"],"g.datec",$param,"",'align="right"',$sortfield,$sortorder); print "\n"; @@ -200,6 +205,7 @@ if ($resql) print ''; } print ''; + print ''; print ''; print "\n"; $i++; diff --git a/htdocs/user/group/perms.php b/htdocs/user/group/perms.php index 740b106c728..ef5d286ca11 100644 --- a/htdocs/user/group/perms.php +++ b/htdocs/user/group/perms.php @@ -37,7 +37,7 @@ $action=GETPOST('action', 'alpha'); $confirm=GETPOST('confirm', 'alpha'); $module=GETPOST('module', 'alpha'); $rights=GETPOST('rights', 'int'); - +$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'groupperms'; // To manage different context of search // Defini si peux lire les permissions $canreadperms=($user->admin || $user->rights->user->user->lire); @@ -60,8 +60,7 @@ $object->fetch($id); $entity=$conf->entity; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('groupcard','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('groupperms','globalcard')); /** @@ -191,7 +190,7 @@ if ($object->id > 0) dol_print_error($db); } - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/hierarchy.php b/htdocs/user/hierarchy.php index ec85428bcab..930e911afaa 100644 --- a/htdocs/user/hierarchy.php +++ b/htdocs/user/hierarchy.php @@ -64,7 +64,7 @@ $arrayofcss=array('/includes/jquery/plugins/jquerytreeview/jquery.treeview.css') llxHeader('',$langs->trans("ListOfUsers"). ' ('.$langs->trans("HierarchicView").')','','',0,0,$arrayofjs,$arrayofcss); -print load_fiche_titre($langs->trans("ListOfUsers"). ' ('.$langs->trans("HierarchicView").')', ''); +print load_fiche_titre($langs->trans("ListOfUsers"). ' ('.$langs->trans("HierarchicView").')', ''); diff --git a/htdocs/user/home.php b/htdocs/user/home.php index 6600d262002..64162d47a8f 100644 --- a/htdocs/user/home.php +++ b/htdocs/user/home.php @@ -24,6 +24,8 @@ require '../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/user/class/usergroup.class.php'; +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userhome'; // To manage different context of search + if (! $user->rights->user->user->lire && ! $user->admin) { // Redirection vers la page de l'utilisateur @@ -46,6 +48,9 @@ if ($user->societe_id > 0) $socid = $user->societe_id; $companystatic = new Societe($db); $fuserstatic = new User($db); +// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array +$hookmanager->initHooks(array('userhome')); + /* * View @@ -89,7 +94,7 @@ print '
    '; */ $max=10; -$sql = "SELECT u.rowid, u.lastname, u.firstname, u.admin, u.login, u.fk_soc, u.datec, u.statut"; +$sql = "SELECT DISTINCT u.rowid, u.lastname, u.firstname, u.admin, u.login, u.fk_soc, u.datec, u.statut"; $sql.= ", u.entity"; $sql.= ", u.ldap_sid"; $sql.= ", u.photo"; @@ -101,13 +106,13 @@ $sql.= ", s.code_client"; $sql.= ", s.canvas"; $sql.= " FROM ".MAIN_DB_PREFIX."user as u"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON u.fk_soc = s.rowid"; -if (! empty($conf->multicompany->enabled) && $conf->entity == 1 && ($conf->global->MULTICOMPANY_TRANSVERSE_MODE || ($user->admin && ! $user->entity))) -{ - $sql.= " WHERE u.entity IS NOT NULL"; -} -else -{ - $sql.= " WHERE u.entity IN (0,".$conf->entity.")"; +// Add fields from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printUserListWhere',$parameters); // Note that $action and $object may have been modified by hook +if ($reshook > 0) { + $sql.=$hookmanager->resPrint; +} else { + $sql.= " WHERE u.entity IN (".getEntity('user').")"; } if (!empty($socid)) $sql.= " AND u.fk_soc = ".$socid; $sql.= $db->order("u.datec","DESC"); @@ -213,7 +218,7 @@ if ($canreadperms) $sql = "SELECT g.rowid, g.nom as name, g.note, g.entity, g.datec"; $sql.= " FROM ".MAIN_DB_PREFIX."usergroup as g"; - if(! empty($conf->multicompany->enabled) && $conf->entity == 1 && ($conf->global->MULTICOMPANY_TRANSVERSE_MODE || ($user->admin && ! $user->entity))) + if (! empty($conf->multicompany->enabled) && $conf->entity == 1 && ($conf->global->MULTICOMPANY_TRANSVERSE_MODE || ($user->admin && ! $user->entity))) { $sql.= " WHERE g.entity IS NOT NULL"; } diff --git a/htdocs/user/info.php b/htdocs/user/info.php index d20624063f2..9e349675625 100644 --- a/htdocs/user/info.php +++ b/htdocs/user/info.php @@ -71,7 +71,7 @@ dol_fiche_head($head, 'info', $title, -1, 'user'); $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object, 'id', $linkback, $user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/ldap.php b/htdocs/user/ldap.php index 1b5f7c77b78..79a1fd38bb5 100644 --- a/htdocs/user/ldap.php +++ b/htdocs/user/ldap.php @@ -33,6 +33,7 @@ $langs->load("companies"); $langs->load("ldap"); $id = GETPOST('id', 'int'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userldap'; // To manage different context of search // Security check $socid=0; @@ -46,8 +47,7 @@ $object->fetch($id, '', '', 1); $object->getrights(); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('usercard','userldap','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','userldap','globalcard')); /* @@ -100,7 +100,7 @@ dol_fiche_head($head, 'ldap', $title, 0, 'user'); $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/index.php b/htdocs/user/list.php similarity index 96% rename from htdocs/user/index.php rename to htdocs/user/list.php index 6f9ac1f638a..2020af40323 100644 --- a/htdocs/user/index.php +++ b/htdocs/user/list.php @@ -20,7 +20,7 @@ */ /** - * \file htdocs/user/index.php + * \file htdocs/user/list.php * \ingroup core * \brief Page of users */ @@ -34,6 +34,8 @@ $langs->load("users"); $langs->load("companies"); $langs->load('hrm'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userlist'; // To manage different context of search + // Security check (for external users) $socid=0; if ($user->societe_id > 0) @@ -54,11 +56,9 @@ $pagenext = $page + 1; if (! $sortfield) $sortfield="u.login"; if (! $sortorder) $sortorder="ASC"; -// Initialize context for list -$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userlist'; - // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$hookmanager->initHooks(array($contextpage)); +$object = new User($db); +$hookmanager->initHooks(array('userlist')); $extrafields = new ExtraFields($db); // fetch optionals attributes and labels @@ -175,7 +175,7 @@ $user2=new User($db); $buttonviewhierarchy='
    '; -$sql = "SELECT u.rowid, u.lastname, u.firstname, u.admin, u.fk_soc, u.login, u.email, u.accountancy_code, u.gender, u.employee, u.photo,"; +$sql = "SELECT DISTINCT u.rowid, u.lastname, u.firstname, u.admin, u.fk_soc, u.login, u.email, u.accountancy_code, u.gender, u.employee, u.photo,"; $sql.= " u.datelastlogin, u.datepreviouslogin,"; $sql.= " u.ldap_sid, u.statut, u.entity,"; $sql.= " u.tms as date_update, u.datec as date_creation,"; @@ -191,12 +191,12 @@ $sql.= " FROM ".MAIN_DB_PREFIX."user as u"; if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label)) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."user_extrafields as ef on (u.rowid = ef.fk_object)"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON u.fk_soc = s.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."user as u2 ON u.fk_user = u2.rowid"; -if(! empty($conf->multicompany->enabled) && $conf->entity == 1 && (! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) || (! empty($user->admin) && empty($user->entity)))) -{ - $sql.= " WHERE u.entity IS NOT NULL"; -} -else -{ +// Add fields from hooks +$parameters=array(); +$reshook=$hookmanager->executeHooks('printUserListWhere',$parameters); // Note that $action and $object may have been modified by hook +if ($reshook > 0) { + $sql.=$hookmanager->resPrint; +} else { $sql.= " WHERE u.entity IN (".getEntity('user').")"; } if ($socid > 0) $sql.= " AND u.fk_soc = ".$socid; @@ -271,6 +271,12 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; $text = $langs->trans("ListOfUsers"); +$newcardbutton=''; +if ($user->rights->propal->creer) +{ + $newcardbutton=''.$langs->trans('NewUser').''; +} + print '
    '."\n"; if ($optioncss != '') print ''; print ''; @@ -281,7 +287,9 @@ print ''; print ''; print ''; -print_barre_liste($text, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, "", $num, $nbtotalofrecords, 'title_generic', 0, '', '', $limit); + + +print_barre_liste($text, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, "", $num, $nbtotalofrecords, 'title_generic', 0, $newcardbutton, '', $limit); if ($sall) { @@ -406,7 +414,7 @@ if (! empty($arrayfields['u.datepreviouslogin']['checked'])) print_liste_field_t // Extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields -$parameters=array('arrayfields'=>$arrayfields); +$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder); $reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (! empty($arrayfields['u.datec']['checked'])) print_liste_field_titre("DateCreationShort",$_SERVER["PHP_SELF"],"u.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder); diff --git a/htdocs/user/note.php b/htdocs/user/note.php index 55dd46e1129..2f027f38504 100644 --- a/htdocs/user/note.php +++ b/htdocs/user/note.php @@ -29,6 +29,7 @@ require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; $id = GETPOST('id','int'); $action = GETPOST('action','aZ09'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'usernote'; // To manage different context of search $langs->load("companies"); $langs->load("members"); @@ -50,8 +51,7 @@ if ($user->id == $id) $feature2=''; // A user can always read its own card $result = restrictedArea($user, 'user', $id, 'user&user', $feature2); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('usercard','usernote','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','usernote','globalcard')); /* @@ -95,7 +95,7 @@ if ($id) $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/notify/card.php b/htdocs/user/notify/card.php index 04e524cef92..096567d728a 100644 --- a/htdocs/user/notify/card.php +++ b/htdocs/user/notify/card.php @@ -139,7 +139,7 @@ if ($result > 0) dol_fiche_head($head, 'notify', $langs->trans("User"), -1, 'user'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object, 'id', $linkback, $user->rights->user->user->lire || $user->admin, 'rowid', 'ref', '', '', 0, '', '', 0, ''); diff --git a/htdocs/user/param_ihm.php b/htdocs/user/param_ihm.php index 81e8996aff2..c797a11ccad 100644 --- a/htdocs/user/param_ihm.php +++ b/htdocs/user/param_ihm.php @@ -40,6 +40,7 @@ $canreaduser=($user->admin || $user->rights->user->user->lire); $id = GETPOST('id','int'); $action = GETPOST('action','alpha'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userihm'; // To manage different context of search if ($id) { @@ -79,8 +80,7 @@ $form = new Form($db); $formadmin=new FormAdmin($db); // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('usercard','userihm','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','userihm','globalcard')); /* @@ -162,7 +162,7 @@ if (! empty($conf->societe->enabled)) $tmparray['societe/index.php?mainmenu=comp if (! empty($conf->projet->enabled)) $tmparray['projet/index.php?mainmenu=project&leftmenu=']='ProjectsArea'; if (! empty($conf->holiday->enabled) || ! empty($conf->expensereport->enabled)) $tmparray['hrm/index.php?mainmenu=hrm&leftmenu=']='HRMArea'; // TODO Complete list with first level of menus if (! empty($conf->product->enabled) || ! empty($conf->service->enabled)) $tmparray['product/index.php?mainmenu=products&leftmenu=']='ProductsAndServicesArea'; -if (! empty($conf->propal->enabled) || ! empty($conf->commande->enabled) || ! empty($conf->fichinter->enabled) || ! empty($conf->contrat->enabled)) $tmparray['comm/index.php?mainmenu=commercial&leftmenu=']='CommercialArea'; +if (! empty($conf->propal->enabled) || ! empty($conf->commande->enabled) || ! empty($conf->ficheinter->enabled) || ! empty($conf->contrat->enabled)) $tmparray['comm/index.php?mainmenu=commercial&leftmenu=']='CommercialArea'; if (! empty($conf->compta->enabled) || ! empty($conf->accounting->enabled)) $tmparray['compta/index.php?mainmenu=compta&leftmenu=']='AccountancyTreasuryArea'; if (! empty($conf->adherent->enabled)) $tmparray['adherents/index.php?mainmenu=members&leftmenu=']='MembersArea'; if (! empty($conf->agenda->enabled)) $tmparray['comm/action/index.php?mainmenu=agenda&leftmenu=']='Agenda'; @@ -187,7 +187,7 @@ if ($action == 'edit') $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); @@ -302,7 +302,7 @@ else { dol_fiche_head($head, 'guisetup', $title, -1, 'user'); - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/user/perms.php b/htdocs/user/perms.php index b83a6831053..77277b54102 100644 --- a/htdocs/user/perms.php +++ b/htdocs/user/perms.php @@ -38,6 +38,7 @@ $action=GETPOST('action', 'alpha'); $confirm=GETPOST('confirm', 'alpha'); $module=GETPOST('module', 'alpha'); $rights=GETPOST('rights', 'int'); +$contextpage=GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'userperms'; // To manage different context of search if (! isset($id) || empty($id)) accessforbidden(); @@ -73,8 +74,7 @@ $object->getrights(); $entity=$conf->entity; // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context -$contextpage=array('usercard','userperms','globalcard'); -$hookmanager->initHooks($contextpage); +$hookmanager->initHooks(array('usercard','userperms','globalcard')); /** @@ -176,11 +176,9 @@ $db->commit(); // Lecture des droits utilisateurs $permsuser = array(); -$sql = "SELECT DISTINCT r.id, r.libelle, r.module"; -$sql.= " FROM ".MAIN_DB_PREFIX."rights_def as r,"; -$sql.= " ".MAIN_DB_PREFIX."user_rights as ur"; -$sql.= " WHERE ur.fk_id = r.id"; -$sql.= " AND ur.entity = ".$entity; +$sql = "SELECT DISTINCT ur.fk_id"; +$sql.= " FROM ".MAIN_DB_PREFIX."user_rights as ur"; +$sql.= " WHERE ur.entity = ".$entity; $sql.= " AND ur.fk_user = ".$object->id; dol_syslog("get user perms", LOG_DEBUG); @@ -192,7 +190,7 @@ if ($result) while ($i < $num) { $obj = $db->fetch_object($result); - array_push($permsuser,$obj->id); + array_push($permsuser,$obj->fk_id); $i++; } $db->free($result); @@ -205,12 +203,10 @@ else // Lecture des droits groupes $permsgroupbyentity = array(); -$sql = "SELECT DISTINCT r.id, r.libelle, r.module, gu.entity"; -$sql.= " FROM ".MAIN_DB_PREFIX."rights_def as r,"; -$sql.= " ".MAIN_DB_PREFIX."usergroup_rights as gr,"; +$sql = "SELECT DISTINCT gr.fk_id, gu.entity"; +$sql.= " FROM ".MAIN_DB_PREFIX."usergroup_rights as gr,"; $sql.= " ".MAIN_DB_PREFIX."usergroup_user as gu"; -$sql.= " WHERE gr.fk_id = r.id"; -$sql.= " AND gr.entity = ".$entity; +$sql.= " WHERE gr.entity = ".$entity; $sql.= " AND gr.fk_usergroup = gu.fk_usergroup"; $sql.= " AND gu.fk_user = ".$object->id; @@ -225,7 +221,7 @@ if ($result) $obj = $db->fetch_object($result); if (! isset($permsgroupbyentity[$obj->entity])) $permsgroupbyentity[$obj->entity] = array(); - array_push($permsgroupbyentity[$obj->entity], $obj->id); + array_push($permsgroupbyentity[$obj->entity], $obj->fk_id); $i++; } $db->free($result); @@ -243,7 +239,7 @@ else $linkback = ''; if ($user->rights->user->user->lire || $user->admin) { - $linkback = ''.$langs->trans("BackToList").''; + $linkback = ''.$langs->trans("BackToList").''; } dol_banner_tab($object,'id',$linkback,$user->rights->user->user->lire || $user->admin); diff --git a/htdocs/variants/admin/admin.php b/htdocs/variants/admin/admin.php index f3752469538..57d4640633e 100644 --- a/htdocs/variants/admin/admin.php +++ b/htdocs/variants/admin/admin.php @@ -48,7 +48,7 @@ if ($_POST) { $title = $langs->trans('ModuleSetup').' '.$langs->trans('ProductAttributes'); llxHeader('', $title); -$linkback=''.$langs->trans("BackToModuleList").''; +$linkback=''.$langs->trans("BackToModuleList").''; print load_fiche_titre($title,$linkback,'title_setup'); dol_fiche_head(array(), 'general', $tab, 0, 'product'); diff --git a/htdocs/variants/class/ProductAttributeValue.class.php b/htdocs/variants/class/ProductAttributeValue.class.php index 9c9d9929f46..9b455851507 100644 --- a/htdocs/variants/class/ProductAttributeValue.class.php +++ b/htdocs/variants/class/ProductAttributeValue.class.php @@ -140,15 +140,16 @@ class ProductAttributeValue /** * Creates a value for a product attribute * - * @return int <0 KO >0 OK + * @param User $user Object user + * @return int <0 KO >0 OK */ - public function create() + public function create(User $user) { if (!$this->fk_product_attribute) { return -1; } - //Ref must be uppercase + // Ref must be uppercase $this->ref = strtoupper($this->ref); $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_value (fk_product_attribute, ref, value, entity) diff --git a/htdocs/variants/combinations.php b/htdocs/variants/combinations.php index 3101d44ecf5..84f3b7fa91c 100644 --- a/htdocs/variants/combinations.php +++ b/htdocs/variants/combinations.php @@ -346,12 +346,12 @@ if (! empty($id) || ! empty($ref)) foreach ($prodattr_all as $each) { $prodattr_alljson[$each->id] = $each; } - + ?>
    '.$langs->trans("Name").''; + print ''; print "
    '.$mc->label.''.$obj->nb.''.$obj->nbpermissions.''.dol_print_date($db->jdate($obj->datec),"dayhour").'